From 22ded872501348c63d4a308475740278863d544d Mon Sep 17 00:00:00 2001 From: crisleo94 Date: Sat, 20 Jun 2026 13:33:44 -0500 Subject: [PATCH] feat: implement mobile reader - implement mobile epub reader --- docs/mobile/010-mobile-reader.md | 150 ++ mobile/.env.example | 10 + mobile/App.tsx | 20 +- mobile/app.json | 1 - mobile/eas.json | 42 + mobile/metro.config.js | 25 + mobile/package.json | 10 +- mobile/src/api/annotations.ts | 68 +- mobile/src/api/books.ts | 21 +- mobile/src/api/client.ts | 32 +- mobile/src/api/ebooks.ts | 53 +- mobile/src/api/index.ts | 4 - mobile/src/api/reader.ts | 75 + .../src/components/reader/BookmarksModal.tsx | 147 ++ .../src/components/reader/EpubReaderView.tsx | 436 ++++++ .../components/reader/ReaderBottomSheet.tsx | 92 ++ .../src/components/reader/ReaderToolbar.tsx | 152 ++ .../reader/ReadingSettingsModal.tsx | 249 +++ mobile/src/components/reader/TocModal.tsx | 111 ++ mobile/src/hooks/index.ts | 2 - mobile/src/hooks/useAsyncData.ts | 33 - mobile/src/hooks/useReadingSettings.ts | 82 + mobile/src/navigation/AppNavigator.tsx | 45 - mobile/src/navigation/AppStack.tsx | 36 + mobile/src/navigation/AuthNavigator.tsx | 2 +- mobile/src/navigation/MainTabs.tsx | 55 - mobile/src/navigation/RootNavigator.tsx | 4 +- mobile/src/screens/AuthFormError.tsx | 11 + mobile/src/screens/BookDetailScreen.tsx | 226 +++ mobile/src/screens/LibraryScreen.tsx | 106 +- mobile/src/screens/LoginScreen.tsx | 132 +- mobile/src/screens/ReaderScreen.tsx | 133 ++ mobile/src/screens/RegisterScreen.tsx | 146 +- mobile/src/screens/SettingsScreen.tsx | 6 +- mobile/src/screens/authStyles.ts | 88 ++ mobile/src/screens/authValidation.ts | 15 + mobile/src/types/index.ts | 41 +- mobile/src/types/reader.ts | 32 + mobile/src/utils/epubTheme.ts | 66 + mobile/tsconfig.json | 3 +- yarn.lock | 1333 ++++++++++------- 41 files changed, 3184 insertions(+), 1111 deletions(-) create mode 100644 docs/mobile/010-mobile-reader.md create mode 100644 mobile/.env.example create mode 100644 mobile/eas.json create mode 100644 mobile/metro.config.js delete mode 100644 mobile/src/api/index.ts create mode 100644 mobile/src/api/reader.ts create mode 100644 mobile/src/components/reader/BookmarksModal.tsx create mode 100644 mobile/src/components/reader/EpubReaderView.tsx create mode 100644 mobile/src/components/reader/ReaderBottomSheet.tsx create mode 100644 mobile/src/components/reader/ReaderToolbar.tsx create mode 100644 mobile/src/components/reader/ReadingSettingsModal.tsx create mode 100644 mobile/src/components/reader/TocModal.tsx delete mode 100644 mobile/src/hooks/index.ts delete mode 100644 mobile/src/hooks/useAsyncData.ts create mode 100644 mobile/src/hooks/useReadingSettings.ts delete mode 100644 mobile/src/navigation/AppNavigator.tsx create mode 100644 mobile/src/navigation/AppStack.tsx delete mode 100644 mobile/src/navigation/MainTabs.tsx create mode 100644 mobile/src/screens/AuthFormError.tsx create mode 100644 mobile/src/screens/BookDetailScreen.tsx create mode 100644 mobile/src/screens/ReaderScreen.tsx create mode 100644 mobile/src/screens/authStyles.ts create mode 100644 mobile/src/screens/authValidation.ts create mode 100644 mobile/src/types/reader.ts create mode 100644 mobile/src/utils/epubTheme.ts diff --git a/docs/mobile/010-mobile-reader.md b/docs/mobile/010-mobile-reader.md new file mode 100644 index 0000000..0e8c069 --- /dev/null +++ b/docs/mobile/010-mobile-reader.md @@ -0,0 +1,150 @@ +# 010 — Mobile EPUB Reader + +**Status:** Implemented +**Created:** 2026-06-04 + +## Objective + +Mirror the web reading experience (`frontend/src/pages/ReadingPage.tsx` and +`frontend/src/components/reader/*`) inside the Expo app so users can open their +uploaded library, read EPUBs with persisted progress and typography settings, +and manage bookmarks/highlights — reusing the same Django REST API as the web +client. + +The web EPUB renderer (`react-reader` / epub.js) is DOM-only, so mobile renders +EPUBs with `@epubjs-react-native/core`, which runs epub.js inside a +`react-native-webview`. This keeps behavior (CFI locations, themes, font +controls, TOC, annotations) close to web while staying inside the managed Expo +workflow. + +## Scope + +### Mirrored from web + +- EPUB rendering with swipe pagination (`flow: "paginated"`). +- Resume position and debounced progress save (CFI + percentage). +- Table of contents drawer with jump-to-chapter. +- Reading settings: theme presets (light/sepia/paper/dark), font family, font + size, line spacing — persisted to `/api/reader/settings/`. +- Bookmarks: bookmark the current page, list/jump/delete, and create highlights + from a text selection. Highlights are re-applied on open (best-effort). + +### Deferred (not in this pass) + +- PDF reading. PDF books show a placeholder pointing to the web reader. +- Brightness and orientation-lock controls. +- Per-highlight color picker (highlights use a single default color). +- App-wide internationalization (web uses `react-i18n-lite`). + +## Architecture + +``` +LibraryScreen (EBook list) + | navigate("BookDetail", { ebookId }) + v +BookDetailScreen ----------------> ReaderScreen ({ ebookId }) + | + GET /api/books/ebooks/:id/ (format guard) + | + epub? --------------------- pdf? -> placeholder + | + EpubReaderView (ReaderProvider) + | + expo-file-system downloadAsync(file/, Bearer token) -> file:// uri + | + + | + onLocationChange -> debounce 800ms -> PATCH progress/ + onSelected -> POST bookmarks/ (+ highlight annotation) + useReader().toc -> goToLocation(href) + settings change -> changeTheme / changeFontSize / changeFontFamily + + PATCH /api/reader/settings/ (debounced) +``` + +The ebook file is downloaded to the app cache with an `Authorization` header +(the `/file/` endpoint is JWT-protected) and the local `file://` URI is handed +to the renderer — mirroring how the web client downloads a blob rather than +using a public/signed URL. + +## Mobile changes + +| File | Role | +|------|------| +| `mobile/src/api/client.ts` | Adds `apiClient`, `saveTokens`, `loadTokens`, `getApiBaseUrl` | +| `mobile/src/api/ebooks.ts` | `/api/books/ebooks/` list/detail/toc + `getFileUrl(id)` | +| `mobile/src/api/reader.ts` | Reader settings + per-book progress (mirrors web `api/reader.ts`) | +| `mobile/src/api/annotations.ts` | Bookmarks CRUD against `/api/annotations/bookmarks/` | +| `mobile/src/types/reader.ts` | `ReadingSettings`, `ReadingProgress` (full reader shapes) | +| `mobile/src/types/index.ts` | `AppStackParamList`, `Bookmark`, `CreateMarkerPayload` | +| `mobile/src/hooks/useReadingSettings.ts` | Loads + debounced-saves reader settings | +| `mobile/src/utils/epubTheme.ts` | Font stacks, theme palettes, `buildEpubTheme()` | +| `mobile/src/navigation/AppStack.tsx` | Native stack: Tabs / BookDetail / Reader | +| `mobile/src/screens/LibraryScreen.tsx` | Lists user EBooks (`ebooksApi.list`) | +| `mobile/src/screens/BookDetailScreen.tsx` | Metadata + start/resume button | +| `mobile/src/screens/ReaderScreen.tsx` | Format guard: EPUB view vs PDF placeholder | +| `mobile/src/components/reader/EpubReaderView.tsx` | Reader, progress, bookmarks, settings wiring | +| `mobile/src/components/reader/ReaderToolbar.tsx` | Title, chapter, progress bar, action buttons | +| `mobile/src/components/reader/TocModal.tsx` | Table of contents sheet | +| `mobile/src/components/reader/ReadingSettingsModal.tsx` | Theme/font/size/spacing controls | +| `mobile/src/components/reader/BookmarksModal.tsx` | Bookmarks & highlights list | +| `mobile/App.tsx` | Wraps the tree in `GestureHandlerRootView` | + +Removed unused scaffolding: `mobile/src/navigation/AppNavigator.tsx`, +`mobile/src/navigation/MainTabs.tsx`. + +## API contracts (consumed) + +| Method | Path | Purpose | +|--------|------|---------| +| GET | `/api/books/ebooks/` | User library list | +| GET | `/api/books/ebooks/:id/` | EBook detail (format, progress, cover) | +| GET | `/api/books/ebooks/:id/file/` | Stream EPUB bytes (JWT, owner) | +| GET/PATCH | `/api/books/ebooks/:id/progress/` | Reading progress (`current_position`, `last_page`, `epub_location`) | +| GET/PATCH | `/api/reader/settings/` | Reader typography/theme settings | +| GET/POST/DELETE | `/api/annotations/bookmarks/` | Bookmarks & highlights (`ebook`, `epub_cfi`, `chapter_index`, ...) | + +## Settings mapping (web -> mobile) + +| Reader setting | Web (epub.js) | Mobile (`@epubjs-react-native/core`) | +|----------------|---------------|--------------------------------------| +| `theme` / colors | `themes.register/select` | `changeTheme(buildEpubTheme())` + `defaultTheme` | +| `font_size` | `themes.fontSize` | `changeFontSize("Npx")` | +| `font_family` | body font-family | `changeFontFamily(stack)` | +| `line_height` | body line-height | `buildEpubTheme()` CSS rule | +| `margin_width` | gap-based padding | not applied (deferred) | +| `brightness` / `orientation_lock` | applied on web | deferred | + +## Dependencies added + +- `@epubjs-react-native/core@1.4.7` +- `@epubjs-react-native/expo-file-system@1.1.4` +- `react-native-webview@13.12.5` + +(`react-native-gesture-handler`, `react-native-reanimated`, and +`expo-file-system` were already present.) + +## Configuration + +| Env var | Purpose | +|---------|---------| +| `EXPO_PUBLIC_API_URL` | Backend base URL (e.g. `http://10.0.2.2:8000` on Android emulator, LAN IP on a device) | + +## Compatibility notes + +- The Expo file-system adapter (`@epubjs-react-native/expo-file-system`) depends + only on `expo-file-system`, so the reader runs in Expo Go. (The library's + bare adapter pulls native `@dr.pogodin/react-native-fs`; that path is not + used here.) +- React 19 / Expo SDK 52 may surface peer-dependency warnings for the + `@epubjs-react-native/*` packages. + +## Verification + +- [ ] Log in; Library lists the user's uploaded EBooks with covers/progress. +- [ ] Open an EPUB; it renders and paginates by swipe. +- [ ] Reopen a book; it resumes at the last position. +- [ ] Change theme/font/size/spacing; the page updates and persists across reopen. +- [ ] Open the TOC and jump to a chapter. +- [ ] Bookmark the current page; it appears in the bookmarks list and can be re-opened/deleted. +- [ ] Select text to create a highlight; it persists and re-renders on reopen. +- [ ] Open a PDF book; the placeholder is shown instead of a crash. diff --git a/mobile/.env.example b/mobile/.env.example new file mode 100644 index 0000000..bfc9c40 --- /dev/null +++ b/mobile/.env.example @@ -0,0 +1,10 @@ +# Mobile (Expo) environment (example – never commit real secrets) +# Base URL of the Django backend. Must be reachable from the device/emulator. +# - Android emulator: http://10.0.2.2:8000 +# - iOS simulator: http://localhost:8000 +# - Physical device: http://:8000 +EXPO_PUBLIC_API_URL=http://10.0.2.2:8000 + +# EAS Build: set per profile in mobile/eas.json or via: +# eas secret:create --name EXPO_PUBLIC_API_URL --value https://your-api.example.com +# Run all eas commands from mobile/ (not repo root). diff --git a/mobile/App.tsx b/mobile/App.tsx index 9b3eeea..c3a79d6 100644 --- a/mobile/App.tsx +++ b/mobile/App.tsx @@ -1,4 +1,8 @@ import React from "react"; +// Peer dependencies for @epubjs-react-native/core and gesture-handler. +import "react-native-webview"; +import "react-native-reanimated"; +import { GestureHandlerRootView } from "react-native-gesture-handler"; import { NavigationContainer } from "@react-navigation/native"; import { StatusBar } from "expo-status-bar"; import { AuthProvider } from "./src/context/AuthContext"; @@ -6,11 +10,13 @@ import { RootNavigator } from "./src/navigation/RootNavigator"; export default function App() { return ( - - - - - - + + + + + + + + ); -} \ No newline at end of file +} diff --git a/mobile/app.json b/mobile/app.json index 7ccd6b0..ef88074 100644 --- a/mobile/app.json +++ b/mobile/app.json @@ -21,7 +21,6 @@ "package": "com.cloudreader.app" }, "plugins": [ - "expo-document-picker", "expo-file-system" ] } diff --git a/mobile/eas.json b/mobile/eas.json new file mode 100644 index 0000000..5c23e85 --- /dev/null +++ b/mobile/eas.json @@ -0,0 +1,42 @@ +{ + "cli": { + "version": ">= 13.0.0", + "appVersionSource": "remote" + }, + "build": { + "base": { + "node": "20.18.0", + "env": { + "EXPO_PUBLIC_API_URL": "https://api.example.com" + } + }, + "development": { + "extends": "base", + "developmentClient": true, + "distribution": "internal", + "ios": { + "simulator": true + }, + "android": { + "buildType": "apk" + }, + "env": { + "EXPO_PUBLIC_API_URL": "http://10.0.2.2:8000" + } + }, + "preview": { + "extends": "base", + "distribution": "internal", + "android": { + "buildType": "apk" + } + }, + "production": { + "extends": "base", + "autoIncrement": true + } + }, + "submit": { + "production": {} + } +} diff --git a/mobile/metro.config.js b/mobile/metro.config.js new file mode 100644 index 0000000..cd02e9a --- /dev/null +++ b/mobile/metro.config.js @@ -0,0 +1,25 @@ +const { getDefaultConfig } = require("expo/metro-config"); +const path = require("path"); + +const projectRoot = __dirname; +const monorepoRoot = path.resolve(projectRoot, ".."); + +/** + * Expo SDK 52+ configures Metro for Yarn/npm/pnpm workspaces automatically + * when using expo/metro-config. This file keeps an explicit monorepo root so + * @cloud-reader/shared (packages/shared) resolves reliably in dev and on EAS. + * + * If you previously added manual watchFolders/nodeModulesPaths and things + * work, prefer this minimal config. After changes: npx expo start --clear + * + * @see https://docs.expo.dev/guides/monorepos/ + */ +const config = getDefaultConfig(projectRoot); + +config.watchFolders = [monorepoRoot]; +config.resolver.nodeModulesPaths = [ + path.resolve(projectRoot, "node_modules"), + path.resolve(monorepoRoot, "node_modules"), +]; + +module.exports = config; diff --git a/mobile/package.json b/mobile/package.json index 231691c..f52bbba 100644 --- a/mobile/package.json +++ b/mobile/package.json @@ -8,7 +8,11 @@ "android": "expo start --android", "ios": "expo start --ios", "web": "expo start --web", - "lint": "eslint ." + "lint": "eslint .", + "eas:init": "eas init", + "eas:build:dev": "eas build --profile development --platform all", + "eas:build:preview": "eas build --profile preview --platform all", + "eas:build:prod": "eas build --profile production --platform all" }, "dependencies": { "expo": "~52.0.0", @@ -22,10 +26,12 @@ "@react-navigation/bottom-tabs": "^7.0.0", "axios": "^1.7.9", "@react-native-async-storage/async-storage": "2.1.0", - "expo-document-picker": "~13.0.0", + "@epubjs-react-native/core": "1.4.7", + "@epubjs-react-native/expo-file-system": "1.1.4", "expo-file-system": "~18.0.0", "react-native-gesture-handler": "~2.20.0", "react-native-reanimated": "~3.16.0", + "react-native-webview": "13.12.5", "@cloud-reader/shared": "*" }, "devDependencies": { diff --git a/mobile/src/api/annotations.ts b/mobile/src/api/annotations.ts index 434fb89..ef2d259 100644 --- a/mobile/src/api/annotations.ts +++ b/mobile/src/api/annotations.ts @@ -1,55 +1,31 @@ import api from "./client"; -import type { - Bookmark, - Note, - CreateBookmarkPayload, - CreateNotePayload, - PaginatedResponse, -} from "@cloud-reader/shared"; +import type { PaginatedResponse } from "@cloud-reader/shared"; +import type { Bookmark, CreateMarkerPayload } from "../types"; -export function fetchBookmarks( - bookId?: string, +export async function fetchBookmarks( + ebookId?: string | number, ): Promise> { - const params = bookId ? { book: bookId } : {}; - return api - .get>("/api/annotations/bookmarks/", { params }) - .then((res) => res.data); + const params: Record = {}; + if (ebookId != null && ebookId !== "") { + params.ebook = String(ebookId); + } + const { data } = await api.get>( + "/api/annotations/bookmarks/", + { params }, + ); + return data; } -export function createBookmark( - payload: CreateBookmarkPayload, +export async function createMarker( + payload: CreateMarkerPayload, ): Promise { - return api - .post("/api/annotations/bookmarks/", payload) - .then((res) => res.data); + const { data } = await api.post( + "/api/annotations/bookmarks/", + payload, + ); + return data; } -export function deleteBookmark(id: string): Promise { - return api.delete(`/api/annotations/bookmarks/${id}/`).then(() => {}); +export async function deleteBookmark(id: string): Promise { + await api.delete(`/api/annotations/bookmarks/${id}/`); } - -export function fetchNotes(bookId?: string): Promise> { - const params = bookId ? { book: bookId } : {}; - return api - .get>("/api/annotations/notes/", { params }) - .then((res) => res.data); -} - -export function createNote(payload: CreateNotePayload): Promise { - return api - .post("/api/annotations/notes/", payload) - .then((res) => res.data); -} - -export function updateNote( - id: string, - content: string, -): Promise { - return api - .patch(`/api/annotations/notes/${id}/`, { content }) - .then((res) => res.data); -} - -export function deleteNote(id: string): Promise { - return api.delete(`/api/annotations/notes/${id}/`).then(() => {}); -} \ No newline at end of file diff --git a/mobile/src/api/books.ts b/mobile/src/api/books.ts index 9045b61..f273356 100644 --- a/mobile/src/api/books.ts +++ b/mobile/src/api/books.ts @@ -1,31 +1,12 @@ import api from "./client"; import type { Book, PaginatedResponse } from "@cloud-reader/shared"; -export function fetchBooks( - page = 1, - pageSize = 20, -): Promise> { - return api - .get>("/api/books/", { - params: { page, page_size: pageSize }, - }) - .then((res) => res.data); -} - -export function fetchBook(id: string): Promise { - return api.get(`/api/books/${id}/`).then((res) => res.data); -} - export function searchBooks( query: string, ): Promise> { return api - .get>("/api/books/search/", { + .get>("/api/books/", { params: { q: query }, }) .then((res) => res.data); } - -export function deleteBook(id: string): Promise { - return api.delete(`/api/books/${id}/`).then(() => {}); -} \ No newline at end of file diff --git a/mobile/src/api/client.ts b/mobile/src/api/client.ts index f1963dc..f6e832d 100644 --- a/mobile/src/api/client.ts +++ b/mobile/src/api/client.ts @@ -1,5 +1,6 @@ import axios, { type AxiosError, type InternalAxiosRequestConfig } from "axios"; import AsyncStorage from "@react-native-async-storage/async-storage"; +import type { TokenResponse } from "@cloud-reader/shared"; const STORAGE_KEYS = { ACCESS_TOKEN: "access_token", @@ -39,6 +40,26 @@ async function clearTokens(): Promise { ]); } +async function saveTokens(tokens: TokenResponse): Promise { + await setTokens(tokens.access, tokens.refresh); +} + +async function loadTokens(): Promise { + const [access, refresh] = await Promise.all([ + getAccessToken(), + getRefreshToken(), + ]); + if (!access || !refresh) { + return null; + } + return { access, refresh }; +} + +/** Absolute base URL used for direct (non-axios) requests like file downloads. */ +function getApiBaseUrl(): string { + return api.defaults.baseURL ?? ""; +} + // ── Request interceptor ───────────────────────────────────────────── api.interceptors.request.use(async (config: InternalAxiosRequestConfig) => { @@ -136,5 +157,14 @@ api.interceptors.response.use( }, ); -export { getAccessToken, getRefreshToken, setTokens, clearTokens }; +const apiClient = api; + +export { + apiClient, + getAccessToken, + saveTokens, + loadTokens, + clearTokens, + getApiBaseUrl, +}; export default api; \ No newline at end of file diff --git a/mobile/src/api/ebooks.ts b/mobile/src/api/ebooks.ts index 26b5d50..8e1e504 100644 --- a/mobile/src/api/ebooks.ts +++ b/mobile/src/api/ebooks.ts @@ -1,54 +1,35 @@ -import { apiClient } from "./client"; +import { apiClient, getApiBaseUrl } from "./client"; import type { EBookListItem, EBookDetail, - ReadingProgress, - ReadingSettings, TocResponse, - ContentResponse, PaginatedResponse, } from "@cloud-reader/shared"; export const ebooksApi = { - /** List uploaded e-books */ + /** List the authenticated user's uploaded e-books */ list() { - return apiClient.get>("/api/ebooks/"); + return apiClient.get>( + "/api/books/ebooks/", + ); }, - /** Get e-book detail */ + /** Get e-book detail (metadata, format, progress) */ get(id: number) { - return apiClient.get(`/api/ebooks/${id}/`); + return apiClient.get(`/api/books/ebooks/${id}/`); }, - /** Get table of contents */ + /** Get the table of contents (used as a fallback for chapter navigation) */ getToc(id: number) { - return apiClient.get(`/api/ebooks/${id}/toc/`); + return apiClient.get(`/api/books/ebooks/${id}/toc/`); }, - /** Get page content */ - getContent(id: number, page: number) { - return apiClient.get( - `/api/ebooks/${id}/content/?page=${page}`, - ); + /** + * Absolute URL to stream the raw ebook file. The endpoint requires JWT auth, + * so callers download it with an Authorization header (e.g. via + * expo-file-system) rather than handing the URL to a renderer directly. + */ + getFileUrl(id: number): string { + return `${getApiBaseUrl()}/api/books/ebooks/${id}/file/`; }, - - /** Update reading progress */ - updateProgress(id: number, data: Partial) { - return apiClient.patch( - `/api/ebooks/${id}/progress/`, - data, - ); - }, - - /** Get or update reading settings */ - getSettings(id: number) { - return apiClient.get(`/api/ebooks/${id}/settings/`); - }, - - updateSettings(id: number, data: Partial) { - return apiClient.patch( - `/api/ebooks/${id}/settings/`, - data, - ); - }, -}; \ No newline at end of file +}; diff --git a/mobile/src/api/index.ts b/mobile/src/api/index.ts deleted file mode 100644 index 3ba8646..0000000 --- a/mobile/src/api/index.ts +++ /dev/null @@ -1,4 +0,0 @@ -export { apiClient, saveTokens, loadTokens, clearTokens } from "./client"; -export { booksApi } from "./books"; -export { ebooksApi } from "./ebooks"; -export { annotationsApi } from "./annotations"; \ No newline at end of file diff --git a/mobile/src/api/reader.ts b/mobile/src/api/reader.ts new file mode 100644 index 0000000..99fd225 --- /dev/null +++ b/mobile/src/api/reader.ts @@ -0,0 +1,75 @@ +/** + * Reader API client — reading settings and per-book progress. + * Mirrors frontend/src/api/reader.ts. Uses the shared axios client so the + * JWT access token is attached automatically. + */ + +import api from "./client"; +import type { ReadingProgress, ReadingSettings } from "../types/reader"; + +export async function getReadingSettings(): Promise { + const { data } = await api.get("/api/reader/settings/"); + return data; +} + +export async function updateReadingSettings( + settings: Partial, +): Promise { + const { data } = await api.patch( + "/api/reader/settings/", + settings, + ); + return data; +} + +interface ProgressWire { + current_position: number; + last_page: number; + epub_location?: string; + updated_at?: string; +} + +function toReadingProgress(bookId: number, data: ProgressWire): ReadingProgress { + return { + id: bookId, + book: bookId, + current_chapter: data.last_page || 1, + current_position: data.current_position ?? 0, + percentage: data.current_position ?? 0, + epub_location: data.epub_location ?? "", + updated_at: data.updated_at ?? "", + }; +} + +export async function getReadingProgress( + bookId: number, +): Promise { + const { data } = await api.get( + `/api/books/ebooks/${bookId}/progress/`, + ); + return toReadingProgress(bookId, data); +} + +export async function updateReadingProgress( + bookId: number, + progress: Partial, +): Promise { + const body: Record = {}; + if ( + progress.percentage !== undefined || + progress.current_position !== undefined + ) { + body.current_position = progress.percentage ?? progress.current_position ?? 0; + } + if (progress.current_chapter !== undefined) { + body.last_page = progress.current_chapter; + } + if (progress.epub_location !== undefined) { + body.epub_location = progress.epub_location; + } + const { data } = await api.patch( + `/api/books/ebooks/${bookId}/progress/`, + body, + ); + return toReadingProgress(bookId, data); +} diff --git a/mobile/src/components/reader/BookmarksModal.tsx b/mobile/src/components/reader/BookmarksModal.tsx new file mode 100644 index 0000000..ad79add --- /dev/null +++ b/mobile/src/components/reader/BookmarksModal.tsx @@ -0,0 +1,147 @@ +import { type ReactNode } from "react"; +import { + Text, + FlatList, + TouchableOpacity, + StyleSheet, + View, +} from "react-native"; +import { resolvePalette } from "../../utils/epubTheme"; +import type { ReadingSettings } from "../../types/reader"; +import type { Bookmark } from "../../types"; +import { ReaderBottomSheet } from "./ReaderBottomSheet"; + +interface BookmarksModalProps { + visible: boolean; + bookmarks: Bookmark[]; + settings: ReadingSettings; + onSelect: (bookmark: Bookmark) => void; + onDelete: (bookmark: Bookmark) => void; + onClose: () => void; +} + +export function BookmarksModal({ + visible, + bookmarks, + settings, + onSelect, + onDelete, + onClose, +}: BookmarksModalProps): ReactNode { + const palette = resolvePalette(settings); + + return ( + + item.id} + ListEmptyComponent={ + + No bookmarks yet. Tap the star to bookmark a page, or select text to + highlight it. + + } + renderItem={({ item }) => ( + + onSelect(item)} + > + {item.highlight_color ? ( + + ) : ( + + )} + + {item.chapter_title ? ( + + {item.chapter_title} + + ) : null} + + {item.content || item.location_text || "Bookmarked page"} + + + + onDelete(item)} + hitSlop={8} + style={styles.deleteButton} + > + Delete + + + )} + /> + + ); +} + +const styles = StyleSheet.create({ + row: { + flexDirection: "row", + alignItems: "center", + paddingVertical: 12, + paddingHorizontal: 16, + borderTopWidth: StyleSheet.hairlineWidth, + borderTopColor: "rgba(127,127,127,0.2)", + }, + rowMain: { + flex: 1, + flexDirection: "row", + alignItems: "center", + }, + rowTextWrap: { + flex: 1, + marginLeft: 10, + }, + star: { + fontSize: 16, + color: "#4f8ef7", + }, + dot: { + width: 14, + height: 14, + borderRadius: 7, + }, + chapter: { + fontSize: 12, + opacity: 0.7, + marginBottom: 2, + }, + excerpt: { + fontSize: 14, + }, + deleteButton: { + marginLeft: 12, + paddingHorizontal: 10, + paddingVertical: 6, + }, + deleteText: { + color: "#ff453a", + fontSize: 13, + fontWeight: "600", + }, + empty: { + padding: 24, + textAlign: "center", + opacity: 0.7, + }, +}); diff --git a/mobile/src/components/reader/EpubReaderView.tsx b/mobile/src/components/reader/EpubReaderView.tsx new file mode 100644 index 0000000..85fa1ad --- /dev/null +++ b/mobile/src/components/reader/EpubReaderView.tsx @@ -0,0 +1,436 @@ +import { useCallback, useEffect, useRef, useState, type ReactNode } from "react"; +import { + View, + Text, + ActivityIndicator, + StyleSheet, + type LayoutChangeEvent, +} from "react-native"; +import { SafeAreaView } from "react-native-safe-area-context"; +import { Reader, ReaderProvider, useReader } from "@epubjs-react-native/core"; +import { useFileSystem } from "@epubjs-react-native/expo-file-system"; +import * as FileSystem from "expo-file-system"; +import type { EBookDetail } from "@cloud-reader/shared"; +import { getAccessToken } from "../../api/client"; +import { ebooksApi } from "../../api/ebooks"; +import { getReadingProgress, updateReadingProgress } from "../../api/reader"; +import { + fetchBookmarks, + createMarker, + deleteBookmark, +} from "../../api/annotations"; +import { useReadingSettings } from "../../hooks/useReadingSettings"; +import { + buildEpubTheme, + FONT_STACKS, + resolvePalette, +} from "../../utils/epubTheme"; +import type { Bookmark } from "../../types"; +import { ReaderToolbar } from "./ReaderToolbar"; +import { TocModal, type TocItem } from "./TocModal"; +import { ReadingSettingsModal } from "./ReadingSettingsModal"; +import { BookmarksModal } from "./BookmarksModal"; + +const HIGHLIGHT_COLOR = "#ffd54a"; +const PROGRESS_SAVE_MS = 800; + +/** Loose view of the epubjs-react-native reader API we rely on. */ +interface ReaderApi { + goToLocation?: (target: string) => void; + changeTheme?: (theme: Record>) => void; + changeFontSize?: (size: string) => void; + changeFontFamily?: (font: string) => void; + addAnnotation?: ( + type: string, + cfiRange: string, + data?: unknown, + styles?: Record, + ) => void; + removeAnnotationByCfi?: (cfi: string) => void; + removeSelection?: () => void; + toc?: TocItem[]; +} + +interface EpubLocation { + start?: { cfi?: string }; +} + +interface EpubSection { + label?: string; + index?: number; +} + +interface EpubReaderViewProps { + book: EBookDetail; + ebookId: number; + onClose: () => void; +} + +export function EpubReaderView(props: EpubReaderViewProps): ReactNode { + return ( + + + + ); +} + +function EpubReaderInner({ + book, + ebookId, + onClose, +}: EpubReaderViewProps): ReactNode { + const reader = useReader() as unknown as ReaderApi; + const { settings, updateSettings } = useReadingSettings(); + + const [src, setSrc] = useState(null); + const [initialLocation, setInitialLocation] = useState(); + const [fileError, setFileError] = useState(false); + const [ready, setReady] = useState(false); + const [size, setSize] = useState<{ width: number; height: number } | null>( + null, + ); + + const [progressPct, setProgressPct] = useState( + Math.round(book.progress?.current_position ?? 0), + ); + const [chapterTitle, setChapterTitle] = useState(""); + const [currentCfi, setCurrentCfi] = useState(""); + const [bookmarks, setBookmarks] = useState([]); + + const [tocOpen, setTocOpen] = useState(false); + const [settingsOpen, setSettingsOpen] = useState(false); + const [bookmarksOpen, setBookmarksOpen] = useState(false); + + const currentCfiRef = useRef(""); + const progressPctRef = useRef(progressPct); + const sectionRef = useRef<{ index: number; label: string }>({ + index: 0, + label: "", + }); + const bookmarksRef = useRef([]); + const saveTimerRef = useRef | null>(null); + + useEffect(() => { + bookmarksRef.current = bookmarks; + }, [bookmarks]); + + // Download the EPUB with auth, and resolve the saved resume location. + useEffect(() => { + let active = true; + (async () => { + try { + const progress = await getReadingProgress(ebookId).catch(() => null); + if (active && progress?.epub_location) { + setInitialLocation(progress.epub_location); + currentCfiRef.current = progress.epub_location; + setCurrentCfi(progress.epub_location); + } + const token = await getAccessToken(); + const url = ebooksApi.getFileUrl(ebookId); + const dest = `${FileSystem.cacheDirectory}ebook-${ebookId}.epub`; + const result = await FileSystem.downloadAsync(url, dest, { + headers: token ? { Authorization: `Bearer ${token}` } : undefined, + }); + if (!active) return; + if (result.status >= 400) { + setFileError(true); + return; + } + setSrc(result.uri); + } catch { + if (active) setFileError(true); + } + })(); + return () => { + active = false; + }; + }, [ebookId]); + + // Load existing bookmarks/highlights for this book. + useEffect(() => { + let active = true; + fetchBookmarks(ebookId) + .then((res) => { + if (active) setBookmarks(res.results); + }) + .catch(() => { + /* bookmarks are optional */ + }); + return () => { + active = false; + }; + }, [ebookId]); + + // Apply typography/theme to the rendition once it is ready and on changes. + useEffect(() => { + if (!ready) return; + try { + reader.changeTheme?.(buildEpubTheme(settings)); + reader.changeFontSize?.(`${settings.font_size}px`); + reader.changeFontFamily?.(FONT_STACKS[settings.font_family]); + } catch { + /* ignore rendition styling errors */ + } + }, [ready, settings, reader]); + + const flushProgress = useCallback(() => { + const cfi = currentCfiRef.current; + if (!cfi) return; + updateReadingProgress(ebookId, { + percentage: progressPctRef.current, + epub_location: cfi, + current_chapter: sectionRef.current.index + 1, + }).catch(() => { + /* progress is best-effort */ + }); + }, [ebookId]); + + // Flush the latest progress when leaving the reader. + useEffect( + () => () => { + if (saveTimerRef.current) clearTimeout(saveTimerRef.current); + flushProgress(); + }, + [flushProgress], + ); + + const handleReady = useCallback(() => { + setReady(true); + bookmarksRef.current.forEach((bookmark) => { + if (bookmark.highlight_color && bookmark.epub_cfi) { + try { + reader.addAnnotation?.("highlight", bookmark.epub_cfi, undefined, { + fill: bookmark.highlight_color, + }); + } catch { + /* highlight rendering is best-effort */ + } + } + }); + }, [reader]); + + const handleLocationChange = useCallback( + ( + _total: number, + location: EpubLocation, + progress: number, + section: EpubSection | null, + ) => { + const cfi = location?.start?.cfi ?? ""; + if (cfi) { + currentCfiRef.current = cfi; + setCurrentCfi(cfi); + } + const pct = Math.round(progress ?? 0); + progressPctRef.current = pct; + setProgressPct(pct); + const label = section?.label?.trim() ?? ""; + sectionRef.current = { + index: section?.index ?? sectionRef.current.index, + label, + }; + setChapterTitle(label); + + if (saveTimerRef.current) clearTimeout(saveTimerRef.current); + saveTimerRef.current = setTimeout(flushProgress, PROGRESS_SAVE_MS); + }, + [flushProgress], + ); + + const handleSelected = useCallback( + async (selectedText: string, cfiRange: string) => { + const excerpt = (selectedText ?? "").slice(0, 280); + try { + const created = await createMarker({ + ebook: ebookId, + epub_cfi: cfiRange, + chapter_index: sectionRef.current.index, + chapter_title: sectionRef.current.label, + location_text: excerpt, + content: excerpt, + highlight_color: HIGHLIGHT_COLOR, + }); + setBookmarks((prev) => [created, ...prev]); + try { + reader.addAnnotation?.("highlight", cfiRange, undefined, { + fill: HIGHLIGHT_COLOR, + }); + reader.removeSelection?.(); + } catch { + /* annotation rendering is best-effort */ + } + } catch { + /* ignore highlight save failures */ + } + }, + [ebookId, reader], + ); + + const handleToggleBookmarkHere = useCallback(async () => { + const cfi = currentCfiRef.current; + if (!cfi) return; + const existing = bookmarksRef.current.find((b) => b.epub_cfi === cfi); + if (existing) { + setBookmarks((prev) => prev.filter((b) => b.id !== existing.id)); + try { + reader.removeAnnotationByCfi?.(cfi); + } catch { + /* best-effort */ + } + deleteBookmark(existing.id).catch(() => {}); + return; + } + try { + const created = await createMarker({ + ebook: ebookId, + epub_cfi: cfi, + chapter_index: sectionRef.current.index, + chapter_title: sectionRef.current.label, + location_text: sectionRef.current.label, + }); + setBookmarks((prev) => [created, ...prev]); + } catch { + /* ignore bookmark save failures */ + } + }, [ebookId, reader]); + + const handleSelectBookmark = useCallback( + (bookmark: Bookmark) => { + setBookmarksOpen(false); + try { + reader.goToLocation?.(bookmark.epub_cfi); + } catch { + /* best-effort */ + } + }, + [reader], + ); + + const handleDeleteBookmark = useCallback( + (bookmark: Bookmark) => { + setBookmarks((prev) => prev.filter((b) => b.id !== bookmark.id)); + try { + if (bookmark.highlight_color) { + reader.removeAnnotationByCfi?.(bookmark.epub_cfi); + } + } catch { + /* best-effort */ + } + deleteBookmark(bookmark.id).catch(() => {}); + }, + [reader], + ); + + const handleTocSelect = useCallback( + (href: string) => { + setTocOpen(false); + try { + reader.goToLocation?.(href); + } catch { + /* best-effort */ + } + }, + [reader], + ); + + const onReaderLayout = useCallback((event: LayoutChangeEvent) => { + const { width, height } = event.nativeEvent.layout; + setSize((prev) => + prev && prev.width === width && prev.height === height + ? prev + : { width, height }, + ); + }, []); + + const palette = resolvePalette(settings); + const isBookmarked = bookmarks.some((b) => b.epub_cfi === currentCfi); + + return ( + + setTocOpen(true)} + onToggleSettings={() => setSettingsOpen(true)} + onToggleBookmarks={() => setBookmarksOpen(true)} + onToggleBookmarkHere={handleToggleBookmarkHere} + /> + + + {fileError ? ( + + + Could not download this book. Check your connection and try again. + + + ) : src && size ? ( + + ) : ( + + + + )} + + + setTocOpen(false)} + /> + setSettingsOpen(false)} + /> + setBookmarksOpen(false)} + /> + + ); +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + }, + readerArea: { + flex: 1, + }, + centered: { + flex: 1, + justifyContent: "center", + alignItems: "center", + padding: 24, + }, + message: { + fontSize: 15, + textAlign: "center", + }, +}); diff --git a/mobile/src/components/reader/ReaderBottomSheet.tsx b/mobile/src/components/reader/ReaderBottomSheet.tsx new file mode 100644 index 0000000..6e8538b --- /dev/null +++ b/mobile/src/components/reader/ReaderBottomSheet.tsx @@ -0,0 +1,92 @@ +import { type ReactNode } from "react"; +import { + Modal, + View, + Text, + TouchableOpacity, + StyleSheet, + type StyleProp, + type ViewStyle, +} from "react-native"; + +interface ReaderBottomSheetProps { + visible: boolean; + title: string; + chromeColor: string; + textColor: string; + onClose: () => void; + children: ReactNode; + /** When true, caps sheet height (TOC/bookmarks). Settings uses a full-width panel. */ + tall?: boolean; + sheetStyle?: StyleProp; +} + +export function ReaderBottomSheet({ + visible, + title, + chromeColor, + textColor, + onClose, + children, + tall = true, + sheetStyle, +}: ReaderBottomSheetProps): ReactNode { + return ( + + + + + + {title} + + + + + + {children} + + + + ); +} + +const styles = StyleSheet.create({ + backdrop: { + flex: 1, + backgroundColor: "rgba(0,0,0,0.4)", + justifyContent: "flex-end", + }, + sheet: { + borderTopLeftRadius: 16, + borderTopRightRadius: 16, + paddingBottom: 24, + }, + sheetTall: { + maxHeight: "75%", + }, + header: { + flexDirection: "row", + alignItems: "center", + justifyContent: "space-between", + padding: 16, + }, + headerTitle: { + fontSize: 16, + fontWeight: "700", + }, + close: { + fontSize: 18, + }, +}); diff --git a/mobile/src/components/reader/ReaderToolbar.tsx b/mobile/src/components/reader/ReaderToolbar.tsx new file mode 100644 index 0000000..3cce9ee --- /dev/null +++ b/mobile/src/components/reader/ReaderToolbar.tsx @@ -0,0 +1,152 @@ +import { type ReactNode } from "react"; +import { View, Text, TouchableOpacity, StyleSheet } from "react-native"; +import { resolvePalette } from "../../utils/epubTheme"; +import type { ReadingSettings } from "../../types/reader"; + +interface ReaderToolbarProps { + title: string; + chapterTitle: string; + progressPct: number; + settings: ReadingSettings; + isBookmarked: boolean; + onBack: () => void; + onToggleToc: () => void; + onToggleSettings: () => void; + onToggleBookmarks: () => void; + onToggleBookmarkHere: () => void; +} + +export function ReaderToolbar({ + title, + chapterTitle, + progressPct, + settings, + isBookmarked, + onBack, + onToggleToc, + onToggleSettings, + onToggleBookmarks, + onToggleBookmarkHere, +}: ReaderToolbarProps): ReactNode { + const palette = resolvePalette(settings); + + return ( + + + + + + + + + {title} + + {chapterTitle ? ( + + {chapterTitle} + + ) : null} + + + + + {isBookmarked ? "★" : "☆"} + + + + + + + + + + Aa + + + + + + + + ); +} + +const styles = StyleSheet.create({ + bar: { + paddingTop: 8, + paddingHorizontal: 8, + paddingBottom: 6, + }, + row: { + flexDirection: "row", + alignItems: "center", + }, + titles: { + flex: 1, + marginHorizontal: 8, + }, + title: { + fontSize: 14, + fontWeight: "600", + }, + chapter: { + fontSize: 11, + opacity: 0.7, + }, + iconButton: { + paddingHorizontal: 8, + paddingVertical: 4, + minWidth: 28, + alignItems: "center", + }, + icon: { + fontSize: 28, + lineHeight: 30, + }, + iconSmall: { + fontSize: 17, + fontWeight: "600", + }, + progressTrack: { + height: 3, + borderRadius: 2, + backgroundColor: "rgba(127,127,127,0.25)", + marginTop: 6, + overflow: "hidden", + }, + progressFill: { + height: "100%", + backgroundColor: "#4f8ef7", + }, +}); diff --git a/mobile/src/components/reader/ReadingSettingsModal.tsx b/mobile/src/components/reader/ReadingSettingsModal.tsx new file mode 100644 index 0000000..b789fad --- /dev/null +++ b/mobile/src/components/reader/ReadingSettingsModal.tsx @@ -0,0 +1,249 @@ +import { type ReactNode } from "react"; +import { + View, + Text, + TouchableOpacity, + StyleSheet, +} from "react-native"; +import { resolvePalette, THEME_PALETTES } from "../../utils/epubTheme"; +import type { + FontFamily, + ReadingSettings, + ThemePreset, +} from "../../types/reader"; +import { ReaderBottomSheet } from "./ReaderBottomSheet"; + +const THEMES: ThemePreset[] = ["light", "sepia", "paper", "dark"]; +const FONTS: { value: FontFamily; label: string }[] = [ + { value: "serif", label: "Serif" }, + { value: "sans-serif", label: "Sans" }, + { value: "monospace", label: "Mono" }, +]; +const FONT_SIZE_MIN = 12; +const FONT_SIZE_MAX = 32; +const LINE_HEIGHT_MIN = 1.2; +const LINE_HEIGHT_MAX = 2.2; + +interface ReadingSettingsModalProps { + visible: boolean; + settings: ReadingSettings; + onChange: (patch: Partial) => void; + onClose: () => void; +} + +export function ReadingSettingsModal({ + visible, + settings, + onChange, + onClose, +}: ReadingSettingsModalProps): ReactNode { + const palette = resolvePalette(settings); + + const adjustFontSize = (delta: number) => { + const next = Math.min( + FONT_SIZE_MAX, + Math.max(FONT_SIZE_MIN, settings.font_size + delta), + ); + onChange({ font_size: next }); + }; + + const adjustLineHeight = (delta: number) => { + const next = Math.min( + LINE_HEIGHT_MAX, + Math.max( + LINE_HEIGHT_MIN, + Math.round((settings.line_height + delta) * 10) / 10, + ), + ); + onChange({ line_height: next }); + }; + + return ( + + Theme + + {THEMES.map((theme) => { + const p = THEME_PALETTES[theme]; + const active = settings.theme === theme; + return ( + + onChange({ + theme, + background_color: p.background, + text_color: p.text, + }) + } + style={[ + styles.themeSwatch, + { backgroundColor: p.background }, + active && styles.themeSwatchActive, + ]} + > + + Aa + + + ); + })} + + + Font + + {FONTS.map((font) => { + const active = settings.font_family === font.value; + return ( + onChange({ font_family: font.value })} + style={[styles.pill, active && styles.pillActive]} + > + + {font.label} + + + ); + })} + + + adjustFontSize(-1)} + onIncrease={() => adjustFontSize(1)} + /> + adjustLineHeight(-0.1)} + onIncrease={() => adjustLineHeight(0.1)} + /> + + ); +} + +function Stepper({ + label, + value, + color, + onDecrease, + onIncrease, +}: { + label: string; + value: string; + color: string; + onDecrease: () => void; + onIncrease: () => void; +}): ReactNode { + return ( + + + {label} + + + + + + {value} + + + + + + + ); +} + +const styles = StyleSheet.create({ + sheetBody: { + paddingHorizontal: 20, + paddingBottom: 32, + }, + sectionLabel: { + fontSize: 13, + fontWeight: "600", + marginBottom: 8, + marginTop: 8, + opacity: 0.8, + }, + rowWrap: { + flexDirection: "row", + flexWrap: "wrap", + gap: 10, + }, + themeSwatch: { + width: 56, + height: 48, + borderRadius: 10, + justifyContent: "center", + alignItems: "center", + borderWidth: 2, + borderColor: "transparent", + }, + themeSwatchActive: { + borderColor: "#4f8ef7", + }, + themeSwatchText: { + fontSize: 16, + fontWeight: "600", + }, + pill: { + paddingHorizontal: 16, + paddingVertical: 8, + borderRadius: 20, + borderWidth: 1, + borderColor: "rgba(127,127,127,0.4)", + }, + pillActive: { + backgroundColor: "#4f8ef7", + borderColor: "#4f8ef7", + }, + pillText: { + fontSize: 14, + fontWeight: "600", + }, + stepperRow: { + flexDirection: "row", + alignItems: "center", + justifyContent: "space-between", + marginTop: 16, + }, + stepperControls: { + flexDirection: "row", + alignItems: "center", + }, + stepperButton: { + width: 36, + height: 36, + borderRadius: 18, + backgroundColor: "rgba(127,127,127,0.2)", + justifyContent: "center", + alignItems: "center", + }, + stepperButtonText: { + fontSize: 20, + fontWeight: "700", + color: "#4f8ef7", + }, + stepperValue: { + minWidth: 56, + textAlign: "center", + fontSize: 15, + fontWeight: "600", + }, +}); diff --git a/mobile/src/components/reader/TocModal.tsx b/mobile/src/components/reader/TocModal.tsx new file mode 100644 index 0000000..7028c55 --- /dev/null +++ b/mobile/src/components/reader/TocModal.tsx @@ -0,0 +1,111 @@ +import { type ReactNode } from "react"; +import { + Text, + FlatList, + TouchableOpacity, + StyleSheet, +} from "react-native"; +import { resolvePalette } from "../../utils/epubTheme"; +import type { ReadingSettings } from "../../types/reader"; +import { ReaderBottomSheet } from "./ReaderBottomSheet"; + +export interface TocItem { + id?: string; + label: string; + href: string; + subitems?: TocItem[]; +} + +interface FlatTocItem { + key: string; + label: string; + href: string; + depth: number; +} + +function flatten(items: TocItem[], depth = 0, acc: FlatTocItem[] = []) { + items.forEach((item, index) => { + acc.push({ + key: `${item.id ?? item.href}-${depth}-${index}`, + label: (item.label ?? "").trim() || "Untitled section", + href: item.href, + depth, + }); + if (item.subitems?.length) { + flatten(item.subitems, depth + 1, acc); + } + }); + return acc; +} + +interface TocModalProps { + visible: boolean; + toc: TocItem[]; + settings: ReadingSettings; + onSelect: (href: string) => void; + onClose: () => void; +} + +export function TocModal({ + visible, + toc, + settings, + onSelect, + onClose, +}: TocModalProps): ReactNode { + const palette = resolvePalette(settings); + const data = flatten(toc); + + return ( + + item.key} + ListEmptyComponent={ + + No table of contents available. + + } + renderItem={({ item }) => ( + onSelect(item.href)} + > + + {item.label} + + + )} + /> + + ); +} + +const styles = StyleSheet.create({ + row: { + paddingVertical: 12, + paddingHorizontal: 16, + borderTopWidth: StyleSheet.hairlineWidth, + borderTopColor: "rgba(127,127,127,0.2)", + }, + rowText: { + fontSize: 14, + }, + empty: { + padding: 24, + textAlign: "center", + opacity: 0.7, + }, +}); diff --git a/mobile/src/hooks/index.ts b/mobile/src/hooks/index.ts deleted file mode 100644 index b751c47..0000000 --- a/mobile/src/hooks/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { useAuth } from "../context/AuthContext"; -export { useAsyncData } from "./useAsyncData"; \ No newline at end of file diff --git a/mobile/src/hooks/useAsyncData.ts b/mobile/src/hooks/useAsyncData.ts deleted file mode 100644 index 3f57c37..0000000 --- a/mobile/src/hooks/useAsyncData.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { useState, useEffect, useCallback } from "react"; - -/** - * Generic async data fetching hook for mobile screens. - */ -export function useAsyncData( - fetcher: () => Promise, - deps: unknown[] = [], -) { - const [data, setData] = useState(null); - const [loading, setLoading] = useState(true); - const [error, setError] = useState(null); - - const execute = useCallback(async () => { - setLoading(true); - setError(null); - try { - const result = await fetcher(); - setData(result); - } catch (err) { - setError(err instanceof Error ? err : new Error(String(err))); - } finally { - setLoading(false); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, deps); - - useEffect(() => { - execute(); - }, [execute]); - - return { data, loading, error, refetch: execute }; -} \ No newline at end of file diff --git a/mobile/src/hooks/useReadingSettings.ts b/mobile/src/hooks/useReadingSettings.ts new file mode 100644 index 0000000..ce72a8a --- /dev/null +++ b/mobile/src/hooks/useReadingSettings.ts @@ -0,0 +1,82 @@ +import { useCallback, useEffect, useRef, useState } from "react"; +import { + getReadingSettings, + updateReadingSettings, +} from "../api/reader"; +import type { ReadingSettings } from "../types/reader"; + +const DEFAULT_READING_SETTINGS: ReadingSettings = { + font_family: "serif", + font_size: 18, + line_height: 1.5, + margin_width: 16, + background_color: "#fbfbf8", + text_color: "#2b2b2b", + brightness: 1, + orientation_lock: "auto", + theme: "paper", + created_at: "", + updated_at: "", +}; + +const SAVE_DEBOUNCE_MS = 600; + +interface UseReadingSettingsReturn { + settings: ReadingSettings; + loaded: boolean; + updateSettings: (patch: Partial) => void; +} + +/** + * Loads the user's reader settings and persists changes with a debounce. + * Mirrors frontend/src/hooks/useReadingSettings.ts behavior. + */ +export function useReadingSettings(): UseReadingSettingsReturn { + const [settings, setSettings] = useState( + DEFAULT_READING_SETTINGS, + ); + const [loaded, setLoaded] = useState(false); + const pendingRef = useRef>({}); + const timerRef = useRef | null>(null); + + useEffect(() => { + let active = true; + getReadingSettings() + .then((data) => { + if (active) { + setSettings(data); + } + }) + .catch(() => { + /* keep defaults if the request fails */ + }) + .finally(() => { + if (active) { + setLoaded(true); + } + }); + return () => { + active = false; + if (timerRef.current) { + clearTimeout(timerRef.current); + } + }; + }, []); + + const updateSettings = useCallback((patch: Partial) => { + setSettings((prev) => ({ ...prev, ...patch })); + pendingRef.current = { ...pendingRef.current, ...patch }; + if (timerRef.current) { + clearTimeout(timerRef.current); + } + timerRef.current = setTimeout(() => { + const body = pendingRef.current; + pendingRef.current = {}; + updateReadingSettings(body).catch(() => { + /* ignore transient save failures */ + }); + }, SAVE_DEBOUNCE_MS); + }, []); + + return { settings, loaded, updateSettings }; +} diff --git a/mobile/src/navigation/AppNavigator.tsx b/mobile/src/navigation/AppNavigator.tsx deleted file mode 100644 index d64e751..0000000 --- a/mobile/src/navigation/AppNavigator.tsx +++ /dev/null @@ -1,45 +0,0 @@ -import { type ReactNode } from "react"; -import { NavigationContainer } from "@react-navigation/native"; -import { createNativeStackNavigator } from "@react-navigation/native-stack"; -import { useAuth } from "../context/AuthContext"; -import LoginScreen from "../screens/LoginScreen"; -import RegisterScreen from "../screens/RegisterScreen"; -import MainTabs from "./MainTabs"; - -export type AuthStackParamList = { - Login: undefined; - Register: undefined; -}; - -export type RootStackParamList = { - Auth: undefined; - Main: undefined; -}; - -const RootStack = createNativeStackNavigator(); -const AuthStack = createNativeStackNavigator(); - -function AuthNavigator(): ReactNode { - return ( - - - - - ); -} - -export default function AppNavigator(): ReactNode { - const { state } = useAuth(); - - return ( - - - {state.isAuthenticated ? ( - - ) : ( - - )} - - - ); -} \ No newline at end of file diff --git a/mobile/src/navigation/AppStack.tsx b/mobile/src/navigation/AppStack.tsx new file mode 100644 index 0000000..f90cda6 --- /dev/null +++ b/mobile/src/navigation/AppStack.tsx @@ -0,0 +1,36 @@ +import React from "react"; +import { createNativeStackNavigator } from "@react-navigation/native-stack"; +import { MainNavigator } from "./MainNavigator"; +import BookDetailScreen from "../screens/BookDetailScreen"; +import ReaderScreen from "../screens/ReaderScreen"; +import type { AppStackParamList } from "../types"; + +const Stack = createNativeStackNavigator(); + +export function AppStack() { + return ( + + + + + + ); +} diff --git a/mobile/src/navigation/AuthNavigator.tsx b/mobile/src/navigation/AuthNavigator.tsx index 697e99f..b8601c3 100644 --- a/mobile/src/navigation/AuthNavigator.tsx +++ b/mobile/src/navigation/AuthNavigator.tsx @@ -3,7 +3,7 @@ import { createNativeStackNavigator } from "@react-navigation/native-stack"; import LoginScreen from "../screens/LoginScreen"; import RegisterScreen from "../screens/RegisterScreen"; -export type AuthStackParamList = { +type AuthStackParamList = { Login: undefined; Register: undefined; }; diff --git a/mobile/src/navigation/MainTabs.tsx b/mobile/src/navigation/MainTabs.tsx deleted file mode 100644 index 9f80e33..0000000 --- a/mobile/src/navigation/MainTabs.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import { type ReactNode } from "react"; -import { createBottomTabNavigator } from "@react-navigation/bottom-tabs"; -import { Text } from "react-native"; -import LibraryScreen from "../screens/LibraryScreen"; -import SearchScreen from "../screens/SearchScreen"; -import SettingsScreen from "../screens/SettingsScreen"; - -export type MainTabParamList = { - Library: undefined; - Search: undefined; - Settings: undefined; -}; - -const Tab = createBottomTabNavigator(); - -function TabIcon({ label, focused }: { label: string; focused: boolean }) { - return ( - - {label === "Library" ? "📚" : label === "Search" ? "🔍" : "⚙️"} - - ); -} - -export default function MainTabs(): ReactNode { - return ( - ({ - tabBarIcon: ({ focused }: { focused: boolean }) => ( - - ), - tabBarActiveTintColor: "#4f8ef7", - tabBarInactiveTintColor: "#888", - headerStyle: { backgroundColor: "#1a1a2e" }, - headerTintColor: "#fff", - tabBarStyle: { backgroundColor: "#1a1a2e", borderTopColor: "#333" }, - })} - > - - - - - ); -} \ No newline at end of file diff --git a/mobile/src/navigation/RootNavigator.tsx b/mobile/src/navigation/RootNavigator.tsx index b857e50..7274c9d 100644 --- a/mobile/src/navigation/RootNavigator.tsx +++ b/mobile/src/navigation/RootNavigator.tsx @@ -2,7 +2,7 @@ import React from "react"; import { ActivityIndicator, View } from "react-native"; import { useAuth } from "../context/AuthContext"; import { AuthNavigator } from "./AuthNavigator"; -import { MainNavigator } from "./MainNavigator"; +import { AppStack } from "./AppStack"; export function RootNavigator() { const { isLoading, isAuthenticated } = useAuth(); @@ -19,5 +19,5 @@ export function RootNavigator() { return ; } - return ; + return ; } \ No newline at end of file diff --git a/mobile/src/screens/AuthFormError.tsx b/mobile/src/screens/AuthFormError.tsx new file mode 100644 index 0000000..31eee5c --- /dev/null +++ b/mobile/src/screens/AuthFormError.tsx @@ -0,0 +1,11 @@ +import { type ReactNode } from "react"; +import { View, Text } from "react-native"; +import { authStyles } from "./authStyles"; + +export function AuthFormError({ message }: { message: string }): ReactNode { + return ( + + {message} + + ); +} diff --git a/mobile/src/screens/BookDetailScreen.tsx b/mobile/src/screens/BookDetailScreen.tsx new file mode 100644 index 0000000..4cea681 --- /dev/null +++ b/mobile/src/screens/BookDetailScreen.tsx @@ -0,0 +1,226 @@ +import { useState, useEffect, useCallback, type ReactNode } from "react"; +import { + View, + Text, + Image, + ScrollView, + TouchableOpacity, + StyleSheet, + ActivityIndicator, +} from "react-native"; +import type { RouteProp } from "@react-navigation/native"; +import { ebooksApi } from "../api/ebooks"; +import { formatFileSize } from "@cloud-reader/shared"; +import type { EBookDetail } from "@cloud-reader/shared"; +import type { AppStackParamList } from "../types"; + +type DetailRoute = RouteProp; + +export default function BookDetailScreen({ + route, + navigation, +}: { + route: DetailRoute; + navigation: any; +}): ReactNode { + const { ebookId } = route.params; + const [book, setBook] = useState(null); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(false); + + const load = useCallback(async () => { + try { + const { data } = await ebooksApi.get(ebookId); + setBook(data); + } catch { + setError(true); + } finally { + setLoading(false); + } + }, [ebookId]); + + useEffect(() => { + load(); + }, [load]); + + if (loading) { + return ( + + + + ); + } + + if (error || !book) { + return ( + + Could not load this book. + + ); + } + + const progressPct = + typeof book.progress?.current_position === "number" + ? Math.round(book.progress.current_position) + : 0; + const hasProgress = progressPct > 0; + + return ( + + + {book.cover_image ? ( + + ) : ( + + + {book.title.charAt(0).toUpperCase()} + + + )} + {book.title} + {book.author || "Unknown author"} + + + + + + + + + {hasProgress && ( + + + + + {progressPct}% read + + )} + + navigation.navigate("Reader", { ebookId: book.id })} + > + + {hasProgress ? "Resume reading" : "Start reading"} + + + + ); +} + +function MetaPill({ label, value }: { label: string; value: string }): ReactNode { + return ( + + {value} + {label} + + ); +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + backgroundColor: "#0f0f23", + }, + content: { + padding: 24, + }, + centered: { + flex: 1, + justifyContent: "center", + alignItems: "center", + backgroundColor: "#0f0f23", + padding: 24, + }, + header: { + alignItems: "center", + marginBottom: 24, + }, + cover: { + width: 140, + height: 200, + borderRadius: 12, + backgroundColor: "#2a2a4e", + justifyContent: "center", + alignItems: "center", + marginBottom: 16, + }, + coverText: { + fontSize: 56, + fontWeight: "bold", + color: "#4f8ef7", + }, + title: { + fontSize: 22, + fontWeight: "700", + color: "#fff", + textAlign: "center", + marginBottom: 6, + }, + author: { + fontSize: 15, + color: "#888", + textAlign: "center", + }, + metaRow: { + flexDirection: "row", + justifyContent: "space-between", + marginBottom: 24, + }, + metaPill: { + flex: 1, + backgroundColor: "#1a1a2e", + borderRadius: 10, + paddingVertical: 12, + marginHorizontal: 4, + alignItems: "center", + borderWidth: 1, + borderColor: "#333", + }, + metaValue: { + fontSize: 15, + fontWeight: "600", + color: "#fff", + }, + metaLabel: { + fontSize: 12, + color: "#666", + marginTop: 2, + }, + progressWrap: { + marginBottom: 24, + }, + progressTrack: { + height: 6, + borderRadius: 3, + backgroundColor: "#1a1a2e", + overflow: "hidden", + }, + progressFill: { + height: "100%", + backgroundColor: "#4f8ef7", + }, + progressLabel: { + fontSize: 12, + color: "#888", + marginTop: 6, + }, + readButton: { + backgroundColor: "#4f8ef7", + borderRadius: 10, + paddingVertical: 16, + alignItems: "center", + }, + readButtonText: { + color: "#fff", + fontSize: 16, + fontWeight: "600", + }, + errorText: { + color: "#888", + fontSize: 15, + }, +}); diff --git a/mobile/src/screens/LibraryScreen.tsx b/mobile/src/screens/LibraryScreen.tsx index 6f10d13..a481b8e 100644 --- a/mobile/src/screens/LibraryScreen.tsx +++ b/mobile/src/screens/LibraryScreen.tsx @@ -2,34 +2,31 @@ import { useState, useEffect, useCallback, type ReactNode } from "react"; import { View, Text, + Image, FlatList, TouchableOpacity, StyleSheet, ActivityIndicator, RefreshControl, } from "react-native"; -import { fetchBooks } from "../api/books"; -import type { Book, PaginatedResponse } from "@cloud-reader/shared"; +import { ebooksApi } from "../api/ebooks"; +import type { EBookListItem } from "@cloud-reader/shared"; -export default function LibraryScreen({ navigation }: { navigation: any }): ReactNode { - const [books, setBooks] = useState([]); +export default function LibraryScreen({ + navigation, +}: { + navigation: any; +}): ReactNode { + const [books, setBooks] = useState([]); const [loading, setLoading] = useState(true); const [refreshing, setRefreshing] = useState(false); - const [page, setPage] = useState(1); - const [hasMore, setHasMore] = useState(true); - const loadBooks = useCallback(async (pageNum: number, isRefresh = false) => { + const loadBooks = useCallback(async () => { try { - const data: PaginatedResponse = await fetchBooks(pageNum); - if (isRefresh) { - setBooks(data.results); - } else { - setBooks((prev) => [...prev, ...data.results]); - } - setHasMore(data.next !== null); - setPage(pageNum); + const { data } = await ebooksApi.list(); + setBooks(data.results); } catch { - // Silent error for now + // Silent error for now; pull-to-refresh lets the user retry. } finally { setLoading(false); setRefreshing(false); @@ -37,44 +34,49 @@ export default function LibraryScreen({ navigation }: { navigation: any }): Reac }, []); useEffect(() => { - loadBooks(1, true); + loadBooks(); }, [loadBooks]); const onRefresh = () => { setRefreshing(true); - loadBooks(1, true); + loadBooks(); }; - const loadMore = () => { - if (hasMore && !loading) { - loadBooks(page + 1); - } + const renderBook = ({ item }: { item: EBookListItem }) => { + const progress = + typeof item.progress === "number" + ? Math.round(item.progress) + : null; + return ( + navigation.navigate("BookDetail", { ebookId: item.id })} + > + {item.cover_image ? ( + + ) : ( + + + {item.title.charAt(0).toUpperCase()} + + + )} + + + {item.title} + + + {item.author || "Unknown author"} + + + {item.format?.toUpperCase()} + {progress !== null ? ` · ${progress}% read` : ""} + + + + ); }; - const renderBook = ({ item }: { item: Book }) => ( - - navigation.navigate("BookDetail", { bookId: item.id }) - } - > - - - {item.title.charAt(0).toUpperCase()} - - - - - {item.title} - - - {item.author} - - {item.total_pages} pages - - - ); - if (loading && books.length === 0) { return ( @@ -88,9 +90,7 @@ export default function LibraryScreen({ navigation }: { navigation: any }): Reac item.id} - onEndReached={loadMore} - onEndReachedThreshold={0.5} + keyExtractor={(item) => String(item.id)} contentContainerStyle={styles.list} refreshControl={ Your library is empty - Add books to get started + Upload books from the web app to get started } @@ -125,6 +125,7 @@ const styles = StyleSheet.create({ }, list: { padding: 16, + flexGrow: 1, }, bookCard: { flexDirection: "row", @@ -164,7 +165,7 @@ const styles = StyleSheet.create({ color: "#888", marginBottom: 4, }, - bookPages: { + bookMeta: { fontSize: 12, color: "#666", }, @@ -177,5 +178,6 @@ const styles = StyleSheet.create({ emptySubtext: { fontSize: 14, color: "#666", + textAlign: "center", }, -}); \ No newline at end of file +}); diff --git a/mobile/src/screens/LoginScreen.tsx b/mobile/src/screens/LoginScreen.tsx index 766fafe..c4fd481 100644 --- a/mobile/src/screens/LoginScreen.tsx +++ b/mobile/src/screens/LoginScreen.tsx @@ -4,29 +4,31 @@ import { Text, TextInput, TouchableOpacity, - StyleSheet, Alert, ActivityIndicator, KeyboardAvoidingView, Platform, } from "react-native"; import { useAuth } from "../context/AuthContext"; -import { isValidEmail } from "@cloud-reader/shared"; +import { authStyles } from "./authStyles"; +import { AuthFormError } from "./AuthFormError"; +import { requireValidEmail } from "./authValidation"; -export default function LoginScreen({ navigation }: { navigation: any }): ReactNode { - const { state, login, clearError } = useAuth(); +export default function LoginScreen({ + navigation, +}: { + navigation: any; +}): ReactNode { + const { login } = useAuth(); const [email, setEmail] = useState(""); const [password, setPassword] = useState(""); + const [error, setError] = useState(null); + const [submitting, setSubmitting] = useState(false); const handleLogin = async () => { - clearError(); + setError(null); - if (!email.trim()) { - Alert.alert("Validation Error", "Please enter your email."); - return; - } - if (!isValidEmail(email.trim())) { - Alert.alert("Validation Error", "Please enter a valid email address."); + if (!requireValidEmail(email)) { return; } if (!password) { @@ -34,30 +36,29 @@ export default function LoginScreen({ navigation }: { navigation: any }): ReactN return; } + setSubmitting(true); try { await login(email.trim(), password); } catch { - // Error handled in context + setError("Invalid email or password."); + } finally { + setSubmitting(false); } }; return ( - - Cloud Reader - Sign in to your account + + Cloud Reader + Sign in to your account - {state.error && ( - - {state.error} - - )} + {error ? : null} - {state.isLoading ? ( + {submitting ? ( ) : ( - Sign In + Sign In )} navigation.navigate("Register")}> - + Don't have an account?{" "} - Sign Up + Sign Up ); } - -const styles = StyleSheet.create({ - container: { - flex: 1, - backgroundColor: "#0f0f23", - }, - inner: { - flex: 1, - justifyContent: "center", - paddingHorizontal: 24, - }, - title: { - fontSize: 32, - fontWeight: "bold", - color: "#fff", - textAlign: "center", - marginBottom: 8, - }, - subtitle: { - fontSize: 16, - color: "#888", - textAlign: "center", - marginBottom: 32, - }, - input: { - backgroundColor: "#1a1a2e", - borderRadius: 8, - padding: 16, - fontSize: 16, - color: "#fff", - marginBottom: 12, - borderWidth: 1, - borderColor: "#333", - }, - button: { - backgroundColor: "#4f8ef7", - borderRadius: 8, - padding: 16, - alignItems: "center", - marginTop: 8, - marginBottom: 24, - }, - buttonDisabled: { - opacity: 0.6, - }, - buttonText: { - color: "#fff", - fontSize: 16, - fontWeight: "600", - }, - errorBox: { - backgroundColor: "rgba(255, 69, 58, 0.15)", - borderRadius: 8, - padding: 12, - marginBottom: 16, - borderWidth: 1, - borderColor: "rgba(255, 69, 58, 0.3)", - }, - errorText: { - color: "#ff453a", - fontSize: 14, - textAlign: "center", - }, - linkText: { - color: "#888", - textAlign: "center", - fontSize: 14, - }, - linkBold: { - color: "#4f8ef7", - fontWeight: "600", - }, -}); \ No newline at end of file diff --git a/mobile/src/screens/ReaderScreen.tsx b/mobile/src/screens/ReaderScreen.tsx new file mode 100644 index 0000000..14d5544 --- /dev/null +++ b/mobile/src/screens/ReaderScreen.tsx @@ -0,0 +1,133 @@ +import { useEffect, useState, type ReactNode } from "react"; +import { + View, + Text, + TouchableOpacity, + ActivityIndicator, + StyleSheet, +} from "react-native"; +import { SafeAreaView } from "react-native-safe-area-context"; +import type { RouteProp } from "@react-navigation/native"; +import type { EBookDetail } from "@cloud-reader/shared"; +import { ebooksApi } from "../api/ebooks"; +import { EpubReaderView } from "../components/reader/EpubReaderView"; +import type { AppStackParamList } from "../types"; + +type ReaderRoute = RouteProp; + +export default function ReaderScreen({ + route, + navigation, +}: { + route: ReaderRoute; + navigation: any; +}): ReactNode { + const { ebookId } = route.params; + const [book, setBook] = useState(null); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(false); + + useEffect(() => { + let active = true; + ebooksApi + .get(ebookId) + .then(({ data }) => { + if (active) setBook(data); + }) + .catch(() => { + if (active) setError(true); + }) + .finally(() => { + if (active) setLoading(false); + }); + return () => { + active = false; + }; + }, [ebookId]); + + if (loading) { + return ( + + + + ); + } + + if (error || !book) { + return ( + + Could not open this book. + navigation.goBack()} + > + Go back + + + ); + } + + const format = (book.format ?? "").toLowerCase(); + + if (format !== "epub") { + return ( + + PDF reading is coming soon + + “{book.title}” is a {format.toUpperCase() || "non-EPUB"} file. PDF + reading isn't available on mobile yet — open it in the web reader + for now. + + navigation.goBack()} + > + Go back + + + ); + } + + return ( + navigation.goBack()} + /> + ); +} + +const styles = StyleSheet.create({ + centered: { + flex: 1, + justifyContent: "center", + alignItems: "center", + backgroundColor: "#0f0f23", + padding: 24, + }, + placeholderTitle: { + fontSize: 20, + fontWeight: "700", + color: "#fff", + marginBottom: 12, + textAlign: "center", + }, + message: { + fontSize: 15, + color: "#aaa", + textAlign: "center", + lineHeight: 22, + }, + backButton: { + marginTop: 24, + backgroundColor: "#4f8ef7", + borderRadius: 10, + paddingHorizontal: 24, + paddingVertical: 12, + }, + backText: { + color: "#fff", + fontSize: 15, + fontWeight: "600", + }, +}); diff --git a/mobile/src/screens/RegisterScreen.tsx b/mobile/src/screens/RegisterScreen.tsx index e2cce09..3080b78 100644 --- a/mobile/src/screens/RegisterScreen.tsx +++ b/mobile/src/screens/RegisterScreen.tsx @@ -1,10 +1,8 @@ import { useState, type ReactNode } from "react"; import { - View, Text, TextInput, TouchableOpacity, - StyleSheet, Alert, ActivityIndicator, KeyboardAvoidingView, @@ -12,40 +10,39 @@ import { ScrollView, } from "react-native"; import { useAuth } from "../context/AuthContext"; -import { - isValidEmail, - validatePasswordStrength, -} from "@cloud-reader/shared"; +import { isStrongPassword } from "@cloud-reader/shared"; +import { authStyles } from "./authStyles"; +import { AuthFormError } from "./AuthFormError"; +import { requireValidEmail } from "./authValidation"; export default function RegisterScreen({ navigation, }: { navigation: any; }): ReactNode { - const { state, register, clearError } = useAuth(); + const { register } = useAuth(); const [username, setUsername] = useState(""); const [email, setEmail] = useState(""); const [password, setPassword] = useState(""); const [confirmPassword, setConfirmPassword] = useState(""); + const [error, setError] = useState(null); + const [submitting, setSubmitting] = useState(false); const handleRegister = async () => { - clearError(); + setError(null); if (!username.trim()) { Alert.alert("Validation Error", "Please enter a username."); return; } - if (!email.trim()) { - Alert.alert("Validation Error", "Please enter your email."); + if (!requireValidEmail(email)) { return; } - if (!isValidEmail(email.trim())) { - Alert.alert("Validation Error", "Please enter a valid email address."); - return; - } - const passwordError = validatePasswordStrength(password); - if (passwordError) { - Alert.alert("Validation Error", passwordError); + if (!isStrongPassword(password)) { + Alert.alert( + "Validation Error", + "Password must be at least 8 characters and include uppercase, lowercase, and a number.", + ); return; } if (password !== confirmPassword) { @@ -53,30 +50,29 @@ export default function RegisterScreen({ return; } + setSubmitting(true); try { - await register(email.trim(), password, username.trim()); + await register(email.trim(), username.trim(), password); } catch { - // Error handled in context + setError("Could not create the account. Try a different email."); + } finally { + setSubmitting(false); } }; return ( - - Create Account - Join Cloud Reader + + Create Account + Join Cloud Reader - {state.error && ( - - {state.error} - - )} + {error ? : null} - {state.isLoading ? ( + {submitting ? ( ) : ( - Create Account + Create Account )} navigation.goBack()}> - + Already have an account?{" "} - Sign In + Sign In ); } - -const styles = StyleSheet.create({ - container: { - flex: 1, - backgroundColor: "#0f0f23", - }, - inner: { - flexGrow: 1, - justifyContent: "center", - paddingHorizontal: 24, - paddingVertical: 48, - }, - title: { - fontSize: 28, - fontWeight: "bold", - color: "#fff", - textAlign: "center", - marginBottom: 8, - }, - subtitle: { - fontSize: 16, - color: "#888", - textAlign: "center", - marginBottom: 32, - }, - input: { - backgroundColor: "#1a1a2e", - borderRadius: 8, - padding: 16, - fontSize: 16, - color: "#fff", - marginBottom: 12, - borderWidth: 1, - borderColor: "#333", - }, - button: { - backgroundColor: "#4f8ef7", - borderRadius: 8, - padding: 16, - alignItems: "center", - marginTop: 8, - marginBottom: 24, - }, - buttonDisabled: { - opacity: 0.6, - }, - buttonText: { - color: "#fff", - fontSize: 16, - fontWeight: "600", - }, - errorBox: { - backgroundColor: "rgba(255, 69, 58, 0.15)", - borderRadius: 8, - padding: 12, - marginBottom: 16, - borderWidth: 1, - borderColor: "rgba(255, 69, 58, 0.3)", - }, - errorText: { - color: "#ff453a", - fontSize: 14, - textAlign: "center", - }, - linkText: { - color: "#888", - textAlign: "center", - fontSize: 14, - }, - linkBold: { - color: "#4f8ef7", - fontWeight: "600", - }, -}); \ No newline at end of file diff --git a/mobile/src/screens/SettingsScreen.tsx b/mobile/src/screens/SettingsScreen.tsx index 399b66d..62207e5 100644 --- a/mobile/src/screens/SettingsScreen.tsx +++ b/mobile/src/screens/SettingsScreen.tsx @@ -9,7 +9,7 @@ import { import { useAuth } from "../context/AuthContext"; export default function SettingsScreen(): ReactNode { - const { state, logout } = useAuth(); + const { user, logout } = useAuth(); const handleLogout = () => { Alert.alert("Logout", "Are you sure you want to sign out?", [ @@ -24,11 +24,11 @@ export default function SettingsScreen(): ReactNode { Account Username - {state.user?.username ?? "—"} + {user?.username ?? "—"} Email - {state.user?.email ?? "—"} + {user?.email ?? "—"} diff --git a/mobile/src/screens/authStyles.ts b/mobile/src/screens/authStyles.ts new file mode 100644 index 0000000..6a61efb --- /dev/null +++ b/mobile/src/screens/authStyles.ts @@ -0,0 +1,88 @@ +import { StyleSheet } from "react-native"; + +/** Shared layout/styles for Login and Register screens. */ +export const authStyles = StyleSheet.create({ + container: { + flex: 1, + backgroundColor: "#0f0f23", + }, + inner: { + flex: 1, + justifyContent: "center", + paddingHorizontal: 24, + }, + innerScroll: { + flexGrow: 1, + justifyContent: "center", + paddingHorizontal: 24, + paddingVertical: 48, + }, + title: { + fontSize: 32, + fontWeight: "bold", + color: "#fff", + textAlign: "center", + marginBottom: 8, + }, + titleCompact: { + fontSize: 28, + fontWeight: "bold", + color: "#fff", + textAlign: "center", + marginBottom: 8, + }, + subtitle: { + fontSize: 16, + color: "#888", + textAlign: "center", + marginBottom: 32, + }, + input: { + backgroundColor: "#1a1a2e", + borderRadius: 8, + padding: 16, + fontSize: 16, + color: "#fff", + marginBottom: 12, + borderWidth: 1, + borderColor: "#333", + }, + button: { + backgroundColor: "#4f8ef7", + borderRadius: 8, + padding: 16, + alignItems: "center", + marginTop: 8, + marginBottom: 24, + }, + buttonDisabled: { + opacity: 0.6, + }, + buttonText: { + color: "#fff", + fontSize: 16, + fontWeight: "600", + }, + errorBox: { + backgroundColor: "rgba(255, 69, 58, 0.15)", + borderRadius: 8, + padding: 12, + marginBottom: 16, + borderWidth: 1, + borderColor: "rgba(255, 69, 58, 0.3)", + }, + errorText: { + color: "#ff453a", + fontSize: 14, + textAlign: "center", + }, + linkText: { + color: "#888", + textAlign: "center", + fontSize: 14, + }, + linkBold: { + color: "#4f8ef7", + fontWeight: "600", + }, +}); diff --git a/mobile/src/screens/authValidation.ts b/mobile/src/screens/authValidation.ts new file mode 100644 index 0000000..8c5bdff --- /dev/null +++ b/mobile/src/screens/authValidation.ts @@ -0,0 +1,15 @@ +import { Alert } from "react-native"; +import { isValidEmail } from "@cloud-reader/shared"; + +/** Returns true when email is non-empty and well-formed. */ +export function requireValidEmail(email: string): boolean { + if (!email.trim()) { + Alert.alert("Validation Error", "Please enter your email."); + return false; + } + if (!isValidEmail(email.trim())) { + Alert.alert("Validation Error", "Please enter a valid email address."); + return false; + } + return true; +} diff --git a/mobile/src/types/index.ts b/mobile/src/types/index.ts index ee4da34..b040b11 100644 --- a/mobile/src/types/index.ts +++ b/mobile/src/types/index.ts @@ -1,8 +1,37 @@ // Mobile-specific type aliases and extensions not covered by shared types -export type RootStackParamList = { - Auth: undefined; - Main: undefined; - BookReader: { bookId: number }; - BookDetail: { bookId: number }; -}; \ No newline at end of file +import type { NavigatorScreenParams } from "@react-navigation/native"; +import type { MainTabParamList } from "../navigation/MainNavigator"; + +/** Stack hosted above the bottom tabs once the user is authenticated. */ +export type AppStackParamList = { + Tabs: NavigatorScreenParams | undefined; + BookDetail: { ebookId: number }; + Reader: { ebookId: number }; +}; + +/** EPUB bookmark/highlight, mirrors frontend/src/types Bookmark. */ +export interface Bookmark { + id: string; + ebook: number; + ebook_title: string; + epub_cfi: string; + chapter_index: number; + chapter_title: string; + page: number; + location_text: string; + content: string; + highlight_color?: string; + created_at: string; + updated_at: string; +} + +export interface CreateMarkerPayload { + ebook: number; + epub_cfi: string; + chapter_index: number; + chapter_title?: string; + location_text?: string; + content?: string; + highlight_color?: string; +} diff --git a/mobile/src/types/reader.ts b/mobile/src/types/reader.ts new file mode 100644 index 0000000..2670a86 --- /dev/null +++ b/mobile/src/types/reader.ts @@ -0,0 +1,32 @@ +/** + * Reader module types for the mobile app. Mirrors + * frontend/src/types/reader.ts so settings and progress behave the same + * way across web and mobile. + */ + +export interface ReadingSettings { + font_family: "sans-serif" | "serif" | "monospace"; + font_size: number; + line_height: number; + margin_width: number; + background_color: string; + text_color: string; + brightness: number; + orientation_lock: "auto" | "portrait" | "landscape"; + theme: "sepia" | "dark" | "light" | "paper"; + created_at: string; + updated_at: string; +} + +export type ThemePreset = ReadingSettings["theme"]; +export type FontFamily = ReadingSettings["font_family"]; + +export interface ReadingProgress { + id: number; + book: number; + current_chapter: number; + current_position: number; + percentage: number; + epub_location: string; + updated_at: string; +} diff --git a/mobile/src/utils/epubTheme.ts b/mobile/src/utils/epubTheme.ts new file mode 100644 index 0000000..d92dacf --- /dev/null +++ b/mobile/src/utils/epubTheme.ts @@ -0,0 +1,66 @@ +import type { ReadingSettings, ThemePreset } from "../types/reader"; + +/** CSS font stacks per family, mirrors frontend/src/utils/epubRendition.ts. */ +export const FONT_STACKS: Record = { + serif: 'Georgia, "Times New Roman", serif', + "sans-serif": + 'system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif', + monospace: 'ui-monospace, "Cascadia Code", monospace', +}; + +/** Background/text/chrome colors for each reader theme preset. */ +export const THEME_PALETTES: Record< + ThemePreset, + { background: string; text: string; chrome: string } +> = { + light: { background: "#ffffff", text: "#1a1a1a", chrome: "#f2f2f2" }, + dark: { background: "#121212", text: "#e0e0e0", chrome: "#1c1c1e" }, + sepia: { background: "#f4ecd8", text: "#5b4636", chrome: "#e8ddc4" }, + paper: { background: "#fbfbf8", text: "#2b2b2b", chrome: "#efefe9" }, +}; + +export function resolvePalette(settings: ReadingSettings) { + const preset = THEME_PALETTES[settings.theme] ?? THEME_PALETTES.light; + return { + background: settings.background_color || preset.background, + text: settings.text_color || preset.text, + chrome: preset.chrome, + }; +} + +/** + * Build an epub.js theme object (selector -> CSS rules) from reader settings. + * Used with the epubjs-react-native `changeTheme` method. + */ +export function buildEpubTheme( + settings: ReadingSettings, +): Record> { + const { background, text } = resolvePalette(settings); + const fontStack = FONT_STACKS[settings.font_family] ?? FONT_STACKS.serif; + const lineHeight = `${settings.line_height} !important`; + const fontFamily = `${fontStack} !important`; + const color = `${text} !important`; + + return { + body: { + background: `${background} !important`, + color, + "font-family": fontFamily, + "line-height": lineHeight, + }, + p: { + color, + "font-family": fontFamily, + "line-height": lineHeight, + }, + li: { color, "font-family": fontFamily, "line-height": lineHeight }, + span: { color }, + a: { color }, + h1: { color }, + h2: { color }, + h3: { color }, + h4: { color }, + h5: { color }, + h6: { color }, + }; +} diff --git a/mobile/tsconfig.json b/mobile/tsconfig.json index 52c6bb3..7d7ab21 100644 --- a/mobile/tsconfig.json +++ b/mobile/tsconfig.json @@ -12,7 +12,8 @@ "forceConsistentCasingInFileNames": true, "baseUrl": ".", "paths": { - "@/*": ["src/*"] + "@/*": ["src/*"], + "@cloud-reader/shared": ["../packages/shared/src/index.ts"] } }, "include": ["**/*.ts", "**/*.tsx"], diff --git a/yarn.lock b/yarn.lock index 0ed8c5a..ab47131 100644 --- a/yarn.lock +++ b/yarn.lock @@ -57,7 +57,7 @@ resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz" integrity sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg== -"@babel/core@^7.0.0", "@babel/core@^7.0.0 || ^8.0.0-0", "@babel/core@^7.0.0-0", "@babel/core@^7.0.0-0 || ^8.0.0-0 <8.0.0", "@babel/core@^7.1.0", "@babel/core@^7.11.0", "@babel/core@^7.11.1", "@babel/core@^7.11.6", "@babel/core@^7.12.0", "@babel/core@^7.12.3", "@babel/core@^7.13.0", "@babel/core@^7.13.16", "@babel/core@^7.16.0", "@babel/core@^7.20.0", "@babel/core@^7.25.2", "@babel/core@^7.28.0", "@babel/core@^7.4.0 || ^8.0.0-0 <8.0.0", "@babel/core@^7.7.2", "@babel/core@^7.8.0": +"@babel/core@^7.1.0", "@babel/core@^7.11.1", "@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.13.16", "@babel/core@^7.16.0", "@babel/core@^7.20.0", "@babel/core@^7.25.2", "@babel/core@^7.28.0", "@babel/core@^7.7.2", "@babel/core@^7.8.0": version "7.29.7" resolved "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz" integrity sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA== @@ -370,6 +370,11 @@ "@babel/helper-create-class-features-plugin" "^7.18.6" "@babel/helper-plugin-utils" "^7.18.6" +"@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2": + version "7.21.0-placeholder-for-preset-env.2" + resolved "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz" + integrity sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w== + "@babel/plugin-proposal-private-property-in-object@^7.16.7": version "7.21.11" resolved "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.11.tgz" @@ -380,11 +385,6 @@ "@babel/helper-plugin-utils" "^7.20.2" "@babel/plugin-syntax-private-property-in-object" "^7.14.5" -"@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2": - version "7.21.0-placeholder-for-preset-env.2" - resolved "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz" - integrity sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w== - "@babel/plugin-syntax-async-generators@^7.8.4": version "7.8.4" resolved "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz" @@ -434,7 +434,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.29.7" -"@babel/plugin-syntax-flow@^7.12.1", "@babel/plugin-syntax-flow@^7.14.5", "@babel/plugin-syntax-flow@^7.29.7": +"@babel/plugin-syntax-flow@^7.12.1", "@babel/plugin-syntax-flow@^7.29.7": version "7.29.7" resolved "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.29.7.tgz" integrity sha512-ajMX6QPcyomotqwpzhkYGxcK2i/us0rs1Qo9QvUpa+Fca0FTmqrzKrctoIYLMxcOhGZldGT/BAVkRGTWBiR8gQ== @@ -898,7 +898,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.29.7" -"@babel/plugin-transform-react-jsx@^7.14.9", "@babel/plugin-transform-react-jsx@^7.25.2", "@babel/plugin-transform-react-jsx@^7.29.7": +"@babel/plugin-transform-react-jsx@^7.25.2", "@babel/plugin-transform-react-jsx@^7.29.7": version "7.29.7" resolved "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.29.7.tgz" integrity sha512-WsZulLVBUHXVj2cUcPVx6UE21TpalB6bHbSFErKT0Ib++ax24jjXe73FqlWvdylFOjiuPHYi6VCcgRad1ItN+A== @@ -1029,7 +1029,7 @@ "@babel/helper-create-regexp-features-plugin" "^7.29.7" "@babel/helper-plugin-utils" "^7.29.7" -"@babel/preset-env@^7.1.6", "@babel/preset-env@^7.11.0", "@babel/preset-env@^7.12.1", "@babel/preset-env@^7.16.4": +"@babel/preset-env@^7.11.0", "@babel/preset-env@^7.12.1", "@babel/preset-env@^7.16.4": version "7.29.7" resolved "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.29.7.tgz" integrity sha512-GYzX36n1nsciIb0uyH0GHwxwtNwPQIcpxSeiVLDtG/B7jB5xXgchnmL1f/jCX5o+pwnaDBtO60ONSJhEBJfxYA== @@ -1211,46 +1211,6 @@ resolved "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== -"@cloud-reader/frontend@file:C:\\Users\\Cristhian\\Projects\\codes\\cloud-reader\\frontend": - version "1.0.0" - resolved "file:frontend" - dependencies: - axios "^1.7.9" - dompurify "^3.4.7" - pdfjs-dist "^4.10.38" - react "^19.0.0" - react-dom "^19.0.0" - react-i18n-lite "^1.0.10" - react-reader "^2.0.15" - react-router-dom "^7.1.0" - -"@cloud-reader/mobile@file:C:\\Users\\Cristhian\\Projects\\codes\\cloud-reader\\mobile": - version "1.0.0" - resolved "file:mobile" - dependencies: - "@cloud-reader/shared" "*" - "@react-native-async-storage/async-storage" "2.1.0" - "@react-navigation/bottom-tabs" "^7.0.0" - "@react-navigation/native" "^7.0.0" - "@react-navigation/native-stack" "^7.0.0" - axios "^1.7.9" - expo "~52.0.0" - expo-document-picker "~13.0.0" - expo-file-system "~18.0.0" - expo-status-bar "~2.0.0" - react "^19.0.0" - react-native "0.76.6" - react-native-gesture-handler "~2.20.0" - react-native-reanimated "~3.16.0" - react-native-safe-area-context "4.14.1" - react-native-screens "~4.4.0" - -"@cloud-reader/shared@*", "@cloud-reader/shared@file:C:\\Users\\Cristhian\\Projects\\codes\\cloud-reader\\packages\\shared": - version "1.0.0" - resolved "file:packages/shared" - dependencies: - typescript "~5.7.0" - "@csstools/color-helpers@^5.1.0": version "5.1.0" resolved "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.1.0.tgz" @@ -1269,12 +1229,12 @@ "@csstools/color-helpers" "^5.1.0" "@csstools/css-calc" "^2.1.4" -"@csstools/css-parser-algorithms@^3.0.4", "@csstools/css-parser-algorithms@^3.0.5": +"@csstools/css-parser-algorithms@^3.0.4": version "3.0.5" resolved "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.5.tgz" integrity sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ== -"@csstools/css-tokenizer@^3.0.3", "@csstools/css-tokenizer@^3.0.4": +"@csstools/css-tokenizer@^3.0.3": version "3.0.4" resolved "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.4.tgz" integrity sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw== @@ -1397,6 +1357,251 @@ dependencies: "@types/hammerjs" "^2.0.36" +"@epubjs-react-native/core@1.4.7": + version "1.4.7" + resolved "https://registry.yarnpkg.com/@epubjs-react-native/core/-/core-1.4.7.tgz#0c19a07a26dc9941805fbe78c6201e7af7c1a70b" + integrity sha512-bDzL0DU13IyfRZRydz36bh7HqDyUV3VKIFYOAYPBejMUAK4LYZclUb+NdgiwyynhhkUZQNdaWgSdtAGzGmsxEw== + +"@epubjs-react-native/expo-file-system@1.1.4": + version "1.1.4" + resolved "https://registry.yarnpkg.com/@epubjs-react-native/expo-file-system/-/expo-file-system-1.1.4.tgz#8699e19053b8e7ed458970177bffc72bce65e276" + integrity sha512-/Ked2Zg9UART5ZYMI1KAVdUOd8mK9xVFD6d7rATfGhzIGbReX4WrR03oG5LdcdCMHHZbDis0ztRnUX9VSScZvw== + +"@esbuild/aix-ppc64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz#c7184a326533fcdf1b8ee0733e21c713b975575f" + integrity sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ== + +"@esbuild/aix-ppc64@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz#80fcbe36130e58b7670511e888b8e88a259ed76c" + integrity sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA== + +"@esbuild/android-arm64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz#09d9b4357780da9ea3a7dfb833a1f1ff439b4052" + integrity sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A== + +"@esbuild/android-arm64@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz#8aa4965f8d0a7982dc21734bf6601323a66da752" + integrity sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg== + +"@esbuild/android-arm@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.21.5.tgz#9b04384fb771926dfa6d7ad04324ecb2ab9b2e28" + integrity sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg== + +"@esbuild/android-arm@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.25.12.tgz#300712101f7f50f1d2627a162e6e09b109b6767a" + integrity sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg== + +"@esbuild/android-x64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.21.5.tgz#29918ec2db754cedcb6c1b04de8cd6547af6461e" + integrity sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA== + +"@esbuild/android-x64@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.25.12.tgz#87dfb27161202bdc958ef48bb61b09c758faee16" + integrity sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg== + +"@esbuild/darwin-arm64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz#e495b539660e51690f3928af50a76fb0a6ccff2a" + integrity sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ== + +"@esbuild/darwin-arm64@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz#79197898ec1ff745d21c071e1c7cc3c802f0c1fd" + integrity sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg== + +"@esbuild/darwin-x64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz#c13838fa57372839abdddc91d71542ceea2e1e22" + integrity sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw== + +"@esbuild/darwin-x64@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz#146400a8562133f45c4d2eadcf37ddd09718079e" + integrity sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA== + +"@esbuild/freebsd-arm64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz#646b989aa20bf89fd071dd5dbfad69a3542e550e" + integrity sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g== + +"@esbuild/freebsd-arm64@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz#1c5f9ba7206e158fd2b24c59fa2d2c8bb47ca0fe" + integrity sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg== + +"@esbuild/freebsd-x64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz#aa615cfc80af954d3458906e38ca22c18cf5c261" + integrity sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ== + +"@esbuild/freebsd-x64@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz#ea631f4a36beaac4b9279fa0fcc6ca29eaeeb2b3" + integrity sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ== + +"@esbuild/linux-arm64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz#70ac6fa14f5cb7e1f7f887bcffb680ad09922b5b" + integrity sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q== + +"@esbuild/linux-arm64@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz#e1066bce58394f1b1141deec8557a5f0a22f5977" + integrity sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ== + +"@esbuild/linux-arm@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz#fc6fd11a8aca56c1f6f3894f2bea0479f8f626b9" + integrity sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA== + +"@esbuild/linux-arm@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz#452cd66b20932d08bdc53a8b61c0e30baf4348b9" + integrity sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw== + +"@esbuild/linux-ia32@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz#3271f53b3f93e3d093d518d1649d6d68d346ede2" + integrity sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg== + +"@esbuild/linux-ia32@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz#b24f8acc45bcf54192c7f2f3be1b53e6551eafe0" + integrity sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA== + +"@esbuild/linux-loong64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz#ed62e04238c57026aea831c5a130b73c0f9f26df" + integrity sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg== + +"@esbuild/linux-loong64@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz#f9cfffa7fc8322571fbc4c8b3268caf15bd81ad0" + integrity sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng== + +"@esbuild/linux-mips64el@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz#e79b8eb48bf3b106fadec1ac8240fb97b4e64cbe" + integrity sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg== + +"@esbuild/linux-mips64el@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz#575a14bd74644ffab891adc7d7e60d275296f2cd" + integrity sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw== + +"@esbuild/linux-ppc64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz#5f2203860a143b9919d383ef7573521fb154c3e4" + integrity sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w== + +"@esbuild/linux-ppc64@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz#75b99c70a95fbd5f7739d7692befe60601591869" + integrity sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA== + +"@esbuild/linux-riscv64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz#07bcafd99322d5af62f618cb9e6a9b7f4bb825dc" + integrity sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA== + +"@esbuild/linux-riscv64@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz#2e3259440321a44e79ddf7535c325057da875cd6" + integrity sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w== + +"@esbuild/linux-s390x@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz#b7ccf686751d6a3e44b8627ababc8be3ef62d8de" + integrity sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A== + +"@esbuild/linux-s390x@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz#17676cabbfe5928da5b2a0d6df5d58cd08db2663" + integrity sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg== + +"@esbuild/linux-x64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz#6d8f0c768e070e64309af8004bb94e68ab2bb3b0" + integrity sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ== + +"@esbuild/linux-x64@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz#0583775685ca82066d04c3507f09524d3cd7a306" + integrity sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw== + +"@esbuild/netbsd-arm64@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz#f04c4049cb2e252fe96b16fed90f70746b13f4a4" + integrity sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg== + +"@esbuild/netbsd-x64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz#bbe430f60d378ecb88decb219c602667387a6047" + integrity sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg== + +"@esbuild/netbsd-x64@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz#77da0d0a0d826d7c921eea3d40292548b258a076" + integrity sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ== + +"@esbuild/openbsd-arm64@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz#6296f5867aedef28a81b22ab2009c786a952dccd" + integrity sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A== + +"@esbuild/openbsd-x64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz#99d1cf2937279560d2104821f5ccce220cb2af70" + integrity sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow== + +"@esbuild/openbsd-x64@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz#f8d23303360e27b16cf065b23bbff43c14142679" + integrity sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw== + +"@esbuild/openharmony-arm64@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz#49e0b768744a3924be0d7fd97dd6ce9b2923d88d" + integrity sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg== + +"@esbuild/sunos-x64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz#08741512c10d529566baba837b4fe052c8f3487b" + integrity sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg== + +"@esbuild/sunos-x64@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz#a6ed7d6778d67e528c81fb165b23f4911b9b13d6" + integrity sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w== + +"@esbuild/win32-arm64@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz#675b7385398411240735016144ab2e99a60fc75d" + integrity sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A== + +"@esbuild/win32-arm64@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz#9ac14c378e1b653af17d08e7d3ce34caef587323" + integrity sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg== + +"@esbuild/win32-ia32@0.21.5": + version "0.21.5" + resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz#1bfc3ce98aa6ca9a0969e4d2af72144c59c1193b" + integrity sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA== + +"@esbuild/win32-ia32@0.25.12": + version "0.25.12" + resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz#918942dcbbb35cc14fca39afb91b5e6a3d127267" + integrity sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ== + "@esbuild/win32-x64@0.21.5": version "0.21.5" resolved "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz" @@ -1651,7 +1856,7 @@ json5 "^2.2.3" write-file-atomic "^2.3.0" -"@expo/metro-config@~0.19.12", "@expo/metro-config@0.19.12": +"@expo/metro-config@0.19.12", "@expo/metro-config@~0.19.12": version "0.19.12" resolved "https://registry.npmjs.org/@expo/metro-config/-/metro-config-0.19.12.tgz" integrity sha512-fhT3x1ikQWHpZgw7VrEghBdscFPz1laRYa8WcVRB18nTTqorF6S8qPYslkJu1faEziHZS7c2uyDzTYnrg/CKbg== @@ -2149,6 +2354,56 @@ resolved "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz" integrity sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw== +"@napi-rs/canvas-android-arm64@0.1.100": + version "0.1.100" + resolved "https://registry.yarnpkg.com/@napi-rs/canvas-android-arm64/-/canvas-android-arm64-0.1.100.tgz#b7c68b91d57702a5fc523fa82de72ea741e6766e" + integrity sha512-hjhCKhntPv9+t4ckHymdx0phYNcVW+GKQR6Lzw2zE+pOVjOplSmtx9nNNknTjbEDLcuLZqA1y8ufKg1XfgftzQ== + +"@napi-rs/canvas-darwin-arm64@0.1.100": + version "0.1.100" + resolved "https://registry.yarnpkg.com/@napi-rs/canvas-darwin-arm64/-/canvas-darwin-arm64-0.1.100.tgz#d1686fa6ca699b07640efa5f45425db0a4e725e2" + integrity sha512-2PcswRaC7Ly645DGt88///zuFDhJxJYdKAs1uU3mfk1atYkXufgcgLfBpk6Tm12nCQBaNt1wpybuPZ4qOhTo8A== + +"@napi-rs/canvas-darwin-x64@0.1.100": + version "0.1.100" + resolved "https://registry.yarnpkg.com/@napi-rs/canvas-darwin-x64/-/canvas-darwin-x64-0.1.100.tgz#92978fd7eca21f7f1b59bd13ba3ce7c0e7c879a1" + integrity sha512-ePNZtj7pNIva/siZMg+HmbeozkIjqUIYdoymH8HaA3qK7LfzFN4WMBM8G6HQ9ZC+H3+Dnn5pqtiXpgLykaPOhw== + +"@napi-rs/canvas-linux-arm-gnueabihf@0.1.100": + version "0.1.100" + resolved "https://registry.yarnpkg.com/@napi-rs/canvas-linux-arm-gnueabihf/-/canvas-linux-arm-gnueabihf-0.1.100.tgz#95f9892e1d5a8274871d8ee406374e9ef692bd9f" + integrity sha512-d5cDB48oWFGU8/XPhUOFAlySgb/VAu7D+s8fi55K1Pcfg8aPplHWqMgibhVLU8ky7Pyg/fuiVLz4Nf3JrSTuUA== + +"@napi-rs/canvas-linux-arm64-gnu@0.1.100": + version "0.1.100" + resolved "https://registry.yarnpkg.com/@napi-rs/canvas-linux-arm64-gnu/-/canvas-linux-arm64-gnu-0.1.100.tgz#6b2b7c4bb016b8f5308115ac9ae909df4967a94b" + integrity sha512-rDxgxRu69RvDlX/bh9o22DxLsGr8EqsNgotL9+RwQE1S0b0cqeatqsw6aW45mukm0B42DIAaAacKaYQ8cqS1nw== + +"@napi-rs/canvas-linux-arm64-musl@0.1.100": + version "0.1.100" + resolved "https://registry.yarnpkg.com/@napi-rs/canvas-linux-arm64-musl/-/canvas-linux-arm64-musl-0.1.100.tgz#48cf6342543e4f87cf1f8e9b1aaa19ad85bcc178" + integrity sha512-K3mDW66N+xT2/V439u1alFANiBUjdEx2gLiNYnCmUsva5jZMxWTjafBYwTzYK+EMFMHrUoabuU+T1BIP5CgbYQ== + +"@napi-rs/canvas-linux-riscv64-gnu@0.1.100": + version "0.1.100" + resolved "https://registry.yarnpkg.com/@napi-rs/canvas-linux-riscv64-gnu/-/canvas-linux-riscv64-gnu-0.1.100.tgz#71b011007b03755c834a961735302c5837b041da" + integrity sha512-mooqUBTIsccZpnoQC4NgrC1v6C1vof39etLNMnBwCY+p0gajWJvAHLGQ6g/gGyS5YrpDW+GefSN4+Cvcr08UWw== + +"@napi-rs/canvas-linux-x64-gnu@0.1.100": + version "0.1.100" + resolved "https://registry.yarnpkg.com/@napi-rs/canvas-linux-x64-gnu/-/canvas-linux-x64-gnu-0.1.100.tgz#3f479d3b8b8c4658e5dec1b943ad7d2eefd2811f" + integrity sha512-1eCvkDCazm7FFhsT7DfGOdSaHgZVK3bt/dSBl5EWHOWmnz+I7j8tPseJqqD81NF+MH21jKUK4wQSDjN0mdhnTg== + +"@napi-rs/canvas-linux-x64-musl@0.1.100": + version "0.1.100" + resolved "https://registry.yarnpkg.com/@napi-rs/canvas-linux-x64-musl/-/canvas-linux-x64-musl-0.1.100.tgz#1beaca22c1fe97709a9c287115cb3c90194ddec6" + integrity sha512-20arT6lnI19S68qNlii73TSEDbECNgzMz2EpldC1V3mZFuRkeujXkcebRk0LRJe9SEUAooYiLokfMViY8IX7yA== + +"@napi-rs/canvas-win32-arm64-msvc@0.1.100": + version "0.1.100" + resolved "https://registry.yarnpkg.com/@napi-rs/canvas-win32-arm64-msvc/-/canvas-win32-arm64-msvc-0.1.100.tgz#8a1728b455dd17965f95c0bfdf6753be8c5851c0" + integrity sha512-DZFFT1wIAg37LJw37yhMRFfjATd3vTQzjZ1Yki8u2vhO6Hi5VE6BVaGQ1aaDu7xb4iMErz+9EOwjpS7xcxFeBw== + "@napi-rs/canvas-win32-x64-msvc@0.1.100": version "0.1.100" resolved "https://registry.npmjs.org/@napi-rs/canvas-win32-x64-msvc/-/canvas-win32-x64-msvc-0.1.100.tgz" @@ -2186,7 +2441,7 @@ "@nodelib/fs.stat" "2.0.5" run-parallel "^1.1.9" -"@nodelib/fs.stat@^2.0.2", "@nodelib/fs.stat@2.0.5": +"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": version "2.0.5" resolved "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz" integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== @@ -2522,7 +2777,7 @@ sf-symbols-typescript "^2.1.0" warn-once "^0.1.1" -"@react-navigation/native@^7.0.0", "@react-navigation/native@^7.2.5": +"@react-navigation/native@^7.0.0": version "7.2.5" resolved "https://registry.npmjs.org/@react-navigation/native/-/native-7.2.5.tgz" integrity sha512-01AAUQiiHQAfTabq+ZyU1/ZWq+AbB/J3v0CB0UTJSON6M6cuadWNsbChzrZUdqQvHrXvg96U5i2PQLJzK3+zpg== @@ -2582,6 +2837,121 @@ estree-walker "^1.0.1" picomatch "^2.2.2" +"@rollup/rollup-android-arm-eabi@4.60.4": + version "4.60.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.4.tgz#3a04f01e9f01392bbef5920b94aa3b88794be7ab" + integrity sha512-F5QXMSiFebS9hKZj02XhWLLnRpJ3B3AROP0tWbFBSj+6kCbg5m9j5JoHKd4mmSVy5mS/IMQloYgYxCuJC0fxEQ== + +"@rollup/rollup-android-arm64@4.60.4": + version "4.60.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.4.tgz#e371b653ceabc900790ae73f5548a0fd7cd63a70" + integrity sha512-GxxTKApUpzRhof7poWvCJHRF51C67u1R7D6DiluBE8wKU1u5GWE8t+v81JvJYtbawoBFX1hLv5Ei4eVjkWokaw== + +"@rollup/rollup-darwin-arm64@4.60.4": + version "4.60.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.4.tgz#2a5aa70432e39816d666d79287a7324cfc3b4e72" + integrity sha512-tua0TaJxMOB1R0V0RS1jFZ/RpURFDJIOR2A6jWwQeawuFyS4gBW+rntLRaQd0EQ4bd6Vp44Z2rXW+YYDBsj6IA== + +"@rollup/rollup-darwin-x64@4.60.4": + version "4.60.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.4.tgz#c3b5b49629379cd9cdc5d841bf00ed44ebf393dd" + integrity sha512-CSKq7MsP+5PFIcydhAiR1K0UhEI1A2jWXVKHPCBZ151yOutENwvnPocgVHkivu2kviURtCEB6zUQw0vs8RrhMg== + +"@rollup/rollup-freebsd-arm64@4.60.4": + version "4.60.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.4.tgz#f929d8e0462fae6602fc960beeabd7287d859283" + integrity sha512-+O8OkVdyvXMtJEciu2wS/pzm1IxntEEQx3z5TAVy4l32G0etZn+RsA48ARRrFm6Ri8fvqPQfgrvNxSjKAbnd3g== + +"@rollup/rollup-freebsd-x64@4.60.4": + version "4.60.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.4.tgz#c01cb58031226f95d0900b1ec847f4fb32c6e809" + integrity sha512-Iw3oMskH3AfNuhU0MSN7vNbdi4me/NiYo2azqPz/Le16zHSa+3RRmliCMWWQmh4lcndccU40xcJuTYJZxNo/lw== + +"@rollup/rollup-linux-arm-gnueabihf@4.60.4": + version "4.60.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.4.tgz#f29d890c4858c8e0d3be01677eef4f6a359eed9d" + integrity sha512-EIPRXTVQpHyF8WOo219AD2yEltPehLTcTMz2fn6JsatLYSzQf00hj3rulF+yauOlF9/FtM2WpkT/hJh/KJFGhA== + +"@rollup/rollup-linux-arm-musleabihf@4.60.4": + version "4.60.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.4.tgz#1ebfc8eb9f66136ed2faae5f44995add5ca3c964" + integrity sha512-J3Yh9PzzF1Ovah2At+lHiGQdsYgArxBbXv/zHfSyaiFQEqvNv7DcW98pCrmdjCZBrqBiKrKKe2V+aaSGWuBe/w== + +"@rollup/rollup-linux-arm64-gnu@4.60.4": + version "4.60.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.4.tgz#c1fa823c2c4ce46ba7f61de1a4c3fdadd4fb4e7b" + integrity sha512-BFDEZMYfUvLn37ONE1yMBojPxnMlTFsdyNoqncT0qFq1mAfllL+ATMMJd8TeuVMiX84s1KbcxcZbXInmcO2mRg== + +"@rollup/rollup-linux-arm64-musl@4.60.4": + version "4.60.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.4.tgz#a7f18854d0471b78bda8ea38f0891a4e059b571d" + integrity sha512-pc9EYOSlOgdQ2uPl1o9PF6/kLSgaUosia7gOuS8mB69IxJvlclko1MECXysjs5ryez1/5zjYqx3+xYU0TU6R1A== + +"@rollup/rollup-linux-loong64-gnu@4.60.4": + version "4.60.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.4.tgz#83658a9a4576bcce8cef85b2c78b9b649d2200c4" + integrity sha512-NxnomyxYerDh5n4iLrNa+sH+Z+U4BMEE46V2PgQ/hoB909i8gV1M5wPojWg9fk1jWpO3IQnOs20K4wyZuFLEFQ== + +"@rollup/rollup-linux-loong64-musl@4.60.4": + version "4.60.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.4.tgz#fd2af677ae3417bb58d57ae37dd0d84686e40244" + integrity sha512-nbJnQ8a3z1mtmrwImCYhc6BGpThAyYVRQxw9uKSKG4wR6aAYno9sVjJ0zaZcW9BPJX1GbrDPf+SvdWjgTuDmnw== + +"@rollup/rollup-linux-ppc64-gnu@4.60.4": + version "4.60.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.4.tgz#6481647181c4cf8f1ddbd99f62c84cfc56c1a94a" + integrity sha512-2EU6acNrQLd8tYvo/LXW535wupT3m6fo7HKo6lr7ktQoItxTyOL1ZCR/GfGCuXl2vR+zmfI6eRXkSemafv+iVg== + +"@rollup/rollup-linux-ppc64-musl@4.60.4": + version "4.60.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.4.tgz#18610a1a1550e28a5042ca916f898419540f17f4" + integrity sha512-WeBtoMuaMxiiIrO2IYP3xs6GMWkJP2C0EoT8beTLkUPmzV1i/UcOSVw1d5r9KBODtHKilG5yFxsGRnBbK3wJ4A== + +"@rollup/rollup-linux-riscv64-gnu@4.60.4": + version "4.60.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.4.tgz#597bb80465a2621dbe0de0a41c66394a8a7e9a6e" + integrity sha512-FJHFfqpKUI3A10WrWKiFbBZ7yVbGT4q4B5o1qKFFojqpaYoh9LrQgqWCmmcxQzVSXYtyB5bzkXrYzlHTs21MYA== + +"@rollup/rollup-linux-riscv64-musl@4.60.4": + version "4.60.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.4.tgz#a2a919a9f927ef7f24a60af77e3cb55f1ad59e4d" + integrity sha512-mcEl6CUT5IAUmQf1m9FYSmVqCJlpQ8r8eyftFUHG8i9OhY7BkBXSUdnLH5DOf0wCOjcP9v/QO93zpmF1SptCCw== + +"@rollup/rollup-linux-s390x-gnu@4.60.4": + version "4.60.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.4.tgz#3166f6ceae7df9bbfddf9f36be1937231e13e3c6" + integrity sha512-ynt3JxVd2w2buzoKDWIyiV1pJW93xlQic1THVLXilz429oijRpSHivZAgp65KBu+cMcgf1eVVjdnTLvPxgCuoQ== + +"@rollup/rollup-linux-x64-gnu@4.60.4": + version "4.60.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.4.tgz#23c9bf79771d804fb87415eb0767569f273261e5" + integrity sha512-Boiz5+MsaROEWDf+GGEwF8VMHGhlUoQMtIPjOgA5fv4osupqTVnJteQNKJwUcnUog2G55jYXH7KZFFiJe0TEzQ== + +"@rollup/rollup-linux-x64-musl@4.60.4": + version "4.60.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.4.tgz#97941c6b94d67fe25cde0f027c10a19f2d1fdd39" + integrity sha512-+qfSY27qIrFfI/Hom04KYFw3GKZSGU4lXus51wsb5EuySfFlWRwjkKWoE9emgRw/ukoT4Udsj4W/+xxG8VbPKg== + +"@rollup/rollup-openbsd-x64@4.60.4": + version "4.60.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.4.tgz#7aeb7d92e2cd1d399f56daf75c39040b777b6c77" + integrity sha512-VpTfOPHgVXEBeeR8hZ2O0F3aSso+JDWqTWmTmzcQKted54IAdUVbxE+j/MVxUsKa8L20HJhv3vUezVPoquqWjA== + +"@rollup/rollup-openharmony-arm64@4.60.4": + version "4.60.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.4.tgz#925de61ae83bf99aa636e8acea87432e8c0ffaab" + integrity sha512-IPOsh5aRYuLv/nkU51X10Bf75Bsf6+gZdx1X+QP5QM6lIJFHHqbHLG0uJn/hWthzo13UAc2umiUorqZy3axoZg== + +"@rollup/rollup-win32-arm64-msvc@4.60.4": + version "4.60.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.4.tgz#888ab83842721491044c46a7407e1f38f3235bb4" + integrity sha512-4QzE9E81OohJ/HKzHhsqU+zcYYojVOXlFMs1DdyMT6qXl/niOH7AVElmmEdUNHHS/oRkc++d5k6Vy85zFs0DEw== + +"@rollup/rollup-win32-ia32-msvc@4.60.4": + version "4.60.4" + resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.4.tgz#fa30ac24e3f0232139d2a47500560a28695764d4" + integrity sha512-zTPgT1YuHHcd+Tmx7h8aml0FWFVelV5N54oHow9SLj+GfoDy/huQ+UV396N/C7KpMDMiPspRktzM1/0r1usYEA== + "@rollup/rollup-win32-x64-gnu@4.60.4": version "4.60.4" resolved "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.4.tgz" @@ -2785,7 +3155,7 @@ resolved "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz" integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw== -"@types/babel__core@^7.0.0", "@types/babel__core@^7.1.14", "@types/babel__core@^7.1.9", "@types/babel__core@^7.20.5": +"@types/babel__core@^7.0.0", "@types/babel__core@^7.1.14", "@types/babel__core@^7.20.5": version "7.20.5" resolved "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz" integrity sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA== @@ -2987,7 +3357,7 @@ dependencies: "@types/node" "*" -"@types/node@*", "@types/node@^18.0.0 || ^20.0.0 || >=22.0.0", "@types/node@^18.0.0 || >=20.0.0": +"@types/node@*": version "25.9.1" resolved "https://registry.npmjs.org/@types/node/-/node-25.9.1.tgz" integrity sha512-xfrlY7UD5rMJk3ZVJP8BNzS28J36YJg+xp+LPXV1TdWxr8uMH5A860QNxYDGQe/ylDSgjxE52Q9VnO7p75tJxg== @@ -3019,12 +3389,12 @@ resolved "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz" integrity sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ== -"@types/react-dom@^18.0.0 || ^19.0.0", "@types/react-dom@^19.0.0": +"@types/react-dom@^19.0.0": version "19.2.3" resolved "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz" integrity sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ== -"@types/react@^18.0.0 || ^19.0.0", "@types/react@^18.2.6", "@types/react@^19.0.0", "@types/react@^19.2.0": +"@types/react@^19.0.0": version "19.2.15" resolved "https://registry.npmjs.org/@types/react/-/react-19.2.15.tgz" integrity sha512-eRwcGNHve+E8qtEQSSRl6urh+rFop4v8gm6O8rGv25CodbvFdLjA1vVQ1KkiFE0w0UPOnb8tDiFKL5lp0rtY5Q== @@ -3106,7 +3476,7 @@ resolved "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz" integrity sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw== -"@types/trusted-types@^2.0.2", "@types/trusted-types@^2.0.7": +"@types/trusted-types@^2.0.2": version "2.0.7" resolved "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz" integrity sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw== @@ -3137,7 +3507,7 @@ dependencies: "@types/yargs-parser" "*" -"@typescript-eslint/eslint-plugin@^4.0.0 || ^5.0.0", "@typescript-eslint/eslint-plugin@^5.5.0": +"@typescript-eslint/eslint-plugin@^5.5.0": version "5.62.0" resolved "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz" integrity sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag== @@ -3160,7 +3530,7 @@ dependencies: "@typescript-eslint/utils" "5.62.0" -"@typescript-eslint/parser@^5.0.0", "@typescript-eslint/parser@^5.5.0": +"@typescript-eslint/parser@^5.5.0": version "5.62.0" resolved "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.62.0.tgz" integrity sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA== @@ -3206,7 +3576,7 @@ semver "^7.3.7" tsutils "^3.21.0" -"@typescript-eslint/utils@^5.58.0", "@typescript-eslint/utils@5.62.0": +"@typescript-eslint/utils@5.62.0", "@typescript-eslint/utils@^5.58.0": version "5.62.0" resolved "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz" integrity sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ== @@ -3280,7 +3650,7 @@ estree-walker "^3.0.3" magic-string "^0.30.12" -"@vitest/pretty-format@^2.1.9", "@vitest/pretty-format@2.1.9": +"@vitest/pretty-format@2.1.9", "@vitest/pretty-format@^2.1.9": version "2.1.9" resolved "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.9.tgz" integrity sha512-KhRIdGV2U9HOUzxfiHmY8IFHTdqtOhIzCpd8WRdJiE7D/HUcZVD0EgQCVjm+Q9gkUXWgBvMmTtZgIG48wq7sOQ== @@ -3320,7 +3690,7 @@ loupe "^3.1.2" tinyrainbow "^1.2.0" -"@webassemblyjs/ast@^1.14.1", "@webassemblyjs/ast@1.14.1": +"@webassemblyjs/ast@1.14.1", "@webassemblyjs/ast@^1.14.1": version "1.14.1" resolved "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz" integrity sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ== @@ -3421,7 +3791,7 @@ "@webassemblyjs/wasm-gen" "1.14.1" "@webassemblyjs/wasm-parser" "1.14.1" -"@webassemblyjs/wasm-parser@^1.14.1", "@webassemblyjs/wasm-parser@1.14.1": +"@webassemblyjs/wasm-parser@1.14.1", "@webassemblyjs/wasm-parser@^1.14.1": version "1.14.1" resolved "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz" integrity sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ== @@ -3504,16 +3874,16 @@ acorn-walk@^7.1.1: resolved "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz" integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA== -"acorn@^6.0.0 || ^7.0.0 || ^8.0.0", acorn@^8.14.0, acorn@^8.15.0, acorn@^8.16.0, acorn@^8.2.4, acorn@^8.9.0: - version "8.16.0" - resolved "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz" - integrity sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw== - acorn@^7.1.1: version "7.4.1" resolved "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz" integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== +acorn@^8.15.0, acorn@^8.16.0, acorn@^8.2.4, acorn@^8.9.0: + version "8.16.0" + resolved "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz" + integrity sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw== + address@^1.0.1, address@^1.1.2: version "1.2.2" resolved "https://registry.npmjs.org/address/-/address-1.2.2.tgz" @@ -3527,16 +3897,6 @@ adjust-sourcemap-loader@^4.0.0: loader-utils "^2.0.0" regex-parser "^2.2.11" -agent-base@^7.1.0: - version "7.1.4" - resolved "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz" - integrity sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ== - -agent-base@^7.1.2: - version "7.1.4" - resolved "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz" - integrity sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ== - agent-base@6: version "6.0.2" resolved "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz" @@ -3544,6 +3904,11 @@ agent-base@6: dependencies: debug "4" +agent-base@^7.1.0, agent-base@^7.1.2: + version "7.1.4" + resolved "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz" + integrity sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ== + aggregate-error@^3.0.0: version "3.1.0" resolved "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz" @@ -3571,7 +3936,7 @@ ajv-keywords@^5.1.0: dependencies: fast-deep-equal "^3.1.3" -ajv@^6.12.2, ajv@^6.12.4, ajv@^6.12.5, ajv@^6.9.1: +ajv@^6.12.2, ajv@^6.12.4, ajv@^6.12.5: version "6.15.0" resolved "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz" integrity sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw== @@ -3581,17 +3946,7 @@ ajv@^6.12.2, ajv@^6.12.4, ajv@^6.12.5, ajv@^6.9.1: json-schema-traverse "^0.4.1" uri-js "^4.2.2" -ajv@^8.0.0, ajv@^8.8.2, ajv@^8.9.0: - version "8.20.0" - resolved "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz" - integrity sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA== - dependencies: - fast-deep-equal "^3.1.3" - fast-uri "^3.0.1" - json-schema-traverse "^1.0.0" - require-from-string "^2.0.2" - -ajv@^8.6.0, ajv@>=8: +ajv@^8.0.0, ajv@^8.6.0, ajv@^8.9.0: version "8.20.0" resolved "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz" integrity sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA== @@ -4225,13 +4580,6 @@ bplist-creator@0.1.0: dependencies: stream-buffers "2.2.x" -bplist-parser@^0.3.1: - 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== - dependencies: - big-integer "1.6.x" - bplist-parser@0.3.1: version "0.3.1" resolved "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.3.1.tgz" @@ -4239,6 +4587,13 @@ bplist-parser@0.3.1: dependencies: big-integer "1.6.x" +bplist-parser@^0.3.1: + 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== + dependencies: + big-integer "1.6.x" + brace-expansion@^1.1.7: version "1.1.15" resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz" @@ -4266,7 +4621,7 @@ browser-process-hrtime@^1.0.0: resolved "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz" integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow== -browserslist@^4.0.0, browserslist@^4.18.1, browserslist@^4.21.4, browserslist@^4.24.0, browserslist@^4.28.1, browserslist@^4.28.2, "browserslist@>= 4", "browserslist@>= 4.21.0", browserslist@>=4: +browserslist@^4.0.0, browserslist@^4.18.1, browserslist@^4.21.4, browserslist@^4.24.0, browserslist@^4.28.1, browserslist@^4.28.2: version "4.28.2" resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz" integrity sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg== @@ -4320,7 +4675,7 @@ builtin-modules@^3.1.0: resolved "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.3.0.tgz" integrity sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw== -bytes@~3.1.2, bytes@3.1.2: +bytes@3.1.2, bytes@~3.1.2: version "3.1.2" resolved "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz" integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== @@ -4452,25 +4807,7 @@ chai@^5.1.2: loupe "^3.1.0" pathval "^2.0.0" -chalk@^2.0.1: - version "2.4.2" - resolved "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz" - integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== - dependencies: - ansi-styles "^3.2.1" - escape-string-regexp "^1.0.5" - supports-color "^5.3.0" - -chalk@^2.4.1: - version "2.4.2" - resolved "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz" - integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== - dependencies: - ansi-styles "^3.2.1" - escape-string-regexp "^1.0.5" - supports-color "^5.3.0" - -chalk@^2.4.2: +chalk@^2.0.1, chalk@^2.4.1, chalk@^2.4.2: version "2.4.2" resolved "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== @@ -4663,16 +5000,16 @@ color-convert@^2.0.1: dependencies: color-name "~1.1.4" -color-name@^1.0.0, color-name@~1.1.4: - version "1.1.4" - resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" - integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== - color-name@1.1.3: version "1.1.3" resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz" integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== +color-name@^1.0.0, color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + color-string@^1.9.0: version "1.9.1" resolved "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz" @@ -4995,14 +5332,6 @@ css-select@^4.1.3: domutils "^2.8.0" nth-check "^2.0.1" -css-tree@^1.1.2, css-tree@^1.1.3: - version "1.1.3" - resolved "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz" - integrity sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q== - dependencies: - mdn-data "2.0.14" - source-map "^0.6.1" - css-tree@1.0.0-alpha.37: version "1.0.0-alpha.37" resolved "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz" @@ -5011,6 +5340,14 @@ css-tree@1.0.0-alpha.37: mdn-data "2.0.4" source-map "^0.6.1" +css-tree@^1.1.2, css-tree@^1.1.3: + version "1.1.3" + resolved "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz" + integrity sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q== + dependencies: + mdn-data "2.0.14" + source-map "^0.6.1" + css-what@^3.2.1: version "3.4.2" resolved "https://registry.npmjs.org/css-what/-/css-what-3.4.2.tgz" @@ -5122,7 +5459,7 @@ csstype@^3.0.2, csstype@^3.2.2: resolved "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz" integrity sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ== -d@^1.0.1, d@^1.0.2, d@1: +d@1, d@^1.0.1, d@^1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/d/-/d-1.0.2.tgz" integrity sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw== @@ -5179,54 +5516,26 @@ data-view-byte-offset@^1.0.1: es-errors "^1.3.0" is-data-view "^1.0.1" -debug@^2.2.0: +debug@2.6.9, debug@^2.2.0, debug@^2.6.0, debug@^2.6.9: version "2.6.9" resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz" integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== dependencies: ms "2.0.0" -debug@^2.6.0: - version "2.6.9" - resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz" - integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== - dependencies: - ms "2.0.0" - -debug@^2.6.9: - version "2.6.9" - resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz" - integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== - dependencies: - ms "2.0.0" - -debug@^3.1.0: - version "3.2.7" - resolved "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz" - integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== - dependencies: - ms "^2.1.1" - -debug@^3.2.7: - version "3.2.7" - resolved "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz" - integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== - dependencies: - ms "^2.1.1" - -debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4, debug@^4.3.5, debug@^4.3.7, debug@^4.4.3, debug@4: +debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4, debug@^4.3.5, debug@^4.3.7, debug@^4.4.3: version "4.4.3" resolved "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz" integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA== dependencies: ms "^2.1.3" -debug@2.6.9: - version "2.6.9" - resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz" - integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== +debug@^3.1.0, debug@^3.2.7: + version "3.2.7" + resolved "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz" + integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== dependencies: - ms "2.0.0" + ms "^2.1.1" decimal.js@^10.2.1, decimal.js@^10.4.3: version "10.6.0" @@ -5327,17 +5636,17 @@ delayed-stream@~1.0.0: resolved "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz" integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== +depd@2.0.0, depd@~2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz" + integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== + depd@~1.1.2: version "1.1.2" resolved "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz" integrity sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ== -depd@~2.0.0, depd@2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz" - integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== - -destroy@~1.2.0, destroy@1.2.0: +destroy@1.2.0, destroy@~1.2.0: version "1.2.0" resolved "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz" integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== @@ -5420,6 +5729,14 @@ dom-converter@^0.2.0: dependencies: utila "~0.4" +dom-serializer@0: + version "0.2.2" + resolved "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz" + integrity sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g== + dependencies: + domelementtype "^2.0.1" + entities "^2.0.0" + dom-serializer@^1.0.1: version "1.4.1" resolved "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz" @@ -5429,24 +5746,16 @@ dom-serializer@^1.0.1: domhandler "^4.2.0" entities "^2.0.0" -dom-serializer@0: - version "0.2.2" - resolved "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz" - integrity sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g== - dependencies: - domelementtype "^2.0.1" - entities "^2.0.0" +domelementtype@1: + version "1.3.1" + resolved "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz" + integrity sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w== domelementtype@^2.0.1, domelementtype@^2.2.0: version "2.3.0" resolved "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz" integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw== -domelementtype@1: - version "1.3.1" - resolved "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz" - integrity sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w== - domexception@^2.0.1: version "2.0.1" resolved "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz" @@ -5461,13 +5770,6 @@ domhandler@^4.0.0, domhandler@^4.2.0, domhandler@^4.3.1: dependencies: domelementtype "^2.2.0" -dompurify@^3.4.7: - version "3.4.7" - resolved "https://registry.npmjs.org/dompurify/-/dompurify-3.4.7.tgz" - integrity sha512-2jBxDJY4RR06tQNy4w5FlFH7kfxsQZlufd0sbv+chfHCxeJwrFw2baUDsSwvBISD4K4RDbd0PTfy3uNXsR6siA== - optionalDependencies: - "@types/trusted-types" "^2.0.7" - domutils@^1.7.0: version "1.7.0" resolved "https://registry.npmjs.org/domutils/-/domutils-1.7.0.tgz" @@ -6047,7 +6349,7 @@ eslint-plugin-testing-library@^5.0.1: dependencies: "@typescript-eslint/utils" "^5.58.0" -eslint-scope@^5.1.1, eslint-scope@5.1.1: +eslint-scope@5.1.1, eslint-scope@^5.1.1: version "5.1.1" resolved "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz" integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== @@ -6084,7 +6386,7 @@ eslint-webpack-plugin@^3.1.1: normalize-path "^3.0.0" schema-utils "^4.0.0" -eslint@*, "eslint@^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9", "eslint@^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9", "eslint@^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7", "eslint@^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0", "eslint@^6.0.0 || ^7.0.0 || ^8.0.0", "eslint@^6.0.0 || ^7.0.0 || >=8.0.0", "eslint@^7.0.0 || ^8.0.0", "eslint@^7.5.0 || ^8.0.0", "eslint@^7.5.0 || ^8.0.0 || ^9.0.0", eslint@^8.0.0, eslint@^8.1.0, eslint@^8.3.0, "eslint@>= 6": +eslint@^8.3.0: version "8.57.1" resolved "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz" integrity sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA== @@ -6147,16 +6449,16 @@ espree@^9.6.0, espree@^9.6.1: acorn-jsx "^5.3.2" eslint-visitor-keys "^3.4.1" -esprima@^4.0.0, esprima@^4.0.1, esprima@~4.0.0: - version "4.0.1" - resolved "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz" - integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== - esprima@1.2.5: version "1.2.5" resolved "https://registry.npmjs.org/esprima/-/esprima-1.2.5.tgz" integrity sha512-S9VbPDU0adFErpDai3qDkjq8+G05ONtKzcyNrPKg/ZKa+tf879nX2KexNU95b31UoTJjRLInNBHHHjFPoCd7lQ== +esprima@^4.0.0, esprima@^4.0.1, esprima@~4.0.0: + version "4.0.1" + resolved "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz" + integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== + esquery@^1.4.2: version "1.7.0" resolved "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz" @@ -6292,11 +6594,6 @@ expo-constants@~17.0.8: "@expo/config" "~10.0.11" "@expo/env" "~0.4.2" -expo-document-picker@~13.0.0: - version "13.0.3" - resolved "https://registry.npmjs.org/expo-document-picker/-/expo-document-picker-13.0.3.tgz" - integrity sha512-348xcsiA/YhgWm1SuJNNdb5cUDpRJYCyIk8MhOU2MEDxbVRR+Q1TiUBTCIMVqaWHcxsFQzP56Wwv9n24qjeILg== - expo-file-system@~18.0.0, expo-file-system@~18.0.12: version "18.0.12" resolved "https://registry.npmjs.org/expo-file-system/-/expo-file-system-18.0.12.tgz" @@ -6342,7 +6639,7 @@ expo-status-bar@~2.0.0: resolved "https://registry.npmjs.org/expo-status-bar/-/expo-status-bar-2.0.1.tgz" integrity sha512-AkIPX7jWHRPp83UBZ1iXtVvyr0g+DgBVvIXTtlmPtmUsm8Vq9Bb5IGj86PW8osuFlgoTVAg7HI/+Ok7yEYwiRg== -expo@*, expo@~52.0.0: +expo@~52.0.0: version "52.0.49" resolved "https://registry.npmjs.org/expo/-/expo-52.0.49.tgz" integrity sha512-ge3gUnuyGEePWWKzPY7TQ7FsvtFTdmsdYDHeBVUjMr9KIoQig/gf8A03oH26p3UtTL6sUJcyOIg9vwIHGNPSUw== @@ -6492,12 +6789,7 @@ fbjs@^3.0.0: setimmediate "^1.0.5" ua-parser-js "^1.0.35" -fdir@^6.4.4: - version "6.5.0" - resolved "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz" - integrity sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg== - -fdir@^6.5.0: +fdir@^6.4.4, fdir@^6.5.0: version "6.5.0" resolved "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz" integrity sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg== @@ -6546,19 +6838,6 @@ filter-obj@^1.1.0: resolved "https://registry.npmjs.org/filter-obj/-/filter-obj-1.1.0.tgz" integrity sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ== -finalhandler@~1.3.1: - version "1.3.2" - resolved "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz" - integrity sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg== - dependencies: - debug "2.6.9" - encodeurl "~2.0.0" - escape-html "~1.0.3" - on-finished "~2.4.1" - parseurl "~1.3.3" - statuses "~2.0.2" - unpipe "~1.0.0" - finalhandler@1.1.2: version "1.1.2" resolved "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz" @@ -6572,6 +6851,19 @@ finalhandler@1.1.2: statuses "~1.5.0" unpipe "~1.0.0" +finalhandler@~1.3.1: + version "1.3.2" + resolved "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz" + integrity sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg== + dependencies: + debug "2.6.9" + encodeurl "~2.0.0" + escape-html "~1.0.3" + on-finished "~2.4.1" + parseurl "~1.3.3" + statuses "~2.0.2" + unpipe "~1.0.0" + find-cache-dir@^2.0.0: version "2.1.0" resolved "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz" @@ -6597,15 +6889,7 @@ find-up@^3.0.0: dependencies: locate-path "^3.0.0" -find-up@^4.0.0: - version "4.1.0" - resolved "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz" - integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== - dependencies: - locate-path "^5.0.0" - path-exists "^4.0.0" - -find-up@^4.1.0: +find-up@^4.0.0, find-up@^4.1.0: version "4.1.0" resolved "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz" integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== @@ -6700,18 +6984,7 @@ form-data@^3.0.0, form-data@^3.0.1: hasown "^2.0.2" mime-types "^2.1.35" -form-data@^4.0.0: - version "4.0.5" - resolved "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz" - integrity sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w== - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.8" - es-set-tostringtag "^2.1.0" - hasown "^2.0.2" - mime-types "^2.1.12" - -form-data@^4.0.5: +form-data@^4.0.0, form-data@^4.0.5: version "4.0.5" resolved "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz" integrity sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w== @@ -6742,6 +7015,16 @@ fresh@~0.5.2: resolved "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz" integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q== +fs-extra@9.0.0: + version "9.0.0" + resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-9.0.0.tgz" + integrity sha512-pmEYSk3vYsG/bF651KPUXZ+hvjpgWYw/Gc7W9NFUe3ZVLczKKWIij3IKpOrQcdw4TILtibFslZ0UmR8Vvzig4g== + dependencies: + at-least-node "^1.0.0" + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^1.0.0" + fs-extra@^10.0.0: version "10.1.0" resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz" @@ -6770,16 +7053,6 @@ fs-extra@~8.1.0: jsonfile "^4.0.0" universalify "^0.1.0" -fs-extra@9.0.0: - version "9.0.0" - resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-9.0.0.tgz" - integrity sha512-pmEYSk3vYsG/bF651KPUXZ+hvjpgWYw/Gc7W9NFUe3ZVLczKKWIij3IKpOrQcdw4TILtibFslZ0UmR8Vvzig4g== - dependencies: - at-least-node "^1.0.0" - graceful-fs "^4.2.0" - jsonfile "^6.0.1" - universalify "^1.0.0" - fs-minipass@^2.0.0: version "2.1.0" resolved "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz" @@ -6804,6 +7077,11 @@ fs.realpath@^1.0.0: resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== +fsevents@^2.3.2, fsevents@~2.3.2, fsevents@~2.3.3: + version "2.3.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" + integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== + function-bind@^1.1.2: version "1.1.2" resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz" @@ -6920,31 +7198,7 @@ glob-to-regexp@^0.4.1: resolved "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz" integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== -glob@^10.2.2: - version "10.5.0" - resolved "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz" - integrity sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg== - dependencies: - foreground-child "^3.1.0" - jackspeak "^3.1.2" - minimatch "^9.0.4" - minipass "^7.1.2" - package-json-from-dist "^1.0.0" - path-scurry "^1.11.1" - -glob@^10.3.10: - version "10.5.0" - resolved "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz" - integrity sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg== - dependencies: - foreground-child "^3.1.0" - jackspeak "^3.1.2" - minimatch "^9.0.4" - minipass "^7.1.2" - package-json-from-dist "^1.0.0" - path-scurry "^1.11.1" - -glob@^10.4.2: +glob@^10.2.2, glob@^10.3.10, glob@^10.4.2: version "10.5.0" resolved "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz" integrity sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg== @@ -7297,21 +7551,14 @@ human-signals@^2.1.0: resolved "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz" integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== -iconv-lite@^0.6.3: - version "0.6.3" - resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz" - integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== - dependencies: - safer-buffer ">= 2.1.2 < 3.0.0" - -iconv-lite@~0.4.24, iconv-lite@0.4.24: +iconv-lite@0.4.24, iconv-lite@~0.4.24: version "0.4.24" resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz" integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== dependencies: safer-buffer ">= 2.1.2 < 3" -iconv-lite@0.6.3: +iconv-lite@0.6.3, iconv-lite@^0.6.3: version "0.6.3" resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz" integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== @@ -7404,7 +7651,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.3, inherits@~2.0.4, inherits@2, inherits@2.0.4: +inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.3, inherits@~2.0.4: version "2.0.4" resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -7431,7 +7678,7 @@ internal-slot@^1.1.0: hasown "^2.0.2" side-channel "^1.1.0" -invariant@^2.2.4: +invariant@2.2.4, invariant@^2.2.4: version "2.2.4" resolved "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz" integrity sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA== @@ -7443,7 +7690,7 @@ ip-regex@^2.1.0: resolved "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz" integrity sha512-58yWmlHpp7VYfcdTwMTvwMmqx/Elfxjd9RXTDyMsbL7lLWmhMylLEqiYVLKuLzOZqVgiWXD9MfR62Vv89VRxkw== -ipaddr.js@^1.9.0, ipaddr.js@1.9.1: +ipaddr.js@1.9.1, ipaddr.js@^1.9.0: version "1.9.1" resolved "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz" integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== @@ -8172,7 +8419,7 @@ jest-resolve-dependencies@^27.5.1: jest-regex-util "^27.5.1" jest-snapshot "^27.5.1" -jest-resolve@*, jest-resolve@^27.4.2, jest-resolve@^27.5.1: +jest-resolve@^27.4.2, jest-resolve@^27.5.1: version "27.5.1" resolved "https://registry.npmjs.org/jest-resolve/-/jest-resolve-27.5.1.tgz" integrity sha512-FFDy8/9E6CV83IMbDpcjOhumAQPDyETnU2KZ1O98DwTnz8AOBsW/Xv3GySr1mOZdItLR+zDZ7I/UdTFbgSOVCw== @@ -8416,7 +8663,7 @@ jest-worker@^29.7.0: merge-stream "^2.0.0" supports-color "^8.0.0" -"jest@^27.0.0 || ^28.0.0", jest@^27.4.3: +jest@^27.4.3: version "27.5.1" resolved "https://registry.npmjs.org/jest/-/jest-27.5.1.tgz" integrity sha512-Yn0mADZB89zTtjkPJEXwrac3LHudkQMR+Paqa8uxJHCBr9agxztUifWCyiYrjhMPBoUVBjyny0I7XH6ozDr7QQ== @@ -8430,7 +8677,7 @@ jimp-compact@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== -jiti@^1.21.7, jiti@>=1.21.0: +jiti@^1.21.7: version "1.21.7" resolved "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz" integrity sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A== @@ -8495,33 +8742,6 @@ jscodeshift@^0.14.0: temp "^0.8.4" write-file-atomic "^2.3.0" -jsdom@*, jsdom@^25.0.0: - version "25.0.1" - resolved "https://registry.npmjs.org/jsdom/-/jsdom-25.0.1.tgz" - integrity sha512-8i7LzZj7BF8uplX+ZyOlIz86V6TAsSs+np6m1kpW9u0JWi4z/1t+FzcK1aek+ybTnAC4KhBL4uXCNT0wcUIeCw== - dependencies: - cssstyle "^4.1.0" - data-urls "^5.0.0" - decimal.js "^10.4.3" - form-data "^4.0.0" - html-encoding-sniffer "^4.0.0" - http-proxy-agent "^7.0.2" - https-proxy-agent "^7.0.5" - is-potential-custom-element-name "^1.0.1" - nwsapi "^2.2.12" - parse5 "^7.1.2" - rrweb-cssom "^0.7.1" - saxes "^6.0.0" - symbol-tree "^3.2.4" - tough-cookie "^5.0.0" - w3c-xmlserializer "^5.0.0" - webidl-conversions "^7.0.0" - whatwg-encoding "^3.1.1" - whatwg-mimetype "^4.0.0" - whatwg-url "^14.0.0" - ws "^8.18.0" - xml-name-validator "^5.0.0" - jsdom@^16.6.0: version "16.7.0" resolved "https://registry.npmjs.org/jsdom/-/jsdom-16.7.0.tgz" @@ -8555,6 +8775,33 @@ jsdom@^16.6.0: ws "^7.4.6" xml-name-validator "^3.0.0" +jsdom@^25.0.0: + version "25.0.1" + resolved "https://registry.npmjs.org/jsdom/-/jsdom-25.0.1.tgz" + integrity sha512-8i7LzZj7BF8uplX+ZyOlIz86V6TAsSs+np6m1kpW9u0JWi4z/1t+FzcK1aek+ybTnAC4KhBL4uXCNT0wcUIeCw== + dependencies: + cssstyle "^4.1.0" + data-urls "^5.0.0" + decimal.js "^10.4.3" + form-data "^4.0.0" + html-encoding-sniffer "^4.0.0" + http-proxy-agent "^7.0.2" + https-proxy-agent "^7.0.5" + is-potential-custom-element-name "^1.0.1" + nwsapi "^2.2.12" + parse5 "^7.1.2" + rrweb-cssom "^0.7.1" + saxes "^6.0.0" + symbol-tree "^3.2.4" + tough-cookie "^5.0.0" + w3c-xmlserializer "^5.0.0" + webidl-conversions "^7.0.0" + whatwg-encoding "^3.1.1" + whatwg-mimetype "^4.0.0" + whatwg-url "^14.0.0" + ws "^8.18.0" + xml-name-validator "^5.0.0" + jsesc@^3.0.2, jsesc@~3.1.0: version "3.1.0" resolved "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz" @@ -8707,13 +8954,6 @@ levn@^0.4.1: prelude-ls "^1.2.1" type-check "~0.4.0" -lie@~3.3.0: - version "3.3.0" - resolved "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz" - integrity sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ== - dependencies: - immediate "~3.0.5" - lie@3.1.1: version "3.1.1" resolved "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz" @@ -8721,6 +8961,13 @@ lie@3.1.1: dependencies: immediate "~3.0.5" +lie@~3.3.0: + version "3.3.0" + resolved "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz" + integrity sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ== + dependencies: + immediate "~3.0.5" + lighthouse-logger@^1.0.0: version "1.4.2" resolved "https://registry.npmjs.org/lighthouse-logger/-/lighthouse-logger-1.4.2.tgz" @@ -8729,12 +8976,57 @@ lighthouse-logger@^1.0.0: debug "^2.6.9" marky "^1.2.2" +lightningcss-darwin-arm64@1.27.0: + version "1.27.0" + resolved "https://registry.yarnpkg.com/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.27.0.tgz#565bd610533941cba648a70e105987578d82f996" + integrity sha512-Gl/lqIXY+d+ySmMbgDf0pgaWSqrWYxVHoc88q+Vhf2YNzZ8DwoRzGt5NZDVqqIW5ScpSnmmjcgXP87Dn2ylSSQ== + +lightningcss-darwin-x64@1.27.0: + version "1.27.0" + resolved "https://registry.yarnpkg.com/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.27.0.tgz#c906a267237b1c7fe08bff6c5ac032c099bc9482" + integrity sha512-0+mZa54IlcNAoQS9E0+niovhyjjQWEMrwW0p2sSdLRhLDc8LMQ/b67z7+B5q4VmjYCMSfnFi3djAAQFIDuj/Tg== + +lightningcss-freebsd-x64@1.27.0: + version "1.27.0" + resolved "https://registry.yarnpkg.com/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.27.0.tgz#a7c3c4d6ee18dffeb8fa69f14f8f9267f7dc0c34" + integrity sha512-n1sEf85fePoU2aDN2PzYjoI8gbBqnmLGEhKq7q0DKLj0UTVmOTwDC7PtLcy/zFxzASTSBlVQYJUhwIStQMIpRA== + +lightningcss-linux-arm-gnueabihf@1.27.0: + version "1.27.0" + resolved "https://registry.yarnpkg.com/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.27.0.tgz#c7c16432a571ec877bf734fe500e4a43d48c2814" + integrity sha512-MUMRmtdRkOkd5z3h986HOuNBD1c2lq2BSQA1Jg88d9I7bmPGx08bwGcnB75dvr17CwxjxD6XPi3Qh8ArmKFqCA== + +lightningcss-linux-arm64-gnu@1.27.0: + version "1.27.0" + resolved "https://registry.yarnpkg.com/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.27.0.tgz#cfd9e18df1cd65131da286ddacfa3aee6862a752" + integrity sha512-cPsxo1QEWq2sfKkSq2Bq5feQDHdUEwgtA9KaB27J5AX22+l4l0ptgjMZZtYtUnteBofjee+0oW1wQ1guv04a7A== + +lightningcss-linux-arm64-musl@1.27.0: + version "1.27.0" + resolved "https://registry.yarnpkg.com/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.27.0.tgz#6682ff6b9165acef9a6796bd9127a8e1247bb0ed" + integrity sha512-rCGBm2ax7kQ9pBSeITfCW9XSVF69VX+fm5DIpvDZQl4NnQoMQyRwhZQm9pd59m8leZ1IesRqWk2v/DntMo26lg== + +lightningcss-linux-x64-gnu@1.27.0: + version "1.27.0" + resolved "https://registry.yarnpkg.com/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.27.0.tgz#714221212ad184ddfe974bbb7dbe9300dfde4bc0" + integrity sha512-Dk/jovSI7qqhJDiUibvaikNKI2x6kWPN79AQiD/E/KeQWMjdGe9kw51RAgoWFDi0coP4jinaH14Nrt/J8z3U4A== + +lightningcss-linux-x64-musl@1.27.0: + version "1.27.0" + resolved "https://registry.yarnpkg.com/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.27.0.tgz#247958daf622a030a6dc2285afa16b7184bdf21e" + integrity sha512-QKjTxXm8A9s6v9Tg3Fk0gscCQA1t/HMoF7Woy1u68wCk5kS4fR+q3vXa1p3++REW784cRAtkYKrPy6JKibrEZA== + +lightningcss-win32-arm64-msvc@1.27.0: + version "1.27.0" + resolved "https://registry.yarnpkg.com/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.27.0.tgz#64cfe473c264ef5dc275a4d57a516d77fcac6bc9" + integrity sha512-/wXegPS1hnhkeG4OXQKEMQeJd48RDC3qdh+OA8pCuOPCyvnm/yEayrJdJVqzBsqpy1aJklRCVxscpFur80o6iQ== + lightningcss-win32-x64-msvc@1.27.0: 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== -lightningcss@^1.21.0, lightningcss@~1.27.0: +lightningcss@~1.27.0: version "1.27.0" resolved "https://registry.npmjs.org/lightningcss/-/lightningcss-1.27.0.tgz" integrity sha512-8f7aNmS1+etYSLHht0fQApPc2kNO8qGRutifN5rVIc6Xo6ABsEbqOr758UwI7ALVbTt4x1fllKt0PYgzD9S3yQ== @@ -8893,14 +9185,7 @@ lru-cache@^5.1.1: dependencies: yallist "^3.0.2" -magic-string@^0.25.0: - version "0.25.9" - resolved "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz" - integrity sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ== - dependencies: - sourcemap-codec "^1.4.8" - -magic-string@^0.25.7: +magic-string@^0.25.0, magic-string@^0.25.7: version "0.25.9" resolved "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz" integrity sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ== @@ -9054,7 +9339,7 @@ metro-cache@0.81.5: flow-enums-runtime "^0.0.6" metro-core "0.81.5" -metro-config@^0.81.0, metro-config@0.81.5: +metro-config@0.81.5, metro-config@^0.81.0: version "0.81.5" resolved "https://registry.npmjs.org/metro-config/-/metro-config-0.81.5.tgz" integrity sha512-oDRAzUvj6RNRxratFdcVAqtAsg+T3qcKrGdqGZFUdwzlFJdHGR9Z413sW583uD2ynsuOjA2QB6US8FdwiBdNKg== @@ -9068,7 +9353,7 @@ metro-config@^0.81.0, metro-config@0.81.5: metro-core "0.81.5" metro-runtime "0.81.5" -metro-core@^0.81.0, metro-core@0.81.5: +metro-core@0.81.5, metro-core@^0.81.0: version "0.81.5" resolved "https://registry.npmjs.org/metro-core/-/metro-core-0.81.5.tgz" integrity sha512-+2R0c8ByfV2N7CH5wpdIajCWa8escUFd8TukfoXyBq/vb6yTCsznoA25FhNXJ+MC/cz1L447Zj3vdUfCXIZBwg== @@ -9107,7 +9392,7 @@ metro-resolver@0.81.5: dependencies: flow-enums-runtime "^0.0.6" -metro-runtime@^0.81.0, metro-runtime@0.81.5: +metro-runtime@0.81.5, metro-runtime@^0.81.0: version "0.81.5" resolved "https://registry.npmjs.org/metro-runtime/-/metro-runtime-0.81.5.tgz" integrity sha512-M/Gf71ictUKP9+77dV/y8XlAWg7xl76uhU7ggYFUwEdOHHWPG6gLBr1iiK0BmTjPFH8yRo/xyqMli4s3oGorPQ== @@ -9115,7 +9400,7 @@ metro-runtime@^0.81.0, metro-runtime@0.81.5: "@babel/runtime" "^7.25.0" flow-enums-runtime "^0.0.6" -metro-source-map@^0.81.0, metro-source-map@0.81.5: +metro-source-map@0.81.5, metro-source-map@^0.81.0: version "0.81.5" resolved "https://registry.npmjs.org/metro-source-map/-/metro-source-map-0.81.5.tgz" integrity sha512-Jz+CjvCKLNbJZYJTBeN3Kq9kIJf6b61MoLBdaOQZJ5Ajhw6Pf95Nn21XwA8BwfUYgajsi6IXsp/dTZsYJbN00Q== @@ -9174,7 +9459,7 @@ metro-transform-worker@0.81.5: metro-transform-plugins "0.81.5" nullthrows "^1.1.1" -metro@^0.81.0, metro@0.81.5: +metro@0.81.5, metro@^0.81.0: version "0.81.5" resolved "https://registry.npmjs.org/metro/-/metro-0.81.5.tgz" integrity sha512-YpFF0DDDpDVygeca2mAn7K0+us+XKmiGk4rIYMz/CRdjFoCGqAei/IQSpV0UrGfQbToSugpMQeQJveaWSH88Hg== @@ -9228,16 +9513,16 @@ micromatch@^4.0.2, micromatch@^4.0.4, micromatch@^4.0.5, micromatch@^4.0.8: braces "^3.0.3" picomatch "^2.3.1" -mime-db@^1.54.0, "mime-db@>= 1.43.0 < 2": - version "1.54.0" - resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz" - integrity sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ== - mime-db@1.52.0: version "1.52.0" resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz" integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== +"mime-db@>= 1.43.0 < 2", mime-db@^1.54.0: + version "1.54.0" + resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz" + integrity sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ== + mime-types@^2.1.12, mime-types@^2.1.27, mime-types@^2.1.31, mime-types@^2.1.35, mime-types@~2.1.24, mime-types@~2.1.34, mime-types@~2.1.35: version "2.1.35" resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz" @@ -9332,16 +9617,16 @@ minipass@^3.0.0: dependencies: yallist "^4.0.0" -"minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.0.3, minipass@^7.1.2: - version "7.1.3" - resolved "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz" - integrity sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A== - minipass@^5.0.0: version "5.0.0" resolved "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz" integrity sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ== +"minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.0.3, minipass@^7.1.2: + version "7.1.3" + resolved "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz" + integrity sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A== + minizlib@^2.1.1: version "2.1.2" resolved "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz" @@ -9357,26 +9642,21 @@ mkdirp@^0.5.1, mkdirp@~0.5.1: dependencies: minimist "^1.2.6" -mkdirp@^1.0.3: +mkdirp@^1.0.3, mkdirp@^1.0.4: version "1.0.4" resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== -mkdirp@^1.0.4: - version "1.0.4" - resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz" - integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== - -ms@^2.1.1, ms@^2.1.3, ms@2.1.3: - version "2.1.3" - resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz" - integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== - ms@2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz" integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== +ms@2.1.3, ms@^2.1.1, ms@^2.1.3: + version "2.1.3" + resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + multicast-dns@^7.2.5: version "7.2.5" resolved "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz" @@ -9409,16 +9689,16 @@ natural-compare@^1.4.0: resolved "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz" integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== -negotiator@~0.6.4: - version "0.6.4" - resolved "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz" - integrity sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w== - negotiator@0.6.3: version "0.6.3" resolved "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz" integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== +negotiator@~0.6.4: + version "0.6.4" + resolved "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz" + integrity sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w== + neo-async@^2.5.0, neo-async@^2.6.2: version "2.6.2" resolved "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz" @@ -9742,14 +10022,7 @@ p-limit@^2.0.0, p-limit@^2.2.0: dependencies: p-try "^2.0.0" -p-limit@^3.0.2: - version "3.1.0" - resolved "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz" - integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== - dependencies: - yocto-queue "^0.1.0" - -p-limit@^3.1.0: +p-limit@^3.0.2, p-limit@^3.1.0: version "3.1.0" resolved "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz" integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== @@ -9847,6 +10120,11 @@ parse-png@^2.1.0: dependencies: pngjs "^3.3.0" +parse5@6.0.1: + version "6.0.1" + resolved "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz" + integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== + parse5@^7.1.2: version "7.3.0" resolved "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz" @@ -9854,11 +10132,6 @@ parse5@^7.1.2: dependencies: entities "^6.0.0" -parse5@6.0.1: - version "6.0.1" - resolved "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz" - integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== - parseurl@~1.3.3: version "1.3.3" resolved "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz" @@ -9892,12 +10165,7 @@ path-key@^2.0.0, path-key@^2.0.1: resolved "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz" integrity sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw== -path-key@^3.0.0: - version "3.1.1" - resolved "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz" - integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== - -path-key@^3.1.0: +path-key@^3.0.0, path-key@^3.1.0: version "3.1.1" resolved "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz" integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== @@ -9967,16 +10235,16 @@ picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.2, picomatch@^2.2.3, picomatc resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz" integrity sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA== -"picomatch@^3 || ^4", picomatch@^4.0.2, picomatch@^4.0.4: - version "4.0.4" - resolved "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz" - integrity sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A== - picomatch@^3.0.1: version "3.0.2" resolved "https://registry.npmjs.org/picomatch/-/picomatch-3.0.2.tgz" integrity sha512-cfDHL6LStTEKlNilboNtobT/kEa30PtAf2Q1OgszfrG/rpVl1xaFWT9ktfkS306GmHgmnad1Sw4wabhlvFtsTw== +picomatch@^4.0.2, picomatch@^4.0.4: + version "4.0.4" + resolved "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz" + integrity sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A== + pify@^2.3.0: version "2.3.0" resolved "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz" @@ -10574,15 +10842,6 @@ postcss-value-parser@^4.0.0, postcss-value-parser@^4.1.0, postcss-value-parser@^ resolved "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz" integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== -"postcss@^7.0.0 || ^8.0.1", postcss@^8, postcss@^8.0.0, postcss@^8.0.3, postcss@^8.0.9, postcss@^8.1.0, postcss@^8.1.4, postcss@^8.2, postcss@^8.2.14, postcss@^8.2.15, postcss@^8.2.2, postcss@^8.3, postcss@^8.3.5, postcss@^8.4, postcss@^8.4.21, postcss@^8.4.33, postcss@^8.4.4, postcss@^8.4.43, postcss@^8.4.47, postcss@^8.4.6, postcss@^8.5.3, "postcss@>= 8", postcss@>=8, postcss@>=8.0.9: - version "8.5.15" - resolved "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz" - integrity sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A== - dependencies: - nanoid "^3.3.12" - picocolors "^1.1.1" - source-map-js "^1.2.1" - postcss@^7.0.35: version "7.0.39" resolved "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz" @@ -10591,6 +10850,15 @@ postcss@^7.0.35: picocolors "^0.2.1" source-map "^0.6.1" +postcss@^8.3.5, postcss@^8.4.33, postcss@^8.4.4, postcss@^8.4.43, postcss@^8.4.47, postcss@^8.5.3: + version "8.5.15" + resolved "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz" + integrity sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A== + dependencies: + nanoid "^3.3.12" + picocolors "^1.1.1" + source-map-js "^1.2.1" + postcss@~8.4.32: version "8.4.49" resolved "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz" @@ -10858,7 +11126,7 @@ react-devtools-core@^5.3.1: shell-quote "^1.6.1" ws "^7" -"react-dom@^18.0.0 || ^19.0.0", react-dom@^18.2.0, react-dom@^19.0.0, react-dom@>=18: +react-dom@^19.0.0: version "19.2.6" resolved "https://registry.npmjs.org/react-dom/-/react-dom-19.2.6.tgz" integrity sha512-0prMI+hvBbPjsWnxDLxlCGyM8PN6UuWjEUCYmZhO67xIV9Xasa/r/vDnq+Xyq4Lo27g8QSbO5YzARu0D1Sps3g== @@ -10930,12 +11198,12 @@ react-native-reanimated@~3.16.0: convert-source-map "^2.0.0" invariant "^2.2.4" -"react-native-safe-area-context@>= 4.0.0", react-native-safe-area-context@4.14.1: +react-native-safe-area-context@4.14.1: version "4.14.1" resolved "https://registry.npmjs.org/react-native-safe-area-context/-/react-native-safe-area-context-4.14.1.tgz" integrity sha512-+tUhT5WBl8nh5+P+chYhAjR470iCByf9z5EYdCEbPaAK3Yfzw+o8VRPnUgmPAKlSccOgQBxx3NOl/Wzckn9ujg== -"react-native-screens@>= 4.0.0", react-native-screens@~4.4.0: +react-native-screens@~4.4.0: version "4.4.0" resolved "https://registry.npmjs.org/react-native-screens/-/react-native-screens-4.4.0.tgz" integrity sha512-c7zc7Zwjty6/pGyuuvh9gK3YBYqHPOxrhXfG1lF4gHlojQSmIx2piNbNaV+Uykj+RDTmFXK0e/hA+fucw/Qozg== @@ -10943,7 +11211,15 @@ react-native-reanimated@~3.16.0: react-freeze "^1.0.0" warn-once "^0.1.0" -react-native@*, "react-native@^0.0.0-0 || >=0.65 <1.0", react-native@0.76.6: +react-native-webview@13.12.5: + version "13.12.5" + resolved "https://registry.yarnpkg.com/react-native-webview/-/react-native-webview-13.12.5.tgz#ed9eec1eda234d7cf18d329859b9bdebf7e258b6" + integrity sha512-INOKPom4dFyzkbxbkuQNfeRG9/iYnyRDzrDkJeyvSWgJAW2IDdJkWFJBS2v0RxIL4gqLgHkiIZDOfiLaNnw83Q== + dependencies: + escape-string-regexp "^4.0.0" + invariant "2.2.4" + +react-native@0.76.6: version "0.76.6" resolved "https://registry.npmjs.org/react-native/-/react-native-0.76.6.tgz" integrity sha512-AsRi+ud6v6ADH7ZtSOY42kRB4nbM0KtSu450pGO4pDudl4AEK/AF96ai88snb2/VJJSGGa/49QyJVFXxz/qoFg== @@ -10995,7 +11271,7 @@ react-reader@^2.0.15: epubjs "^0.3.93" react-swipeable "^7.0.2" -react-refresh@^0.11.0, "react-refresh@>=0.10.0 <1.0.0": +react-refresh@^0.11.0: version "0.11.0" resolved "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz" integrity sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A== @@ -11085,7 +11361,7 @@ react-swipeable@^7.0.2: resolved "https://registry.npmjs.org/react-swipeable/-/react-swipeable-7.0.2.tgz" integrity sha512-v1Qx1l+aC2fdxKa9aKJiaU/ZxmJ5o98RMoFwUqAAzVWUcxgfHFXDDruCKXhw6zIYXm6V64JiHgP9f6mlME5l8w== -react@*, "react@^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react@^16.8.3 || ^17 || ^18 || ^19.0.0 || ^19.0.0-rc", "react@^18.0.0 || ^19.0.0", react@^18.2.0, react@^19.0.0, react@^19.2.6, "react@>= 16", "react@>= 18.2.0", react@>=16.8, react@>=17.0.0, react@>=18: +react@^19.0.0: version "19.2.6" resolved "https://registry.npmjs.org/react/-/react-19.2.6.tgz" integrity sha512-sfWGGfavi0xr8Pg0sVsyHMAOziVYKgPLNrS7ig+ivMNb3wbCBw3KxtflsGBAwD3gYQlE/AEZsTLgToRrSCjb0Q== @@ -11331,19 +11607,7 @@ resolve@^1.1.7, resolve@^1.19.0, resolve@^1.20.0, resolve@^1.22.11, resolve@^1.2 path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" -resolve@^2.0.0-next.5: - version "2.0.0-next.7" - resolved "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.7.tgz" - integrity sha512-tqt+NBWwyaMgw3zDsnygx4CByWjQEJHOPMdslYhppaQSJUtL/D4JO9CcBBlhPoI8lz9oJIDXkwXfhF4aWqP8xQ== - dependencies: - es-errors "^1.3.0" - is-core-module "^2.16.2" - node-exports-info "^1.6.0" - object-keys "^1.1.1" - path-parse "^1.0.7" - supports-preserve-symlinks-flag "^1.0.0" - -resolve@^2.0.0-next.6: +resolve@^2.0.0-next.5, resolve@^2.0.0-next.6: version "2.0.0-next.7" resolved "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.7.tgz" integrity sha512-tqt+NBWwyaMgw3zDsnygx4CByWjQEJHOPMdslYhppaQSJUtL/D4JO9CcBBlhPoI8lz9oJIDXkwXfhF4aWqP8xQ== @@ -11404,48 +11668,14 @@ rollup-plugin-terser@^7.0.0: serialize-javascript "^4.0.0" terser "^5.0.0" -"rollup@^1.20.0 || ^2.0.0", rollup@^1.20.0||^2.0.0, rollup@^2.0.0, rollup@^2.43.1: +rollup@^2.43.1: version "2.80.0" resolved "https://registry.npmjs.org/rollup/-/rollup-2.80.0.tgz" integrity sha512-cIFJOD1DESzpjOBl763Kp1AH7UE/0fcdHe6rZXUdQ9c50uvgigvW97u3IcSeBwOkgqL/PXPBktBCh0KEu5L8XQ== optionalDependencies: fsevents "~2.3.2" -rollup@^4.20.0: - version "4.60.4" - resolved "https://registry.npmjs.org/rollup/-/rollup-4.60.4.tgz" - integrity sha512-WHeFSbZYsPu3+bLoNRUuAO+wavNlocOPf3wSHTP7hcFKVnJeWsYlCDbr3mTS14FCizf9ccIxXA8sGL8zKeQN3g== - dependencies: - "@types/estree" "1.0.8" - optionalDependencies: - "@rollup/rollup-android-arm-eabi" "4.60.4" - "@rollup/rollup-android-arm64" "4.60.4" - "@rollup/rollup-darwin-arm64" "4.60.4" - "@rollup/rollup-darwin-x64" "4.60.4" - "@rollup/rollup-freebsd-arm64" "4.60.4" - "@rollup/rollup-freebsd-x64" "4.60.4" - "@rollup/rollup-linux-arm-gnueabihf" "4.60.4" - "@rollup/rollup-linux-arm-musleabihf" "4.60.4" - "@rollup/rollup-linux-arm64-gnu" "4.60.4" - "@rollup/rollup-linux-arm64-musl" "4.60.4" - "@rollup/rollup-linux-loong64-gnu" "4.60.4" - "@rollup/rollup-linux-loong64-musl" "4.60.4" - "@rollup/rollup-linux-ppc64-gnu" "4.60.4" - "@rollup/rollup-linux-ppc64-musl" "4.60.4" - "@rollup/rollup-linux-riscv64-gnu" "4.60.4" - "@rollup/rollup-linux-riscv64-musl" "4.60.4" - "@rollup/rollup-linux-s390x-gnu" "4.60.4" - "@rollup/rollup-linux-x64-gnu" "4.60.4" - "@rollup/rollup-linux-x64-musl" "4.60.4" - "@rollup/rollup-openbsd-x64" "4.60.4" - "@rollup/rollup-openharmony-arm64" "4.60.4" - "@rollup/rollup-win32-arm64-msvc" "4.60.4" - "@rollup/rollup-win32-ia32-msvc" "4.60.4" - "@rollup/rollup-win32-x64-gnu" "4.60.4" - "@rollup/rollup-win32-x64-msvc" "4.60.4" - fsevents "~2.3.2" - -rollup@^4.34.9: +rollup@^4.20.0, rollup@^4.34.9: version "4.60.4" resolved "https://registry.npmjs.org/rollup/-/rollup-4.60.4.tgz" integrity sha512-WHeFSbZYsPu3+bLoNRUuAO+wavNlocOPf3wSHTP7hcFKVnJeWsYlCDbr3mTS14FCizf9ccIxXA8sGL8zKeQN3g== @@ -11507,17 +11737,12 @@ safe-array-concat@^1.1.3: has-symbols "^1.1.0" isarray "^2.0.5" -safe-buffer@^5.1.0, safe-buffer@>=5.1.0, safe-buffer@~5.2.0, safe-buffer@5.2.1: +safe-buffer@5.2.1, safe-buffer@>=5.1.0, safe-buffer@^5.1.0, safe-buffer@~5.2.0: version "5.2.1" resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== -safe-buffer@~5.1.0: - version "5.1.2" - resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz" - integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== - -safe-buffer@~5.1.1: +safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== @@ -11557,7 +11782,7 @@ sass-loader@^12.3.0: klona "^2.0.4" neo-async "^2.6.2" -sax@^1.5.0, sax@>=0.6.0: +sax@>=0.6.0, sax@^1.5.0: version "1.6.0" resolved "https://registry.npmjs.org/sax/-/sax-1.6.0.tgz" integrity sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA== @@ -11581,11 +11806,6 @@ saxes@^6.0.0: dependencies: xmlchars "^2.2.0" -scheduler@^0.27.0: - version "0.27.0" - resolved "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz" - integrity sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q== - scheduler@0.24.0-canary-efb381bbf-20230505: version "0.24.0-canary-efb381bbf-20230505" resolved "https://registry.npmjs.org/scheduler/-/scheduler-0.24.0-canary-efb381bbf-20230505.tgz" @@ -11593,6 +11813,20 @@ scheduler@0.24.0-canary-efb381bbf-20230505: dependencies: loose-envify "^1.1.0" +scheduler@^0.27.0: + version "0.27.0" + resolved "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz" + integrity sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q== + +schema-utils@2.7.0: + version "2.7.0" + resolved "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz" + integrity sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A== + dependencies: + "@types/json-schema" "^7.0.4" + ajv "^6.12.2" + ajv-keywords "^3.4.1" + schema-utils@^2.6.5: version "2.7.1" resolved "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.1.tgz" @@ -11621,15 +11855,6 @@ schema-utils@^4.0.0, schema-utils@^4.2.0, schema-utils@^4.3.0, schema-utils@^4.3 ajv-formats "^2.1.1" ajv-keywords "^5.1.0" -schema-utils@2.7.0: - version "2.7.0" - resolved "https://registry.npmjs.org/schema-utils/-/schema-utils-2.7.0.tgz" - integrity sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A== - dependencies: - "@types/json-schema" "^7.0.4" - ajv "^6.12.2" - ajv-keywords "^3.4.1" - select-hose@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz" @@ -11643,27 +11868,12 @@ selfsigned@^2.1.1, selfsigned@^2.4.1: "@types/node-forge" "^1.3.0" node-forge "^1" -semver@^5.5.0: +semver@^5.5.0, semver@^5.6.0: version "5.7.2" resolved "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz" integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== -semver@^5.6.0: - version "5.7.2" - resolved "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz" - integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== - -semver@^6.0.0: - version "6.3.1" - resolved "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz" - integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== - -semver@^6.3.0: - version "6.3.1" - resolved "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz" - integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== - -semver@^6.3.1: +semver@^6.0.0, semver@^6.3.0, semver@^6.3.1: version "6.3.1" resolved "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz" integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== @@ -11775,7 +11985,7 @@ setimmediate@^1.0.5: resolved "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz" integrity sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA== -setprototypeof@~1.2.0, setprototypeof@1.2.0: +setprototypeof@1.2.0, setprototypeof@~1.2.0: version "1.2.0" resolved "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz" integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== @@ -11948,16 +12158,16 @@ source-map-support@^0.5.16, source-map-support@^0.5.6, source-map-support@~0.5.2 buffer-from "^1.0.0" source-map "^0.6.0" +source-map@0.6.1, source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, source-map@~0.6.1: + version "0.6.1" + resolved "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + source-map@^0.5.6: version "0.5.7" resolved "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz" integrity sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ== -source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, source-map@~0.6.1, source-map@0.6.1: - version "0.6.1" - resolved "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz" - integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== - source-map@^0.7.3: version "0.7.6" resolved "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz" @@ -12051,12 +12261,7 @@ static-eval@2.1.1: dependencies: escodegen "^2.1.0" -"statuses@>= 1.5.0 < 2": - version "1.5.0" - resolved "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz" - integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA== - -statuses@~1.5.0: +"statuses@>= 1.5.0 < 2", statuses@~1.5.0: version "1.5.0" resolved "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz" integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA== @@ -12079,7 +12284,7 @@ stop-iteration-iterator@^1.1.0: es-errors "^1.3.0" internal-slot "^1.1.0" -stream-buffers@~2.2.0, stream-buffers@2.2.x: +stream-buffers@2.2.x, stream-buffers@~2.2.0: version "2.2.0" resolved "https://registry.npmjs.org/stream-buffers/-/stream-buffers-2.2.0.tgz" integrity sha512-uyQK/mx5QjHun80FLJTfaWE7JtwfRMKBLkMne6udYOmvH0CawotVa7TfgYHzAnpphn4+TweIx1QKMnRIbipmUg== @@ -12089,20 +12294,6 @@ strict-uri-encode@^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== -string_decoder@^1.1.1: - version "1.3.0" - resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz" - integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== - dependencies: - safe-buffer "~5.2.0" - -string_decoder@~1.1.1: - version "1.1.1" - resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz" - integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== - dependencies: - safe-buffer "~5.1.0" - string-length@^4.0.1: version "4.0.2" resolved "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz" @@ -12219,6 +12410,20 @@ string.prototype.trimstart@^1.0.8: define-properties "^1.2.1" es-object-atoms "^1.0.0" +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + +string_decoder@~1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz" + integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== + dependencies: + safe-buffer "~5.1.0" + stringify-object@^3.3.0: version "3.3.0" resolved "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz" @@ -12316,19 +12521,6 @@ stylehacks@^5.1.1: browserslist "^4.21.4" postcss-selector-parser "^6.0.4" -sucrase@^3.35.0: - version "3.35.1" - resolved "https://registry.npmjs.org/sucrase/-/sucrase-3.35.1.tgz" - integrity sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw== - 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" - sucrase@3.35.0: version "3.35.0" resolved "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz" @@ -12342,6 +12534,19 @@ sucrase@3.35.0: pirates "^4.0.1" ts-interface-checker "^0.1.9" +sucrase@^3.35.0: + version "3.35.1" + resolved "https://registry.npmjs.org/sucrase/-/sucrase-3.35.1.tgz" + integrity sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw== + 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" + supports-color@^5.3.0: version "5.5.0" resolved "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz" @@ -12519,7 +12724,7 @@ terser-webpack-plugin@^5.2.5, terser-webpack-plugin@^5.5.0: schema-utils "^4.3.0" terser "^5.31.1" -terser@^5.0.0, terser@^5.10.0, terser@^5.15.0, terser@^5.16.0, terser@^5.31.1, terser@^5.4.0: +terser@^5.0.0, terser@^5.10.0, terser@^5.15.0, terser@^5.31.1: version "5.48.0" resolved "https://registry.npmjs.org/terser/-/terser-5.48.0.tgz" integrity sha512-J/9An6vs9Us6wKRriSFXBWdRZapREHqFzdNUKk0pmu804EMR6dr6winwo7e5JDxN4xahxQsuysyYFwlwj4XN/Q== @@ -12637,7 +12842,7 @@ to-regex-range@^5.0.1: dependencies: is-number "^7.0.0" -toidentifier@~1.0.1, toidentifier@1.0.1: +toidentifier@1.0.1, toidentifier@~1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz" integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== @@ -12734,7 +12939,7 @@ type-detect@4.0.8: resolved "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz" integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== -type-fest@^0.16.0, "type-fest@>=0.17.0 <5.0.0": +type-fest@^0.16.0: version "0.16.0" resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.16.0.tgz" integrity sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg== @@ -12819,7 +13024,7 @@ typedarray-to-buffer@^3.1.5: dependencies: is-typedarray "^1.0.0" -"typescript@^3.2.1 || ^4", "typescript@>= 2.7", "typescript@>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta", typescript@~5.7.0: +typescript@~5.7.0: version "5.7.3" resolved "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz" integrity sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw== @@ -13031,18 +13236,6 @@ vite-node@2.1.9: pathe "^1.1.2" vite "^5.0.0" -"vite@^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0": - version "6.4.2" - dependencies: - esbuild "^0.25.0" - fdir "^6.4.4" - picomatch "^4.0.2" - postcss "^8.5.3" - rollup "^4.34.9" - tinyglobby "^0.2.13" - optionalDependencies: - fsevents "~2.3.3" - vite@^5.0.0: version "5.4.21" resolved "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz" @@ -13195,7 +13388,7 @@ webpack-dev-middleware@^5.3.4: range-parser "^1.2.1" schema-utils "^4.0.0" -webpack-dev-server@^4.6.0, "webpack-dev-server@3.x || 4.x || 5.x": +webpack-dev-server@^4.6.0: version "4.15.2" resolved "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.15.2.tgz" integrity sha512-0XavAZbNJ5sDrCbkpWL8mia0o5WPOd2YGtxrEiZkBK9FjLppIUK2TgxK6qGD2P3hUXTJNNPVibrerKcx5WkR1g== @@ -13260,7 +13453,7 @@ webpack-sources@^3.5.0: resolved "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.5.0.tgz" integrity sha512-HPuy+uuoTCaaoEoI1LQ3JN9+vrPBvEesnnX1jADHy728cHSMlq4wUc4afYqahq2B1mhQVZxCXOkNTnXltr+2vQ== -"webpack@^4.0.0 || ^5.0.0", "webpack@^4.37.0 || ^5.0.0", "webpack@^4.4.0 || ^5.9.0", "webpack@^4.44.2 || ^5.47.0", webpack@^5.0.0, webpack@^5.1.0, webpack@^5.20.0, webpack@^5.64.4, "webpack@>= 4", webpack@>=2, "webpack@>=4.43.0 <6.0.0": +webpack@^5.64.4: version "5.107.2" resolved "https://registry.npmjs.org/webpack/-/webpack-5.107.2.tgz" integrity sha512-v7RhXaJbpMlV0D7hC7lb2EbnxkoeUqf9qhKr6lozx3Q48pmFrqqNRmZFUEGmi7pSwm6fCQ2H1IjvCkHqdpVdjQ== @@ -13289,7 +13482,7 @@ webpack-sources@^3.5.0: watchpack "^2.5.1" webpack-sources "^3.5.0" -websocket-driver@^0.7.4, websocket-driver@>=0.5.1: +websocket-driver@>=0.5.1, websocket-driver@^0.7.4: version "0.7.4" resolved "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz" integrity sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg== @@ -13695,32 +13888,12 @@ ws@^6.2.3: dependencies: async-limiter "~1.0.0" -ws@^7: +ws@^7, ws@^7.4.6, ws@^7.5.10: version "7.5.11" resolved "https://registry.npmjs.org/ws/-/ws-7.5.11.tgz" integrity sha512-zS54Oen9bITtp7kp2XM3AydrCIq1D+HwJOuH+c+e4LfpL/lotP5osijd+UoMnxwAam1GN8R4KtLAyIrIcBNpiA== -ws@^7.4.6: - version "7.5.11" - resolved "https://registry.npmjs.org/ws/-/ws-7.5.11.tgz" - integrity sha512-zS54Oen9bITtp7kp2XM3AydrCIq1D+HwJOuH+c+e4LfpL/lotP5osijd+UoMnxwAam1GN8R4KtLAyIrIcBNpiA== - -ws@^7.5.10: - version "7.5.11" - resolved "https://registry.npmjs.org/ws/-/ws-7.5.11.tgz" - integrity sha512-zS54Oen9bITtp7kp2XM3AydrCIq1D+HwJOuH+c+e4LfpL/lotP5osijd+UoMnxwAam1GN8R4KtLAyIrIcBNpiA== - -ws@^8.12.1: - version "8.21.0" - resolved "https://registry.npmjs.org/ws/-/ws-8.21.0.tgz" - integrity sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g== - -ws@^8.13.0: - version "8.21.0" - resolved "https://registry.npmjs.org/ws/-/ws-8.21.0.tgz" - integrity sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g== - -ws@^8.18.0: +ws@^8.12.1, ws@^8.13.0, ws@^8.18.0: version "8.21.0" resolved "https://registry.npmjs.org/ws/-/ws-8.21.0.tgz" integrity sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g== @@ -13786,7 +13959,7 @@ yallist@^4.0.0: resolved "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz" integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== -yaml@^1.10.0, yaml@^1.10.2, yaml@^1.7.2, yaml@^2.4.2: +yaml@^1.10.0, yaml@^1.10.2, yaml@^1.7.2: version "1.10.3" resolved "https://registry.npmjs.org/yaml/-/yaml-1.10.3.tgz" integrity sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA==