diff --git a/.gitignore b/.gitignore index c421965..55730ec 100644 --- a/.gitignore +++ b/.gitignore @@ -7,11 +7,16 @@ node_modules/ npm-debug.log* yarn-debug.log* yarn-error.log* +package-lock.json # Astro build output dist/ .astro/ +# Generated CSS from Tailwind builds +public/css/theme-light.css +public/css/theme-dark.css + # Compiled binaries and libraries *.node *.so @@ -31,10 +36,21 @@ RTSDA/ # Build outputs build/ *.log +index.cjs +index.d.ts + +# Environment variables +.env +.env.local +.env.development.local +.env.test.local +.env.production.local # IDE .vscode/ .idea/ +*.swp +*.swo # OS generated files .DS_Store @@ -43,4 +59,11 @@ ehthumbs.db # Temporary files *.tmp -*.temp \ No newline at end of file +*.temp + +# Cargo build metadata +.cargo/ + +# Coverage reports +coverage/ +*.lcov \ No newline at end of file diff --git a/.serena/cache/rust/document_symbols_cache_v23-06-25.pkl b/.serena/cache/rust/document_symbols_cache_v23-06-25.pkl new file mode 100644 index 0000000..78cb919 Binary files /dev/null and b/.serena/cache/rust/document_symbols_cache_v23-06-25.pkl differ diff --git a/.serena/memories/project_overview.md b/.serena/memories/project_overview.md new file mode 100644 index 0000000..2b1695b --- /dev/null +++ b/.serena/memories/project_overview.md @@ -0,0 +1,56 @@ +# RTSDA Web Project Overview + +## Project Purpose +This is a church website project for Rockville Tolland Seventh-day Adventist Church, featuring: +- Event management and submission system +- Sermon archive and streaming +- Bulletin management +- Contact forms +- Admin interface + +## Tech Stack +- **Frontend**: Astro 5.13.0 with TailwindCSS +- **Backend**: Rust-based church-core library with NAPI-RS bindings +- **Architecture**: Hybrid - Astro frontend with Rust backend via native bindings +- **API**: External API at `https://api.rockvilletollandsda.church/api/` + +## Key Components +- `astro-church-website/` - Main Astro frontend +- `church-core/` - Rust backend library +- `axum-church-website/` - Alternative backend (Axum-based) + +## Project Structure +``` +/ +├── astro-church-website/ # Main Astro frontend +│ ├── src/ +│ │ ├── components/ # Reusable components +│ │ ├── layouts/ # Page layouts +│ │ ├── pages/ # Astro pages and API routes +│ │ │ ├── api/ # API endpoints +│ │ │ ├── events/ # Event-related pages +│ │ │ ├── admin/ # Admin interface +│ │ │ └── bulletin/ # Bulletin management +│ │ └── lib/ # Utilities and bindings +│ ├── package.json +│ ├── Cargo.toml # Rust dependencies for NAPI +│ └── build.rs # Build script +├── church-core/ # Rust backend library +└── axum-church-website/ # Alternative Axum backend +``` + +## Development Commands +- `npm run dev` - Start development server (localhost:4321) +- `npm run build` - Build production site (includes native build) +- `npm run build:native` - Build native Rust bindings only +- `npm run preview` - Preview built site + +## Key Features +1. **Event Submission Form** (`/events/submit`) - Users can submit events for approval +2. **Admin Interface** (`/admin`) - Event management and approval +3. **Bulletin Archive** (`/bulletin`) - Historical bulletins +4. **Live Streaming** (`/live`) - Church service streaming +5. **Sermon Archive** (`/sermons`) - Past sermons and audio + +## Known Issue +There's a database error when users select "2nd 3rd Saturday" in the recurring field of the event submission form, causing a 500 error. \ No newline at end of file diff --git a/.serena/memories/suggested_commands.md b/.serena/memories/suggested_commands.md new file mode 100644 index 0000000..834afa2 --- /dev/null +++ b/.serena/memories/suggested_commands.md @@ -0,0 +1,71 @@ +# Suggested Development Commands + +## Core Development Commands +```bash +# Install dependencies +npm install + +# Start development server +npm run dev # Starts at localhost:4321 + +# Build for production +npm run build # Includes native Rust build +npm run build:native # Build only Rust bindings +npm run preview # Preview production build + +# Astro CLI commands +npm run astro ... # Run any Astro CLI command +npm run astro -- --help # Get Astro CLI help +``` + +## System Utilities (Darwin/macOS) +```bash +# Basic file operations +ls # List files +cd # Change directory +find . -name "*.ts" # Find TypeScript files +grep -r "pattern" . # Search for patterns + +# Git operations +git status # Check git status +git add . # Stage all changes +git commit -m "message" # Commit changes +git push # Push to remote + +# Package management +npm install # Install new package +npm uninstall # Remove package +npm audit # Check for vulnerabilities +``` + +## Rust/NAPI Commands +```bash +# Native bindings +cargo build --release # Build Rust code +napi build --platform --release # Build NAPI bindings + +# Rust development +cargo check # Check Rust code +cargo test # Run Rust tests +cargo clippy # Rust linter +``` + +## Testing & Quality (when available) +```bash +# Check if these exist in project: +npm run test # Run tests (if configured) +npm run lint # Lint code (if configured) +npm run format # Format code (if configured) +npm run typecheck # TypeScript checking (if configured) +``` + +## Debugging +```bash +# Development debugging +npm run dev -- --verbose # Verbose development mode +npm run build -- --verbose # Verbose build + +# View logs +tail -f logs/app.log # If logs exist +console logs via browser dev tools # For frontend debugging +``` \ No newline at end of file diff --git a/.serena/memories/task_completion_checklist.md b/.serena/memories/task_completion_checklist.md new file mode 100644 index 0000000..406252d --- /dev/null +++ b/.serena/memories/task_completion_checklist.md @@ -0,0 +1,52 @@ +# Task Completion Checklist + +When completing any development task in this project, follow these steps: + +## 1. Code Quality Checks +- [ ] **TypeScript Validation**: Run `npx tsc --noEmit` to check TypeScript types +- [ ] **Build Test**: Run `npm run build` to ensure the project builds successfully +- [ ] **Development Server**: Test with `npm run dev` to ensure functionality works locally + +## 2. Rust/Native Code (if applicable) +- [ ] **Rust Check**: Run `cargo check` in the church-core directory +- [ ] **Native Build**: Run `npm run build:native` to ensure NAPI bindings compile +- [ ] **Cargo Clippy**: Run `cargo clippy` for Rust linting (if available) + +## 3. Frontend Testing +- [ ] **Manual Testing**: Test all affected functionality in the browser +- [ ] **Cross-browser Check**: Test in different browsers if UI changes were made +- [ ] **Mobile Responsiveness**: Check mobile layout if UI changes were made + +## 4. API Integration +- [ ] **API Endpoints**: Test any modified API routes (`/api/*`) +- [ ] **External API**: Verify integration with `https://api.rockvilletollandsda.church/api/` +- [ ] **Error Handling**: Ensure proper error responses and user feedback + +## 5. Documentation +- [ ] **Code Comments**: Add comments for complex logic +- [ ] **README Updates**: Update documentation if new features or requirements +- [ ] **Memory Updates**: Update project memory files if architecture changes + +## 6. Git Best Practices +- [ ] **Commit Messages**: Use clear, descriptive commit messages +- [ ] **Branch Management**: Work on feature branches when appropriate +- [ ] **Clean History**: Squash commits if multiple small fixes + +## 7. Performance & Security +- [ ] **Bundle Size**: Check if changes affect build size significantly +- [ ] **Security**: Ensure no sensitive data is exposed +- [ ] **Caching**: Verify API caching headers are appropriate + +## Common Issues to Watch For +- **NAPI Bindings**: Ensure native code compiles on target platforms +- **External API Dependency**: Handle cases where external API is unavailable +- **Image Uploads**: Validate file sizes and types properly +- **Form Validation**: Both client-side and server-side validation +- **Recurring Events**: Special attention to date/time handling + +## Pre-deployment Checklist +- [ ] All TypeScript errors resolved +- [ ] Production build succeeds +- [ ] All features tested manually +- [ ] External dependencies verified +- [ ] Environment variables documented (if new ones added) \ No newline at end of file diff --git a/.serena/project.yml b/.serena/project.yml new file mode 100644 index 0000000..f1a0ec1 --- /dev/null +++ b/.serena/project.yml @@ -0,0 +1,68 @@ +# language of the project (csharp, python, rust, java, typescript, go, cpp, or ruby) +# * For C, use cpp +# * For JavaScript, use typescript +# Special requirements: +# * csharp: Requires the presence of a .sln file in the project folder. +language: rust + +# whether to use the project's gitignore file to ignore files +# Added on 2025-04-07 +ignore_all_files_in_gitignore: true +# list of additional paths to ignore +# same syntax as gitignore, so you can use * and ** +# Was previously called `ignored_dirs`, please update your config if you are using that. +# Added (renamed)on 2025-04-07 +ignored_paths: [] + +# whether the project is in read-only mode +# If set to true, all editing tools will be disabled and attempts to use them will result in an error +# Added on 2025-04-18 +read_only: false + + +# list of tool names to exclude. We recommend not excluding any tools, see the readme for more details. +# Below is the complete list of tools for convenience. +# To make sure you have the latest list of tools, and to view their descriptions, +# execute `uv run scripts/print_tool_overview.py`. +# +# * `activate_project`: Activates a project by name. +# * `check_onboarding_performed`: Checks whether project onboarding was already performed. +# * `create_text_file`: Creates/overwrites a file in the project directory. +# * `delete_lines`: Deletes a range of lines within a file. +# * `delete_memory`: Deletes a memory from Serena's project-specific memory store. +# * `execute_shell_command`: Executes a shell command. +# * `find_referencing_code_snippets`: Finds code snippets in which the symbol at the given location is referenced. +# * `find_referencing_symbols`: Finds symbols that reference the symbol at the given location (optionally filtered by type). +# * `find_symbol`: Performs a global (or local) search for symbols with/containing a given name/substring (optionally filtered by type). +# * `get_current_config`: Prints the current configuration of the agent, including the active and available projects, tools, contexts, and modes. +# * `get_symbols_overview`: Gets an overview of the top-level symbols defined in a given file or directory. +# * `initial_instructions`: Gets the initial instructions for the current project. +# Should only be used in settings where the system prompt cannot be set, +# e.g. in clients you have no control over, like Claude Desktop. +# * `insert_after_symbol`: Inserts content after the end of the definition of a given symbol. +# * `insert_at_line`: Inserts content at a given line in a file. +# * `insert_before_symbol`: Inserts content before the beginning of the definition of a given symbol. +# * `list_dir`: Lists files and directories in the given directory (optionally with recursion). +# * `list_memories`: Lists memories in Serena's project-specific memory store. +# * `onboarding`: Performs onboarding (identifying the project structure and essential tasks, e.g. for testing or building). +# * `prepare_for_new_conversation`: Provides instructions for preparing for a new conversation (in order to continue with the necessary context). +# * `read_file`: Reads a file within the project directory. +# * `read_memory`: Reads the memory with the given name from Serena's project-specific memory store. +# * `remove_project`: Removes a project from the Serena configuration. +# * `replace_lines`: Replaces a range of lines within a file with new content. +# * `replace_symbol_body`: Replaces the full definition of a symbol. +# * `restart_language_server`: Restarts the language server, may be necessary when edits not through Serena happen. +# * `search_for_pattern`: Performs a search for a pattern in the project. +# * `summarize_changes`: Provides instructions for summarizing the changes made to the codebase. +# * `switch_modes`: Activates modes by providing a list of their names +# * `think_about_collected_information`: Thinking tool for pondering the completeness of collected information. +# * `think_about_task_adherence`: Thinking tool for determining whether the agent is still on track with the current task. +# * `think_about_whether_you_are_done`: Thinking tool for determining whether the task is truly completed. +# * `write_memory`: Writes a named memory (for future reference) to Serena's project-specific memory store. +excluded_tools: [] + +# initial prompt for the project. It will always be given to the LLM upon activating the project +# (contrary to the memories, which are loaded on demand). +initial_prompt: "" + +project_name: "rtsda-web-main" diff --git a/astro-church-website/Cargo.toml b/Cargo.toml similarity index 73% rename from astro-church-website/Cargo.toml rename to Cargo.toml index e2517b0..08fa8e2 100644 --- a/astro-church-website/Cargo.toml +++ b/Cargo.toml @@ -9,7 +9,7 @@ crate-type = ["cdylib"] [dependencies] napi = { version = "2", default-features = false, features = ["napi4"] } napi-derive = "2" -church-core = { path = "../church-core" } +church-core = { git = "https://git.rockvilletollandsda.church/RTSDA/church-core.git" } tokio = { version = "1", features = ["full"] } [build-dependencies] diff --git a/astro-church-website/DEPLOYMENT.md b/DEPLOYMENT.md similarity index 100% rename from astro-church-website/DEPLOYMENT.md rename to DEPLOYMENT.md diff --git a/astro-church-website/Dockerfile b/Dockerfile similarity index 100% rename from astro-church-website/Dockerfile rename to Dockerfile diff --git a/README.md b/README.md index 31ae941..87b813a 100644 --- a/README.md +++ b/README.md @@ -1,104 +1,43 @@ -# RTSDA Website +# Astro Starter Kit: Minimal -The official website for Rockville Tolland Seventh-day Adventist Church, built with Astro and powered by Rust bindings. - -## Features - -- **Modern Astro Framework** - Fast, component-based architecture -- **Event Management** - Submit and manage church events with recurring types -- **Live Streaming** - Watch services live with HLS.js support -- **Mobile App Integration** - iOS and Android app download links -- **Admin Panel** - Manage events, bulletins, and church content -- **Three Angels' Message** - Dedicated sections for Adventist theology -- **Rust-Powered Backend** - High-performance API bindings via `church-core` - -## Architecture - -``` -astro-church-website/ # Frontend Astro application -├── src/ -│ ├── pages/ # Astro pages and API routes -│ ├── components/ # Reusable UI components -│ └── layouts/ # Page layouts -└── public/ # Static assets - -church-core/ # Rust library for API bindings -├── src/ -│ ├── client/ # API client implementations -│ ├── models/ # Data structures -│ └── uniffi_wrapper.rs # FFI bindings -└── Cargo.toml +```sh +npm create astro@latest -- --template minimal ``` -## Development +> 🧑‍🚀 **Seasoned astronaut?** Delete this file. Have fun! -### Prerequisites +## 🚀 Project Structure -- Node.js 18+ and npm -- Rust 1.70+ -- Cargo +Inside of your Astro project, you'll see the following folders and files: -### Setup +```text +/ +├── public/ +├── src/ +│ └── pages/ +│ └── index.astro +└── package.json +``` -1. **Install dependencies:** - ```bash - cd astro-church-website - npm install - ``` +Astro looks for `.astro` or `.md` files in the `src/pages/` directory. Each page is exposed as a route based on its file name. -2. **Build Rust bindings:** - ```bash - npm run build:native - ``` +There's nothing special about `src/components/`, but that's where we like to put any Astro/React/Vue/Svelte/Preact components. -3. **Start development server:** - ```bash - npm run dev - ``` +Any static assets, like images, can be placed in the `public/` directory. -### Building for Production +## 🧞 Commands -1. **Build native bindings:** - ```bash - npm run build:native - ``` +All commands are run from the root of the project, from a terminal: -2. **Build Astro site:** - ```bash - npm run build - ``` +| Command | Action | +| :------------------------ | :----------------------------------------------- | +| `npm install` | Installs dependencies | +| `npm run dev` | Starts local dev server at `localhost:4321` | +| `npm run build` | Build your production site to `./dist/` | +| `npm run preview` | Preview your build locally, before deploying | +| `npm run astro ...` | Run CLI commands like `astro add`, `astro check` | +| `npm run astro -- --help` | Get help using the Astro CLI | -3. **Deploy to web server:** - ```bash - cp -r dist/* /var/www/rtsda-website/ - ``` +## 👀 Want to learn more? -## Recent Fixes - -- **SecondThirdSaturday Recurring Type** - Added support for `"2nd/3rd Saturday Monthly"` events -- **Event Display Issues** - Fixed events page showing "No Events Scheduled" -- **iOS App Compatibility** - Resolved recurring type parsing errors -- **Mobile App Downloads** - Added iOS App Store and Android APK download buttons -- **Session Management** - Improved admin panel authentication handling - -## API Integration - -The site integrates with the church API at `https://api.rockvilletollandsda.church` for: - -- Events and recurring schedules -- Sermon archives and live streams -- Church bulletins and announcements -- Contact form submissions -- Admin authentication - -## Contributing - -1. Fork the repository -2. Create a feature branch -3. Make your changes -4. Test thoroughly -5. Submit a pull request - -## License - -See LICENSE file for details. \ No newline at end of file +Feel free to check [our documentation](https://docs.astro.build) or jump into our [Discord server](https://astro.build/chat). diff --git a/astro-church-website/.cargo/config.toml b/astro-church-website/.cargo/config.toml deleted file mode 100644 index d10cc96..0000000 --- a/astro-church-website/.cargo/config.toml +++ /dev/null @@ -1,8 +0,0 @@ -[target.x86_64-unknown-linux-gnu] -linker = "x86_64-linux-gnu-gcc" - -[env] -CC_x86_64_unknown_linux_gnu = "x86_64-linux-gnu-gcc" -CXX_x86_64_unknown_linux_gnu = "x86_64-linux-gnu-g++" -AR_x86_64_unknown_linux_gnu = "x86_64-linux-gnu-ar" -CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_LINKER = "x86_64-linux-gnu-gcc" \ No newline at end of file diff --git a/astro-church-website/.gitignore b/astro-church-website/.gitignore deleted file mode 100644 index 16d54bb..0000000 --- a/astro-church-website/.gitignore +++ /dev/null @@ -1,24 +0,0 @@ -# build output -dist/ -# generated types -.astro/ - -# dependencies -node_modules/ - -# logs -npm-debug.log* -yarn-debug.log* -yarn-error.log* -pnpm-debug.log* - - -# environment variables -.env -.env.production - -# macOS-specific files -.DS_Store - -# jetbrains setting folder -.idea/ diff --git a/astro-church-website/README.md b/astro-church-website/README.md deleted file mode 100644 index 87b813a..0000000 --- a/astro-church-website/README.md +++ /dev/null @@ -1,43 +0,0 @@ -# Astro Starter Kit: Minimal - -```sh -npm create astro@latest -- --template minimal -``` - -> 🧑‍🚀 **Seasoned astronaut?** Delete this file. Have fun! - -## 🚀 Project Structure - -Inside of your Astro project, you'll see the following folders and files: - -```text -/ -├── public/ -├── src/ -│ └── pages/ -│ └── index.astro -└── package.json -``` - -Astro looks for `.astro` or `.md` files in the `src/pages/` directory. Each page is exposed as a route based on its file name. - -There's nothing special about `src/components/`, but that's where we like to put any Astro/React/Vue/Svelte/Preact components. - -Any static assets, like images, can be placed in the `public/` directory. - -## 🧞 Commands - -All commands are run from the root of the project, from a terminal: - -| Command | Action | -| :------------------------ | :----------------------------------------------- | -| `npm install` | Installs dependencies | -| `npm run dev` | Starts local dev server at `localhost:4321` | -| `npm run build` | Build your production site to `./dist/` | -| `npm run preview` | Preview your build locally, before deploying | -| `npm run astro ...` | Run CLI commands like `astro add`, `astro check` | -| `npm run astro -- --help` | Get help using the Astro CLI | - -## 👀 Want to learn more? - -Feel free to check [our documentation](https://docs.astro.build) or jump into our [Discord server](https://astro.build/chat). diff --git a/astro-church-website/index.cjs b/astro-church-website/index.cjs deleted file mode 100644 index 9bcb196..0000000 --- a/astro-church-website/index.cjs +++ /dev/null @@ -1,343 +0,0 @@ -/* tslint:disable */ -/* eslint-disable */ -/* prettier-ignore */ - -/* auto-generated by NAPI-RS */ - -const { existsSync, readFileSync } = require('fs') -const { join } = require('path') - -const { platform, arch } = process - -let nativeBinding = null -let localFileExisted = false -let loadError = null - -function isMusl() { - // For Node 10 - if (!process.report || typeof process.report.getReport !== 'function') { - try { - const lddPath = require('child_process').execSync('which ldd').toString().trim() - return readFileSync(lddPath, 'utf8').includes('musl') - } catch (e) { - return true - } - } else { - const { glibcVersionRuntime } = process.report.getReport().header - return !glibcVersionRuntime - } -} - -switch (platform) { - case 'android': - switch (arch) { - case 'arm64': - localFileExisted = existsSync(join(__dirname, 'church-core-bindings.android-arm64.node')) - try { - if (localFileExisted) { - nativeBinding = require('./church-core-bindings.android-arm64.node') - } else { - nativeBinding = require('astro-church-website-android-arm64') - } - } catch (e) { - loadError = e - } - break - case 'arm': - localFileExisted = existsSync(join(__dirname, 'church-core-bindings.android-arm-eabi.node')) - try { - if (localFileExisted) { - nativeBinding = require('./church-core-bindings.android-arm-eabi.node') - } else { - nativeBinding = require('astro-church-website-android-arm-eabi') - } - } catch (e) { - loadError = e - } - break - default: - throw new Error(`Unsupported architecture on Android ${arch}`) - } - break - case 'win32': - switch (arch) { - case 'x64': - localFileExisted = existsSync( - join(__dirname, 'church-core-bindings.win32-x64-msvc.node') - ) - try { - if (localFileExisted) { - nativeBinding = require('./church-core-bindings.win32-x64-msvc.node') - } else { - nativeBinding = require('astro-church-website-win32-x64-msvc') - } - } catch (e) { - loadError = e - } - break - case 'ia32': - localFileExisted = existsSync( - join(__dirname, 'church-core-bindings.win32-ia32-msvc.node') - ) - try { - if (localFileExisted) { - nativeBinding = require('./church-core-bindings.win32-ia32-msvc.node') - } else { - nativeBinding = require('astro-church-website-win32-ia32-msvc') - } - } catch (e) { - loadError = e - } - break - case 'arm64': - localFileExisted = existsSync( - join(__dirname, 'church-core-bindings.win32-arm64-msvc.node') - ) - try { - if (localFileExisted) { - nativeBinding = require('./church-core-bindings.win32-arm64-msvc.node') - } else { - nativeBinding = require('astro-church-website-win32-arm64-msvc') - } - } catch (e) { - loadError = e - } - break - default: - throw new Error(`Unsupported architecture on Windows: ${arch}`) - } - break - case 'darwin': - localFileExisted = existsSync(join(__dirname, 'church-core-bindings.darwin-universal.node')) - try { - if (localFileExisted) { - nativeBinding = require('./church-core-bindings.darwin-universal.node') - } else { - nativeBinding = require('astro-church-website-darwin-universal') - } - break - } catch {} - switch (arch) { - case 'x64': - localFileExisted = existsSync(join(__dirname, 'church-core-bindings.darwin-x64.node')) - try { - if (localFileExisted) { - nativeBinding = require('./church-core-bindings.darwin-x64.node') - } else { - nativeBinding = require('astro-church-website-darwin-x64') - } - } catch (e) { - loadError = e - } - break - case 'arm64': - localFileExisted = existsSync( - join(__dirname, 'church-core-bindings.darwin-arm64.node') - ) - try { - if (localFileExisted) { - nativeBinding = require('./church-core-bindings.darwin-arm64.node') - } else { - nativeBinding = require('astro-church-website-darwin-arm64') - } - } catch (e) { - loadError = e - } - break - default: - throw new Error(`Unsupported architecture on macOS: ${arch}`) - } - break - case 'freebsd': - if (arch !== 'x64') { - throw new Error(`Unsupported architecture on FreeBSD: ${arch}`) - } - localFileExisted = existsSync(join(__dirname, 'church-core-bindings.freebsd-x64.node')) - try { - if (localFileExisted) { - nativeBinding = require('./church-core-bindings.freebsd-x64.node') - } else { - nativeBinding = require('astro-church-website-freebsd-x64') - } - } catch (e) { - loadError = e - } - break - case 'linux': - switch (arch) { - case 'x64': - if (isMusl()) { - localFileExisted = existsSync( - join(__dirname, 'church-core-bindings.linux-x64-musl.node') - ) - try { - if (localFileExisted) { - nativeBinding = require('./church-core-bindings.linux-x64-musl.node') - } else { - nativeBinding = require('astro-church-website-linux-x64-musl') - } - } catch (e) { - loadError = e - } - } else { - localFileExisted = existsSync( - join(__dirname, 'church-core-bindings.linux-x64-gnu.node') - ) - try { - if (localFileExisted) { - nativeBinding = require('./church-core-bindings.linux-x64-gnu.node') - } else { - nativeBinding = require('astro-church-website-linux-x64-gnu') - } - } catch (e) { - loadError = e - } - } - break - case 'arm64': - if (isMusl()) { - localFileExisted = existsSync( - join(__dirname, 'church-core-bindings.linux-arm64-musl.node') - ) - try { - if (localFileExisted) { - nativeBinding = require('./church-core-bindings.linux-arm64-musl.node') - } else { - nativeBinding = require('astro-church-website-linux-arm64-musl') - } - } catch (e) { - loadError = e - } - } else { - localFileExisted = existsSync( - join(__dirname, 'church-core-bindings.linux-arm64-gnu.node') - ) - try { - if (localFileExisted) { - nativeBinding = require('./church-core-bindings.linux-arm64-gnu.node') - } else { - nativeBinding = require('astro-church-website-linux-arm64-gnu') - } - } catch (e) { - loadError = e - } - } - break - case 'arm': - if (isMusl()) { - localFileExisted = existsSync( - join(__dirname, 'church-core-bindings.linux-arm-musleabihf.node') - ) - try { - if (localFileExisted) { - nativeBinding = require('./church-core-bindings.linux-arm-musleabihf.node') - } else { - nativeBinding = require('astro-church-website-linux-arm-musleabihf') - } - } catch (e) { - loadError = e - } - } else { - localFileExisted = existsSync( - join(__dirname, 'church-core-bindings.linux-arm-gnueabihf.node') - ) - try { - if (localFileExisted) { - nativeBinding = require('./church-core-bindings.linux-arm-gnueabihf.node') - } else { - nativeBinding = require('astro-church-website-linux-arm-gnueabihf') - } - } catch (e) { - loadError = e - } - } - break - case 'riscv64': - if (isMusl()) { - localFileExisted = existsSync( - join(__dirname, 'church-core-bindings.linux-riscv64-musl.node') - ) - try { - if (localFileExisted) { - nativeBinding = require('./church-core-bindings.linux-riscv64-musl.node') - } else { - nativeBinding = require('astro-church-website-linux-riscv64-musl') - } - } catch (e) { - loadError = e - } - } else { - localFileExisted = existsSync( - join(__dirname, 'church-core-bindings.linux-riscv64-gnu.node') - ) - try { - if (localFileExisted) { - nativeBinding = require('./church-core-bindings.linux-riscv64-gnu.node') - } else { - nativeBinding = require('astro-church-website-linux-riscv64-gnu') - } - } catch (e) { - loadError = e - } - } - break - case 's390x': - localFileExisted = existsSync( - join(__dirname, 'church-core-bindings.linux-s390x-gnu.node') - ) - try { - if (localFileExisted) { - nativeBinding = require('./church-core-bindings.linux-s390x-gnu.node') - } else { - nativeBinding = require('astro-church-website-linux-s390x-gnu') - } - } catch (e) { - loadError = e - } - break - default: - throw new Error(`Unsupported architecture on Linux: ${arch}`) - } - break - default: - throw new Error(`Unsupported OS: ${platform}, architecture: ${arch}`) -} - -if (!nativeBinding) { - if (loadError) { - throw loadError - } - throw new Error(`Failed to load native binding`) -} - -const { getChurchName, fetchEventsJson, fetchFeaturedEventsJson, fetchSermonsJson, fetchConfigJson, getMissionStatement, fetchRandomBibleVerseJson, getStreamLiveStatus, getLivestreamUrl, getChurchAddress, getChurchPhysicalAddress, getChurchPoBox, getContactPhone, getContactEmail, getFacebookUrl, getYoutubeUrl, getInstagramUrl, submitContactV2Json, validateContactFormJson, fetchLivestreamArchiveJson, fetchBulletinsJson, fetchCurrentBulletinJson, fetchBibleVerseJson, submitEventJson, testAdminFunction, fetchAllSchedulesJson, createScheduleJson, updateScheduleJson, deleteScheduleJson } = nativeBinding - -module.exports.getChurchName = getChurchName -module.exports.fetchEventsJson = fetchEventsJson -module.exports.fetchFeaturedEventsJson = fetchFeaturedEventsJson -module.exports.fetchSermonsJson = fetchSermonsJson -module.exports.fetchConfigJson = fetchConfigJson -module.exports.getMissionStatement = getMissionStatement -module.exports.fetchRandomBibleVerseJson = fetchRandomBibleVerseJson -module.exports.getStreamLiveStatus = getStreamLiveStatus -module.exports.getLivestreamUrl = getLivestreamUrl -module.exports.getChurchAddress = getChurchAddress -module.exports.getChurchPhysicalAddress = getChurchPhysicalAddress -module.exports.getChurchPoBox = getChurchPoBox -module.exports.getContactPhone = getContactPhone -module.exports.getContactEmail = getContactEmail -module.exports.getFacebookUrl = getFacebookUrl -module.exports.getYoutubeUrl = getYoutubeUrl -module.exports.getInstagramUrl = getInstagramUrl -module.exports.submitContactV2Json = submitContactV2Json -module.exports.validateContactFormJson = validateContactFormJson -module.exports.fetchLivestreamArchiveJson = fetchLivestreamArchiveJson -module.exports.fetchBulletinsJson = fetchBulletinsJson -module.exports.fetchCurrentBulletinJson = fetchCurrentBulletinJson -module.exports.fetchBibleVerseJson = fetchBibleVerseJson -module.exports.submitEventJson = submitEventJson -module.exports.testAdminFunction = testAdminFunction -module.exports.fetchAllSchedulesJson = fetchAllSchedulesJson -module.exports.createScheduleJson = createScheduleJson -module.exports.updateScheduleJson = updateScheduleJson -module.exports.deleteScheduleJson = deleteScheduleJson diff --git a/astro-church-website/index.d.ts b/astro-church-website/index.d.ts deleted file mode 100644 index a7d66f4..0000000 --- a/astro-church-website/index.d.ts +++ /dev/null @@ -1,34 +0,0 @@ -/* tslint:disable */ -/* eslint-disable */ - -/* auto-generated by NAPI-RS */ - -export declare function getChurchName(): string -export declare function fetchEventsJson(): string -export declare function fetchFeaturedEventsJson(): string -export declare function fetchSermonsJson(): string -export declare function fetchConfigJson(): string -export declare function getMissionStatement(): string -export declare function fetchRandomBibleVerseJson(): string -export declare function getStreamLiveStatus(): boolean -export declare function getLivestreamUrl(): string -export declare function getChurchAddress(): string -export declare function getChurchPhysicalAddress(): string -export declare function getChurchPoBox(): string -export declare function getContactPhone(): string -export declare function getContactEmail(): string -export declare function getFacebookUrl(): string -export declare function getYoutubeUrl(): string -export declare function getInstagramUrl(): string -export declare function submitContactV2Json(name: string, email: string, subject: string, message: string, phone: string): string -export declare function validateContactFormJson(formJson: string): string -export declare function fetchLivestreamArchiveJson(): string -export declare function fetchBulletinsJson(): string -export declare function fetchCurrentBulletinJson(): string -export declare function fetchBibleVerseJson(query: string): string -export declare function submitEventJson(title: string, description: string, startTime: string, endTime: string, location: string, locationUrl: string | undefined | null, category: string, recurringType?: string | undefined | null, submitterEmail?: string | undefined | null): string -export declare function testAdminFunction(): string -export declare function fetchAllSchedulesJson(): string -export declare function createScheduleJson(scheduleJson: string): string -export declare function updateScheduleJson(date: string, updateJson: string): string -export declare function deleteScheduleJson(date: string): string diff --git a/astro-church-website/linux/index.d.ts b/astro-church-website/linux/index.d.ts deleted file mode 100644 index c3f76de..0000000 --- a/astro-church-website/linux/index.d.ts +++ /dev/null @@ -1,27 +0,0 @@ -/* tslint:disable */ -/* eslint-disable */ - -/* auto-generated by NAPI-RS */ - -export declare function getChurchName(): string -export declare function fetchEventsJson(): string -export declare function fetchFeaturedEventsJson(): string -export declare function fetchSermonsJson(): string -export declare function fetchConfigJson(): string -export declare function getMissionStatement(): string -export declare function fetchRandomBibleVerseJson(): string -export declare function getStreamLiveStatus(): boolean -export declare function getLivestreamUrl(): string -export declare function getChurchAddress(): string -export declare function getContactPhone(): string -export declare function getContactEmail(): string -export declare function getFacebookUrl(): string -export declare function getYoutubeUrl(): string -export declare function getInstagramUrl(): string -export declare function submitContactV2Json(name: string, email: string, subject: string, message: string, phone: string): string -export declare function validateContactFormJson(formJson: string): string -export declare function fetchLivestreamArchiveJson(): string -export declare function fetchBulletinsJson(): string -export declare function fetchCurrentBulletinJson(): string -export declare function fetchBibleVerseJson(query: string): string -export declare function submitEventJson(title: string, description: string, startTime: string, endTime: string, location: string, locationUrl: string | undefined | null, category: string, recurringType?: string | undefined | null, submitterEmail?: string | undefined | null): string diff --git a/astro-church-website/package-lock.json b/astro-church-website/package-lock.json deleted file mode 100644 index 99a88d6..0000000 --- a/astro-church-website/package-lock.json +++ /dev/null @@ -1,6752 +0,0 @@ -{ - "name": "astro-church-website", - "version": "0.0.1", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "astro-church-website", - "version": "0.0.1", - "dependencies": { - "@astrojs/node": "^9.4.2", - "@astrojs/tailwind": "^6.0.2", - "astro": "^5.13.0", - "form-data": "^4.0.4", - "node-fetch": "^3.3.2", - "tailwindcss": "^3.4.17" - }, - "devDependencies": { - "@napi-rs/cli": "^2.18.4" - } - }, - "node_modules/@alloc/quick-lru": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", - "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@astrojs/compiler": { - "version": "2.12.2", - "resolved": "https://registry.npmjs.org/@astrojs/compiler/-/compiler-2.12.2.tgz", - "integrity": "sha512-w2zfvhjNCkNMmMMOn5b0J8+OmUaBL1o40ipMvqcG6NRpdC+lKxmTi48DT8Xw0SzJ3AfmeFLB45zXZXtmbsjcgw==", - "license": "MIT" - }, - "node_modules/@astrojs/internal-helpers": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/@astrojs/internal-helpers/-/internal-helpers-0.7.2.tgz", - "integrity": "sha512-KCkCqR3Goym79soqEtbtLzJfqhTWMyVaizUi35FLzgGSzBotSw8DB1qwsu7U96ihOJgYhDk2nVPz+3LnXPeX6g==", - "license": "MIT" - }, - "node_modules/@astrojs/markdown-remark": { - "version": "6.3.6", - "resolved": "https://registry.npmjs.org/@astrojs/markdown-remark/-/markdown-remark-6.3.6.tgz", - "integrity": "sha512-bwylYktCTsLMVoCOEHbn2GSUA3c5KT/qilekBKA3CBng0bo1TYjNZPr761vxumRk9kJGqTOtU+fgCAp5Vwokug==", - "license": "MIT", - "dependencies": { - "@astrojs/internal-helpers": "0.7.2", - "@astrojs/prism": "3.3.0", - "github-slugger": "^2.0.0", - "hast-util-from-html": "^2.0.3", - "hast-util-to-text": "^4.0.2", - "import-meta-resolve": "^4.1.0", - "js-yaml": "^4.1.0", - "mdast-util-definitions": "^6.0.0", - "rehype-raw": "^7.0.0", - "rehype-stringify": "^10.0.1", - "remark-gfm": "^4.0.1", - "remark-parse": "^11.0.0", - "remark-rehype": "^11.1.2", - "remark-smartypants": "^3.0.2", - "shiki": "^3.2.1", - "smol-toml": "^1.3.4", - "unified": "^11.0.5", - "unist-util-remove-position": "^5.0.0", - "unist-util-visit": "^5.0.0", - "unist-util-visit-parents": "^6.0.1", - "vfile": "^6.0.3" - } - }, - "node_modules/@astrojs/node": { - "version": "9.4.2", - "resolved": "https://registry.npmjs.org/@astrojs/node/-/node-9.4.2.tgz", - "integrity": "sha512-4whvXWUIL7yi84ayEXCZd/G2sLMqJKiA7hKps2Z3AVPlymXWY7qyafJ/5gphD6CzRjen6+mqPRYeqxnJG8VcDw==", - "license": "MIT", - "dependencies": { - "@astrojs/internal-helpers": "0.7.2", - "send": "^1.2.0", - "server-destroy": "^1.0.1" - }, - "peerDependencies": { - "astro": "^5.3.0" - } - }, - "node_modules/@astrojs/prism": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/@astrojs/prism/-/prism-3.3.0.tgz", - "integrity": "sha512-q8VwfU/fDZNoDOf+r7jUnMC2//H2l0TuQ6FkGJL8vD8nw/q5KiL3DS1KKBI3QhI9UQhpJ5dc7AtqfbXWuOgLCQ==", - "license": "MIT", - "dependencies": { - "prismjs": "^1.30.0" - }, - "engines": { - "node": "18.20.8 || ^20.3.0 || >=22.0.0" - } - }, - "node_modules/@astrojs/tailwind": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@astrojs/tailwind/-/tailwind-6.0.2.tgz", - "integrity": "sha512-j3mhLNeugZq6A8dMNXVarUa8K6X9AW+QHU9u3lKNrPLMHhOQ0S7VeWhHwEeJFpEK1BTKEUY1U78VQv2gN6hNGg==", - "license": "MIT", - "dependencies": { - "autoprefixer": "^10.4.21", - "postcss": "^8.5.3", - "postcss-load-config": "^4.0.2" - }, - "peerDependencies": { - "astro": "^3.0.0 || ^4.0.0 || ^5.0.0", - "tailwindcss": "^3.0.24" - } - }, - "node_modules/@astrojs/telemetry": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/@astrojs/telemetry/-/telemetry-3.3.0.tgz", - "integrity": "sha512-UFBgfeldP06qu6khs/yY+q1cDAaArM2/7AEIqQ9Cuvf7B1hNLq0xDrZkct+QoIGyjq56y8IaE2I3CTvG99mlhQ==", - "license": "MIT", - "dependencies": { - "ci-info": "^4.2.0", - "debug": "^4.4.0", - "dlv": "^1.1.3", - "dset": "^3.1.4", - "is-docker": "^3.0.0", - "is-wsl": "^3.1.0", - "which-pm-runs": "^1.1.0" - }, - "engines": { - "node": "18.20.8 || ^20.3.0 || >=22.0.0" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", - "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", - "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/parser": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.3.tgz", - "integrity": "sha512-7+Ey1mAgYqFAx2h0RuoxcQT5+MlG3GTV0TQrgr7/ZliKsm/MNDxVVutlWaziMq7wJNAz8MTqz55XLpWvva6StA==", - "license": "MIT", - "dependencies": { - "@babel/types": "^7.28.2" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/types": { - "version": "7.28.2", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.2.tgz", - "integrity": "sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==", - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@capsizecss/unpack": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@capsizecss/unpack/-/unpack-2.4.0.tgz", - "integrity": "sha512-GrSU71meACqcmIUxPYOJvGKF0yryjN/L1aCuE9DViCTJI7bfkjgYDPD1zbNDcINJwSSP6UaBZY9GAbYDO7re0Q==", - "license": "MIT", - "dependencies": { - "blob-to-buffer": "^1.2.8", - "cross-fetch": "^3.0.4", - "fontkit": "^2.0.2" - } - }, - "node_modules/@emnapi/runtime": { - "version": "1.4.5", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.5.tgz", - "integrity": "sha512-++LApOtY0pEEz1zrd9vy1/zXVaVJJ/EbAF3u0fXIzPJEDtnITsBGbbK0EkM72amhl/R5b+5xx0Y/QhcVOpuulg==", - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.9.tgz", - "integrity": "sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA==", - "cpu": [ - "ppc64" - ], - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.9.tgz", - "integrity": "sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.9.tgz", - "integrity": "sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.9.tgz", - "integrity": "sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.9.tgz", - "integrity": "sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.9.tgz", - "integrity": "sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.9.tgz", - "integrity": "sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.9.tgz", - "integrity": "sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.9.tgz", - "integrity": "sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.9.tgz", - "integrity": "sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.9.tgz", - "integrity": "sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A==", - "cpu": [ - "ia32" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.9.tgz", - "integrity": "sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ==", - "cpu": [ - "loong64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.9.tgz", - "integrity": "sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA==", - "cpu": [ - "mips64el" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.9.tgz", - "integrity": "sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w==", - "cpu": [ - "ppc64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.9.tgz", - "integrity": "sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg==", - "cpu": [ - "riscv64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.9.tgz", - "integrity": "sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA==", - "cpu": [ - "s390x" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.9.tgz", - "integrity": "sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.9.tgz", - "integrity": "sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.9.tgz", - "integrity": "sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.9.tgz", - "integrity": "sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.9.tgz", - "integrity": "sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openharmony-arm64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.9.tgz", - "integrity": "sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.9.tgz", - "integrity": "sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.9.tgz", - "integrity": "sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.9.tgz", - "integrity": "sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww==", - "cpu": [ - "ia32" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.9.tgz", - "integrity": "sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@img/sharp-darwin-arm64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz", - "integrity": "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-darwin-arm64": "1.0.4" - } - }, - "node_modules/@img/sharp-darwin-x64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.5.tgz", - "integrity": "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-darwin-x64": "1.0.4" - } - }, - "node_modules/@img/sharp-libvips-darwin-arm64": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.4.tgz", - "integrity": "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==", - "cpu": [ - "arm64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "darwin" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-darwin-x64": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.4.tgz", - "integrity": "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==", - "cpu": [ - "x64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "darwin" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-arm": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.5.tgz", - "integrity": "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==", - "cpu": [ - "arm" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-arm64": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.4.tgz", - "integrity": "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==", - "cpu": [ - "arm64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-s390x": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.4.tgz", - "integrity": "sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==", - "cpu": [ - "s390x" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-x64": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.4.tgz", - "integrity": "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==", - "cpu": [ - "x64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linuxmusl-arm64": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.4.tgz", - "integrity": "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==", - "cpu": [ - "arm64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linuxmusl-x64": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.4.tgz", - "integrity": "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==", - "cpu": [ - "x64" - ], - "license": "LGPL-3.0-or-later", - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-linux-arm": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.5.tgz", - "integrity": "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==", - "cpu": [ - "arm" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-arm": "1.0.5" - } - }, - "node_modules/@img/sharp-linux-arm64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.5.tgz", - "integrity": "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-arm64": "1.0.4" - } - }, - "node_modules/@img/sharp-linux-s390x": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.5.tgz", - "integrity": "sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==", - "cpu": [ - "s390x" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-s390x": "1.0.4" - } - }, - "node_modules/@img/sharp-linux-x64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.5.tgz", - "integrity": "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-x64": "1.0.4" - } - }, - "node_modules/@img/sharp-linuxmusl-arm64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.5.tgz", - "integrity": "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-arm64": "1.0.4" - } - }, - "node_modules/@img/sharp-linuxmusl-x64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.5.tgz", - "integrity": "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-x64": "1.0.4" - } - }, - "node_modules/@img/sharp-wasm32": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.33.5.tgz", - "integrity": "sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==", - "cpu": [ - "wasm32" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", - "optional": true, - "dependencies": { - "@emnapi/runtime": "^1.2.0" - }, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-win32-ia32": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.5.tgz", - "integrity": "sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==", - "cpu": [ - "ia32" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-win32-x64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.5.tgz", - "integrity": "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0 AND LGPL-3.0-or-later", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@isaacs/cliui": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "license": "ISC", - "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@isaacs/cliui/node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "license": "MIT" - }, - "node_modules/@isaacs/cliui/node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "license": "MIT", - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.13", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", - "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", - "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.30", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.30.tgz", - "integrity": "sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==", - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@napi-rs/cli": { - "version": "2.18.4", - "resolved": "https://registry.npmjs.org/@napi-rs/cli/-/cli-2.18.4.tgz", - "integrity": "sha512-SgJeA4df9DE2iAEpr3M2H0OKl/yjtg1BnRI5/JyowS71tUWhrfSu2LT0V3vlHET+g1hBVlrO60PmEXwUEKp8Mg==", - "dev": true, - "license": "MIT", - "bin": { - "napi": "scripts/index.js" - }, - "engines": { - "node": ">= 10" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Brooooooklyn" - } - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "license": "MIT", - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@oslojs/encoding": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@oslojs/encoding/-/encoding-1.1.0.tgz", - "integrity": "sha512-70wQhgYmndg4GCPxPPxPGevRKqTIJ2Nh4OkiMWmDAVYsTQ+Ta7Sq+rPevXyXGdzr30/qZBnyOalCszoMxlyldQ==", - "license": "MIT" - }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">=14" - } - }, - "node_modules/@rollup/pluginutils": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.2.0.tgz", - "integrity": "sha512-qWJ2ZTbmumwiLFomfzTyt5Kng4hwPi9rwCYN4SHb6eaRU1KNO4ccxINHr/VhH4GgPlt1XfSTLX2LBTme8ne4Zw==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "estree-walker": "^2.0.2", - "picomatch": "^4.0.2" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" - }, - "peerDependenciesMeta": { - "rollup": { - "optional": true - } - } - }, - "node_modules/@rollup/pluginutils/node_modules/estree-walker": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", - "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", - "license": "MIT" - }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.46.2.tgz", - "integrity": "sha512-Zj3Hl6sN34xJtMv7Anwb5Gu01yujyE/cLBDB2gnHTAHaWS1Z38L7kuSG+oAh0giZMqG060f/YBStXtMH6FvPMA==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.46.2.tgz", - "integrity": "sha512-nTeCWY83kN64oQ5MGz3CgtPx8NSOhC5lWtsjTs+8JAJNLcP3QbLCtDDgUKQc/Ro/frpMq4SHUaHN6AMltcEoLQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.46.2.tgz", - "integrity": "sha512-HV7bW2Fb/F5KPdM/9bApunQh68YVDU8sO8BvcW9OngQVN3HHHkw99wFupuUJfGR9pYLLAjcAOA6iO+evsbBaPQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.46.2.tgz", - "integrity": "sha512-SSj8TlYV5nJixSsm/y3QXfhspSiLYP11zpfwp6G/YDXctf3Xkdnk4woJIF5VQe0of2OjzTt8EsxnJDCdHd2xMA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.46.2.tgz", - "integrity": "sha512-ZyrsG4TIT9xnOlLsSSi9w/X29tCbK1yegE49RYm3tu3wF1L/B6LVMqnEWyDB26d9Ecx9zrmXCiPmIabVuLmNSg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.46.2.tgz", - "integrity": "sha512-pCgHFoOECwVCJ5GFq8+gR8SBKnMO+xe5UEqbemxBpCKYQddRQMgomv1104RnLSg7nNvgKy05sLsY51+OVRyiVw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.46.2.tgz", - "integrity": "sha512-EtP8aquZ0xQg0ETFcxUbU71MZlHaw9MChwrQzatiE8U/bvi5uv/oChExXC4mWhjiqK7azGJBqU0tt5H123SzVA==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.46.2.tgz", - "integrity": "sha512-qO7F7U3u1nfxYRPM8HqFtLd+raev2K137dsV08q/LRKRLEc7RsiDWihUnrINdsWQxPR9jqZ8DIIZ1zJJAm5PjQ==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.46.2.tgz", - "integrity": "sha512-3dRaqLfcOXYsfvw5xMrxAk9Lb1f395gkoBYzSFcc/scgRFptRXL9DOaDpMiehf9CO8ZDRJW2z45b6fpU5nwjng==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.46.2.tgz", - "integrity": "sha512-fhHFTutA7SM+IrR6lIfiHskxmpmPTJUXpWIsBXpeEwNgZzZZSg/q4i6FU4J8qOGyJ0TR+wXBwx/L7Ho9z0+uDg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.46.2.tgz", - "integrity": "sha512-i7wfGFXu8x4+FRqPymzjD+Hyav8l95UIZ773j7J7zRYc3Xsxy2wIn4x+llpunexXe6laaO72iEjeeGyUFmjKeA==", - "cpu": [ - "loong64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.46.2.tgz", - "integrity": "sha512-B/l0dFcHVUnqcGZWKcWBSV2PF01YUt0Rvlurci5P+neqY/yMKchGU8ullZvIv5e8Y1C6wOn+U03mrDylP5q9Yw==", - "cpu": [ - "ppc64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.46.2.tgz", - "integrity": "sha512-32k4ENb5ygtkMwPMucAb8MtV8olkPT03oiTxJbgkJa7lJ7dZMr0GCFJlyvy+K8iq7F/iuOr41ZdUHaOiqyR3iQ==", - "cpu": [ - "riscv64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.46.2.tgz", - "integrity": "sha512-t5B2loThlFEauloaQkZg9gxV05BYeITLvLkWOkRXogP4qHXLkWSbSHKM9S6H1schf/0YGP/qNKtiISlxvfmmZw==", - "cpu": [ - "riscv64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.46.2.tgz", - "integrity": "sha512-YKjekwTEKgbB7n17gmODSmJVUIvj8CX7q5442/CK80L8nqOUbMtf8b01QkG3jOqyr1rotrAnW6B/qiHwfcuWQA==", - "cpu": [ - "s390x" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.46.2.tgz", - "integrity": "sha512-Jj5a9RUoe5ra+MEyERkDKLwTXVu6s3aACP51nkfnK9wJTraCC8IMe3snOfALkrjTYd2G1ViE1hICj0fZ7ALBPA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.46.2.tgz", - "integrity": "sha512-7kX69DIrBeD7yNp4A5b81izs8BqoZkCIaxQaOpumcJ1S/kmqNFjPhDu1LHeVXv0SexfHQv5cqHsxLOjETuqDuA==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.46.2.tgz", - "integrity": "sha512-wiJWMIpeaak/jsbaq2HMh/rzZxHVW1rU6coyeNNpMwk5isiPjSTx0a4YLSlYDwBH/WBvLz+EtsNqQScZTLJy3g==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.46.2.tgz", - "integrity": "sha512-gBgaUDESVzMgWZhcyjfs9QFK16D8K6QZpwAaVNJxYDLHWayOta4ZMjGm/vsAEy3hvlS2GosVFlBlP9/Wb85DqQ==", - "cpu": [ - "ia32" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.46.2.tgz", - "integrity": "sha512-CvUo2ixeIQGtF6WvuB87XWqPQkoFAFqW+HUo/WzHwuHDvIwZCtjdWXoYCcr06iKGydiqTclC4jU/TNObC/xKZg==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@shikijs/core": { - "version": "3.11.0", - "resolved": "https://registry.npmjs.org/@shikijs/core/-/core-3.11.0.tgz", - "integrity": "sha512-oJwU+DxGqp6lUZpvtQgVOXNZcVsirN76tihOLBmwILkKuRuwHteApP8oTXmL4tF5vS5FbOY0+8seXmiCoslk4g==", - "license": "MIT", - "dependencies": { - "@shikijs/types": "3.11.0", - "@shikijs/vscode-textmate": "^10.0.2", - "@types/hast": "^3.0.4", - "hast-util-to-html": "^9.0.5" - } - }, - "node_modules/@shikijs/engine-javascript": { - "version": "3.11.0", - "resolved": "https://registry.npmjs.org/@shikijs/engine-javascript/-/engine-javascript-3.11.0.tgz", - "integrity": "sha512-6/ov6pxrSvew13k9ztIOnSBOytXeKs5kfIR7vbhdtVRg+KPzvp2HctYGeWkqv7V6YIoLicnig/QF3iajqyElZA==", - "license": "MIT", - "dependencies": { - "@shikijs/types": "3.11.0", - "@shikijs/vscode-textmate": "^10.0.2", - "oniguruma-to-es": "^4.3.3" - } - }, - "node_modules/@shikijs/engine-oniguruma": { - "version": "3.11.0", - "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-3.11.0.tgz", - "integrity": "sha512-4DwIjIgETK04VneKbfOE4WNm4Q7WC1wo95wv82PoHKdqX4/9qLRUwrfKlmhf0gAuvT6GHy0uc7t9cailk6Tbhw==", - "license": "MIT", - "dependencies": { - "@shikijs/types": "3.11.0", - "@shikijs/vscode-textmate": "^10.0.2" - } - }, - "node_modules/@shikijs/langs": { - "version": "3.11.0", - "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-3.11.0.tgz", - "integrity": "sha512-Njg/nFL4HDcf/ObxcK2VeyidIq61EeLmocrwTHGGpOQx0BzrPWM1j55XtKQ1LvvDWH15cjQy7rg96aJ1/l63uw==", - "license": "MIT", - "dependencies": { - "@shikijs/types": "3.11.0" - } - }, - "node_modules/@shikijs/themes": { - "version": "3.11.0", - "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-3.11.0.tgz", - "integrity": "sha512-BhhWRzCTEk2CtWt4S4bgsOqPJRkapvxdsifAwqP+6mk5uxboAQchc0etiJ0iIasxnMsb764qGD24DK9albcU9Q==", - "license": "MIT", - "dependencies": { - "@shikijs/types": "3.11.0" - } - }, - "node_modules/@shikijs/types": { - "version": "3.11.0", - "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-3.11.0.tgz", - "integrity": "sha512-RB7IMo2E7NZHyfkqAuaf4CofyY8bPzjWPjJRzn6SEak3b46fIQyG6Vx5fG/obqkfppQ+g8vEsiD7Uc6lqQt32Q==", - "license": "MIT", - "dependencies": { - "@shikijs/vscode-textmate": "^10.0.2", - "@types/hast": "^3.0.4" - } - }, - "node_modules/@shikijs/vscode-textmate": { - "version": "10.0.2", - "resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-10.0.2.tgz", - "integrity": "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==", - "license": "MIT" - }, - "node_modules/@swc/helpers": { - "version": "0.5.17", - "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.17.tgz", - "integrity": "sha512-5IKx/Y13RsYd+sauPb2x+U/xZikHjolzfuDgTAl/Tdf3Q8rslRvC19NKDLgAJQ6wsqADk10ntlv08nPFw/gO/A==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.8.0" - } - }, - "node_modules/@types/debug": { - "version": "4.1.12", - "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", - "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", - "license": "MIT", - "dependencies": { - "@types/ms": "*" - } - }, - "node_modules/@types/estree": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", - "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", - "license": "MIT" - }, - "node_modules/@types/fontkit": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/@types/fontkit/-/fontkit-2.0.8.tgz", - "integrity": "sha512-wN+8bYxIpJf+5oZdrdtaX04qUuWHcKxcDEgRS9Qm9ZClSHjzEn13SxUC+5eRM+4yXIeTYk8mTzLAWGF64847ew==", - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/hast": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", - "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", - "license": "MIT", - "dependencies": { - "@types/unist": "*" - } - }, - "node_modules/@types/mdast": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", - "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", - "license": "MIT", - "dependencies": { - "@types/unist": "*" - } - }, - "node_modules/@types/ms": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", - "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", - "license": "MIT" - }, - "node_modules/@types/nlcst": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@types/nlcst/-/nlcst-2.0.3.tgz", - "integrity": "sha512-vSYNSDe6Ix3q+6Z7ri9lyWqgGhJTmzRjZRqyq15N0Z/1/UnVsno9G/N40NBijoYx2seFDIl0+B2mgAb9mezUCA==", - "license": "MIT", - "dependencies": { - "@types/unist": "*" - } - }, - "node_modules/@types/node": { - "version": "24.2.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.2.1.tgz", - "integrity": "sha512-DRh5K+ka5eJic8CjH7td8QpYEV6Zo10gfRkjHCO3weqZHWDtAaSTFtl4+VMqOJ4N5jcuhZ9/l+yy8rVgw7BQeQ==", - "license": "MIT", - "dependencies": { - "undici-types": "~7.10.0" - } - }, - "node_modules/@types/unist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", - "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", - "license": "MIT" - }, - "node_modules/@ungap/structured-clone": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", - "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", - "license": "ISC" - }, - "node_modules/acorn": { - "version": "8.15.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", - "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/ansi-align": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", - "integrity": "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==", - "license": "ISC", - "dependencies": { - "string-width": "^4.1.0" - } - }, - "node_modules/ansi-align/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-align/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "license": "MIT" - }, - "node_modules/ansi-align/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-align/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/any-promise": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", - "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", - "license": "MIT" - }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "license": "ISC", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/anymatch/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/arg": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", - "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", - "license": "MIT" - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "license": "Python-2.0" - }, - "node_modules/aria-query": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", - "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", - "license": "Apache-2.0", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/array-iterate": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/array-iterate/-/array-iterate-2.0.1.tgz", - "integrity": "sha512-I1jXZMjAgCMmxT4qxXfPXa6SthSoE8h6gkSI9BGGNv8mP8G/v0blc+qFnZu6K42vTOiuME596QaLO0TP3Lk0xg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/astro": { - "version": "5.13.3", - "resolved": "https://registry.npmjs.org/astro/-/astro-5.13.3.tgz", - "integrity": "sha512-V0mUOUK70UZ7xqXp5Noqse/SREU0P756KgFufBEluq5LkmBejzC2GENMUA2Na+PFwUjemElJtRlpKyrnKpFhSQ==", - "license": "MIT", - "dependencies": { - "@astrojs/compiler": "^2.12.2", - "@astrojs/internal-helpers": "0.7.2", - "@astrojs/markdown-remark": "6.3.6", - "@astrojs/telemetry": "3.3.0", - "@capsizecss/unpack": "^2.4.0", - "@oslojs/encoding": "^1.1.0", - "@rollup/pluginutils": "^5.1.4", - "acorn": "^8.14.1", - "aria-query": "^5.3.2", - "axobject-query": "^4.1.0", - "boxen": "8.0.1", - "ci-info": "^4.2.0", - "clsx": "^2.1.1", - "common-ancestor-path": "^1.0.1", - "cookie": "^1.0.2", - "cssesc": "^3.0.0", - "debug": "^4.4.0", - "deterministic-object-hash": "^2.0.2", - "devalue": "^5.1.1", - "diff": "^5.2.0", - "dlv": "^1.1.3", - "dset": "^3.1.4", - "es-module-lexer": "^1.6.0", - "esbuild": "^0.25.0", - "estree-walker": "^3.0.3", - "flattie": "^1.1.1", - "fontace": "~0.3.0", - "github-slugger": "^2.0.0", - "html-escaper": "3.0.3", - "http-cache-semantics": "^4.1.1", - "import-meta-resolve": "^4.1.0", - "js-yaml": "^4.1.0", - "kleur": "^4.1.5", - "magic-string": "^0.30.17", - "magicast": "^0.3.5", - "mrmime": "^2.0.1", - "neotraverse": "^0.6.18", - "p-limit": "^6.2.0", - "p-queue": "^8.1.0", - "package-manager-detector": "^1.1.0", - "picomatch": "^4.0.2", - "prompts": "^2.4.2", - "rehype": "^13.0.2", - "semver": "^7.7.1", - "shiki": "^3.2.1", - "smol-toml": "^1.3.4", - "tinyexec": "^0.3.2", - "tinyglobby": "^0.2.12", - "tsconfck": "^3.1.5", - "ultrahtml": "^1.6.0", - "unifont": "~0.5.0", - "unist-util-visit": "^5.0.0", - "unstorage": "^1.15.0", - "vfile": "^6.0.3", - "vite": "^6.3.4", - "vitefu": "^1.0.6", - "xxhash-wasm": "^1.1.0", - "yargs-parser": "^21.1.1", - "yocto-spinner": "^0.2.1", - "zod": "^3.24.4", - "zod-to-json-schema": "^3.24.5", - "zod-to-ts": "^1.2.0" - }, - "bin": { - "astro": "astro.js" - }, - "engines": { - "node": "18.20.8 || ^20.3.0 || >=22.0.0", - "npm": ">=9.6.5", - "pnpm": ">=7.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/astrodotbuild" - }, - "optionalDependencies": { - "sharp": "^0.33.3" - } - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "license": "MIT" - }, - "node_modules/autoprefixer": { - "version": "10.4.21", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.21.tgz", - "integrity": "sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/autoprefixer" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "browserslist": "^4.24.4", - "caniuse-lite": "^1.0.30001702", - "fraction.js": "^4.3.7", - "normalize-range": "^0.1.2", - "picocolors": "^1.1.1", - "postcss-value-parser": "^4.2.0" - }, - "bin": { - "autoprefixer": "bin/autoprefixer" - }, - "engines": { - "node": "^10 || ^12 || >=14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/axobject-query": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", - "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", - "license": "Apache-2.0", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/bail": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", - "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "license": "MIT" - }, - "node_modules/base-64": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/base-64/-/base-64-1.0.0.tgz", - "integrity": "sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg==", - "license": "MIT" - }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/binary-extensions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", - "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/blob-to-buffer": { - "version": "1.2.9", - "resolved": "https://registry.npmjs.org/blob-to-buffer/-/blob-to-buffer-1.2.9.tgz", - "integrity": "sha512-BF033y5fN6OCofD3vgHmNtwZWRcq9NLyyxyILx9hfMy1sXYy4ojFl765hJ2lP0YaN2fuxPaLO2Vzzoxy0FLFFA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/boxen": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/boxen/-/boxen-8.0.1.tgz", - "integrity": "sha512-F3PH5k5juxom4xktynS7MoFY+NUWH5LC4CnH11YB8NPew+HLpmBLCybSAEyb2F+4pRXhuhWqFesoQd6DAyc2hw==", - "license": "MIT", - "dependencies": { - "ansi-align": "^3.0.1", - "camelcase": "^8.0.0", - "chalk": "^5.3.0", - "cli-boxes": "^3.0.0", - "string-width": "^7.2.0", - "type-fest": "^4.21.0", - "widest-line": "^5.0.0", - "wrap-ansi": "^9.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "license": "MIT", - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/brotli": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/brotli/-/brotli-1.3.3.tgz", - "integrity": "sha512-oTKjJdShmDuGW94SyyaoQvAjf30dZaHnjJ8uAF+u2/vGJkJbJPJAT1gDiOJP5v1Zb6f9KEyW/1HpuaWIXtGHPg==", - "license": "MIT", - "dependencies": { - "base64-js": "^1.1.2" - } - }, - "node_modules/browserslist": { - "version": "4.25.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.2.tgz", - "integrity": "sha512-0si2SJK3ooGzIawRu61ZdPCO1IncZwS8IzuX73sPZsXW6EQ/w/DAfPyKI8l1ETTCr2MnvqWitmlCUxgdul45jA==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "caniuse-lite": "^1.0.30001733", - "electron-to-chromium": "^1.5.199", - "node-releases": "^2.0.19", - "update-browserslist-db": "^1.1.3" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/call-bind-apply-helpers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/camelcase": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-8.0.0.tgz", - "integrity": "sha512-8WB3Jcas3swSvjIeA2yvCJ+Miyz5l1ZmB6HFb9R1317dt9LCQoswg/BGrmAmkWVEszSrrg4RwmO46qIm2OEnSA==", - "license": "MIT", - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/camelcase-css": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", - "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001735", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001735.tgz", - "integrity": "sha512-EV/laoX7Wq2J9TQlyIXRxTJqIw4sxfXS4OYgudGxBYRuTv0q7AM6yMEpU/Vo1I94thg9U6EZ2NfZx9GJq83u7w==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "CC-BY-4.0" - }, - "node_modules/ccount": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", - "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/chalk": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.5.0.tgz", - "integrity": "sha512-1tm8DTaJhPBG3bIkVeZt1iZM9GfSX2lzOeDVZH9R9ffRHpmHvxZ/QhgQH/aDTkswQVt+YHdXAdS/In/30OjCbg==", - "license": "MIT", - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/character-entities": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", - "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/character-entities-html4": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", - "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/character-entities-legacy": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", - "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/chokidar": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", - "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", - "license": "MIT", - "dependencies": { - "readdirp": "^4.0.1" - }, - "engines": { - "node": ">= 14.16.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/ci-info": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.3.0.tgz", - "integrity": "sha512-l+2bNRMiQgcfILUi33labAZYIWlH1kWDp+ecNo5iisRKrbm0xcRyCww71/YU0Fkw0mAFpz9bJayXPjey6vkmaQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/cli-boxes": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-3.0.0.tgz", - "integrity": "sha512-/lzGpEWL/8PfI0BmBOPRwp0c/wFNX1RdUML3jK/RcSBA9T8mZDdQpqYBKtCFTOfQbwPqWEOpjqW+Fnayc0969g==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/clone": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", - "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", - "license": "MIT", - "engines": { - "node": ">=0.8" - } - }, - "node_modules/clsx": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", - "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/color": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", - "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", - "license": "MIT", - "optional": true, - "dependencies": { - "color-convert": "^2.0.1", - "color-string": "^1.9.0" - }, - "engines": { - "node": ">=12.5.0" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT" - }, - "node_modules/color-string": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", - "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", - "license": "MIT", - "optional": true, - "dependencies": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" - } - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "license": "MIT", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/comma-separated-tokens": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", - "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/commander": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", - "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/common-ancestor-path": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/common-ancestor-path/-/common-ancestor-path-1.0.1.tgz", - "integrity": "sha512-L3sHRo1pXXEqX8VU28kfgUY+YGsk09hPqZiZmLacNib6XNTCM8ubYeT7ryXQw8asB1sKgcU5lkB7ONug08aB8w==", - "license": "ISC" - }, - "node_modules/cookie": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz", - "integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==", - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/cookie-es": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/cookie-es/-/cookie-es-1.2.2.tgz", - "integrity": "sha512-+W7VmiVINB+ywl1HGXJXmrqkOhpKrIiVZV6tQuV54ZyQC7MMuBt81Vc336GMLoHBq5hV/F9eXgt5Mnx0Rha5Fg==", - "license": "MIT" - }, - "node_modules/cross-fetch": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.2.0.tgz", - "integrity": "sha512-Q+xVJLoGOeIMXZmbUK4HYk+69cQH6LudR0Vu/pRm2YlU/hDV9CiS0gKUMaWY5f2NeUH9C1nV3bsTlCo0FsTV1Q==", - "license": "MIT", - "dependencies": { - "node-fetch": "^2.7.0" - } - }, - "node_modules/cross-fetch/node_modules/node-fetch": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "license": "MIT", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/crossws": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/crossws/-/crossws-0.3.5.tgz", - "integrity": "sha512-ojKiDvcmByhwa8YYqbQI/hg7MEU0NC03+pSdEq4ZUnZR9xXpwk7E43SMNGkn+JxJGPFtNvQ48+vV2p+P1ml5PA==", - "license": "MIT", - "dependencies": { - "uncrypto": "^0.1.3" - } - }, - "node_modules/css-tree": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.1.0.tgz", - "integrity": "sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w==", - "license": "MIT", - "dependencies": { - "mdn-data": "2.12.2", - "source-map-js": "^1.0.1" - }, - "engines": { - "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" - } - }, - "node_modules/cssesc": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", - "license": "MIT", - "bin": { - "cssesc": "bin/cssesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/data-uri-to-buffer": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", - "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", - "license": "MIT", - "engines": { - "node": ">= 12" - } - }, - "node_modules/debug": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/decode-named-character-reference": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.2.0.tgz", - "integrity": "sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q==", - "license": "MIT", - "dependencies": { - "character-entities": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/defu": { - "version": "6.1.4", - "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.4.tgz", - "integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==", - "license": "MIT" - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "license": "MIT", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/dequal": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", - "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/destr": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/destr/-/destr-2.0.5.tgz", - "integrity": "sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==", - "license": "MIT" - }, - "node_modules/detect-libc": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", - "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==", - "license": "Apache-2.0", - "optional": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/deterministic-object-hash": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/deterministic-object-hash/-/deterministic-object-hash-2.0.2.tgz", - "integrity": "sha512-KxektNH63SrbfUyDiwXqRb1rLwKt33AmMv+5Nhsw1kqZ13SJBRTgZHtGbE+hH3a1mVW1cz+4pqSWVPAtLVXTzQ==", - "license": "MIT", - "dependencies": { - "base-64": "^1.0.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/devalue": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/devalue/-/devalue-5.1.1.tgz", - "integrity": "sha512-maua5KUiapvEwiEAe+XnlZ3Rh0GD+qI1J/nb9vrJc3muPXvcF/8gXYTWF76+5DAqHyDUtOIImEuo0YKE9mshVw==", - "license": "MIT" - }, - "node_modules/devlop": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", - "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", - "license": "MIT", - "dependencies": { - "dequal": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/dfa": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/dfa/-/dfa-1.2.0.tgz", - "integrity": "sha512-ED3jP8saaweFTjeGX8HQPjeC1YYyZs98jGNZx6IiBvxW7JG5v492kamAQB3m2wop07CvU/RQmzcKr6bgcC5D/Q==", - "license": "MIT" - }, - "node_modules/didyoumean": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", - "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", - "license": "Apache-2.0" - }, - "node_modules/diff": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", - "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.3.1" - } - }, - "node_modules/dlv": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", - "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", - "license": "MIT" - }, - "node_modules/dset": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/dset/-/dset-3.1.4.tgz", - "integrity": "sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/dunder-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "license": "MIT" - }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", - "license": "MIT" - }, - "node_modules/electron-to-chromium": { - "version": "1.5.201", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.201.tgz", - "integrity": "sha512-ZG65vsrLClodGqywuigc+7m0gr4ISoTQttfVh7nfpLv0M7SIwF4WbFNEOywcqTiujs12AUeeXbFyQieDICAIxg==", - "license": "ISC" - }, - "node_modules/emoji-regex": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", - "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==", - "license": "MIT" - }, - "node_modules/encodeurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", - "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/entities": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", - "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/es-define-property": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-module-lexer": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", - "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", - "license": "MIT" - }, - "node_modules/es-object-atoms": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-set-tostringtag": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", - "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/esbuild": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.9.tgz", - "integrity": "sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g==", - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.9", - "@esbuild/android-arm": "0.25.9", - "@esbuild/android-arm64": "0.25.9", - "@esbuild/android-x64": "0.25.9", - "@esbuild/darwin-arm64": "0.25.9", - "@esbuild/darwin-x64": "0.25.9", - "@esbuild/freebsd-arm64": "0.25.9", - "@esbuild/freebsd-x64": "0.25.9", - "@esbuild/linux-arm": "0.25.9", - "@esbuild/linux-arm64": "0.25.9", - "@esbuild/linux-ia32": "0.25.9", - "@esbuild/linux-loong64": "0.25.9", - "@esbuild/linux-mips64el": "0.25.9", - "@esbuild/linux-ppc64": "0.25.9", - "@esbuild/linux-riscv64": "0.25.9", - "@esbuild/linux-s390x": "0.25.9", - "@esbuild/linux-x64": "0.25.9", - "@esbuild/netbsd-arm64": "0.25.9", - "@esbuild/netbsd-x64": "0.25.9", - "@esbuild/openbsd-arm64": "0.25.9", - "@esbuild/openbsd-x64": "0.25.9", - "@esbuild/openharmony-arm64": "0.25.9", - "@esbuild/sunos-x64": "0.25.9", - "@esbuild/win32-arm64": "0.25.9", - "@esbuild/win32-ia32": "0.25.9", - "@esbuild/win32-x64": "0.25.9" - } - }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", - "license": "MIT" - }, - "node_modules/escape-string-regexp": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", - "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/estree-walker": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", - "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0" - } - }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/eventemitter3": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", - "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==", - "license": "MIT" - }, - "node_modules/extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "license": "MIT" - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "license": "MIT" - }, - "node_modules/fast-glob": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", - "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.8" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fast-glob/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/fastq": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", - "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", - "license": "ISC", - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/fdir": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", - "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", - "license": "MIT", - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } - } - }, - "node_modules/fetch-blob": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", - "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/jimmywarting" - }, - { - "type": "paypal", - "url": "https://paypal.me/jimmywarting" - } - ], - "license": "MIT", - "dependencies": { - "node-domexception": "^1.0.0", - "web-streams-polyfill": "^3.0.3" - }, - "engines": { - "node": "^12.20 || >= 14.13" - } - }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "license": "MIT", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/flattie": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/flattie/-/flattie-1.1.1.tgz", - "integrity": "sha512-9UbaD6XdAL97+k/n+N7JwX46K/M6Zc6KcFYskrYL8wbBV/Uyk0CTAMY0VT+qiK5PM7AIc9aTWYtq65U7T+aCNQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/fontace": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/fontace/-/fontace-0.3.0.tgz", - "integrity": "sha512-czoqATrcnxgWb/nAkfyIrRp6Q8biYj7nGnL6zfhTcX+JKKpWHFBnb8uNMw/kZr7u++3Y3wYSYoZgHkCcsuBpBg==", - "license": "MIT", - "dependencies": { - "@types/fontkit": "^2.0.8", - "fontkit": "^2.0.4" - } - }, - "node_modules/fontkit": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/fontkit/-/fontkit-2.0.4.tgz", - "integrity": "sha512-syetQadaUEDNdxdugga9CpEYVaQIxOwk7GlwZWWZ19//qW4zE5bknOKeMBDYAASwnpaSHKJITRLMF9m1fp3s6g==", - "license": "MIT", - "dependencies": { - "@swc/helpers": "^0.5.12", - "brotli": "^1.3.2", - "clone": "^2.1.2", - "dfa": "^1.2.0", - "fast-deep-equal": "^3.1.3", - "restructure": "^3.0.0", - "tiny-inflate": "^1.0.3", - "unicode-properties": "^1.4.0", - "unicode-trie": "^2.0.0" - } - }, - "node_modules/foreground-child": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", - "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", - "license": "ISC", - "dependencies": { - "cross-spawn": "^7.0.6", - "signal-exit": "^4.0.1" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/form-data": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", - "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", - "license": "MIT", - "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" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/form-data/node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/form-data/node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/formdata-polyfill": { - "version": "4.0.10", - "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", - "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", - "license": "MIT", - "dependencies": { - "fetch-blob": "^3.1.2" - }, - "engines": { - "node": ">=12.20.0" - } - }, - "node_modules/fraction.js": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", - "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", - "license": "MIT", - "engines": { - "node": "*" - }, - "funding": { - "type": "patreon", - "url": "https://github.com/sponsors/rawify" - } - }, - "node_modules/fresh": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", - "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-east-asian-width": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.3.0.tgz", - "integrity": "sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/get-intrinsic": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", - "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "function-bind": "^1.1.2", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "math-intrinsics": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", - "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/github-slugger": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-2.0.0.tgz", - "integrity": "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==", - "license": "ISC" - }, - "node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", - "license": "ISC", - "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" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/gopd": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/h3": { - "version": "1.15.4", - "resolved": "https://registry.npmjs.org/h3/-/h3-1.15.4.tgz", - "integrity": "sha512-z5cFQWDffyOe4vQ9xIqNfCZdV4p//vy6fBnr8Q1AWnVZ0teurKMG66rLj++TKwKPUP3u7iMUvrvKaEUiQw2QWQ==", - "license": "MIT", - "dependencies": { - "cookie-es": "^1.2.2", - "crossws": "^0.3.5", - "defu": "^6.1.4", - "destr": "^2.0.5", - "iron-webcrypto": "^1.2.1", - "node-mock-http": "^1.0.2", - "radix3": "^1.1.2", - "ufo": "^1.6.1", - "uncrypto": "^0.1.3" - } - }, - "node_modules/has-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-tostringtag": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", - "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "license": "MIT", - "dependencies": { - "has-symbols": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/hast-util-from-html": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/hast-util-from-html/-/hast-util-from-html-2.0.3.tgz", - "integrity": "sha512-CUSRHXyKjzHov8yKsQjGOElXy/3EKpyX56ELnkHH34vDVw1N1XSQ1ZcAvTyAPtGqLTuKP/uxM+aLkSPqF/EtMw==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "devlop": "^1.1.0", - "hast-util-from-parse5": "^8.0.0", - "parse5": "^7.0.0", - "vfile": "^6.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-from-parse5": { - "version": "8.0.3", - "resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-8.0.3.tgz", - "integrity": "sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/unist": "^3.0.0", - "devlop": "^1.0.0", - "hastscript": "^9.0.0", - "property-information": "^7.0.0", - "vfile": "^6.0.0", - "vfile-location": "^5.0.0", - "web-namespaces": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-is-element": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/hast-util-is-element/-/hast-util-is-element-3.0.0.tgz", - "integrity": "sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-parse-selector": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-4.0.0.tgz", - "integrity": "sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-raw": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/hast-util-raw/-/hast-util-raw-9.1.0.tgz", - "integrity": "sha512-Y8/SBAHkZGoNkpzqqfCldijcuUKh7/su31kEBp67cFY09Wy0mTRgtsLYsiIxMJxlu0f6AA5SUTbDR8K0rxnbUw==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/unist": "^3.0.0", - "@ungap/structured-clone": "^1.0.0", - "hast-util-from-parse5": "^8.0.0", - "hast-util-to-parse5": "^8.0.0", - "html-void-elements": "^3.0.0", - "mdast-util-to-hast": "^13.0.0", - "parse5": "^7.0.0", - "unist-util-position": "^5.0.0", - "unist-util-visit": "^5.0.0", - "vfile": "^6.0.0", - "web-namespaces": "^2.0.0", - "zwitch": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-to-html": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/hast-util-to-html/-/hast-util-to-html-9.0.5.tgz", - "integrity": "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/unist": "^3.0.0", - "ccount": "^2.0.0", - "comma-separated-tokens": "^2.0.0", - "hast-util-whitespace": "^3.0.0", - "html-void-elements": "^3.0.0", - "mdast-util-to-hast": "^13.0.0", - "property-information": "^7.0.0", - "space-separated-tokens": "^2.0.0", - "stringify-entities": "^4.0.0", - "zwitch": "^2.0.4" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-to-parse5": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/hast-util-to-parse5/-/hast-util-to-parse5-8.0.0.tgz", - "integrity": "sha512-3KKrV5ZVI8if87DVSi1vDeByYrkGzg4mEfeu4alwgmmIeARiBLKCZS2uw5Gb6nU9x9Yufyj3iudm6i7nl52PFw==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "comma-separated-tokens": "^2.0.0", - "devlop": "^1.0.0", - "property-information": "^6.0.0", - "space-separated-tokens": "^2.0.0", - "web-namespaces": "^2.0.0", - "zwitch": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-to-parse5/node_modules/property-information": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.5.0.tgz", - "integrity": "sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/hast-util-to-text": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/hast-util-to-text/-/hast-util-to-text-4.0.2.tgz", - "integrity": "sha512-KK6y/BN8lbaq654j7JgBydev7wuNMcID54lkRav1P0CaE1e47P72AWWPiGKXTJU271ooYzcvTAn/Zt0REnvc7A==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/unist": "^3.0.0", - "hast-util-is-element": "^3.0.0", - "unist-util-find-after": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-whitespace": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", - "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hastscript": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-9.0.1.tgz", - "integrity": "sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "comma-separated-tokens": "^2.0.0", - "hast-util-parse-selector": "^4.0.0", - "property-information": "^7.0.0", - "space-separated-tokens": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/html-escaper": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-3.0.3.tgz", - "integrity": "sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ==", - "license": "MIT" - }, - "node_modules/html-void-elements": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz", - "integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/http-cache-semantics": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", - "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==", - "license": "BSD-2-Clause" - }, - "node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "license": "MIT", - "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/http-errors/node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/import-meta-resolve": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.1.0.tgz", - "integrity": "sha512-I6fiaX09Xivtk+THaMfAwnA3MVA5Big1WHF1Dfx9hFuvNIWpXnorlkzhcQf6ehrqQiiZECRt1poOAkPmer3ruw==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "license": "ISC" - }, - "node_modules/iron-webcrypto": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/iron-webcrypto/-/iron-webcrypto-1.2.1.tgz", - "integrity": "sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/brc-dd" - } - }, - "node_modules/is-arrayish": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", - "license": "MIT", - "optional": true - }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "license": "MIT", - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-core-module": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", - "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", - "license": "MIT", - "dependencies": { - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-docker": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", - "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", - "license": "MIT", - "bin": { - "is-docker": "cli.js" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "license": "MIT", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-inside-container": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", - "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", - "license": "MIT", - "dependencies": { - "is-docker": "^3.0.0" - }, - "bin": { - "is-inside-container": "cli.js" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-plain-obj": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", - "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-wsl": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.0.tgz", - "integrity": "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==", - "license": "MIT", - "dependencies": { - "is-inside-container": "^1.0.0" - }, - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "license": "ISC" - }, - "node_modules/jackspeak": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", - "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", - "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" - } - }, - "node_modules/jiti": { - "version": "1.21.7", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", - "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", - "license": "MIT", - "bin": { - "jiti": "bin/jiti.js" - } - }, - "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/kleur": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", - "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/lilconfig": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", - "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", - "license": "MIT", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/antonk52" - } - }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "license": "MIT" - }, - "node_modules/longest-streak": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", - "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "license": "ISC" - }, - "node_modules/magic-string": { - "version": "0.30.17", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", - "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0" - } - }, - "node_modules/magicast": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.3.5.tgz", - "integrity": "sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==", - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.25.4", - "@babel/types": "^7.25.4", - "source-map-js": "^1.2.0" - } - }, - "node_modules/markdown-table": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz", - "integrity": "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/math-intrinsics": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/mdast-util-definitions": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-definitions/-/mdast-util-definitions-6.0.0.tgz", - "integrity": "sha512-scTllyX6pnYNZH/AIp/0ePz6s4cZtARxImwoPJ7kS42n+MnVsI4XbnG6d4ibehRIldYMWM2LD7ImQblVhUejVQ==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "@types/unist": "^3.0.0", - "unist-util-visit": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-find-and-replace": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.2.tgz", - "integrity": "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "escape-string-regexp": "^5.0.0", - "unist-util-is": "^6.0.0", - "unist-util-visit-parents": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-from-markdown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.2.tgz", - "integrity": "sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "@types/unist": "^3.0.0", - "decode-named-character-reference": "^1.0.0", - "devlop": "^1.0.0", - "mdast-util-to-string": "^4.0.0", - "micromark": "^4.0.0", - "micromark-util-decode-numeric-character-reference": "^2.0.0", - "micromark-util-decode-string": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0", - "unist-util-stringify-position": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-3.1.0.tgz", - "integrity": "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==", - "license": "MIT", - "dependencies": { - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-gfm-autolink-literal": "^2.0.0", - "mdast-util-gfm-footnote": "^2.0.0", - "mdast-util-gfm-strikethrough": "^2.0.0", - "mdast-util-gfm-table": "^2.0.0", - "mdast-util-gfm-task-list-item": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-autolink-literal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.1.tgz", - "integrity": "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "ccount": "^2.0.0", - "devlop": "^1.0.0", - "mdast-util-find-and-replace": "^3.0.0", - "micromark-util-character": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-footnote": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.1.0.tgz", - "integrity": "sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "devlop": "^1.1.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-strikethrough": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz", - "integrity": "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-table": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz", - "integrity": "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "devlop": "^1.0.0", - "markdown-table": "^3.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-task-list-item": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz", - "integrity": "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "devlop": "^1.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-phrasing": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz", - "integrity": "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "unist-util-is": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-to-hast": { - "version": "13.2.0", - "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.0.tgz", - "integrity": "sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "@ungap/structured-clone": "^1.0.0", - "devlop": "^1.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "trim-lines": "^3.0.0", - "unist-util-position": "^5.0.0", - "unist-util-visit": "^5.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-to-markdown": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.2.tgz", - "integrity": "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "@types/unist": "^3.0.0", - "longest-streak": "^3.0.0", - "mdast-util-phrasing": "^4.0.0", - "mdast-util-to-string": "^4.0.0", - "micromark-util-classify-character": "^2.0.0", - "micromark-util-decode-string": "^2.0.0", - "unist-util-visit": "^5.0.0", - "zwitch": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-to-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", - "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdn-data": { - "version": "2.12.2", - "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.12.2.tgz", - "integrity": "sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==", - "license": "CC0-1.0" - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/micromark": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz", - "integrity": "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "@types/debug": "^4.0.0", - "debug": "^4.0.0", - "decode-named-character-reference": "^1.0.0", - "devlop": "^1.0.0", - "micromark-core-commonmark": "^2.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-combine-extensions": "^2.0.0", - "micromark-util-decode-numeric-character-reference": "^2.0.0", - "micromark-util-encode": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-resolve-all": "^2.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "micromark-util-subtokenize": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-core-commonmark": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz", - "integrity": "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "decode-named-character-reference": "^1.0.0", - "devlop": "^1.0.0", - "micromark-factory-destination": "^2.0.0", - "micromark-factory-label": "^2.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-factory-title": "^2.0.0", - "micromark-factory-whitespace": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-classify-character": "^2.0.0", - "micromark-util-html-tag-name": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-resolve-all": "^2.0.0", - "micromark-util-subtokenize": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-extension-gfm": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz", - "integrity": "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==", - "license": "MIT", - "dependencies": { - "micromark-extension-gfm-autolink-literal": "^2.0.0", - "micromark-extension-gfm-footnote": "^2.0.0", - "micromark-extension-gfm-strikethrough": "^2.0.0", - "micromark-extension-gfm-table": "^2.0.0", - "micromark-extension-gfm-tagfilter": "^2.0.0", - "micromark-extension-gfm-task-list-item": "^2.0.0", - "micromark-util-combine-extensions": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-autolink-literal": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz", - "integrity": "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==", - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-footnote": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz", - "integrity": "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==", - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-core-commonmark": "^2.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-strikethrough": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.1.0.tgz", - "integrity": "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==", - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-classify-character": "^2.0.0", - "micromark-util-resolve-all": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-table": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.1.tgz", - "integrity": "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==", - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-tagfilter": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz", - "integrity": "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==", - "license": "MIT", - "dependencies": { - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-task-list-item": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.1.0.tgz", - "integrity": "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==", - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-factory-destination": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz", - "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-label": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz", - "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-space": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", - "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-title": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz", - "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-whitespace": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz", - "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-character": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", - "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-chunked": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz", - "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-classify-character": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz", - "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-combine-extensions": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz", - "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-chunked": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-decode-numeric-character-reference": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz", - "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-decode-string": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz", - "integrity": "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "decode-named-character-reference": "^1.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-decode-numeric-character-reference": "^2.0.0", - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-encode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", - "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-util-html-tag-name": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz", - "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-util-normalize-identifier": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz", - "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-resolve-all": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz", - "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-sanitize-uri": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", - "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-encode": "^2.0.0", - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-subtokenize": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz", - "integrity": "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-symbol": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", - "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-util-types": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz", - "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "license": "MIT", - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/micromatch/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/mime-db": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", - "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz", - "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==", - "license": "MIT", - "dependencies": { - "mime-db": "^1.54.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "license": "ISC", - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/mrmime": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", - "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==", - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/mz": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", - "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", - "license": "MIT", - "dependencies": { - "any-promise": "^1.0.0", - "object-assign": "^4.0.1", - "thenify-all": "^1.0.0" - } - }, - "node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/neotraverse": { - "version": "0.6.18", - "resolved": "https://registry.npmjs.org/neotraverse/-/neotraverse-0.6.18.tgz", - "integrity": "sha512-Z4SmBUweYa09+o6pG+eASabEpP6QkQ70yHj351pQoEXIs8uHbaU2DWVmzBANKgflPa47A50PtB2+NgRpQvr7vA==", - "license": "MIT", - "engines": { - "node": ">= 10" - } - }, - "node_modules/nlcst-to-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/nlcst-to-string/-/nlcst-to-string-4.0.0.tgz", - "integrity": "sha512-YKLBCcUYKAg0FNlOBT6aI91qFmSiFKiluk655WzPF+DDMA02qIyy8uiRqI8QXtcFpEvll12LpL5MXqEmAZ+dcA==", - "license": "MIT", - "dependencies": { - "@types/nlcst": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/node-domexception": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", - "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", - "deprecated": "Use your platform's native DOMException instead", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/jimmywarting" - }, - { - "type": "github", - "url": "https://paypal.me/jimmywarting" - } - ], - "license": "MIT", - "engines": { - "node": ">=10.5.0" - } - }, - "node_modules/node-fetch": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", - "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", - "license": "MIT", - "dependencies": { - "data-uri-to-buffer": "^4.0.0", - "fetch-blob": "^3.1.4", - "formdata-polyfill": "^4.0.10" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/node-fetch" - } - }, - "node_modules/node-fetch-native": { - "version": "1.6.7", - "resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.7.tgz", - "integrity": "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==", - "license": "MIT" - }, - "node_modules/node-mock-http": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/node-mock-http/-/node-mock-http-1.0.2.tgz", - "integrity": "sha512-zWaamgDUdo9SSLw47we78+zYw/bDr5gH8pH7oRRs8V3KmBtu8GLgGIbV2p/gRPd3LWpEOpjQj7X1FOU3VFMJ8g==", - "license": "MIT" - }, - "node_modules/node-releases": { - "version": "2.0.19", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", - "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", - "license": "MIT" - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/normalize-range": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", - "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-hash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", - "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/ofetch": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/ofetch/-/ofetch-1.4.1.tgz", - "integrity": "sha512-QZj2DfGplQAr2oj9KzceK9Hwz6Whxazmn85yYeVuS3u9XTMOGMRx0kO95MQ+vLsj/S/NwBDMMLU5hpxvI6Tklw==", - "license": "MIT", - "dependencies": { - "destr": "^2.0.3", - "node-fetch-native": "^1.6.4", - "ufo": "^1.5.4" - } - }, - "node_modules/ohash": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/ohash/-/ohash-2.0.11.tgz", - "integrity": "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==", - "license": "MIT" - }, - "node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "license": "MIT", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/oniguruma-parser": { - "version": "0.12.1", - "resolved": "https://registry.npmjs.org/oniguruma-parser/-/oniguruma-parser-0.12.1.tgz", - "integrity": "sha512-8Unqkvk1RYc6yq2WBYRj4hdnsAxVze8i7iPfQr8e4uSP3tRv0rpZcbGUDvxfQQcdwHt/e9PrMvGCsa8OqG9X3w==", - "license": "MIT" - }, - "node_modules/oniguruma-to-es": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/oniguruma-to-es/-/oniguruma-to-es-4.3.3.tgz", - "integrity": "sha512-rPiZhzC3wXwE59YQMRDodUwwT9FZ9nNBwQQfsd1wfdtlKEyCdRV0avrTcSZ5xlIvGRVPd/cx6ZN45ECmS39xvg==", - "license": "MIT", - "dependencies": { - "oniguruma-parser": "^0.12.1", - "regex": "^6.0.1", - "regex-recursion": "^6.0.2" - } - }, - "node_modules/p-limit": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-6.2.0.tgz", - "integrity": "sha512-kuUqqHNUqoIWp/c467RI4X6mmyuojY5jGutNU0wVTmEOOfcuwLqyMVoAi9MKi2Ak+5i9+nhmrK4ufZE8069kHA==", - "license": "MIT", - "dependencies": { - "yocto-queue": "^1.1.1" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-queue": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-8.1.0.tgz", - "integrity": "sha512-mxLDbbGIBEXTJL0zEx8JIylaj3xQ7Z/7eEVjcF9fJX4DBiH9oqe+oahYnlKKxm0Ci9TlWTyhSHgygxMxjIB2jw==", - "license": "MIT", - "dependencies": { - "eventemitter3": "^5.0.1", - "p-timeout": "^6.1.2" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-timeout": { - "version": "6.1.4", - "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-6.1.4.tgz", - "integrity": "sha512-MyIV3ZA/PmyBN/ud8vV9XzwTrNtR4jFrObymZYnZqMmW0zA8Z17vnT0rBgFE/TlohB+YCHqXMgZzb3Csp49vqg==", - "license": "MIT", - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/package-json-from-dist": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", - "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", - "license": "BlueOak-1.0.0" - }, - "node_modules/package-manager-detector": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-1.3.0.tgz", - "integrity": "sha512-ZsEbbZORsyHuO00lY1kV3/t72yp6Ysay6Pd17ZAlNGuGwmWDLCJxFpRs0IzfXfj1o4icJOkUEioexFHzyPurSQ==", - "license": "MIT" - }, - "node_modules/pako": { - "version": "0.2.9", - "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz", - "integrity": "sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA==", - "license": "MIT" - }, - "node_modules/parse-latin": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/parse-latin/-/parse-latin-7.0.0.tgz", - "integrity": "sha512-mhHgobPPua5kZ98EF4HWiH167JWBfl4pvAIXXdbaVohtK7a6YBOy56kvhCqduqyo/f3yrHFWmqmiMg/BkBkYYQ==", - "license": "MIT", - "dependencies": { - "@types/nlcst": "^2.0.0", - "@types/unist": "^3.0.0", - "nlcst-to-string": "^4.0.0", - "unist-util-modify-children": "^4.0.0", - "unist-util-visit-children": "^3.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/parse5": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", - "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", - "license": "MIT", - "dependencies": { - "entities": "^6.0.0" - }, - "funding": { - "url": "https://github.com/inikulin/parse5?sponsor=1" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "license": "MIT" - }, - "node_modules/path-scurry": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", - "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", - "license": "BlueOak-1.0.0", - "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" - }, - "engines": { - "node": ">=16 || 14 >=14.18" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "license": "ISC" - }, - "node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/pirates": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", - "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/postcss": { - "version": "8.5.6", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", - "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "nanoid": "^3.3.11", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/postcss-import": { - "version": "15.1.0", - "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", - "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", - "license": "MIT", - "dependencies": { - "postcss-value-parser": "^4.0.0", - "read-cache": "^1.0.0", - "resolve": "^1.1.7" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "postcss": "^8.0.0" - } - }, - "node_modules/postcss-js": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", - "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", - "license": "MIT", - "dependencies": { - "camelcase-css": "^2.0.1" - }, - "engines": { - "node": "^12 || ^14 || >= 16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - "peerDependencies": { - "postcss": "^8.4.21" - } - }, - "node_modules/postcss-load-config": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz", - "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "lilconfig": "^3.0.0", - "yaml": "^2.3.4" - }, - "engines": { - "node": ">= 14" - }, - "peerDependencies": { - "postcss": ">=8.0.9", - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "postcss": { - "optional": true - }, - "ts-node": { - "optional": true - } - } - }, - "node_modules/postcss-nested": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", - "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "postcss-selector-parser": "^6.1.1" - }, - "engines": { - "node": ">=12.0" - }, - "peerDependencies": { - "postcss": "^8.2.14" - } - }, - "node_modules/postcss-selector-parser": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", - "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", - "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/postcss-value-parser": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", - "license": "MIT" - }, - "node_modules/prismjs": { - "version": "1.30.0", - "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz", - "integrity": "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/prompts": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", - "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", - "license": "MIT", - "dependencies": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/prompts/node_modules/kleur": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/property-information": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.1.0.tgz", - "integrity": "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/radix3": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/radix3/-/radix3-1.1.2.tgz", - "integrity": "sha512-b484I/7b8rDEdSDKckSSBA8knMpcdsXudlE/LNL639wFoHKwLbEkQFZHWEYwDC0wa0FKUcCY+GAF73Z7wxNVFA==", - "license": "MIT" - }, - "node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/read-cache": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", - "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", - "license": "MIT", - "dependencies": { - "pify": "^2.3.0" - } - }, - "node_modules/readdirp": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", - "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", - "license": "MIT", - "engines": { - "node": ">= 14.18.0" - }, - "funding": { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/regex/-/regex-6.0.1.tgz", - "integrity": "sha512-uorlqlzAKjKQZ5P+kTJr3eeJGSVroLKoHmquUj4zHWuR+hEyNqlXsSKlYYF5F4NI6nl7tWCs0apKJ0lmfsXAPA==", - "license": "MIT", - "dependencies": { - "regex-utilities": "^2.3.0" - } - }, - "node_modules/regex-recursion": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/regex-recursion/-/regex-recursion-6.0.2.tgz", - "integrity": "sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg==", - "license": "MIT", - "dependencies": { - "regex-utilities": "^2.3.0" - } - }, - "node_modules/regex-utilities": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/regex-utilities/-/regex-utilities-2.3.0.tgz", - "integrity": "sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng==", - "license": "MIT" - }, - "node_modules/rehype": { - "version": "13.0.2", - "resolved": "https://registry.npmjs.org/rehype/-/rehype-13.0.2.tgz", - "integrity": "sha512-j31mdaRFrwFRUIlxGeuPXXKWQxet52RBQRvCmzl5eCefn/KGbomK5GMHNMsOJf55fgo3qw5tST5neDuarDYR2A==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "rehype-parse": "^9.0.0", - "rehype-stringify": "^10.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/rehype-parse": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/rehype-parse/-/rehype-parse-9.0.1.tgz", - "integrity": "sha512-ksCzCD0Fgfh7trPDxr2rSylbwq9iYDkSn8TCDmEJ49ljEUBxDVCzCHv7QNzZOfODanX4+bWQ4WZqLCRWYLfhag==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "hast-util-from-html": "^2.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/rehype-raw": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/rehype-raw/-/rehype-raw-7.0.0.tgz", - "integrity": "sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "hast-util-raw": "^9.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/rehype-stringify": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/rehype-stringify/-/rehype-stringify-10.0.1.tgz", - "integrity": "sha512-k9ecfXHmIPuFVI61B9DeLPN0qFHfawM6RsuX48hoqlaKSF61RskNjSm1lI8PhBEM0MRdLxVVm4WmTqJQccH9mA==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "hast-util-to-html": "^9.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-gfm": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.1.tgz", - "integrity": "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-gfm": "^3.0.0", - "micromark-extension-gfm": "^3.0.0", - "remark-parse": "^11.0.0", - "remark-stringify": "^11.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-parse": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz", - "integrity": "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-from-markdown": "^2.0.0", - "micromark-util-types": "^2.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-rehype": { - "version": "11.1.2", - "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.1.2.tgz", - "integrity": "sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "mdast-util-to-hast": "^13.0.0", - "unified": "^11.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-smartypants": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/remark-smartypants/-/remark-smartypants-3.0.2.tgz", - "integrity": "sha512-ILTWeOriIluwEvPjv67v7Blgrcx+LZOkAUVtKI3putuhlZm84FnqDORNXPPm+HY3NdZOMhyDwZ1E+eZB/Df5dA==", - "license": "MIT", - "dependencies": { - "retext": "^9.0.0", - "retext-smartypants": "^6.0.0", - "unified": "^11.0.4", - "unist-util-visit": "^5.0.0" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/remark-stringify": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-11.0.0.tgz", - "integrity": "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-to-markdown": "^2.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/resolve": { - "version": "1.22.10", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", - "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", - "license": "MIT", - "dependencies": { - "is-core-module": "^2.16.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/restructure": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/restructure/-/restructure-3.0.2.tgz", - "integrity": "sha512-gSfoiOEA0VPE6Tukkrr7I0RBdE0s7H1eFCDBk05l1KIQT1UIKNc5JZy6jdyW6eYH3aR3g5b3PuL77rq0hvwtAw==", - "license": "MIT" - }, - "node_modules/retext": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/retext/-/retext-9.0.0.tgz", - "integrity": "sha512-sbMDcpHCNjvlheSgMfEcVrZko3cDzdbe1x/e7G66dFp0Ff7Mldvi2uv6JkJQzdRcvLYE8CA8Oe8siQx8ZOgTcA==", - "license": "MIT", - "dependencies": { - "@types/nlcst": "^2.0.0", - "retext-latin": "^4.0.0", - "retext-stringify": "^4.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/retext-latin": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/retext-latin/-/retext-latin-4.0.0.tgz", - "integrity": "sha512-hv9woG7Fy0M9IlRQloq/N6atV82NxLGveq+3H2WOi79dtIYWN8OaxogDm77f8YnVXJL2VD3bbqowu5E3EMhBYA==", - "license": "MIT", - "dependencies": { - "@types/nlcst": "^2.0.0", - "parse-latin": "^7.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/retext-smartypants": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/retext-smartypants/-/retext-smartypants-6.2.0.tgz", - "integrity": "sha512-kk0jOU7+zGv//kfjXEBjdIryL1Acl4i9XNkHxtM7Tm5lFiCog576fjNC9hjoR7LTKQ0DsPWy09JummSsH1uqfQ==", - "license": "MIT", - "dependencies": { - "@types/nlcst": "^2.0.0", - "nlcst-to-string": "^4.0.0", - "unist-util-visit": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/retext-stringify": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/retext-stringify/-/retext-stringify-4.0.0.tgz", - "integrity": "sha512-rtfN/0o8kL1e+78+uxPTqu1Klt0yPzKuQ2BfWwwfgIUSayyzxpM1PJzkKt4V8803uB9qSy32MvI7Xep9khTpiA==", - "license": "MIT", - "dependencies": { - "@types/nlcst": "^2.0.0", - "nlcst-to-string": "^4.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/reusify": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", - "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", - "license": "MIT", - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/rollup": { - "version": "4.46.2", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.46.2.tgz", - "integrity": "sha512-WMmLFI+Boh6xbop+OAGo9cQ3OgX9MIg7xOQjn+pTCwOkk+FNDAeAemXkJ3HzDJrVXleLOFVa1ipuc1AmEx1Dwg==", - "license": "MIT", - "dependencies": { - "@types/estree": "1.0.8" - }, - "bin": { - "rollup": "dist/bin/rollup" - }, - "engines": { - "node": ">=18.0.0", - "npm": ">=8.0.0" - }, - "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.46.2", - "@rollup/rollup-android-arm64": "4.46.2", - "@rollup/rollup-darwin-arm64": "4.46.2", - "@rollup/rollup-darwin-x64": "4.46.2", - "@rollup/rollup-freebsd-arm64": "4.46.2", - "@rollup/rollup-freebsd-x64": "4.46.2", - "@rollup/rollup-linux-arm-gnueabihf": "4.46.2", - "@rollup/rollup-linux-arm-musleabihf": "4.46.2", - "@rollup/rollup-linux-arm64-gnu": "4.46.2", - "@rollup/rollup-linux-arm64-musl": "4.46.2", - "@rollup/rollup-linux-loongarch64-gnu": "4.46.2", - "@rollup/rollup-linux-ppc64-gnu": "4.46.2", - "@rollup/rollup-linux-riscv64-gnu": "4.46.2", - "@rollup/rollup-linux-riscv64-musl": "4.46.2", - "@rollup/rollup-linux-s390x-gnu": "4.46.2", - "@rollup/rollup-linux-x64-gnu": "4.46.2", - "@rollup/rollup-linux-x64-musl": "4.46.2", - "@rollup/rollup-win32-arm64-msvc": "4.46.2", - "@rollup/rollup-win32-ia32-msvc": "4.46.2", - "@rollup/rollup-win32-x64-msvc": "4.46.2", - "fsevents": "~2.3.2" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/send": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz", - "integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==", - "license": "MIT", - "dependencies": { - "debug": "^4.3.5", - "encodeurl": "^2.0.0", - "escape-html": "^1.0.3", - "etag": "^1.8.1", - "fresh": "^2.0.0", - "http-errors": "^2.0.0", - "mime-types": "^3.0.1", - "ms": "^2.1.3", - "on-finished": "^2.4.1", - "range-parser": "^1.2.1", - "statuses": "^2.0.1" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/server-destroy": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/server-destroy/-/server-destroy-1.0.1.tgz", - "integrity": "sha512-rb+9B5YBIEzYcD6x2VKidaa+cqYBJQKnU4oe4E3ANwRRN56yk/ua1YCJT1n21NTS8w6CcOclAKNP3PhdCXKYtQ==", - "license": "ISC" - }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", - "license": "ISC" - }, - "node_modules/sharp": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.33.5.tgz", - "integrity": "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==", - "hasInstallScript": true, - "license": "Apache-2.0", - "optional": true, - "dependencies": { - "color": "^4.2.3", - "detect-libc": "^2.0.3", - "semver": "^7.6.3" - }, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-darwin-arm64": "0.33.5", - "@img/sharp-darwin-x64": "0.33.5", - "@img/sharp-libvips-darwin-arm64": "1.0.4", - "@img/sharp-libvips-darwin-x64": "1.0.4", - "@img/sharp-libvips-linux-arm": "1.0.5", - "@img/sharp-libvips-linux-arm64": "1.0.4", - "@img/sharp-libvips-linux-s390x": "1.0.4", - "@img/sharp-libvips-linux-x64": "1.0.4", - "@img/sharp-libvips-linuxmusl-arm64": "1.0.4", - "@img/sharp-libvips-linuxmusl-x64": "1.0.4", - "@img/sharp-linux-arm": "0.33.5", - "@img/sharp-linux-arm64": "0.33.5", - "@img/sharp-linux-s390x": "0.33.5", - "@img/sharp-linux-x64": "0.33.5", - "@img/sharp-linuxmusl-arm64": "0.33.5", - "@img/sharp-linuxmusl-x64": "0.33.5", - "@img/sharp-wasm32": "0.33.5", - "@img/sharp-win32-ia32": "0.33.5", - "@img/sharp-win32-x64": "0.33.5" - } - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/shiki": { - "version": "3.11.0", - "resolved": "https://registry.npmjs.org/shiki/-/shiki-3.11.0.tgz", - "integrity": "sha512-VgKumh/ib38I1i3QkMn6mAQA6XjjQubqaAYhfge71glAll0/4xnt8L2oSuC45Qcr/G5Kbskj4RliMQddGmy/Og==", - "license": "MIT", - "dependencies": { - "@shikijs/core": "3.11.0", - "@shikijs/engine-javascript": "3.11.0", - "@shikijs/engine-oniguruma": "3.11.0", - "@shikijs/langs": "3.11.0", - "@shikijs/themes": "3.11.0", - "@shikijs/types": "3.11.0", - "@shikijs/vscode-textmate": "^10.0.2", - "@types/hast": "^3.0.4" - } - }, - "node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "license": "ISC", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/simple-swizzle": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", - "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", - "license": "MIT", - "optional": true, - "dependencies": { - "is-arrayish": "^0.3.1" - } - }, - "node_modules/sisteransi": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", - "license": "MIT" - }, - "node_modules/smol-toml": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.4.2.tgz", - "integrity": "sha512-rInDH6lCNiEyn3+hH8KVGFdbjc099j47+OSgbMrfDYX1CmXLfdKd7qi6IfcWj2wFxvSVkuI46M+wPGYfEOEj6g==", - "license": "BSD-3-Clause", - "engines": { - "node": ">= 18" - }, - "funding": { - "url": "https://github.com/sponsors/cyyynthia" - } - }, - "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/space-separated-tokens": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", - "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/statuses": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", - "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/string-width": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", - "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", - "license": "MIT", - "dependencies": { - "emoji-regex": "^10.3.0", - "get-east-asian-width": "^1.0.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/string-width-cjs": { - "name": "string-width", - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width-cjs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "license": "MIT" - }, - "node_modules/string-width-cjs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/stringify-entities": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", - "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", - "license": "MIT", - "dependencies": { - "character-entities-html4": "^2.0.0", - "character-entities-legacy": "^3.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/strip-ansi-cjs": { - "name": "strip-ansi", - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/sucrase": { - "version": "3.35.0", - "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", - "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==", - "license": "MIT", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.2", - "commander": "^4.0.0", - "glob": "^10.3.10", - "lines-and-columns": "^1.1.6", - "mz": "^2.7.0", - "pirates": "^4.0.1", - "ts-interface-checker": "^0.1.9" - }, - "bin": { - "sucrase": "bin/sucrase", - "sucrase-node": "bin/sucrase-node" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/tailwindcss": { - "version": "3.4.17", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.17.tgz", - "integrity": "sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==", - "license": "MIT", - "dependencies": { - "@alloc/quick-lru": "^5.2.0", - "arg": "^5.0.2", - "chokidar": "^3.6.0", - "didyoumean": "^1.2.2", - "dlv": "^1.1.3", - "fast-glob": "^3.3.2", - "glob-parent": "^6.0.2", - "is-glob": "^4.0.3", - "jiti": "^1.21.6", - "lilconfig": "^3.1.3", - "micromatch": "^4.0.8", - "normalize-path": "^3.0.0", - "object-hash": "^3.0.0", - "picocolors": "^1.1.1", - "postcss": "^8.4.47", - "postcss-import": "^15.1.0", - "postcss-js": "^4.0.1", - "postcss-load-config": "^4.0.2", - "postcss-nested": "^6.2.0", - "postcss-selector-parser": "^6.1.2", - "resolve": "^1.22.8", - "sucrase": "^3.35.0" - }, - "bin": { - "tailwind": "lib/cli.js", - "tailwindcss": "lib/cli.js" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/tailwindcss/node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "license": "MIT", - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/tailwindcss/node_modules/chokidar/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/tailwindcss/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/tailwindcss/node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "license": "MIT", - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/thenify": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", - "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", - "license": "MIT", - "dependencies": { - "any-promise": "^1.0.0" - } - }, - "node_modules/thenify-all": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", - "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", - "license": "MIT", - "dependencies": { - "thenify": ">= 3.1.0 < 4" - }, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/tiny-inflate": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz", - "integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==", - "license": "MIT" - }, - "node_modules/tinyexec": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", - "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", - "license": "MIT" - }, - "node_modules/tinyglobby": { - "version": "0.2.14", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz", - "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==", - "license": "MIT", - "dependencies": { - "fdir": "^6.4.4", - "picomatch": "^4.0.2" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/SuperchupuDev" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "license": "MIT", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "license": "MIT", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "license": "MIT" - }, - "node_modules/trim-lines": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", - "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/trough": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz", - "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/ts-interface-checker": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", - "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", - "license": "Apache-2.0" - }, - "node_modules/tsconfck": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/tsconfck/-/tsconfck-3.1.6.tgz", - "integrity": "sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w==", - "license": "MIT", - "bin": { - "tsconfck": "bin/tsconfck.js" - }, - "engines": { - "node": "^18 || >=20" - }, - "peerDependencies": { - "typescript": "^5.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" - }, - "node_modules/type-fest": { - "version": "4.41.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", - "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/typescript": { - "version": "5.9.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz", - "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==", - "license": "Apache-2.0", - "peer": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/ufo": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.1.tgz", - "integrity": "sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==", - "license": "MIT" - }, - "node_modules/ultrahtml": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/ultrahtml/-/ultrahtml-1.6.0.tgz", - "integrity": "sha512-R9fBn90VTJrqqLDwyMph+HGne8eqY1iPfYhPzZrvKpIfwkWZbcYlfpsb8B9dTvBfpy1/hqAD7Wi8EKfP9e8zdw==", - "license": "MIT" - }, - "node_modules/uncrypto": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/uncrypto/-/uncrypto-0.1.3.tgz", - "integrity": "sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==", - "license": "MIT" - }, - "node_modules/undici-types": { - "version": "7.10.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.10.0.tgz", - "integrity": "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==", - "license": "MIT" - }, - "node_modules/unicode-properties": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/unicode-properties/-/unicode-properties-1.4.1.tgz", - "integrity": "sha512-CLjCCLQ6UuMxWnbIylkisbRj31qxHPAurvena/0iwSVbQ2G1VY5/HjV0IRabOEbDHlzZlRdCrD4NhB0JtU40Pg==", - "license": "MIT", - "dependencies": { - "base64-js": "^1.3.0", - "unicode-trie": "^2.0.0" - } - }, - "node_modules/unicode-trie": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unicode-trie/-/unicode-trie-2.0.0.tgz", - "integrity": "sha512-x7bc76x0bm4prf1VLg79uhAzKw8DVboClSN5VxJuQ+LKDOVEW9CdH+VY7SP+vX7xCYQqzzgQpFqz15zeLvAtZQ==", - "license": "MIT", - "dependencies": { - "pako": "^0.2.5", - "tiny-inflate": "^1.0.0" - } - }, - "node_modules/unified": { - "version": "11.0.5", - "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz", - "integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "bail": "^2.0.0", - "devlop": "^1.0.0", - "extend": "^3.0.0", - "is-plain-obj": "^4.0.0", - "trough": "^2.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unifont": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/unifont/-/unifont-0.5.2.tgz", - "integrity": "sha512-LzR4WUqzH9ILFvjLAUU7dK3Lnou/qd5kD+IakBtBK4S15/+x2y9VX+DcWQv6s551R6W+vzwgVS6tFg3XggGBgg==", - "license": "MIT", - "dependencies": { - "css-tree": "^3.0.0", - "ofetch": "^1.4.1", - "ohash": "^2.0.0" - } - }, - "node_modules/unist-util-find-after": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/unist-util-find-after/-/unist-util-find-after-5.0.0.tgz", - "integrity": "sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-is": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz", - "integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-modify-children": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/unist-util-modify-children/-/unist-util-modify-children-4.0.0.tgz", - "integrity": "sha512-+tdN5fGNddvsQdIzUF3Xx82CU9sMM+fA0dLgR9vOmT0oPT2jH+P1nd5lSqfCfXAw+93NhcXNY2qqvTUtE4cQkw==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "array-iterate": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-position": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", - "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-remove-position": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/unist-util-remove-position/-/unist-util-remove-position-5.0.0.tgz", - "integrity": "sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-visit": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-stringify-position": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", - "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-visit": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz", - "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0", - "unist-util-visit-parents": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-visit-children": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/unist-util-visit-children/-/unist-util-visit-children-3.0.0.tgz", - "integrity": "sha512-RgmdTfSBOg04sdPcpTSD1jzoNBjt9a80/ZCzp5cI9n1qPzLZWF9YdvWGN2zmTumP1HWhXKdUWexjy/Wy/lJ7tA==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-visit-parents": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz", - "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unstorage": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/unstorage/-/unstorage-1.16.1.tgz", - "integrity": "sha512-gdpZ3guLDhz+zWIlYP1UwQ259tG5T5vYRzDaHMkQ1bBY1SQPutvZnrRjTFaWUUpseErJIgAZS51h6NOcZVZiqQ==", - "license": "MIT", - "dependencies": { - "anymatch": "^3.1.3", - "chokidar": "^4.0.3", - "destr": "^2.0.5", - "h3": "^1.15.3", - "lru-cache": "^10.4.3", - "node-fetch-native": "^1.6.6", - "ofetch": "^1.4.1", - "ufo": "^1.6.1" - }, - "peerDependencies": { - "@azure/app-configuration": "^1.8.0", - "@azure/cosmos": "^4.2.0", - "@azure/data-tables": "^13.3.0", - "@azure/identity": "^4.6.0", - "@azure/keyvault-secrets": "^4.9.0", - "@azure/storage-blob": "^12.26.0", - "@capacitor/preferences": "^6.0.3 || ^7.0.0", - "@deno/kv": ">=0.9.0", - "@netlify/blobs": "^6.5.0 || ^7.0.0 || ^8.1.0 || ^9.0.0 || ^10.0.0", - "@planetscale/database": "^1.19.0", - "@upstash/redis": "^1.34.3", - "@vercel/blob": ">=0.27.1", - "@vercel/kv": "^1.0.1", - "aws4fetch": "^1.0.20", - "db0": ">=0.2.1", - "idb-keyval": "^6.2.1", - "ioredis": "^5.4.2", - "uploadthing": "^7.4.4" - }, - "peerDependenciesMeta": { - "@azure/app-configuration": { - "optional": true - }, - "@azure/cosmos": { - "optional": true - }, - "@azure/data-tables": { - "optional": true - }, - "@azure/identity": { - "optional": true - }, - "@azure/keyvault-secrets": { - "optional": true - }, - "@azure/storage-blob": { - "optional": true - }, - "@capacitor/preferences": { - "optional": true - }, - "@deno/kv": { - "optional": true - }, - "@netlify/blobs": { - "optional": true - }, - "@planetscale/database": { - "optional": true - }, - "@upstash/redis": { - "optional": true - }, - "@vercel/blob": { - "optional": true - }, - "@vercel/kv": { - "optional": true - }, - "aws4fetch": { - "optional": true - }, - "db0": { - "optional": true - }, - "idb-keyval": { - "optional": true - }, - "ioredis": { - "optional": true - }, - "uploadthing": { - "optional": true - } - } - }, - "node_modules/update-browserslist-db": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", - "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "escalade": "^3.2.0", - "picocolors": "^1.1.1" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "license": "MIT" - }, - "node_modules/vfile": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", - "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/vfile-location": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-5.0.3.tgz", - "integrity": "sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/vfile-message": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.3.tgz", - "integrity": "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-stringify-position": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/vite": { - "version": "6.3.5", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz", - "integrity": "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==", - "license": "MIT", - "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" - }, - "bin": { - "vite": "bin/vite.js" - }, - "engines": { - "node": "^18.0.0 || ^20.0.0 || >=22.0.0" - }, - "funding": { - "url": "https://github.com/vitejs/vite?sponsor=1" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - }, - "peerDependencies": { - "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", - "jiti": ">=1.21.0", - "less": "*", - "lightningcss": "^1.21.0", - "sass": "*", - "sass-embedded": "*", - "stylus": "*", - "sugarss": "*", - "terser": "^5.16.0", - "tsx": "^4.8.1", - "yaml": "^2.4.2" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "jiti": { - "optional": true - }, - "less": { - "optional": true - }, - "lightningcss": { - "optional": true - }, - "sass": { - "optional": true - }, - "sass-embedded": { - "optional": true - }, - "stylus": { - "optional": true - }, - "sugarss": { - "optional": true - }, - "terser": { - "optional": true - }, - "tsx": { - "optional": true - }, - "yaml": { - "optional": true - } - } - }, - "node_modules/vitefu": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-1.1.1.tgz", - "integrity": "sha512-B/Fegf3i8zh0yFbpzZ21amWzHmuNlLlmJT6n7bu5e+pCHUKQIfXSYokrqOBGEMMe9UG2sostKQF9mml/vYaWJQ==", - "license": "MIT", - "workspaces": [ - "tests/deps/*", - "tests/projects/*", - "tests/projects/workspace/packages/*" - ], - "peerDependencies": { - "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0-beta.0" - }, - "peerDependenciesMeta": { - "vite": { - "optional": true - } - } - }, - "node_modules/web-namespaces": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-2.0.1.tgz", - "integrity": "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/web-streams-polyfill": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", - "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", - "license": "BSD-2-Clause" - }, - "node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "license": "MIT", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/which-pm-runs": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/which-pm-runs/-/which-pm-runs-1.1.0.tgz", - "integrity": "sha512-n1brCuqClxfFfq/Rb0ICg9giSZqCS+pLtccdag6C2HyufBrh3fBOiy9nb6ggRMvWOVH5GrdJskj5iGTZNxd7SA==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/widest-line": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-5.0.0.tgz", - "integrity": "sha512-c9bZp7b5YtRj2wOe6dlj32MK+Bx/M/d+9VB2SHM1OtsUHR0aV0tdP6DWh/iMt0kWi1t5g1Iudu6hQRNd1A4PVA==", - "license": "MIT", - "dependencies": { - "string-width": "^7.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/wrap-ansi": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz", - "integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.2.1", - "string-width": "^7.0.0", - "strip-ansi": "^7.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs": { - "name": "wrap-ansi", - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "license": "MIT" - }, - "node_modules/wrap-ansi-cjs/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/xxhash-wasm": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/xxhash-wasm/-/xxhash-wasm-1.1.0.tgz", - "integrity": "sha512-147y/6YNh+tlp6nd/2pWq38i9h6mz/EuQ6njIrmW8D1BS5nCqs0P6DG+m6zTGnNz5I+uhZ0SHxBs9BsPrwcKDA==", - "license": "MIT" - }, - "node_modules/yaml": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.1.tgz", - "integrity": "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==", - "license": "ISC", - "bin": { - "yaml": "bin.mjs" - }, - "engines": { - "node": ">= 14.6" - } - }, - "node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/yocto-queue": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.2.1.tgz", - "integrity": "sha512-AyeEbWOu/TAXdxlV9wmGcR0+yh2j3vYPGOECcIj2S7MkrLyC7ne+oye2BKTItt0ii2PHk4cDy+95+LshzbXnGg==", - "license": "MIT", - "engines": { - "node": ">=12.20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/yocto-spinner": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/yocto-spinner/-/yocto-spinner-0.2.3.tgz", - "integrity": "sha512-sqBChb33loEnkoXte1bLg45bEBsOP9N1kzQh5JZNKj/0rik4zAPTNSAVPj3uQAdc6slYJ0Ksc403G2XgxsJQFQ==", - "license": "MIT", - "dependencies": { - "yoctocolors": "^2.1.1" - }, - "engines": { - "node": ">=18.19" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/yoctocolors": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.1.1.tgz", - "integrity": "sha512-GQHQqAopRhwU8Kt1DDM8NjibDXHC8eoh1erhGAJPEyveY9qqVeXvVikNKrDz69sHowPMorbPUrH/mx8c50eiBQ==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/zod": { - "version": "3.25.76", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", - "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/colinhacks" - } - }, - "node_modules/zod-to-json-schema": { - "version": "3.24.6", - "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.24.6.tgz", - "integrity": "sha512-h/z3PKvcTcTetyjl1fkj79MHNEjm+HpD6NXheWjzOekY7kV+lwDYnHw+ivHkijnCSMz1yJaWBD9vu/Fcmk+vEg==", - "license": "ISC", - "peerDependencies": { - "zod": "^3.24.1" - } - }, - "node_modules/zod-to-ts": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/zod-to-ts/-/zod-to-ts-1.2.0.tgz", - "integrity": "sha512-x30XE43V+InwGpvTySRNz9kB7qFU8DlyEy7BsSTCHPH1R0QasMmHWZDCzYm6bVXtj/9NNJAZF3jW8rzFvH5OFA==", - "peerDependencies": { - "typescript": "^4.9.4 || ^5.0.2", - "zod": "^3" - } - }, - "node_modules/zwitch": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", - "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - } - } -} diff --git a/astro-church-website/public/css/theme-dark.css b/astro-church-website/public/css/theme-dark.css deleted file mode 100644 index 483033f..0000000 --- a/astro-church-website/public/css/theme-dark.css +++ /dev/null @@ -1 +0,0 @@ -*,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(37,99,235,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(37,99,235,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }/*! tailwindcss v3.4.17 | MIT License | https://tailwindcss.com*/*,:after,:before{box-sizing:border-box;border:0 solid #e5e7eb}:after,:before{--tw-content:""}:host,html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}.container{width:100%}@media (min-width:640px){.container{max-width:640px}}@media (min-width:768px){.container{max-width:768px}}@media (min-width:1024px){.container{max-width:1024px}}@media (min-width:1280px){.container{max-width:1280px}}@media (min-width:1536px){.container{max-width:1536px}}.pointer-events-none{pointer-events:none}.invisible{visibility:hidden}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.inset-0{inset:0}.-right-1{right:-.25rem}.-right-2{right:-.5rem}.-top-1{top:-.25rem}.-top-2{top:-.5rem}.bottom-32{bottom:8rem}.left-0{left:0}.left-1\/4{left:25%}.left-10{left:2.5rem}.left-3{left:.75rem}.right-0{right:0}.right-20{right:5rem}.right-8{right:2rem}.top-0{top:0}.top-1\/2{top:50%}.top-20{top:5rem}.top-40{top:10rem}.top-8{top:2rem}.top-full{top:100%}.z-10{z-index:10}.z-20{z-index:20}.z-50{z-index:50}.order-1{order:1}.order-2{order:2}.order-first{order:-9999}.mx-auto{margin-left:auto;margin-right:auto}.-mt-1{margin-top:-.25rem}.mb-0{margin-bottom:0}.mb-1{margin-bottom:.25rem}.mb-12{margin-bottom:3rem}.mb-16{margin-bottom:4rem}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.mb-4{margin-bottom:1rem}.mb-6{margin-bottom:1.5rem}.mb-8{margin-bottom:2rem}.ml-1{margin-left:.25rem}.ml-2{margin-left:.5rem}.mr-2{margin-right:.5rem}.mr-3{margin-right:.75rem}.mt-0\.5{margin-top:.125rem}.mt-1{margin-top:.25rem}.mt-12{margin-top:3rem}.mt-2{margin-top:.5rem}.mt-3{margin-top:.75rem}.mt-4{margin-top:1rem}.mt-6{margin-top:1.5rem}.mt-8{margin-top:2rem}.mt-auto{margin-top:auto}.line-clamp-2{-webkit-line-clamp:2}.line-clamp-2,.line-clamp-3{overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical}.line-clamp-3{-webkit-line-clamp:3}.block{display:block}.inline-block{display:inline-block}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.grid{display:grid}.hidden{display:none}.aspect-video{aspect-ratio:16/9}.h-10{height:2.5rem}.h-12{height:3rem}.h-16{height:4rem}.h-2{height:.5rem}.h-20{height:5rem}.h-24{height:6rem}.h-3{height:.75rem}.h-32{height:8rem}.h-4{height:1rem}.h-40{height:10rem}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-7{height:1.75rem}.h-8{height:2rem}.h-auto{height:auto}.h-full{height:100%}.max-h-96{max-height:24rem}.min-h-screen{min-height:100vh}.w-10{width:2.5rem}.w-12{width:3rem}.w-16{width:4rem}.w-20{width:5rem}.w-24{width:6rem}.w-3{width:.75rem}.w-32{width:8rem}.w-4{width:1rem}.w-40{width:10rem}.w-48{width:12rem}.w-5{width:1.25rem}.w-56{width:14rem}.w-6{width:1.5rem}.w-7{width:1.75rem}.w-8{width:2rem}.w-full{width:100%}.max-w-2xl{max-width:42rem}.max-w-3xl{max-width:48rem}.max-w-4xl{max-width:56rem}.max-w-6xl{max-width:72rem}.max-w-7xl{max-width:80rem}.max-w-full{max-width:100%}.max-w-md{max-width:28rem}.max-w-none{max-width:none}.max-w-sm{max-width:24rem}.flex-1{flex:1 1 0%}.flex-shrink-0{flex-shrink:0}.grow{flex-grow:1}.-translate-y-1\/2{--tw-translate-y:-50%}.-translate-y-1\/2,.translate-y-2{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-y-2{--tw-translate-y:0.5rem}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.animate-float{animation:float 6s ease-in-out infinite}@keyframes ping{75%,to{transform:scale(2);opacity:0}}.animate-ping{animation:ping 1s cubic-bezier(0,0,.2,1) infinite}@keyframes pulse{50%{opacity:.5}}.animate-pulse{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}@keyframes spin{to{transform:rotate(1turn)}}.animate-spin{animation:spin 1s linear infinite}.cursor-pointer{cursor:pointer}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-start{align-items:flex-start}.items-center{align-items:center}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-12{gap:3rem}.gap-2{gap:.5rem}.gap-4{gap:1rem}.gap-6{gap:1.5rem}.gap-8{gap:2rem}.space-x-0>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(0px*var(--tw-space-x-reverse));margin-left:calc(0px*(1 - var(--tw-space-x-reverse)))}.space-x-1>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(.25rem*var(--tw-space-x-reverse));margin-left:calc(.25rem*(1 - var(--tw-space-x-reverse)))}.space-x-2>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(.5rem*var(--tw-space-x-reverse));margin-left:calc(.5rem*(1 - var(--tw-space-x-reverse)))}.space-x-3>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(.75rem*var(--tw-space-x-reverse));margin-left:calc(.75rem*(1 - var(--tw-space-x-reverse)))}.space-x-4>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(1rem*var(--tw-space-x-reverse));margin-left:calc(1rem*(1 - var(--tw-space-x-reverse)))}.space-x-6>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(1.5rem*var(--tw-space-x-reverse));margin-left:calc(1.5rem*(1 - var(--tw-space-x-reverse)))}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.25rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.25rem*var(--tw-space-y-reverse))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.5rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem*var(--tw-space-y-reverse))}.space-y-3>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.75rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.75rem*var(--tw-space-y-reverse))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(1rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem*var(--tw-space-y-reverse))}.space-y-6>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(1.5rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1.5rem*var(--tw-space-y-reverse))}.space-y-8>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(2rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(2rem*var(--tw-space-y-reverse))}.divide-y>:not([hidden])~:not([hidden]){--tw-divide-y-reverse:0;border-top-width:calc(1px*(1 - var(--tw-divide-y-reverse)));border-bottom-width:calc(1px*var(--tw-divide-y-reverse))}.divide-gray-200>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(229 231 235/var(--tw-divide-opacity,1))}.overflow-hidden{overflow:hidden}.scroll-smooth{scroll-behavior:smooth}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-line{white-space:pre-line}.rounded{border-radius:.25rem}.rounded-2xl{border-radius:1rem}.rounded-3xl{border-radius:1.5rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.rounded-xl{border-radius:.75rem}.border{border-width:1px}.border-2{border-width:2px}.border-4{border-width:4px}.border-b{border-bottom-width:1px}.border-t{border-top-width:1px}.border-dashed{border-style:dashed}.border-gray-100{--tw-border-opacity:1;border-color:rgb(243 244 246/var(--tw-border-opacity,1))}.border-gray-200{--tw-border-opacity:1;border-color:rgb(229 231 235/var(--tw-border-opacity,1))}.border-gray-200\/20{border-color:rgba(229,231,235,.2)}.border-gray-300{--tw-border-opacity:1;border-color:rgb(209 213 219/var(--tw-border-opacity,1))}.border-green-100{--tw-border-opacity:1;border-color:rgb(220 252 231/var(--tw-border-opacity,1))}.border-green-200{--tw-border-opacity:1;border-color:rgb(187 247 208/var(--tw-border-opacity,1))}.border-green-400{--tw-border-opacity:1;border-color:rgb(74 222 128/var(--tw-border-opacity,1))}.border-orange-100{--tw-border-opacity:1;border-color:rgb(255 237 213/var(--tw-border-opacity,1))}.border-primary-100{--tw-border-opacity:1;border-color:rgb(224 231 255/var(--tw-border-opacity,1))}.border-primary-200{--tw-border-opacity:1;border-color:rgb(199 210 254/var(--tw-border-opacity,1))}.border-primary-600{--tw-border-opacity:1;border-color:rgb(79 70 229/var(--tw-border-opacity,1))}.border-purple-100{--tw-border-opacity:1;border-color:rgb(243 232 255/var(--tw-border-opacity,1))}.border-red-100{--tw-border-opacity:1;border-color:rgb(254 226 226/var(--tw-border-opacity,1))}.border-red-200{--tw-border-opacity:1;border-color:rgb(254 202 202/var(--tw-border-opacity,1))}.border-red-400{--tw-border-opacity:1;border-color:rgb(248 113 113/var(--tw-border-opacity,1))}.border-red-500{--tw-border-opacity:1;border-color:rgb(239 68 68/var(--tw-border-opacity,1))}.border-white{--tw-border-opacity:1;border-color:rgb(255 255 255/var(--tw-border-opacity,1))}.border-white\/10{border-color:hsla(0,0%,100%,.1)}.border-white\/20{border-color:hsla(0,0%,100%,.2)}.border-white\/30{border-color:hsla(0,0%,100%,.3)}.border-t-primary-600{--tw-border-opacity:1;border-top-color:rgb(79 70 229/var(--tw-border-opacity,1))}.bg-amber-500{--tw-bg-opacity:1;background-color:rgb(245 158 11/var(--tw-bg-opacity,1))}.bg-black{--tw-bg-opacity:1;background-color:rgb(0 0 0/var(--tw-bg-opacity,1))}.bg-blue-500{--tw-bg-opacity:1;background-color:rgb(37 99 235/var(--tw-bg-opacity,1))}.bg-blue-600{--tw-bg-opacity:1;background-color:rgb(29 78 216/var(--tw-bg-opacity,1))}.bg-gold-400\/20{background-color:rgba(251,191,36,.2)}.bg-gold-500{--tw-bg-opacity:1;background-color:rgb(245 158 11/var(--tw-bg-opacity,1))}.bg-gray-100{--tw-bg-opacity:1;background-color:rgb(243 244 246/var(--tw-bg-opacity,1))}.bg-gray-300{--tw-bg-opacity:1;background-color:rgb(209 213 219/var(--tw-bg-opacity,1))}.bg-gray-50{--tw-bg-opacity:1;background-color:rgb(249 250 251/var(--tw-bg-opacity,1))}.bg-gray-500{--tw-bg-opacity:1;background-color:rgb(107 114 128/var(--tw-bg-opacity,1))}.bg-gray-600{--tw-bg-opacity:1;background-color:rgb(75 85 99/var(--tw-bg-opacity,1))}.bg-gray-900{--tw-bg-opacity:1;background-color:rgb(17 24 39/var(--tw-bg-opacity,1))}.bg-green-100{--tw-bg-opacity:1;background-color:rgb(220 252 231/var(--tw-bg-opacity,1))}.bg-green-50{--tw-bg-opacity:1;background-color:rgb(240 253 244/var(--tw-bg-opacity,1))}.bg-green-500{--tw-bg-opacity:1;background-color:rgb(34 197 94/var(--tw-bg-opacity,1))}.bg-orange-50{--tw-bg-opacity:1;background-color:rgb(255 247 237/var(--tw-bg-opacity,1))}.bg-orange-600{--tw-bg-opacity:1;background-color:rgb(234 88 12/var(--tw-bg-opacity,1))}.bg-primary-100{--tw-bg-opacity:1;background-color:rgb(224 231 255/var(--tw-bg-opacity,1))}.bg-primary-300\/10{background-color:rgba(165,180,252,.1)}.bg-primary-50{--tw-bg-opacity:1;background-color:rgb(240 244 255/var(--tw-bg-opacity,1))}.bg-primary-500{--tw-bg-opacity:1;background-color:rgb(99 102 241/var(--tw-bg-opacity,1))}.bg-primary-600{--tw-bg-opacity:1;background-color:rgb(79 70 229/var(--tw-bg-opacity,1))}.bg-purple-500{--tw-bg-opacity:1;background-color:rgb(168 85 247/var(--tw-bg-opacity,1))}.bg-purple-600{--tw-bg-opacity:1;background-color:rgb(147 51 234/var(--tw-bg-opacity,1))}.bg-red-100{--tw-bg-opacity:1;background-color:rgb(254 226 226/var(--tw-bg-opacity,1))}.bg-red-50{--tw-bg-opacity:1;background-color:rgb(254 242 242/var(--tw-bg-opacity,1))}.bg-red-500{--tw-bg-opacity:1;background-color:rgb(239 68 68/var(--tw-bg-opacity,1))}.bg-red-600{--tw-bg-opacity:1;background-color:rgb(220 38 38/var(--tw-bg-opacity,1))}.bg-rose-500{--tw-bg-opacity:1;background-color:rgb(244 63 94/var(--tw-bg-opacity,1))}.bg-white{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity,1))}.bg-white\/10{background-color:hsla(0,0%,100%,.1)}.bg-white\/15{background-color:hsla(0,0%,100%,.15)}.bg-white\/20{background-color:hsla(0,0%,100%,.2)}.bg-white\/5{background-color:hsla(0,0%,100%,.05)}.bg-white\/50{background-color:hsla(0,0%,100%,.5)}.bg-white\/60{background-color:hsla(0,0%,100%,.6)}.bg-white\/80{background-color:hsla(0,0%,100%,.8)}.bg-white\/95{background-color:hsla(0,0%,100%,.95)}.bg-opacity-50{--tw-bg-opacity:0.5}.bg-gradient-to-br{background-image:linear-gradient(to bottom right,var(--tw-gradient-stops))}.bg-gradient-to-r{background-image:linear-gradient(to right,var(--tw-gradient-stops))}.from-amber-50{--tw-gradient-from:#fffbeb var(--tw-gradient-from-position);--tw-gradient-to:rgba(255,251,235,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-amber-500{--tw-gradient-from:#f59e0b var(--tw-gradient-from-position);--tw-gradient-to:rgba(245,158,11,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-blue-50{--tw-gradient-from:#eff6ff var(--tw-gradient-from-position);--tw-gradient-to:rgba(239,246,255,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-blue-500{--tw-gradient-from:#2563eb var(--tw-gradient-from-position);--tw-gradient-to:rgba(37,99,235,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-earth-900{--tw-gradient-from:#111827 var(--tw-gradient-from-position);--tw-gradient-to:rgba(17,24,39,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-gold-400{--tw-gradient-from:#fbbf24 var(--tw-gradient-from-position);--tw-gradient-to:rgba(251,191,36,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-gold-50{--tw-gradient-from:#fffbeb var(--tw-gradient-from-position);--tw-gradient-to:rgba(255,251,235,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-gold-500{--tw-gradient-from:#f59e0b var(--tw-gradient-from-position);--tw-gradient-to:rgba(245,158,11,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-gray-50{--tw-gradient-from:#f9fafb var(--tw-gradient-from-position);--tw-gradient-to:rgba(249,250,251,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-gray-700{--tw-gradient-from:#374151 var(--tw-gradient-from-position);--tw-gradient-to:rgba(55,65,81,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-green-100{--tw-gradient-from:#dcfce7 var(--tw-gradient-from-position);--tw-gradient-to:rgba(220,252,231,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-green-50{--tw-gradient-from:#f0fdf4 var(--tw-gradient-from-position);--tw-gradient-to:rgba(240,253,244,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-green-500{--tw-gradient-from:#22c55e var(--tw-gradient-from-position);--tw-gradient-to:rgba(34,197,94,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-orange-100{--tw-gradient-from:#ffedd5 var(--tw-gradient-from-position);--tw-gradient-to:rgba(255,237,213,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-orange-400{--tw-gradient-from:#fb923c var(--tw-gradient-from-position);--tw-gradient-to:rgba(251,146,60,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-orange-50{--tw-gradient-from:#fff7ed var(--tw-gradient-from-position);--tw-gradient-to:rgba(255,247,237,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-orange-500{--tw-gradient-from:#f97316 var(--tw-gradient-from-position);--tw-gradient-to:rgba(249,115,22,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-orange-900{--tw-gradient-from:#7c2d12 var(--tw-gradient-from-position);--tw-gradient-to:rgba(124,45,18,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-primary-100{--tw-gradient-from:#e0e7ff var(--tw-gradient-from-position);--tw-gradient-to:rgba(224,231,255,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-primary-400{--tw-gradient-from:#818cf8 var(--tw-gradient-from-position);--tw-gradient-to:rgba(129,140,248,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-primary-50{--tw-gradient-from:#f0f4ff var(--tw-gradient-from-position);--tw-gradient-to:rgba(240,244,255,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-primary-500{--tw-gradient-from:#6366f1 var(--tw-gradient-from-position);--tw-gradient-to:rgba(99,102,241,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-primary-600{--tw-gradient-from:#4f46e5 var(--tw-gradient-from-position);--tw-gradient-to:rgba(79,70,229,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-primary-900{--tw-gradient-from:#312e81 var(--tw-gradient-from-position);--tw-gradient-to:rgba(49,46,129,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-purple-100{--tw-gradient-from:#f3e8ff var(--tw-gradient-from-position);--tw-gradient-to:rgba(243,232,255,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-purple-50{--tw-gradient-from:#faf5ff var(--tw-gradient-from-position);--tw-gradient-to:rgba(250,245,255,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-purple-500{--tw-gradient-from:#a855f7 var(--tw-gradient-from-position);--tw-gradient-to:rgba(168,85,247,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-red-100{--tw-gradient-from:#fee2e2 var(--tw-gradient-from-position);--tw-gradient-to:hsla(0,93%,94%,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-red-400{--tw-gradient-from:#f87171 var(--tw-gradient-from-position);--tw-gradient-to:hsla(0,91%,71%,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-red-50{--tw-gradient-from:#fef2f2 var(--tw-gradient-from-position);--tw-gradient-to:hsla(0,86%,97%,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-red-500{--tw-gradient-from:#ef4444 var(--tw-gradient-from-position);--tw-gradient-to:rgba(239,68,68,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-red-900{--tw-gradient-from:#7f1d1d var(--tw-gradient-from-position);--tw-gradient-to:rgba(127,29,29,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-rose-50{--tw-gradient-from:#fff1f2 var(--tw-gradient-from-position);--tw-gradient-to:rgba(255,241,242,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-slate-50{--tw-gradient-from:#f8fafc var(--tw-gradient-from-position);--tw-gradient-to:rgba(248,250,252,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-violet-50{--tw-gradient-from:#f5f3ff var(--tw-gradient-from-position);--tw-gradient-to:rgba(245,243,255,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-yellow-500{--tw-gradient-from:#eab308 var(--tw-gradient-from-position);--tw-gradient-to:rgba(234,179,8,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.via-blue-50{--tw-gradient-to:rgba(239,246,255,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),#eff6ff var(--tw-gradient-via-position),var(--tw-gradient-to)}.via-blue-600{--tw-gradient-to:rgba(29,78,216,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),#1d4ed8 var(--tw-gradient-via-position),var(--tw-gradient-to)}.via-earth-800{--tw-gradient-to:rgba(31,41,55,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),#1f2937 var(--tw-gradient-via-position),var(--tw-gradient-to)}.via-orange-800{--tw-gradient-to:rgba(154,52,18,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),#9a3412 var(--tw-gradient-via-position),var(--tw-gradient-to)}.via-primary-800{--tw-gradient-to:rgba(55,48,163,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),#3730a3 var(--tw-gradient-via-position),var(--tw-gradient-to)}.via-purple-600{--tw-gradient-to:rgba(147,51,234,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),#9333ea var(--tw-gradient-via-position),var(--tw-gradient-to)}.via-red-800{--tw-gradient-to:rgba(153,27,27,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),#991b1b var(--tw-gradient-via-position),var(--tw-gradient-to)}.to-amber-500{--tw-gradient-to:#f59e0b var(--tw-gradient-to-position)}.to-black{--tw-gradient-to:#000 var(--tw-gradient-to-position)}.to-blue-100{--tw-gradient-to:#dbeafe var(--tw-gradient-to-position)}.to-blue-50{--tw-gradient-to:#eff6ff var(--tw-gradient-to-position)}.to-blue-600{--tw-gradient-to:#1d4ed8 var(--tw-gradient-to-position)}.to-blue-900{--tw-gradient-to:#1e3a8a var(--tw-gradient-to-position)}.to-earth-900{--tw-gradient-to:#111827 var(--tw-gradient-to-position)}.to-emerald-50{--tw-gradient-to:#ecfdf5 var(--tw-gradient-to-position)}.to-gold-600{--tw-gradient-to:#d97706 var(--tw-gradient-to-position)}.to-green-600{--tw-gradient-to:#16a34a var(--tw-gradient-to-position)}.to-indigo-50{--tw-gradient-to:#eef2ff var(--tw-gradient-to-position)}.to-orange-50{--tw-gradient-to:#fff7ed var(--tw-gradient-to-position)}.to-orange-500{--tw-gradient-to:#f97316 var(--tw-gradient-to-position)}.to-orange-600{--tw-gradient-to:#ea580c var(--tw-gradient-to-position)}.to-pink-100{--tw-gradient-to:#fce7f3 var(--tw-gradient-to-position)}.to-pink-50{--tw-gradient-to:#fdf2f8 var(--tw-gradient-to-position)}.to-pink-500{--tw-gradient-to:#ec4899 var(--tw-gradient-to-position)}.to-pink-900{--tw-gradient-to:#831843 var(--tw-gradient-to-position)}.to-primary-600{--tw-gradient-to:#4f46e5 var(--tw-gradient-to-position)}.to-primary-700{--tw-gradient-to:#4338ca var(--tw-gradient-to-position)}.to-purple-100{--tw-gradient-to:#f3e8ff var(--tw-gradient-to-position)}.to-purple-50{--tw-gradient-to:#faf5ff var(--tw-gradient-to-position)}.to-purple-600{--tw-gradient-to:#9333ea var(--tw-gradient-to-position)}.to-red-100{--tw-gradient-to:#fee2e2 var(--tw-gradient-to-position)}.to-red-50{--tw-gradient-to:#fef2f2 var(--tw-gradient-to-position)}.to-red-600{--tw-gradient-to:#dc2626 var(--tw-gradient-to-position)}.to-red-900{--tw-gradient-to:#7f1d1d var(--tw-gradient-to-position)}.to-yellow-600{--tw-gradient-to:#ca8a04 var(--tw-gradient-to-position)}.fill-gold-400{fill:#fbbf24}.object-contain{-o-object-fit:contain;object-fit:contain}.object-cover{-o-object-fit:cover;object-fit:cover}.p-2{padding:.5rem}.p-3{padding:.75rem}.p-4{padding:1rem}.p-6{padding:1.5rem}.p-8{padding:2rem}.px-16{padding-left:4rem;padding-right:4rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.px-8{padding-left:2rem;padding-right:2rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-12{padding-top:3rem;padding-bottom:3rem}.py-16{padding-top:4rem;padding-bottom:4rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-20{padding-top:5rem;padding-bottom:5rem}.py-24{padding-top:6rem;padding-bottom:6rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-4{padding-top:1rem;padding-bottom:1rem}.py-6{padding-top:1.5rem;padding-bottom:1.5rem}.py-8{padding-top:2rem;padding-bottom:2rem}.pb-2{padding-bottom:.5rem}.pb-20{padding-bottom:5rem}.pb-24{padding-bottom:6rem}.pb-6{padding-bottom:1.5rem}.pl-10{padding-left:2.5rem}.pr-4{padding-right:1rem}.pt-20{padding-top:5rem}.pt-5{padding-top:1.25rem}.pt-8{padding-top:2rem}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.font-body{font-family:Inter,system-ui,sans-serif}.text-2xl{font-size:1.5rem;line-height:2rem}.text-3xl{font-size:1.875rem;line-height:2.25rem}.text-4xl{font-size:2.25rem;line-height:2.5rem}.text-5xl{font-size:3rem;line-height:1}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.lowercase{text-transform:lowercase}.italic{font-style:italic}.leading-relaxed{line-height:1.625}.leading-tight{line-height:1.25}.tracking-wide{letter-spacing:.025em}.tracking-wider{letter-spacing:.05em}.text-amber-600{--tw-text-opacity:1;color:rgb(217 119 6/var(--tw-text-opacity,1))}.text-amber-700{--tw-text-opacity:1;color:rgb(180 83 9/var(--tw-text-opacity,1))}.text-black{--tw-text-opacity:1;color:rgb(0 0 0/var(--tw-text-opacity,1))}.text-blue-100{--tw-text-opacity:1;color:rgb(219 234 254/var(--tw-text-opacity,1))}.text-blue-200{--tw-text-opacity:1;color:rgb(147 197 253/var(--tw-text-opacity,1))}.text-blue-500{--tw-text-opacity:1;color:rgb(37 99 235/var(--tw-text-opacity,1))}.text-blue-600{--tw-text-opacity:1;color:rgb(29 78 216/var(--tw-text-opacity,1))}.text-gold-300{--tw-text-opacity:1;color:rgb(252 211 77/var(--tw-text-opacity,1))}.text-gold-400{--tw-text-opacity:1;color:rgb(251 191 36/var(--tw-text-opacity,1))}.text-gray-200{--tw-text-opacity:1;color:rgb(229 231 235/var(--tw-text-opacity,1))}.text-gray-300{--tw-text-opacity:1;color:rgb(209 213 219/var(--tw-text-opacity,1))}.text-gray-400{--tw-text-opacity:1;color:rgb(156 163 175/var(--tw-text-opacity,1))}.text-gray-500{--tw-text-opacity:1;color:rgb(107 114 128/var(--tw-text-opacity,1))}.text-gray-600{--tw-text-opacity:1;color:rgb(75 85 99/var(--tw-text-opacity,1))}.text-gray-700{--tw-text-opacity:1;color:rgb(55 65 81/var(--tw-text-opacity,1))}.text-gray-800{--tw-text-opacity:1;color:rgb(31 41 55/var(--tw-text-opacity,1))}.text-gray-900{--tw-text-opacity:1;color:rgb(17 24 39/var(--tw-text-opacity,1))}.text-green-500{--tw-text-opacity:1;color:rgb(34 197 94/var(--tw-text-opacity,1))}.text-green-600{--tw-text-opacity:1;color:rgb(22 163 74/var(--tw-text-opacity,1))}.text-green-700{--tw-text-opacity:1;color:rgb(21 128 61/var(--tw-text-opacity,1))}.text-green-800{--tw-text-opacity:1;color:rgb(22 101 52/var(--tw-text-opacity,1))}.text-indigo-600{--tw-text-opacity:1;color:rgb(79 70 229/var(--tw-text-opacity,1))}.text-orange-100{--tw-text-opacity:1;color:rgb(255 237 213/var(--tw-text-opacity,1))}.text-orange-300{--tw-text-opacity:1;color:rgb(253 186 116/var(--tw-text-opacity,1))}.text-orange-500{--tw-text-opacity:1;color:rgb(249 115 22/var(--tw-text-opacity,1))}.text-orange-600{--tw-text-opacity:1;color:rgb(234 88 12/var(--tw-text-opacity,1))}.text-orange-700{--tw-text-opacity:1;color:rgb(194 65 12/var(--tw-text-opacity,1))}.text-primary-300{--tw-text-opacity:1;color:rgb(165 180 252/var(--tw-text-opacity,1))}.text-primary-400{--tw-text-opacity:1;color:rgb(129 140 248/var(--tw-text-opacity,1))}.text-primary-500{--tw-text-opacity:1;color:rgb(99 102 241/var(--tw-text-opacity,1))}.text-primary-600{--tw-text-opacity:1;color:rgb(79 70 229/var(--tw-text-opacity,1))}.text-primary-700{--tw-text-opacity:1;color:rgb(67 56 202/var(--tw-text-opacity,1))}.text-purple-500{--tw-text-opacity:1;color:rgb(168 85 247/var(--tw-text-opacity,1))}.text-purple-600{--tw-text-opacity:1;color:rgb(147 51 234/var(--tw-text-opacity,1))}.text-purple-700{--tw-text-opacity:1;color:rgb(126 34 206/var(--tw-text-opacity,1))}.text-red-100{--tw-text-opacity:1;color:rgb(254 226 226/var(--tw-text-opacity,1))}.text-red-300{--tw-text-opacity:1;color:rgb(252 165 165/var(--tw-text-opacity,1))}.text-red-500{--tw-text-opacity:1;color:rgb(239 68 68/var(--tw-text-opacity,1))}.text-red-600{--tw-text-opacity:1;color:rgb(220 38 38/var(--tw-text-opacity,1))}.text-red-700{--tw-text-opacity:1;color:rgb(185 28 28/var(--tw-text-opacity,1))}.text-red-800{--tw-text-opacity:1;color:rgb(153 27 27/var(--tw-text-opacity,1))}.text-violet-600{--tw-text-opacity:1;color:rgb(124 58 237/var(--tw-text-opacity,1))}.text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity,1))}.antialiased{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.placeholder-gray-500::-moz-placeholder{--tw-placeholder-opacity:1;color:rgb(107 114 128/var(--tw-placeholder-opacity,1))}.placeholder-gray-500::placeholder{--tw-placeholder-opacity:1;color:rgb(107 114 128/var(--tw-placeholder-opacity,1))}.opacity-0{opacity:0}.opacity-30{opacity:.3}.opacity-50{opacity:.5}.opacity-80{opacity:.8}.shadow-2xl{--tw-shadow:0 25px 50px -12px rgba(0,0,0,.25);--tw-shadow-colored:0 25px 50px -12px var(--tw-shadow-color)}.shadow-2xl,.shadow-lg{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-lg{--tw-shadow:0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -4px rgba(0,0,0,.1);--tw-shadow-colored:0 10px 15px -3px var(--tw-shadow-color),0 4px 6px -4px var(--tw-shadow-color)}.shadow-md{--tw-shadow:0 4px 6px -1px rgba(0,0,0,.1),0 2px 4px -2px rgba(0,0,0,.1);--tw-shadow-colored:0 4px 6px -1px var(--tw-shadow-color),0 2px 4px -2px var(--tw-shadow-color)}.shadow-md,.shadow-medium{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-medium{--tw-shadow:0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -2px rgba(0,0,0,.05);--tw-shadow-colored:0 10px 15px -3px var(--tw-shadow-color),0 4px 6px -2px var(--tw-shadow-color)}.shadow-sm{--tw-shadow:0 1px 2px 0 rgba(0,0,0,.05);--tw-shadow-colored:0 1px 2px 0 var(--tw-shadow-color)}.shadow-sm,.shadow-xl{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-xl{--tw-shadow:0 20px 25px -5px rgba(0,0,0,.1),0 8px 10px -6px rgba(0,0,0,.1);--tw-shadow-colored:0 20px 25px -5px var(--tw-shadow-color),0 8px 10px -6px var(--tw-shadow-color)}.blur{--tw-blur:blur(8px)}.blur,.blur-2xl{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.blur-2xl{--tw-blur:blur(40px)}.blur-lg{--tw-blur:blur(16px)}.blur-lg,.blur-xl{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.blur-xl{--tw-blur:blur(24px)}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.backdrop-blur-md{--tw-backdrop-blur:blur(12px)}.backdrop-blur-md,.backdrop-blur-sm{backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.backdrop-blur-sm{--tw-backdrop-blur:blur(4px)}.transition-all{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-shadow{transition-property:box-shadow;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-transform{transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.duration-200{transition-duration:.2s}.duration-300{transition-duration:.3s}.duration-500{transition-duration:.5s}::-webkit-scrollbar{width:6px}::-webkit-scrollbar-track{background:#f1f5f9}::-webkit-scrollbar-thumb{background:#6366f1;border-radius:3px}::-webkit-scrollbar-thumb:hover{background:#4f46e5}*{transition:all .2s ease-in-out}:focus-visible{outline:2px solid #6366f1;outline-offset:2px}.bg-divine-gradient{background:linear-gradient(135deg,#667eea,#764ba2)}.bg-heavenly-gradient{background:linear-gradient(135deg,#6366f1,#8b5cf6 50%,#a855f7)}@media (prefers-color-scheme:light){.bg-heavenly-gradient{background:linear-gradient(135deg,#4338ca,#7c3aed 50%,#9333ea)}}.bg-sacred-gradient{background:linear-gradient(135deg,#f59e0b,#d97706)}.text-divine-gradient{background:linear-gradient(135deg,#6366f1,#8b5cf6);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text}@media (prefers-color-scheme:light){.text-divine-gradient{background:linear-gradient(135deg,#4338ca,#7c3aed);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text}}.text-golden-gradient{background:linear-gradient(135deg,#f59e0b,#d97706);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text}@media (prefers-color-scheme:dark){::-webkit-scrollbar-track{background:#1e293b}}.glass{background:hsla(0,0%,100%,.25);border:1px solid hsla(0,0%,100%,.18)}.glass,.glass-dark{backdrop-filter:blur(10px)}.glass-dark{background:rgba(0,0,0,.25);border:1px solid hsla(0,0%,100%,.1)}@keyframes fadeInUp{0%{opacity:0;transform:translateY(30px)}to{opacity:1;transform:translateY(0)}}@keyframes float{0%,to{transform:translateY(0)}50%{transform:translateY(-10px)}}.animate-fade-in-up{animation:fadeInUp .6s ease-out}.animate-float{animation:float 3s ease-in-out infinite}.loading-shimmer{background:linear-gradient(90deg,#f0f0f0 25%,#e0e0e0 50%,#f0f0f0 75%);background-size:200% 100%;animation:shimmer 1.5s infinite}@keyframes shimmer{0%{background-position:-200% 0}to{background-position:200% 0}}.first\:rounded-t-xl:first-child{border-top-left-radius:.75rem;border-top-right-radius:.75rem}.hover\:-translate-y-1:hover{--tw-translate-y:-0.25rem}.hover\:-translate-y-1:hover,.hover\:-translate-y-2:hover{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.hover\:-translate-y-2:hover{--tw-translate-y:-0.5rem}.hover\:scale-105:hover{--tw-scale-x:1.05;--tw-scale-y:1.05}.hover\:scale-105:hover,.hover\:scale-110:hover{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.hover\:scale-110:hover{--tw-scale-x:1.1;--tw-scale-y:1.1}.hover\:transform:hover{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.hover\:border-primary-500:hover{--tw-border-opacity:1;border-color:rgb(99 102 241/var(--tw-border-opacity,1))}.hover\:border-white\/20:hover{border-color:hsla(0,0%,100%,.2)}.hover\:border-white\/40:hover{border-color:hsla(0,0%,100%,.4)}.hover\:bg-blue-600:hover{--tw-bg-opacity:1;background-color:rgb(29 78 216/var(--tw-bg-opacity,1))}.hover\:bg-gold-400:hover{--tw-bg-opacity:1;background-color:rgb(251 191 36/var(--tw-bg-opacity,1))}.hover\:bg-gold-600:hover{--tw-bg-opacity:1;background-color:rgb(217 119 6/var(--tw-bg-opacity,1))}.hover\:bg-gray-100:hover{--tw-bg-opacity:1;background-color:rgb(243 244 246/var(--tw-bg-opacity,1))}.hover\:bg-gray-200:hover{--tw-bg-opacity:1;background-color:rgb(229 231 235/var(--tw-bg-opacity,1))}.hover\:bg-gray-400:hover{--tw-bg-opacity:1;background-color:rgb(156 163 175/var(--tw-bg-opacity,1))}.hover\:bg-gray-50:hover{--tw-bg-opacity:1;background-color:rgb(249 250 251/var(--tw-bg-opacity,1))}.hover\:bg-gray-600:hover{--tw-bg-opacity:1;background-color:rgb(75 85 99/var(--tw-bg-opacity,1))}.hover\:bg-gray-700:hover{--tw-bg-opacity:1;background-color:rgb(55 65 81/var(--tw-bg-opacity,1))}.hover\:bg-green-600:hover{--tw-bg-opacity:1;background-color:rgb(22 163 74/var(--tw-bg-opacity,1))}.hover\:bg-orange-700:hover{--tw-bg-opacity:1;background-color:rgb(194 65 12/var(--tw-bg-opacity,1))}.hover\:bg-primary-500:hover{--tw-bg-opacity:1;background-color:rgb(99 102 241/var(--tw-bg-opacity,1))}.hover\:bg-primary-600:hover{--tw-bg-opacity:1;background-color:rgb(79 70 229/var(--tw-bg-opacity,1))}.hover\:bg-primary-700:hover{--tw-bg-opacity:1;background-color:rgb(67 56 202/var(--tw-bg-opacity,1))}.hover\:bg-purple-600:hover{--tw-bg-opacity:1;background-color:rgb(147 51 234/var(--tw-bg-opacity,1))}.hover\:bg-purple-700:hover{--tw-bg-opacity:1;background-color:rgb(126 34 206/var(--tw-bg-opacity,1))}.hover\:bg-red-600:hover{--tw-bg-opacity:1;background-color:rgb(220 38 38/var(--tw-bg-opacity,1))}.hover\:bg-red-700:hover{--tw-bg-opacity:1;background-color:rgb(185 28 28/var(--tw-bg-opacity,1))}.hover\:bg-white:hover{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity,1))}.hover\:bg-white\/10:hover{background-color:hsla(0,0%,100%,.1)}.hover\:bg-white\/30:hover{background-color:hsla(0,0%,100%,.3)}.hover\:from-primary-700:hover{--tw-gradient-from:#4338ca var(--tw-gradient-from-position);--tw-gradient-to:rgba(67,56,202,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.hover\:via-blue-700:hover{--tw-gradient-to:rgba(30,64,175,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),#1e40af var(--tw-gradient-via-position),var(--tw-gradient-to)}.hover\:to-purple-700:hover{--tw-gradient-to:#7e22ce var(--tw-gradient-to-position)}.hover\:text-blue-900:hover{--tw-text-opacity:1;color:rgb(30 58 138/var(--tw-text-opacity,1))}.hover\:text-gray-600:hover{--tw-text-opacity:1;color:rgb(75 85 99/var(--tw-text-opacity,1))}.hover\:text-indigo-900:hover{--tw-text-opacity:1;color:rgb(49 46 129/var(--tw-text-opacity,1))}.hover\:text-primary-600:hover{--tw-text-opacity:1;color:rgb(79 70 229/var(--tw-text-opacity,1))}.hover\:text-primary-700:hover{--tw-text-opacity:1;color:rgb(67 56 202/var(--tw-text-opacity,1))}.hover\:text-primary-900:hover{--tw-text-opacity:1;color:rgb(49 46 129/var(--tw-text-opacity,1))}.hover\:text-red-900:hover{--tw-text-opacity:1;color:rgb(127 29 29/var(--tw-text-opacity,1))}.hover\:text-white:hover{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity,1))}.hover\:underline:hover{text-decoration-line:underline}.hover\:shadow-2xl:hover{--tw-shadow:0 25px 50px -12px rgba(0,0,0,.25);--tw-shadow-colored:0 25px 50px -12px var(--tw-shadow-color)}.hover\:shadow-2xl:hover,.hover\:shadow-divine:hover{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.hover\:shadow-divine:hover{--tw-shadow:0 0 50px rgba(99,102,241,.3);--tw-shadow-colored:0 0 50px var(--tw-shadow-color)}.hover\:shadow-large:hover{--tw-shadow:0 25px 50px -12px rgba(0,0,0,.25);--tw-shadow-colored:0 25px 50px -12px var(--tw-shadow-color)}.hover\:shadow-large:hover,.hover\:shadow-lg:hover{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.hover\:shadow-lg:hover{--tw-shadow:0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -4px rgba(0,0,0,.1);--tw-shadow-colored:0 10px 15px -3px var(--tw-shadow-color),0 4px 6px -4px var(--tw-shadow-color)}.hover\:shadow-xl:hover{--tw-shadow:0 20px 25px -5px rgba(0,0,0,.1),0 8px 10px -6px rgba(0,0,0,.1);--tw-shadow-colored:0 20px 25px -5px var(--tw-shadow-color),0 8px 10px -6px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.focus\:border-blue-500:focus{--tw-border-opacity:1;border-color:rgb(37 99 235/var(--tw-border-opacity,1))}.focus\:border-primary-500:focus{--tw-border-opacity:1;border-color:rgb(99 102 241/var(--tw-border-opacity,1))}.focus\:border-transparent:focus{border-color:transparent}.focus\:ring-2:focus{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.focus\:ring-blue-500:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(37 99 235/var(--tw-ring-opacity,1))}.focus\:ring-primary-500:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(99 102 241/var(--tw-ring-opacity,1))}.focus\:ring-offset-2:focus{--tw-ring-offset-width:2px}.focus\:ring-offset-white:focus{--tw-ring-offset-color:#fff}.disabled\:transform-none:disabled{transform:none}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-50:disabled{opacity:.5}.group:hover .group-hover\:visible{visibility:visible}.group:hover .group-hover\:translate-y-0{--tw-translate-y:0px}.group:hover .group-hover\:rotate-12,.group:hover .group-hover\:translate-y-0{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.group:hover .group-hover\:rotate-12{--tw-rotate:12deg}.group:hover .group-hover\:rotate-180{--tw-rotate:180deg}.group:hover .group-hover\:rotate-180,.group:hover .group-hover\:scale-105{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.group:hover .group-hover\:scale-105{--tw-scale-x:1.05;--tw-scale-y:1.05}.group:hover .group-hover\:scale-110{--tw-scale-x:1.1;--tw-scale-y:1.1;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.group:hover .group-hover\:opacity-100{opacity:1}.group:hover .group-hover\:shadow-xl{--tw-shadow:0 20px 25px -5px rgba(0,0,0,.1),0 8px 10px -6px rgba(0,0,0,.1);--tw-shadow-colored:0 20px 25px -5px var(--tw-shadow-color),0 8px 10px -6px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}@media (min-width:640px){.sm\:block{display:block}.sm\:inline-flex{display:inline-flex}.sm\:flex-row{flex-direction:row}.sm\:space-x-8>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(2rem*var(--tw-space-x-reverse));margin-left:calc(2rem*(1 - var(--tw-space-x-reverse)))}.sm\:space-y-0>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(0px*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(0px*var(--tw-space-y-reverse))}.sm\:px-6{padding-left:1.5rem;padding-right:1.5rem}}@media (min-width:768px){.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.md\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.md\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.md\:flex-row{flex-direction:row}.md\:space-y-0>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(0px*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(0px*var(--tw-space-y-reverse))}}@media (min-width:1024px){.lg\:order-1{order:1}.lg\:order-2{order:2}.lg\:order-last{order:9999}.lg\:col-span-2{grid-column:span 2/span 2}.lg\:mx-0{margin-left:0;margin-right:0}.lg\:flex{display:flex}.lg\:hidden{display:none}.lg\:h-20{height:5rem}.lg\:w-80{width:20rem}.lg\:flex-shrink-0{flex-shrink:0}.lg\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.lg\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.lg\:flex-row{flex-direction:row}.lg\:justify-start{justify-content:flex-start}.lg\:gap-12{gap:3rem}.lg\:p-12{padding:3rem}.lg\:px-8{padding-left:2rem;padding-right:2rem}.lg\:text-left{text-align:left}.lg\:text-2xl{font-size:1.5rem;line-height:2rem}.lg\:text-3xl{font-size:1.875rem;line-height:2.25rem}.lg\:text-5xl{font-size:3rem;line-height:1}.lg\:text-6xl{font-size:3.75rem;line-height:1}.lg\:text-7xl{font-size:4.5rem;line-height:1}.lg\:text-xl{font-size:1.25rem;line-height:1.75rem}}@media (min-width:1280px){.xl\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}}@media (prefers-color-scheme:dark){.dark\:border-gray-600{--tw-border-opacity:1;border-color:rgb(75 85 99/var(--tw-border-opacity,1))}.dark\:border-gray-700{--tw-border-opacity:1;border-color:rgb(55 65 81/var(--tw-border-opacity,1))}.dark\:border-gray-700\/20{border-color:rgba(55,65,81,.2)}.dark\:border-green-600{--tw-border-opacity:1;border-color:rgb(22 163 74/var(--tw-border-opacity,1))}.dark\:border-green-700{--tw-border-opacity:1;border-color:rgb(21 128 61/var(--tw-border-opacity,1))}.dark\:border-red-400{--tw-border-opacity:1;border-color:rgb(248 113 113/var(--tw-border-opacity,1))}.dark\:border-red-600{--tw-border-opacity:1;border-color:rgb(220 38 38/var(--tw-border-opacity,1))}.dark\:border-red-700{--tw-border-opacity:1;border-color:rgb(185 28 28/var(--tw-border-opacity,1))}.dark\:border-red-800{--tw-border-opacity:1;border-color:rgb(153 27 27/var(--tw-border-opacity,1))}.dark\:bg-gray-700{--tw-bg-opacity:1;background-color:rgb(55 65 81/var(--tw-bg-opacity,1))}.dark\:bg-gray-800{--tw-bg-opacity:1;background-color:rgb(31 41 55/var(--tw-bg-opacity,1))}.dark\:bg-gray-800\/50{background-color:rgba(31,41,55,.5)}.dark\:bg-gray-800\/60{background-color:rgba(31,41,55,.6)}.dark\:bg-gray-900{--tw-bg-opacity:1;background-color:rgb(17 24 39/var(--tw-bg-opacity,1))}.dark\:bg-gray-900\/80{background-color:rgba(17,24,39,.8)}.dark\:bg-gray-900\/95{background-color:rgba(17,24,39,.95)}.dark\:bg-green-900{--tw-bg-opacity:1;background-color:rgb(20 83 45/var(--tw-bg-opacity,1))}.dark\:bg-red-900{--tw-bg-opacity:1;background-color:rgb(127 29 29/var(--tw-bg-opacity,1))}.dark\:bg-red-900\/20{background-color:rgba(127,29,29,.2)}.dark\:from-gray-800{--tw-gradient-from:#1f2937 var(--tw-gradient-from-position);--tw-gradient-to:rgba(31,41,55,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.dark\:from-gray-900{--tw-gradient-from:#111827 var(--tw-gradient-from-position);--tw-gradient-to:rgba(17,24,39,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.dark\:from-green-900\/20{--tw-gradient-from:rgba(20,83,45,.2) var(--tw-gradient-from-position);--tw-gradient-to:rgba(20,83,45,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.dark\:from-red-900\/20{--tw-gradient-from:rgba(127,29,29,.2) var(--tw-gradient-from-position);--tw-gradient-to:rgba(127,29,29,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.dark\:via-gray-800{--tw-gradient-to:rgba(31,41,55,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),#1f2937 var(--tw-gradient-via-position),var(--tw-gradient-to)}.dark\:to-emerald-900\/20{--tw-gradient-to:rgba(6,78,59,.2) var(--tw-gradient-to-position)}.dark\:to-gray-700{--tw-gradient-to:#374151 var(--tw-gradient-to-position)}.dark\:to-gray-800{--tw-gradient-to:#1f2937 var(--tw-gradient-to-position)}.dark\:to-gray-900{--tw-gradient-to:#111827 var(--tw-gradient-to-position)}.dark\:to-pink-900\/20{--tw-gradient-to:rgba(131,24,67,.2) var(--tw-gradient-to-position)}.dark\:text-amber-300{--tw-text-opacity:1;color:rgb(252 211 77/var(--tw-text-opacity,1))}.dark\:text-amber-400{--tw-text-opacity:1;color:rgb(251 191 36/var(--tw-text-opacity,1))}.dark\:text-blue-100{--tw-text-opacity:1;color:rgb(219 234 254/var(--tw-text-opacity,1))}.dark\:text-gray-100{--tw-text-opacity:1;color:rgb(243 244 246/var(--tw-text-opacity,1))}.dark\:text-gray-200{--tw-text-opacity:1;color:rgb(229 231 235/var(--tw-text-opacity,1))}.dark\:text-gray-300{--tw-text-opacity:1;color:rgb(209 213 219/var(--tw-text-opacity,1))}.dark\:text-gray-400{--tw-text-opacity:1;color:rgb(156 163 175/var(--tw-text-opacity,1))}.dark\:text-gray-500{--tw-text-opacity:1;color:rgb(107 114 128/var(--tw-text-opacity,1))}.dark\:text-green-200{--tw-text-opacity:1;color:rgb(187 247 208/var(--tw-text-opacity,1))}.dark\:text-green-300{--tw-text-opacity:1;color:rgb(134 239 172/var(--tw-text-opacity,1))}.dark\:text-orange-300{--tw-text-opacity:1;color:rgb(253 186 116/var(--tw-text-opacity,1))}.dark\:text-orange-400{--tw-text-opacity:1;color:rgb(251 146 60/var(--tw-text-opacity,1))}.dark\:text-primary-300{--tw-text-opacity:1;color:rgb(165 180 252/var(--tw-text-opacity,1))}.dark\:text-primary-400{--tw-text-opacity:1;color:rgb(129 140 248/var(--tw-text-opacity,1))}.dark\:text-purple-300{--tw-text-opacity:1;color:rgb(216 180 254/var(--tw-text-opacity,1))}.dark\:text-purple-400{--tw-text-opacity:1;color:rgb(192 132 252/var(--tw-text-opacity,1))}.dark\:text-red-200{--tw-text-opacity:1;color:rgb(254 202 202/var(--tw-text-opacity,1))}.dark\:text-red-300{--tw-text-opacity:1;color:rgb(252 165 165/var(--tw-text-opacity,1))}.dark\:text-red-400{--tw-text-opacity:1;color:rgb(248 113 113/var(--tw-text-opacity,1))}.dark\:text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity,1))}.dark\:placeholder-gray-400::-moz-placeholder{--tw-placeholder-opacity:1;color:rgb(156 163 175/var(--tw-placeholder-opacity,1))}.dark\:placeholder-gray-400::placeholder{--tw-placeholder-opacity:1;color:rgb(156 163 175/var(--tw-placeholder-opacity,1))}.dark\:hover\:bg-gray-700:hover{--tw-bg-opacity:1;background-color:rgb(55 65 81/var(--tw-bg-opacity,1))}.dark\:hover\:bg-gray-800:hover{--tw-bg-opacity:1;background-color:rgb(31 41 55/var(--tw-bg-opacity,1))}.dark\:hover\:text-primary-300:hover{--tw-text-opacity:1;color:rgb(165 180 252/var(--tw-text-opacity,1))}.dark\:hover\:text-primary-400:hover{--tw-text-opacity:1;color:rgb(129 140 248/var(--tw-text-opacity,1))}.dark\:hover\:text-white:hover{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity,1))}.dark\:focus\:ring-offset-gray-800:focus{--tw-ring-offset-color:#1f2937}} \ No newline at end of file diff --git a/astro-church-website/public/css/theme-light.css b/astro-church-website/public/css/theme-light.css deleted file mode 100644 index fdf3587..0000000 --- a/astro-church-website/public/css/theme-light.css +++ /dev/null @@ -1 +0,0 @@ -*,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(37,99,235,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(37,99,235,.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }/*! tailwindcss v3.4.17 | MIT License | https://tailwindcss.com*/*,:after,:before{box-sizing:border-box;border:0 solid #e5e7eb}:after,:before{--tw-content:""}:host,html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:ui-sans-serif,system-ui,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]:where(:not([hidden=until-found])){display:none}.container{width:100%}@media (min-width:640px){.container{max-width:640px}}@media (min-width:768px){.container{max-width:768px}}@media (min-width:1024px){.container{max-width:1024px}}@media (min-width:1280px){.container{max-width:1280px}}@media (min-width:1536px){.container{max-width:1536px}}.pointer-events-none{pointer-events:none}.invisible{visibility:hidden}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.inset-0{inset:0}.-right-1{right:-.25rem}.-right-2{right:-.5rem}.-top-1{top:-.25rem}.-top-2{top:-.5rem}.bottom-32{bottom:8rem}.left-0{left:0}.left-1\/4{left:25%}.left-10{left:2.5rem}.left-3{left:.75rem}.right-0{right:0}.right-20{right:5rem}.right-8{right:2rem}.top-0{top:0}.top-1\/2{top:50%}.top-20{top:5rem}.top-40{top:10rem}.top-8{top:2rem}.top-full{top:100%}.z-10{z-index:10}.z-20{z-index:20}.z-50{z-index:50}.order-1{order:1}.order-2{order:2}.order-first{order:-9999}.mx-auto{margin-left:auto;margin-right:auto}.-mt-1{margin-top:-.25rem}.mb-0{margin-bottom:0}.mb-1{margin-bottom:.25rem}.mb-12{margin-bottom:3rem}.mb-16{margin-bottom:4rem}.mb-2{margin-bottom:.5rem}.mb-3{margin-bottom:.75rem}.mb-4{margin-bottom:1rem}.mb-6{margin-bottom:1.5rem}.mb-8{margin-bottom:2rem}.ml-1{margin-left:.25rem}.ml-2{margin-left:.5rem}.mr-2{margin-right:.5rem}.mr-3{margin-right:.75rem}.mt-0\.5{margin-top:.125rem}.mt-1{margin-top:.25rem}.mt-12{margin-top:3rem}.mt-2{margin-top:.5rem}.mt-3{margin-top:.75rem}.mt-4{margin-top:1rem}.mt-6{margin-top:1.5rem}.mt-8{margin-top:2rem}.mt-auto{margin-top:auto}.line-clamp-2{-webkit-line-clamp:2}.line-clamp-2,.line-clamp-3{overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical}.line-clamp-3{-webkit-line-clamp:3}.block{display:block}.inline-block{display:inline-block}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.grid{display:grid}.hidden{display:none}.aspect-video{aspect-ratio:16/9}.h-10{height:2.5rem}.h-12{height:3rem}.h-16{height:4rem}.h-2{height:.5rem}.h-20{height:5rem}.h-24{height:6rem}.h-3{height:.75rem}.h-32{height:8rem}.h-4{height:1rem}.h-40{height:10rem}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-7{height:1.75rem}.h-8{height:2rem}.h-auto{height:auto}.h-full{height:100%}.max-h-96{max-height:24rem}.min-h-screen{min-height:100vh}.w-10{width:2.5rem}.w-12{width:3rem}.w-16{width:4rem}.w-20{width:5rem}.w-24{width:6rem}.w-3{width:.75rem}.w-32{width:8rem}.w-4{width:1rem}.w-40{width:10rem}.w-48{width:12rem}.w-5{width:1.25rem}.w-56{width:14rem}.w-6{width:1.5rem}.w-7{width:1.75rem}.w-8{width:2rem}.w-full{width:100%}.max-w-2xl{max-width:42rem}.max-w-3xl{max-width:48rem}.max-w-4xl{max-width:56rem}.max-w-6xl{max-width:72rem}.max-w-7xl{max-width:80rem}.max-w-full{max-width:100%}.max-w-md{max-width:28rem}.max-w-none{max-width:none}.max-w-sm{max-width:24rem}.flex-1{flex:1 1 0%}.flex-shrink-0{flex-shrink:0}.grow{flex-grow:1}.-translate-y-1\/2{--tw-translate-y:-50%}.-translate-y-1\/2,.translate-y-2{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-y-2{--tw-translate-y:0.5rem}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.animate-float{animation:float 6s ease-in-out infinite}@keyframes ping{75%,to{transform:scale(2);opacity:0}}.animate-ping{animation:ping 1s cubic-bezier(0,0,.2,1) infinite}@keyframes pulse{50%{opacity:.5}}.animate-pulse{animation:pulse 2s cubic-bezier(.4,0,.6,1) infinite}@keyframes spin{to{transform:rotate(1turn)}}.animate-spin{animation:spin 1s linear infinite}.cursor-pointer{cursor:pointer}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-start{align-items:flex-start}.items-center{align-items:center}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-12{gap:3rem}.gap-2{gap:.5rem}.gap-4{gap:1rem}.gap-6{gap:1.5rem}.gap-8{gap:2rem}.space-x-0>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(0px*var(--tw-space-x-reverse));margin-left:calc(0px*(1 - var(--tw-space-x-reverse)))}.space-x-1>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(.25rem*var(--tw-space-x-reverse));margin-left:calc(.25rem*(1 - var(--tw-space-x-reverse)))}.space-x-2>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(.5rem*var(--tw-space-x-reverse));margin-left:calc(.5rem*(1 - var(--tw-space-x-reverse)))}.space-x-3>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(.75rem*var(--tw-space-x-reverse));margin-left:calc(.75rem*(1 - var(--tw-space-x-reverse)))}.space-x-4>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(1rem*var(--tw-space-x-reverse));margin-left:calc(1rem*(1 - var(--tw-space-x-reverse)))}.space-x-6>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(1.5rem*var(--tw-space-x-reverse));margin-left:calc(1.5rem*(1 - var(--tw-space-x-reverse)))}.space-y-1>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.25rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.25rem*var(--tw-space-y-reverse))}.space-y-2>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.5rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.5rem*var(--tw-space-y-reverse))}.space-y-3>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(.75rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(.75rem*var(--tw-space-y-reverse))}.space-y-4>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(1rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1rem*var(--tw-space-y-reverse))}.space-y-6>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(1.5rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(1.5rem*var(--tw-space-y-reverse))}.space-y-8>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(2rem*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(2rem*var(--tw-space-y-reverse))}.divide-y>:not([hidden])~:not([hidden]){--tw-divide-y-reverse:0;border-top-width:calc(1px*(1 - var(--tw-divide-y-reverse)));border-bottom-width:calc(1px*var(--tw-divide-y-reverse))}.divide-gray-200>:not([hidden])~:not([hidden]){--tw-divide-opacity:1;border-color:rgb(229 231 235/var(--tw-divide-opacity,1))}.overflow-hidden{overflow:hidden}.scroll-smooth{scroll-behavior:smooth}.whitespace-nowrap{white-space:nowrap}.whitespace-pre-line{white-space:pre-line}.rounded{border-radius:.25rem}.rounded-2xl{border-radius:1rem}.rounded-3xl{border-radius:1.5rem}.rounded-full{border-radius:9999px}.rounded-lg{border-radius:.5rem}.rounded-md{border-radius:.375rem}.rounded-xl{border-radius:.75rem}.border{border-width:1px}.border-2{border-width:2px}.border-4{border-width:4px}.border-b{border-bottom-width:1px}.border-t{border-top-width:1px}.border-dashed{border-style:dashed}.border-gray-100{--tw-border-opacity:1;border-color:rgb(55 65 81/var(--tw-border-opacity,1))}.border-gray-200{--tw-border-opacity:1;border-color:rgb(229 231 235/var(--tw-border-opacity,1))}.border-gray-200\/20{border-color:rgba(229,231,235,.2)}.border-gray-300{--tw-border-opacity:1;border-color:rgb(209 213 219/var(--tw-border-opacity,1))}.border-green-100{--tw-border-opacity:1;border-color:rgb(220 252 231/var(--tw-border-opacity,1))}.border-green-200{--tw-border-opacity:1;border-color:rgb(187 247 208/var(--tw-border-opacity,1))}.border-green-400{--tw-border-opacity:1;border-color:rgb(74 222 128/var(--tw-border-opacity,1))}.border-orange-100{--tw-border-opacity:1;border-color:rgb(255 237 213/var(--tw-border-opacity,1))}.border-primary-100{--tw-border-opacity:1;border-color:rgb(224 231 255/var(--tw-border-opacity,1))}.border-primary-200{--tw-border-opacity:1;border-color:rgb(199 210 254/var(--tw-border-opacity,1))}.border-primary-600{--tw-border-opacity:1;border-color:rgb(79 70 229/var(--tw-border-opacity,1))}.border-purple-100{--tw-border-opacity:1;border-color:rgb(243 232 255/var(--tw-border-opacity,1))}.border-red-100{--tw-border-opacity:1;border-color:rgb(254 226 226/var(--tw-border-opacity,1))}.border-red-200{--tw-border-opacity:1;border-color:rgb(254 202 202/var(--tw-border-opacity,1))}.border-red-400{--tw-border-opacity:1;border-color:rgb(248 113 113/var(--tw-border-opacity,1))}.border-red-500{--tw-border-opacity:1;border-color:rgb(239 68 68/var(--tw-border-opacity,1))}.border-white{--tw-border-opacity:1;border-color:rgb(255 255 255/var(--tw-border-opacity,1))}.border-white\/10{border-color:hsla(0,0%,100%,.1)}.border-white\/20{border-color:hsla(0,0%,100%,.2)}.border-white\/30{border-color:hsla(0,0%,100%,.3)}.border-t-primary-600{--tw-border-opacity:1;border-top-color:rgb(79 70 229/var(--tw-border-opacity,1))}.bg-amber-500{--tw-bg-opacity:1;background-color:rgb(245 158 11/var(--tw-bg-opacity,1))}.bg-black{--tw-bg-opacity:1;background-color:rgb(0 0 0/var(--tw-bg-opacity,1))}.bg-blue-500{--tw-bg-opacity:1;background-color:rgb(37 99 235/var(--tw-bg-opacity,1))}.bg-blue-600{--tw-bg-opacity:1;background-color:rgb(29 78 216/var(--tw-bg-opacity,1))}.bg-gold-400\/20{background-color:rgba(251,191,36,.2)}.bg-gold-500{--tw-bg-opacity:1;background-color:rgb(245 158 11/var(--tw-bg-opacity,1))}.bg-gray-100{--tw-bg-opacity:1;background-color:rgb(55 65 81/var(--tw-bg-opacity,1))}.bg-gray-300{--tw-bg-opacity:1;background-color:rgb(209 213 219/var(--tw-bg-opacity,1))}.bg-gray-50{--tw-bg-opacity:1;background-color:rgb(249 250 251/var(--tw-bg-opacity,1))}.bg-gray-500{--tw-bg-opacity:1;background-color:rgb(107 114 128/var(--tw-bg-opacity,1))}.bg-gray-600{--tw-bg-opacity:1;background-color:rgb(75 85 99/var(--tw-bg-opacity,1))}.bg-gray-900{--tw-bg-opacity:1;background-color:rgb(17 24 39/var(--tw-bg-opacity,1))}.bg-green-100{--tw-bg-opacity:1;background-color:rgb(220 252 231/var(--tw-bg-opacity,1))}.bg-green-50{--tw-bg-opacity:1;background-color:rgb(240 253 244/var(--tw-bg-opacity,1))}.bg-green-500{--tw-bg-opacity:1;background-color:rgb(34 197 94/var(--tw-bg-opacity,1))}.bg-orange-50{--tw-bg-opacity:1;background-color:rgb(255 247 237/var(--tw-bg-opacity,1))}.bg-orange-600{--tw-bg-opacity:1;background-color:rgb(234 88 12/var(--tw-bg-opacity,1))}.bg-primary-100{--tw-bg-opacity:1;background-color:rgb(224 231 255/var(--tw-bg-opacity,1))}.bg-primary-300\/10{background-color:rgba(165,180,252,.1)}.bg-primary-50{--tw-bg-opacity:1;background-color:rgb(240 244 255/var(--tw-bg-opacity,1))}.bg-primary-500{--tw-bg-opacity:1;background-color:rgb(99 102 241/var(--tw-bg-opacity,1))}.bg-primary-600{--tw-bg-opacity:1;background-color:rgb(79 70 229/var(--tw-bg-opacity,1))}.bg-purple-500{--tw-bg-opacity:1;background-color:rgb(168 85 247/var(--tw-bg-opacity,1))}.bg-purple-600{--tw-bg-opacity:1;background-color:rgb(147 51 234/var(--tw-bg-opacity,1))}.bg-red-100{--tw-bg-opacity:1;background-color:rgb(254 226 226/var(--tw-bg-opacity,1))}.bg-red-50{--tw-bg-opacity:1;background-color:rgb(254 242 242/var(--tw-bg-opacity,1))}.bg-red-500{--tw-bg-opacity:1;background-color:rgb(239 68 68/var(--tw-bg-opacity,1))}.bg-red-600{--tw-bg-opacity:1;background-color:rgb(220 38 38/var(--tw-bg-opacity,1))}.bg-rose-500{--tw-bg-opacity:1;background-color:rgb(244 63 94/var(--tw-bg-opacity,1))}.bg-white{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity,1))}.bg-white\/10{background-color:hsla(0,0%,100%,.1)}.bg-white\/15{background-color:hsla(0,0%,100%,.15)}.bg-white\/20{background-color:hsla(0,0%,100%,.2)}.bg-white\/5{background-color:hsla(0,0%,100%,.05)}.bg-white\/50{background-color:hsla(0,0%,100%,.5)}.bg-white\/60{background-color:hsla(0,0%,100%,.6)}.bg-white\/80{background-color:hsla(0,0%,100%,.8)}.bg-white\/95{background-color:hsla(0,0%,100%,.95)}.bg-opacity-50{--tw-bg-opacity:0.5}.bg-gradient-to-br{background-image:linear-gradient(to bottom right,var(--tw-gradient-stops))}.bg-gradient-to-r{background-image:linear-gradient(to right,var(--tw-gradient-stops))}.from-amber-50{--tw-gradient-from:#fffbeb var(--tw-gradient-from-position);--tw-gradient-to:rgba(255,251,235,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-amber-500{--tw-gradient-from:#f59e0b var(--tw-gradient-from-position);--tw-gradient-to:rgba(245,158,11,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-blue-50{--tw-gradient-from:#eff6ff var(--tw-gradient-from-position);--tw-gradient-to:rgba(239,246,255,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-blue-500{--tw-gradient-from:#2563eb var(--tw-gradient-from-position);--tw-gradient-to:rgba(37,99,235,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-earth-900{--tw-gradient-from:#111827 var(--tw-gradient-from-position);--tw-gradient-to:rgba(17,24,39,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-gold-400{--tw-gradient-from:#fbbf24 var(--tw-gradient-from-position);--tw-gradient-to:rgba(251,191,36,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-gold-50{--tw-gradient-from:#fffbeb var(--tw-gradient-from-position);--tw-gradient-to:rgba(255,251,235,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-gold-500{--tw-gradient-from:#f59e0b var(--tw-gradient-from-position);--tw-gradient-to:rgba(245,158,11,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-gray-50{--tw-gradient-from:#f9fafb var(--tw-gradient-from-position);--tw-gradient-to:rgba(249,250,251,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-gray-700{--tw-gradient-from:#374151 var(--tw-gradient-from-position);--tw-gradient-to:rgba(55,65,81,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-green-100{--tw-gradient-from:#dcfce7 var(--tw-gradient-from-position);--tw-gradient-to:rgba(220,252,231,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-green-50{--tw-gradient-from:#f0fdf4 var(--tw-gradient-from-position);--tw-gradient-to:rgba(240,253,244,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-green-500{--tw-gradient-from:#22c55e var(--tw-gradient-from-position);--tw-gradient-to:rgba(34,197,94,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-orange-100{--tw-gradient-from:#ffedd5 var(--tw-gradient-from-position);--tw-gradient-to:rgba(255,237,213,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-orange-400{--tw-gradient-from:#fb923c var(--tw-gradient-from-position);--tw-gradient-to:rgba(251,146,60,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-orange-50{--tw-gradient-from:#fff7ed var(--tw-gradient-from-position);--tw-gradient-to:rgba(255,247,237,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-orange-500{--tw-gradient-from:#f97316 var(--tw-gradient-from-position);--tw-gradient-to:rgba(249,115,22,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-orange-900{--tw-gradient-from:#7c2d12 var(--tw-gradient-from-position);--tw-gradient-to:rgba(124,45,18,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-primary-100{--tw-gradient-from:#e0e7ff var(--tw-gradient-from-position);--tw-gradient-to:rgba(224,231,255,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-primary-400{--tw-gradient-from:#818cf8 var(--tw-gradient-from-position);--tw-gradient-to:rgba(129,140,248,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-primary-50{--tw-gradient-from:#f0f4ff var(--tw-gradient-from-position);--tw-gradient-to:rgba(240,244,255,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-primary-500{--tw-gradient-from:#6366f1 var(--tw-gradient-from-position);--tw-gradient-to:rgba(99,102,241,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-primary-600{--tw-gradient-from:#4f46e5 var(--tw-gradient-from-position);--tw-gradient-to:rgba(79,70,229,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-primary-900{--tw-gradient-from:#312e81 var(--tw-gradient-from-position);--tw-gradient-to:rgba(49,46,129,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-purple-100{--tw-gradient-from:#f3e8ff var(--tw-gradient-from-position);--tw-gradient-to:rgba(243,232,255,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-purple-50{--tw-gradient-from:#faf5ff var(--tw-gradient-from-position);--tw-gradient-to:rgba(250,245,255,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-purple-500{--tw-gradient-from:#a855f7 var(--tw-gradient-from-position);--tw-gradient-to:rgba(168,85,247,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-red-100{--tw-gradient-from:#fee2e2 var(--tw-gradient-from-position);--tw-gradient-to:hsla(0,93%,94%,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-red-400{--tw-gradient-from:#f87171 var(--tw-gradient-from-position);--tw-gradient-to:hsla(0,91%,71%,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-red-50{--tw-gradient-from:#fef2f2 var(--tw-gradient-from-position);--tw-gradient-to:hsla(0,86%,97%,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-red-500{--tw-gradient-from:#ef4444 var(--tw-gradient-from-position);--tw-gradient-to:rgba(239,68,68,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-red-900{--tw-gradient-from:#7f1d1d var(--tw-gradient-from-position);--tw-gradient-to:rgba(127,29,29,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-rose-50{--tw-gradient-from:#fff1f2 var(--tw-gradient-from-position);--tw-gradient-to:rgba(255,241,242,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-slate-50{--tw-gradient-from:#f8fafc var(--tw-gradient-from-position);--tw-gradient-to:rgba(248,250,252,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-violet-50{--tw-gradient-from:#f5f3ff var(--tw-gradient-from-position);--tw-gradient-to:rgba(245,243,255,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.from-yellow-500{--tw-gradient-from:#eab308 var(--tw-gradient-from-position);--tw-gradient-to:rgba(234,179,8,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.via-blue-50{--tw-gradient-to:rgba(239,246,255,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),#eff6ff var(--tw-gradient-via-position),var(--tw-gradient-to)}.via-blue-600{--tw-gradient-to:rgba(29,78,216,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),#1d4ed8 var(--tw-gradient-via-position),var(--tw-gradient-to)}.via-earth-800{--tw-gradient-to:rgba(31,41,55,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),#1f2937 var(--tw-gradient-via-position),var(--tw-gradient-to)}.via-orange-800{--tw-gradient-to:rgba(154,52,18,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),#9a3412 var(--tw-gradient-via-position),var(--tw-gradient-to)}.via-primary-800{--tw-gradient-to:rgba(55,48,163,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),#3730a3 var(--tw-gradient-via-position),var(--tw-gradient-to)}.via-purple-600{--tw-gradient-to:rgba(147,51,234,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),#9333ea var(--tw-gradient-via-position),var(--tw-gradient-to)}.via-red-800{--tw-gradient-to:rgba(153,27,27,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),#991b1b var(--tw-gradient-via-position),var(--tw-gradient-to)}.to-amber-500{--tw-gradient-to:#f59e0b var(--tw-gradient-to-position)}.to-black{--tw-gradient-to:#000 var(--tw-gradient-to-position)}.to-blue-100{--tw-gradient-to:#1e40af var(--tw-gradient-to-position)}.to-blue-50{--tw-gradient-to:#eff6ff var(--tw-gradient-to-position)}.to-blue-600{--tw-gradient-to:#1d4ed8 var(--tw-gradient-to-position)}.to-blue-900{--tw-gradient-to:#1e3a8a var(--tw-gradient-to-position)}.to-earth-900{--tw-gradient-to:#111827 var(--tw-gradient-to-position)}.to-emerald-50{--tw-gradient-to:#ecfdf5 var(--tw-gradient-to-position)}.to-gold-600{--tw-gradient-to:#d97706 var(--tw-gradient-to-position)}.to-green-600{--tw-gradient-to:#16a34a var(--tw-gradient-to-position)}.to-indigo-50{--tw-gradient-to:#eef2ff var(--tw-gradient-to-position)}.to-orange-50{--tw-gradient-to:#fff7ed var(--tw-gradient-to-position)}.to-orange-500{--tw-gradient-to:#f97316 var(--tw-gradient-to-position)}.to-orange-600{--tw-gradient-to:#ea580c var(--tw-gradient-to-position)}.to-pink-100{--tw-gradient-to:#fce7f3 var(--tw-gradient-to-position)}.to-pink-50{--tw-gradient-to:#fdf2f8 var(--tw-gradient-to-position)}.to-pink-500{--tw-gradient-to:#ec4899 var(--tw-gradient-to-position)}.to-pink-900{--tw-gradient-to:#831843 var(--tw-gradient-to-position)}.to-primary-600{--tw-gradient-to:#4f46e5 var(--tw-gradient-to-position)}.to-primary-700{--tw-gradient-to:#4338ca var(--tw-gradient-to-position)}.to-purple-100{--tw-gradient-to:#f3e8ff var(--tw-gradient-to-position)}.to-purple-50{--tw-gradient-to:#faf5ff var(--tw-gradient-to-position)}.to-purple-600{--tw-gradient-to:#9333ea var(--tw-gradient-to-position)}.to-red-100{--tw-gradient-to:#fee2e2 var(--tw-gradient-to-position)}.to-red-50{--tw-gradient-to:#fef2f2 var(--tw-gradient-to-position)}.to-red-600{--tw-gradient-to:#dc2626 var(--tw-gradient-to-position)}.to-red-900{--tw-gradient-to:#7f1d1d var(--tw-gradient-to-position)}.to-yellow-600{--tw-gradient-to:#ca8a04 var(--tw-gradient-to-position)}.fill-gold-400{fill:#fbbf24}.object-contain{-o-object-fit:contain;object-fit:contain}.object-cover{-o-object-fit:cover;object-fit:cover}.p-2{padding:.5rem}.p-3{padding:.75rem}.p-4{padding:1rem}.p-6{padding:1.5rem}.p-8{padding:2rem}.px-16{padding-left:4rem;padding-right:4rem}.px-2{padding-left:.5rem;padding-right:.5rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.px-8{padding-left:2rem;padding-right:2rem}.py-1{padding-top:.25rem;padding-bottom:.25rem}.py-12{padding-top:3rem;padding-bottom:3rem}.py-16{padding-top:4rem;padding-bottom:4rem}.py-2{padding-top:.5rem;padding-bottom:.5rem}.py-20{padding-top:5rem;padding-bottom:5rem}.py-24{padding-top:6rem;padding-bottom:6rem}.py-3{padding-top:.75rem;padding-bottom:.75rem}.py-4{padding-top:1rem;padding-bottom:1rem}.py-6{padding-top:1.5rem;padding-bottom:1.5rem}.py-8{padding-top:2rem;padding-bottom:2rem}.pb-2{padding-bottom:.5rem}.pb-20{padding-bottom:5rem}.pb-24{padding-bottom:6rem}.pb-6{padding-bottom:1.5rem}.pl-10{padding-left:2.5rem}.pr-4{padding-right:1rem}.pt-20{padding-top:5rem}.pt-5{padding-top:1.25rem}.pt-8{padding-top:2rem}.text-left{text-align:left}.text-center{text-align:center}.text-right{text-align:right}.font-body{font-family:Inter,system-ui,sans-serif}.text-2xl{font-size:1.5rem;line-height:2rem}.text-3xl{font-size:1.875rem;line-height:2.25rem}.text-4xl{font-size:2.25rem;line-height:2.5rem}.text-5xl{font-size:3rem;line-height:1}.text-lg{font-size:1.125rem;line-height:1.75rem}.text-sm{font-size:.875rem;line-height:1.25rem}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-xs{font-size:.75rem;line-height:1rem}.font-bold{font-weight:700}.font-medium{font-weight:500}.font-semibold{font-weight:600}.uppercase{text-transform:uppercase}.lowercase{text-transform:lowercase}.italic{font-style:italic}.leading-relaxed{line-height:1.625}.leading-tight{line-height:1.25}.tracking-wide{letter-spacing:.025em}.tracking-wider{letter-spacing:.05em}.text-amber-600{--tw-text-opacity:1;color:rgb(217 119 6/var(--tw-text-opacity,1))}.text-amber-700{--tw-text-opacity:1;color:rgb(180 83 9/var(--tw-text-opacity,1))}.text-black{--tw-text-opacity:1;color:rgb(0 0 0/var(--tw-text-opacity,1))}.text-blue-100{--tw-text-opacity:1;color:rgb(30 64 175/var(--tw-text-opacity,1))}.text-blue-200{--tw-text-opacity:1;color:rgb(147 197 253/var(--tw-text-opacity,1))}.text-blue-500{--tw-text-opacity:1;color:rgb(37 99 235/var(--tw-text-opacity,1))}.text-blue-600{--tw-text-opacity:1;color:rgb(29 78 216/var(--tw-text-opacity,1))}.text-gold-300{--tw-text-opacity:1;color:rgb(252 211 77/var(--tw-text-opacity,1))}.text-gold-400{--tw-text-opacity:1;color:rgb(251 191 36/var(--tw-text-opacity,1))}.text-gray-200{--tw-text-opacity:1;color:rgb(229 231 235/var(--tw-text-opacity,1))}.text-gray-300{--tw-text-opacity:1;color:rgb(209 213 219/var(--tw-text-opacity,1))}.text-gray-400{--tw-text-opacity:1;color:rgb(156 163 175/var(--tw-text-opacity,1))}.text-gray-500{--tw-text-opacity:1;color:rgb(107 114 128/var(--tw-text-opacity,1))}.text-gray-600{--tw-text-opacity:1;color:rgb(75 85 99/var(--tw-text-opacity,1))}.text-gray-700{--tw-text-opacity:1;color:rgb(55 65 81/var(--tw-text-opacity,1))}.text-gray-800{--tw-text-opacity:1;color:rgb(31 41 55/var(--tw-text-opacity,1))}.text-gray-900{--tw-text-opacity:1;color:rgb(17 24 39/var(--tw-text-opacity,1))}.text-green-500{--tw-text-opacity:1;color:rgb(34 197 94/var(--tw-text-opacity,1))}.text-green-600{--tw-text-opacity:1;color:rgb(22 163 74/var(--tw-text-opacity,1))}.text-green-700{--tw-text-opacity:1;color:rgb(21 128 61/var(--tw-text-opacity,1))}.text-green-800{--tw-text-opacity:1;color:rgb(22 101 52/var(--tw-text-opacity,1))}.text-indigo-600{--tw-text-opacity:1;color:rgb(79 70 229/var(--tw-text-opacity,1))}.text-orange-100{--tw-text-opacity:1;color:rgb(255 237 213/var(--tw-text-opacity,1))}.text-orange-300{--tw-text-opacity:1;color:rgb(253 186 116/var(--tw-text-opacity,1))}.text-orange-500{--tw-text-opacity:1;color:rgb(249 115 22/var(--tw-text-opacity,1))}.text-orange-600{--tw-text-opacity:1;color:rgb(234 88 12/var(--tw-text-opacity,1))}.text-orange-700{--tw-text-opacity:1;color:rgb(194 65 12/var(--tw-text-opacity,1))}.text-primary-300{--tw-text-opacity:1;color:rgb(165 180 252/var(--tw-text-opacity,1))}.text-primary-400{--tw-text-opacity:1;color:rgb(129 140 248/var(--tw-text-opacity,1))}.text-primary-500{--tw-text-opacity:1;color:rgb(99 102 241/var(--tw-text-opacity,1))}.text-primary-600{--tw-text-opacity:1;color:rgb(79 70 229/var(--tw-text-opacity,1))}.text-primary-700{--tw-text-opacity:1;color:rgb(67 56 202/var(--tw-text-opacity,1))}.text-purple-500{--tw-text-opacity:1;color:rgb(168 85 247/var(--tw-text-opacity,1))}.text-purple-600{--tw-text-opacity:1;color:rgb(147 51 234/var(--tw-text-opacity,1))}.text-purple-700{--tw-text-opacity:1;color:rgb(126 34 206/var(--tw-text-opacity,1))}.text-red-100{--tw-text-opacity:1;color:rgb(254 226 226/var(--tw-text-opacity,1))}.text-red-300{--tw-text-opacity:1;color:rgb(252 165 165/var(--tw-text-opacity,1))}.text-red-500{--tw-text-opacity:1;color:rgb(239 68 68/var(--tw-text-opacity,1))}.text-red-600{--tw-text-opacity:1;color:rgb(220 38 38/var(--tw-text-opacity,1))}.text-red-700{--tw-text-opacity:1;color:rgb(185 28 28/var(--tw-text-opacity,1))}.text-red-800{--tw-text-opacity:1;color:rgb(153 27 27/var(--tw-text-opacity,1))}.text-violet-600{--tw-text-opacity:1;color:rgb(124 58 237/var(--tw-text-opacity,1))}.text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity,1))}.antialiased{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.placeholder-gray-500::-moz-placeholder{--tw-placeholder-opacity:1;color:rgb(107 114 128/var(--tw-placeholder-opacity,1))}.placeholder-gray-500::placeholder{--tw-placeholder-opacity:1;color:rgb(107 114 128/var(--tw-placeholder-opacity,1))}.opacity-0{opacity:0}.opacity-30{opacity:.3}.opacity-50{opacity:.5}.opacity-80{opacity:.8}.shadow-2xl{--tw-shadow:0 25px 50px -12px rgba(0,0,0,.25);--tw-shadow-colored:0 25px 50px -12px var(--tw-shadow-color)}.shadow-2xl,.shadow-lg{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-lg{--tw-shadow:0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -4px rgba(0,0,0,.1);--tw-shadow-colored:0 10px 15px -3px var(--tw-shadow-color),0 4px 6px -4px var(--tw-shadow-color)}.shadow-md{--tw-shadow:0 4px 6px -1px rgba(0,0,0,.1),0 2px 4px -2px rgba(0,0,0,.1);--tw-shadow-colored:0 4px 6px -1px var(--tw-shadow-color),0 2px 4px -2px var(--tw-shadow-color)}.shadow-md,.shadow-medium{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-medium{--tw-shadow:0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -2px rgba(0,0,0,.05);--tw-shadow-colored:0 10px 15px -3px var(--tw-shadow-color),0 4px 6px -2px var(--tw-shadow-color)}.shadow-sm{--tw-shadow:0 1px 2px 0 rgba(0,0,0,.05);--tw-shadow-colored:0 1px 2px 0 var(--tw-shadow-color)}.shadow-sm,.shadow-xl{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.shadow-xl{--tw-shadow:0 20px 25px -5px rgba(0,0,0,.1),0 8px 10px -6px rgba(0,0,0,.1);--tw-shadow-colored:0 20px 25px -5px var(--tw-shadow-color),0 8px 10px -6px var(--tw-shadow-color)}.blur{--tw-blur:blur(8px)}.blur,.blur-2xl{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.blur-2xl{--tw-blur:blur(40px)}.blur-lg{--tw-blur:blur(16px)}.blur-lg,.blur-xl{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.blur-xl{--tw-blur:blur(24px)}.filter{filter:var(--tw-blur) var(--tw-brightness) var(--tw-contrast) var(--tw-grayscale) var(--tw-hue-rotate) var(--tw-invert) var(--tw-saturate) var(--tw-sepia) var(--tw-drop-shadow)}.backdrop-blur-md{--tw-backdrop-blur:blur(12px)}.backdrop-blur-md,.backdrop-blur-sm{backdrop-filter:var(--tw-backdrop-blur) var(--tw-backdrop-brightness) var(--tw-backdrop-contrast) var(--tw-backdrop-grayscale) var(--tw-backdrop-hue-rotate) var(--tw-backdrop-invert) var(--tw-backdrop-opacity) var(--tw-backdrop-saturate) var(--tw-backdrop-sepia)}.backdrop-blur-sm{--tw-backdrop-blur:blur(4px)}.transition-all{transition-property:all;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-colors{transition-property:color,background-color,border-color,text-decoration-color,fill,stroke;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-shadow{transition-property:box-shadow;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.transition-transform{transition-property:transform;transition-timing-function:cubic-bezier(.4,0,.2,1);transition-duration:.15s}.duration-200{transition-duration:.2s}.duration-300{transition-duration:.3s}.duration-500{transition-duration:.5s}::-webkit-scrollbar{width:6px}::-webkit-scrollbar-track{background:#f1f5f9}::-webkit-scrollbar-thumb{background:#6366f1;border-radius:3px}::-webkit-scrollbar-thumb:hover{background:#4f46e5}*{transition:all .2s ease-in-out}:focus-visible{outline:2px solid #6366f1;outline-offset:2px}.bg-divine-gradient{background:linear-gradient(135deg,#667eea,#764ba2)}.bg-heavenly-gradient{background:linear-gradient(135deg,#6366f1,#8b5cf6 50%,#a855f7)}@media (prefers-color-scheme:light){.bg-heavenly-gradient{background:linear-gradient(135deg,#4338ca,#7c3aed 50%,#9333ea)}}.bg-sacred-gradient{background:linear-gradient(135deg,#f59e0b,#d97706)}.text-divine-gradient{background:linear-gradient(135deg,#6366f1,#8b5cf6);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text}@media (prefers-color-scheme:light){.text-divine-gradient{background:linear-gradient(135deg,#4338ca,#7c3aed);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text}}.text-golden-gradient{background:linear-gradient(135deg,#f59e0b,#d97706);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text}@media (prefers-color-scheme:dark){::-webkit-scrollbar-track{background:#1e293b}}.glass{background:hsla(0,0%,100%,.25);border:1px solid hsla(0,0%,100%,.18)}.glass,.glass-dark{backdrop-filter:blur(10px)}.glass-dark{background:rgba(0,0,0,.25);border:1px solid hsla(0,0%,100%,.1)}@keyframes fadeInUp{0%{opacity:0;transform:translateY(30px)}to{opacity:1;transform:translateY(0)}}@keyframes float{0%,to{transform:translateY(0)}50%{transform:translateY(-10px)}}.animate-fade-in-up{animation:fadeInUp .6s ease-out}.animate-float{animation:float 3s ease-in-out infinite}.loading-shimmer{background:linear-gradient(90deg,#f0f0f0 25%,#e0e0e0 50%,#f0f0f0 75%);background-size:200% 100%;animation:shimmer 1.5s infinite}@keyframes shimmer{0%{background-position:-200% 0}to{background-position:200% 0}}.first\:rounded-t-xl:first-child{border-top-left-radius:.75rem;border-top-right-radius:.75rem}.hover\:-translate-y-1:hover{--tw-translate-y:-0.25rem}.hover\:-translate-y-1:hover,.hover\:-translate-y-2:hover{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.hover\:-translate-y-2:hover{--tw-translate-y:-0.5rem}.hover\:scale-105:hover{--tw-scale-x:1.05;--tw-scale-y:1.05}.hover\:scale-105:hover,.hover\:scale-110:hover{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.hover\:scale-110:hover{--tw-scale-x:1.1;--tw-scale-y:1.1}.hover\:transform:hover{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.hover\:border-primary-500:hover{--tw-border-opacity:1;border-color:rgb(99 102 241/var(--tw-border-opacity,1))}.hover\:border-white\/20:hover{border-color:hsla(0,0%,100%,.2)}.hover\:border-white\/40:hover{border-color:hsla(0,0%,100%,.4)}.hover\:bg-blue-600:hover{--tw-bg-opacity:1;background-color:rgb(29 78 216/var(--tw-bg-opacity,1))}.hover\:bg-gold-400:hover{--tw-bg-opacity:1;background-color:rgb(251 191 36/var(--tw-bg-opacity,1))}.hover\:bg-gold-600:hover{--tw-bg-opacity:1;background-color:rgb(217 119 6/var(--tw-bg-opacity,1))}.hover\:bg-gray-100:hover{--tw-bg-opacity:1;background-color:rgb(55 65 81/var(--tw-bg-opacity,1))}.hover\:bg-gray-200:hover{--tw-bg-opacity:1;background-color:rgb(229 231 235/var(--tw-bg-opacity,1))}.hover\:bg-gray-400:hover{--tw-bg-opacity:1;background-color:rgb(156 163 175/var(--tw-bg-opacity,1))}.hover\:bg-gray-50:hover{--tw-bg-opacity:1;background-color:rgb(249 250 251/var(--tw-bg-opacity,1))}.hover\:bg-gray-600:hover{--tw-bg-opacity:1;background-color:rgb(75 85 99/var(--tw-bg-opacity,1))}.hover\:bg-gray-700:hover{--tw-bg-opacity:1;background-color:rgb(55 65 81/var(--tw-bg-opacity,1))}.hover\:bg-green-600:hover{--tw-bg-opacity:1;background-color:rgb(22 163 74/var(--tw-bg-opacity,1))}.hover\:bg-orange-700:hover{--tw-bg-opacity:1;background-color:rgb(194 65 12/var(--tw-bg-opacity,1))}.hover\:bg-primary-500:hover{--tw-bg-opacity:1;background-color:rgb(99 102 241/var(--tw-bg-opacity,1))}.hover\:bg-primary-600:hover{--tw-bg-opacity:1;background-color:rgb(79 70 229/var(--tw-bg-opacity,1))}.hover\:bg-primary-700:hover{--tw-bg-opacity:1;background-color:rgb(67 56 202/var(--tw-bg-opacity,1))}.hover\:bg-purple-600:hover{--tw-bg-opacity:1;background-color:rgb(147 51 234/var(--tw-bg-opacity,1))}.hover\:bg-purple-700:hover{--tw-bg-opacity:1;background-color:rgb(126 34 206/var(--tw-bg-opacity,1))}.hover\:bg-red-600:hover{--tw-bg-opacity:1;background-color:rgb(220 38 38/var(--tw-bg-opacity,1))}.hover\:bg-red-700:hover{--tw-bg-opacity:1;background-color:rgb(185 28 28/var(--tw-bg-opacity,1))}.hover\:bg-white:hover{--tw-bg-opacity:1;background-color:rgb(255 255 255/var(--tw-bg-opacity,1))}.hover\:bg-white\/10:hover{background-color:hsla(0,0%,100%,.1)}.hover\:bg-white\/30:hover{background-color:hsla(0,0%,100%,.3)}.hover\:from-primary-700:hover{--tw-gradient-from:#4338ca var(--tw-gradient-from-position);--tw-gradient-to:rgba(67,56,202,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.hover\:via-blue-700:hover{--tw-gradient-to:rgba(30,64,175,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),#1e40af var(--tw-gradient-via-position),var(--tw-gradient-to)}.hover\:to-purple-700:hover{--tw-gradient-to:#7e22ce var(--tw-gradient-to-position)}.hover\:text-blue-900:hover{--tw-text-opacity:1;color:rgb(30 58 138/var(--tw-text-opacity,1))}.hover\:text-gray-600:hover{--tw-text-opacity:1;color:rgb(75 85 99/var(--tw-text-opacity,1))}.hover\:text-indigo-900:hover{--tw-text-opacity:1;color:rgb(49 46 129/var(--tw-text-opacity,1))}.hover\:text-primary-600:hover{--tw-text-opacity:1;color:rgb(79 70 229/var(--tw-text-opacity,1))}.hover\:text-primary-700:hover{--tw-text-opacity:1;color:rgb(67 56 202/var(--tw-text-opacity,1))}.hover\:text-primary-900:hover{--tw-text-opacity:1;color:rgb(49 46 129/var(--tw-text-opacity,1))}.hover\:text-red-900:hover{--tw-text-opacity:1;color:rgb(127 29 29/var(--tw-text-opacity,1))}.hover\:text-white:hover{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity,1))}.hover\:underline:hover{text-decoration-line:underline}.hover\:shadow-2xl:hover{--tw-shadow:0 25px 50px -12px rgba(0,0,0,.25);--tw-shadow-colored:0 25px 50px -12px var(--tw-shadow-color)}.hover\:shadow-2xl:hover,.hover\:shadow-divine:hover{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.hover\:shadow-divine:hover{--tw-shadow:0 0 50px rgba(99,102,241,.3);--tw-shadow-colored:0 0 50px var(--tw-shadow-color)}.hover\:shadow-large:hover{--tw-shadow:0 25px 50px -12px rgba(0,0,0,.25);--tw-shadow-colored:0 25px 50px -12px var(--tw-shadow-color)}.hover\:shadow-large:hover,.hover\:shadow-lg:hover{box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.hover\:shadow-lg:hover{--tw-shadow:0 10px 15px -3px rgba(0,0,0,.1),0 4px 6px -4px rgba(0,0,0,.1);--tw-shadow-colored:0 10px 15px -3px var(--tw-shadow-color),0 4px 6px -4px var(--tw-shadow-color)}.hover\:shadow-xl:hover{--tw-shadow:0 20px 25px -5px rgba(0,0,0,.1),0 8px 10px -6px rgba(0,0,0,.1);--tw-shadow-colored:0 20px 25px -5px var(--tw-shadow-color),0 8px 10px -6px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.focus\:border-blue-500:focus{--tw-border-opacity:1;border-color:rgb(37 99 235/var(--tw-border-opacity,1))}.focus\:border-primary-500:focus{--tw-border-opacity:1;border-color:rgb(99 102 241/var(--tw-border-opacity,1))}.focus\:border-transparent:focus{border-color:transparent}.focus\:ring-2:focus{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow,0 0 #0000)}.focus\:ring-blue-500:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(37 99 235/var(--tw-ring-opacity,1))}.focus\:ring-primary-500:focus{--tw-ring-opacity:1;--tw-ring-color:rgb(99 102 241/var(--tw-ring-opacity,1))}.focus\:ring-offset-2:focus{--tw-ring-offset-width:2px}.focus\:ring-offset-white:focus{--tw-ring-offset-color:#fff}.disabled\:transform-none:disabled{transform:none}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-50:disabled{opacity:.5}.group:hover .group-hover\:visible{visibility:visible}.group:hover .group-hover\:translate-y-0{--tw-translate-y:0px}.group:hover .group-hover\:rotate-12,.group:hover .group-hover\:translate-y-0{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.group:hover .group-hover\:rotate-12{--tw-rotate:12deg}.group:hover .group-hover\:rotate-180{--tw-rotate:180deg}.group:hover .group-hover\:rotate-180,.group:hover .group-hover\:scale-105{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.group:hover .group-hover\:scale-105{--tw-scale-x:1.05;--tw-scale-y:1.05}.group:hover .group-hover\:scale-110{--tw-scale-x:1.1;--tw-scale-y:1.1;transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.group:hover .group-hover\:opacity-100{opacity:1}.group:hover .group-hover\:shadow-xl{--tw-shadow:0 20px 25px -5px rgba(0,0,0,.1),0 8px 10px -6px rgba(0,0,0,.1);--tw-shadow-colored:0 20px 25px -5px var(--tw-shadow-color),0 8px 10px -6px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}@media (min-width:640px){.sm\:block{display:block}.sm\:inline-flex{display:inline-flex}.sm\:flex-row{flex-direction:row}.sm\:space-x-8>:not([hidden])~:not([hidden]){--tw-space-x-reverse:0;margin-right:calc(2rem*var(--tw-space-x-reverse));margin-left:calc(2rem*(1 - var(--tw-space-x-reverse)))}.sm\:space-y-0>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(0px*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(0px*var(--tw-space-y-reverse))}.sm\:px-6{padding-left:1.5rem;padding-right:1.5rem}}@media (min-width:768px){.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.md\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.md\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.md\:flex-row{flex-direction:row}.md\:space-y-0>:not([hidden])~:not([hidden]){--tw-space-y-reverse:0;margin-top:calc(0px*(1 - var(--tw-space-y-reverse)));margin-bottom:calc(0px*var(--tw-space-y-reverse))}}@media (min-width:1024px){.lg\:order-1{order:1}.lg\:order-2{order:2}.lg\:order-last{order:9999}.lg\:col-span-2{grid-column:span 2/span 2}.lg\:mx-0{margin-left:0;margin-right:0}.lg\:flex{display:flex}.lg\:hidden{display:none}.lg\:h-20{height:5rem}.lg\:w-80{width:20rem}.lg\:flex-shrink-0{flex-shrink:0}.lg\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.lg\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.lg\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.lg\:flex-row{flex-direction:row}.lg\:justify-start{justify-content:flex-start}.lg\:gap-12{gap:3rem}.lg\:p-12{padding:3rem}.lg\:px-8{padding-left:2rem;padding-right:2rem}.lg\:text-left{text-align:left}.lg\:text-2xl{font-size:1.5rem;line-height:2rem}.lg\:text-3xl{font-size:1.875rem;line-height:2.25rem}.lg\:text-5xl{font-size:3rem;line-height:1}.lg\:text-6xl{font-size:3.75rem;line-height:1}.lg\:text-7xl{font-size:4.5rem;line-height:1}.lg\:text-xl{font-size:1.25rem;line-height:1.75rem}}@media (min-width:1280px){.xl\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}}@media (prefers-color-scheme:dark){.dark\:border-gray-600{--tw-border-opacity:1;border-color:rgb(75 85 99/var(--tw-border-opacity,1))}.dark\:border-gray-700{--tw-border-opacity:1;border-color:rgb(55 65 81/var(--tw-border-opacity,1))}.dark\:border-gray-700\/20{border-color:rgba(55,65,81,.2)}.dark\:border-green-600{--tw-border-opacity:1;border-color:rgb(22 163 74/var(--tw-border-opacity,1))}.dark\:border-green-700{--tw-border-opacity:1;border-color:rgb(21 128 61/var(--tw-border-opacity,1))}.dark\:border-red-400{--tw-border-opacity:1;border-color:rgb(248 113 113/var(--tw-border-opacity,1))}.dark\:border-red-600{--tw-border-opacity:1;border-color:rgb(220 38 38/var(--tw-border-opacity,1))}.dark\:border-red-700{--tw-border-opacity:1;border-color:rgb(185 28 28/var(--tw-border-opacity,1))}.dark\:border-red-800{--tw-border-opacity:1;border-color:rgb(153 27 27/var(--tw-border-opacity,1))}.dark\:bg-gray-700{--tw-bg-opacity:1;background-color:rgb(55 65 81/var(--tw-bg-opacity,1))}.dark\:bg-gray-800{--tw-bg-opacity:1;background-color:rgb(31 41 55/var(--tw-bg-opacity,1))}.dark\:bg-gray-800\/50{background-color:rgba(31,41,55,.5)}.dark\:bg-gray-800\/60{background-color:rgba(31,41,55,.6)}.dark\:bg-gray-900{--tw-bg-opacity:1;background-color:rgb(17 24 39/var(--tw-bg-opacity,1))}.dark\:bg-gray-900\/80{background-color:rgba(17,24,39,.8)}.dark\:bg-gray-900\/95{background-color:rgba(17,24,39,.95)}.dark\:bg-green-900{--tw-bg-opacity:1;background-color:rgb(20 83 45/var(--tw-bg-opacity,1))}.dark\:bg-red-900{--tw-bg-opacity:1;background-color:rgb(127 29 29/var(--tw-bg-opacity,1))}.dark\:bg-red-900\/20{background-color:rgba(127,29,29,.2)}.dark\:from-gray-800{--tw-gradient-from:#1f2937 var(--tw-gradient-from-position);--tw-gradient-to:rgba(31,41,55,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.dark\:from-gray-900{--tw-gradient-from:#111827 var(--tw-gradient-from-position);--tw-gradient-to:rgba(17,24,39,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.dark\:from-green-900\/20{--tw-gradient-from:rgba(20,83,45,.2) var(--tw-gradient-from-position);--tw-gradient-to:rgba(20,83,45,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.dark\:from-red-900\/20{--tw-gradient-from:rgba(127,29,29,.2) var(--tw-gradient-from-position);--tw-gradient-to:rgba(127,29,29,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),var(--tw-gradient-to)}.dark\:via-gray-800{--tw-gradient-to:rgba(31,41,55,0) var(--tw-gradient-to-position);--tw-gradient-stops:var(--tw-gradient-from),#1f2937 var(--tw-gradient-via-position),var(--tw-gradient-to)}.dark\:to-emerald-900\/20{--tw-gradient-to:rgba(6,78,59,.2) var(--tw-gradient-to-position)}.dark\:to-gray-700{--tw-gradient-to:#374151 var(--tw-gradient-to-position)}.dark\:to-gray-800{--tw-gradient-to:#1f2937 var(--tw-gradient-to-position)}.dark\:to-gray-900{--tw-gradient-to:#111827 var(--tw-gradient-to-position)}.dark\:to-pink-900\/20{--tw-gradient-to:rgba(131,24,67,.2) var(--tw-gradient-to-position)}.dark\:text-amber-300{--tw-text-opacity:1;color:rgb(252 211 77/var(--tw-text-opacity,1))}.dark\:text-amber-400{--tw-text-opacity:1;color:rgb(251 191 36/var(--tw-text-opacity,1))}.dark\:text-blue-100{--tw-text-opacity:1;color:rgb(30 64 175/var(--tw-text-opacity,1))}.dark\:text-gray-100{--tw-text-opacity:1;color:rgb(55 65 81/var(--tw-text-opacity,1))}.dark\:text-gray-200{--tw-text-opacity:1;color:rgb(229 231 235/var(--tw-text-opacity,1))}.dark\:text-gray-300{--tw-text-opacity:1;color:rgb(209 213 219/var(--tw-text-opacity,1))}.dark\:text-gray-400{--tw-text-opacity:1;color:rgb(156 163 175/var(--tw-text-opacity,1))}.dark\:text-gray-500{--tw-text-opacity:1;color:rgb(107 114 128/var(--tw-text-opacity,1))}.dark\:text-green-200{--tw-text-opacity:1;color:rgb(187 247 208/var(--tw-text-opacity,1))}.dark\:text-green-300{--tw-text-opacity:1;color:rgb(134 239 172/var(--tw-text-opacity,1))}.dark\:text-orange-300{--tw-text-opacity:1;color:rgb(253 186 116/var(--tw-text-opacity,1))}.dark\:text-orange-400{--tw-text-opacity:1;color:rgb(251 146 60/var(--tw-text-opacity,1))}.dark\:text-primary-300{--tw-text-opacity:1;color:rgb(165 180 252/var(--tw-text-opacity,1))}.dark\:text-primary-400{--tw-text-opacity:1;color:rgb(129 140 248/var(--tw-text-opacity,1))}.dark\:text-purple-300{--tw-text-opacity:1;color:rgb(216 180 254/var(--tw-text-opacity,1))}.dark\:text-purple-400{--tw-text-opacity:1;color:rgb(192 132 252/var(--tw-text-opacity,1))}.dark\:text-red-200{--tw-text-opacity:1;color:rgb(254 202 202/var(--tw-text-opacity,1))}.dark\:text-red-300{--tw-text-opacity:1;color:rgb(252 165 165/var(--tw-text-opacity,1))}.dark\:text-red-400{--tw-text-opacity:1;color:rgb(248 113 113/var(--tw-text-opacity,1))}.dark\:text-white{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity,1))}.dark\:placeholder-gray-400::-moz-placeholder{--tw-placeholder-opacity:1;color:rgb(156 163 175/var(--tw-placeholder-opacity,1))}.dark\:placeholder-gray-400::placeholder{--tw-placeholder-opacity:1;color:rgb(156 163 175/var(--tw-placeholder-opacity,1))}.dark\:hover\:bg-gray-700:hover{--tw-bg-opacity:1;background-color:rgb(55 65 81/var(--tw-bg-opacity,1))}.dark\:hover\:bg-gray-800:hover{--tw-bg-opacity:1;background-color:rgb(31 41 55/var(--tw-bg-opacity,1))}.dark\:hover\:text-primary-300:hover{--tw-text-opacity:1;color:rgb(165 180 252/var(--tw-text-opacity,1))}.dark\:hover\:text-primary-400:hover{--tw-text-opacity:1;color:rgb(129 140 248/var(--tw-text-opacity,1))}.dark\:hover\:text-white:hover{--tw-text-opacity:1;color:rgb(255 255 255/var(--tw-text-opacity,1))}.dark\:focus\:ring-offset-gray-800:focus{--tw-ring-offset-color:#1f2937}} \ No newline at end of file diff --git a/astro-church-website/astro.config.mjs b/astro.config.mjs similarity index 100% rename from astro-church-website/astro.config.mjs rename to astro.config.mjs diff --git a/astro-church-website/build.rs b/build.rs similarity index 100% rename from astro-church-website/build.rs rename to build.rs diff --git a/church-core/Cargo.toml b/church-core/Cargo.toml deleted file mode 100644 index 9a8b167..0000000 --- a/church-core/Cargo.toml +++ /dev/null @@ -1,99 +0,0 @@ -[package] -name = "church-core" -version = "0.1.0" -edition = "2021" -description = "Shared Rust crate for church application APIs and data models" -authors = ["Benjamin Slingo "] -license = "MIT" - -[dependencies] -# HTTP client (using rustls to avoid OpenSSL cross-compilation issues) -reqwest = { version = "0.11", features = ["json", "multipart", "stream", "rustls-tls"], default-features = false } -tokio = { version = "1.0", features = ["full"] } - -# JSON handling -serde = { version = "1.0", features = ["derive"] } -serde_json = "1.0" - -# Date/time handling -chrono = { version = "0.4", features = ["serde"] } - -# Error handling -thiserror = "1.0" -anyhow = "1.0" - -# Caching and utilities -moka = { version = "0.12", features = ["future"] } -async-trait = "0.1" -rand = "0.8" -urlencoding = "2.1" - -# UUID generation -uuid = { version = "1.0", features = ["v4", "serde"] } - -# Base64 encoding for image caching -base64 = "0.21" - -# URL handling -url = "2.4" - -# Regular expressions -regex = "1.10" - -# System calls for iOS device detection -libc = "0.2" - -# HTML processing -html2text = "0.12" - - - -# Testing dependencies -[dev-dependencies] -tokio-test = "0.4" -mockito = "0.31" -serde_json = "1.0" -tempfile = "3.8" -pretty_assertions = "1.4" - -# Optional FFI support -[dependencies.wasm-bindgen] -version = "0.2" -optional = true - -[dependencies.wasm-bindgen-futures] -version = "0.4" -optional = true - -[dependencies.js-sys] -version = "0.3" -optional = true - -[dependencies.web-sys] -version = "0.3" -optional = true -features = [ - "console", - "Window", - "Document", - "Element", - "HtmlElement", - "Storage", - "Request", - "RequestInit", - "Response", - "Headers", -] - -[features] -default = ["native"] -native = [] -wasm = ["wasm-bindgen", "wasm-bindgen-futures", "js-sys", "web-sys"] - -[lib] -crate-type = ["cdylib", "staticlib", "rlib"] - -[[bin]] -name = "church-core-test" -path = "src/bin/test.rs" - diff --git a/church-core/build.rs b/church-core/build.rs deleted file mode 100644 index 4b739c5..0000000 --- a/church-core/build.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn main() { - // No build steps needed -} \ No newline at end of file diff --git a/church-core/src/api.rs b/church-core/src/api.rs deleted file mode 100644 index 4b7fa37..0000000 --- a/church-core/src/api.rs +++ /dev/null @@ -1,464 +0,0 @@ -use crate::{ - ChurchApiClient, ChurchCoreConfig, - models::{NewSchedule, ScheduleUpdate, NewBulletin, BulletinUpdate, NewEvent, EventUpdate}, -}; -use tokio::runtime::Runtime; -use std::sync::OnceLock; - -static CLIENT: OnceLock = OnceLock::new(); -static RT: OnceLock = OnceLock::new(); - -fn get_client() -> &'static ChurchApiClient { - CLIENT.get_or_init(|| { - let config = ChurchCoreConfig::default(); - ChurchApiClient::new(config).expect("Failed to create church client") - }) -} - -fn get_runtime() -> &'static Runtime { - RT.get_or_init(|| { - Runtime::new().expect("Failed to create async runtime") - }) -} - -// Configuration functions -pub fn get_church_name() -> String { - let client = get_client(); - let rt = get_runtime(); - - match rt.block_on(client.get_config()) { - Ok(config) => config.church_name.unwrap_or_else(|| "".to_string()), - Err(e) => format!("Error: {}", e), - } -} - -pub fn get_contact_phone() -> String { - let client = get_client(); - let rt = get_runtime(); - - match rt.block_on(client.get_config()) { - Ok(config) => config.contact_phone.unwrap_or_else(|| "".to_string()), - Err(e) => format!("Error: {}", e), - } -} - -pub fn get_contact_email() -> String { - let client = get_client(); - let rt = get_runtime(); - - match rt.block_on(client.get_config()) { - Ok(config) => config.contact_email.unwrap_or_else(|| "".to_string()), - Err(e) => format!("Error: {}", e), - } -} - -pub fn get_church_address() -> String { - let client = get_client(); - let rt = get_runtime(); - - match rt.block_on(client.get_config()) { - Ok(config) => config.church_address.unwrap_or_else(|| "".to_string()), - Err(e) => format!("Error: {}", e), - } -} - -pub fn get_church_physical_address() -> String { - get_church_address() -} - -pub fn get_church_po_box() -> String { - let client = get_client(); - let rt = get_runtime(); - - match rt.block_on(client.get_config()) { - Ok(config) => config.po_box.unwrap_or_else(|| "".to_string()), - Err(e) => format!("Error: {}", e), - } -} - -pub fn get_mission_statement() -> String { - let client = get_client(); - let rt = get_runtime(); - - match rt.block_on(client.get_config()) { - Ok(config) => config.mission_statement.unwrap_or_else(|| "".to_string()), - Err(e) => format!("Error: {}", e), - } -} - -pub fn get_facebook_url() -> String { - let client = get_client(); - let rt = get_runtime(); - - match rt.block_on(client.get_config()) { - Ok(config) => config.facebook_url.unwrap_or_else(|| "".to_string()), - Err(e) => format!("Error: {}", e), - } -} - -pub fn get_youtube_url() -> String { - let client = get_client(); - let rt = get_runtime(); - - match rt.block_on(client.get_config()) { - Ok(config) => config.youtube_url.unwrap_or_else(|| "".to_string()), - Err(e) => format!("Error: {}", e), - } -} - -pub fn get_instagram_url() -> String { - let client = get_client(); - let rt = get_runtime(); - - match rt.block_on(client.get_config()) { - Ok(config) => config.instagram_url.unwrap_or_else(|| "".to_string()), - Err(e) => format!("Error: {}", e), - } -} - -pub fn get_stream_live_status() -> bool { - let client = get_client(); - let rt = get_runtime(); - - match rt.block_on(client.get_stream_status()) { - Ok(status) => status.is_live, - Err(_) => false, - } -} - -pub fn get_livestream_url() -> String { - let client = get_client(); - let rt = get_runtime(); - - match rt.block_on(client.get_live_stream()) { - Ok(stream) => stream.stream_title.unwrap_or_else(|| "".to_string()), - Err(e) => format!("Error: {}", e), - } -} - -// JSON API functions -pub fn fetch_events_json() -> String { - let client = get_client(); - let rt = get_runtime(); - - match rt.block_on(client.get_upcoming_events(Some(50))) { - Ok(events) => serde_json::to_string(&events).unwrap_or_else(|_| "[]".to_string()), - Err(_) => "[]".to_string(), - } -} - -pub fn fetch_featured_events_json() -> String { - let client = get_client(); - let rt = get_runtime(); - - match rt.block_on(client.get_featured_events_v2(Some(10))) { - Ok(events) => serde_json::to_string(&events).unwrap_or_else(|_| "[]".to_string()), - Err(_) => "[]".to_string(), - } -} - -pub fn fetch_sermons_json() -> String { - let client = get_client(); - let rt = get_runtime(); - - match rt.block_on(client.get_recent_sermons(Some(20))) { - Ok(sermons) => serde_json::to_string(&sermons).unwrap_or_else(|_| "[]".to_string()), - Err(_) => "[]".to_string(), - } -} - -pub fn fetch_config_json() -> String { - let client = get_client(); - let rt = get_runtime(); - - match rt.block_on(client.get_config()) { - Ok(config) => serde_json::to_string(&config).unwrap_or_else(|_| "{}".to_string()), - Err(_) => "{}".to_string(), - } -} - -pub fn fetch_random_bible_verse_json() -> String { - let client = get_client(); - let rt = get_runtime(); - - match rt.block_on(client.get_random_verse()) { - Ok(verse) => serde_json::to_string(&verse).unwrap_or_else(|_| "{}".to_string()), - Err(_) => "{}".to_string(), - } -} - -pub fn fetch_bulletins_json() -> String { - let client = get_client(); - let rt = get_runtime(); - - match rt.block_on(client.get_bulletins(true)) { - Ok(bulletins) => serde_json::to_string(&bulletins).unwrap_or_else(|_| "[]".to_string()), - Err(_) => "[]".to_string(), - } -} - -pub fn fetch_current_bulletin_json() -> String { - let client = get_client(); - let rt = get_runtime(); - - match rt.block_on(client.get_current_bulletin()) { - Ok(Some(bulletin)) => serde_json::to_string(&bulletin).unwrap_or_else(|_| "{}".to_string()), - Ok(None) => "{}".to_string(), - Err(_) => "{}".to_string(), - } -} - -pub fn fetch_bible_verse_json(query: String) -> String { - let client = get_client(); - let rt = get_runtime(); - - match rt.block_on(client.get_verse_by_reference(&query)) { - Ok(Some(verse)) => serde_json::to_string(&verse).unwrap_or_else(|_| "{}".to_string()), - Ok(None) => "{}".to_string(), - Err(_) => "{}".to_string(), - } -} - -pub fn fetch_livestream_archive_json() -> String { - let client = get_client(); - let rt = get_runtime(); - - match rt.block_on(client.get_livestreams()) { - Ok(streams) => serde_json::to_string(&streams).unwrap_or_else(|_| "[]".to_string()), - Err(_) => "[]".to_string(), - } -} - -pub fn submit_contact_v2_json(name: String, email: String, subject: String, message: String, phone: String) -> String { - let client = get_client(); - let rt = get_runtime(); - - let contact = crate::models::ContactForm::new(name, email, subject, message) - .with_phone(phone); - - match rt.block_on(client.submit_contact_form_v2(contact)) { - Ok(id) => serde_json::to_string(&serde_json::json!({"success": true, "id": id})).unwrap_or_else(|_| "{}".to_string()), - Err(e) => serde_json::to_string(&serde_json::json!({"success": false, "error": e.to_string()})).unwrap_or_else(|_| "{}".to_string()), - } -} - -pub fn validate_contact_form_json(form_json: String) -> String { - match serde_json::from_str::(&form_json) { - Ok(_) => serde_json::to_string(&crate::utils::ValidationResult::valid()).unwrap_or_else(|_| "{}".to_string()), - Err(_) => serde_json::to_string(&crate::utils::ValidationResult::invalid(vec!["Invalid JSON format".to_string()])).unwrap_or_else(|_| "{}".to_string()), - } -} - -pub fn submit_event_json( - title: String, - description: String, - start_time: String, - end_time: String, - location: String, - location_url: Option, - category: String, - recurring_type: Option, - submitter_email: Option -) -> String { - let client = get_client(); - let rt = get_runtime(); - - let submission = crate::models::EventSubmission { - title, - description, - start_time, - end_time, - location, - location_url, - category, - recurring_type, - submitter_email: submitter_email.unwrap_or_else(|| "".to_string()), - is_featured: false, - bulletin_week: None, - }; - - match rt.block_on(client.submit_event(submission)) { - Ok(id) => serde_json::to_string(&serde_json::json!({"success": true, "id": id})).unwrap_or_else(|_| "{}".to_string()), - Err(e) => serde_json::to_string(&serde_json::json!({"success": false, "error": e.to_string()})).unwrap_or_else(|_| "{}".to_string()), - } -} - -// Admin functions -pub fn fetch_all_schedules_json() -> String { - let client = get_client(); - let rt = get_runtime(); - - match rt.block_on(client.get_all_admin_schedules()) { - Ok(schedules) => serde_json::to_string(&schedules).unwrap_or_else(|_| "[]".to_string()), - Err(_) => "[]".to_string(), - } -} - -pub fn create_schedule_json(schedule_json: String) -> String { - let client = get_client(); - let rt = get_runtime(); - - match serde_json::from_str::(&schedule_json) { - Ok(schedule) => { - match rt.block_on(client.create_admin_schedule(schedule)) { - Ok(id) => serde_json::to_string(&serde_json::json!({"success": true, "id": id})).unwrap_or_else(|_| "{}".to_string()), - Err(e) => serde_json::to_string(&serde_json::json!({"success": false, "error": e.to_string()})).unwrap_or_else(|_| "{}".to_string()), - } - }, - Err(e) => serde_json::to_string(&serde_json::json!({"success": false, "error": format!("Invalid JSON: {}", e)})).unwrap_or_else(|_| "{}".to_string()), - } -} - -pub fn update_schedule_json(date: String, update_json: String) -> String { - let client = get_client(); - let rt = get_runtime(); - - match serde_json::from_str::(&update_json) { - Ok(update) => { - match rt.block_on(client.update_admin_schedule(&date, update)) { - Ok(_) => serde_json::to_string(&serde_json::json!({"success": true})).unwrap_or_else(|_| "{}".to_string()), - Err(e) => serde_json::to_string(&serde_json::json!({"success": false, "error": e.to_string()})).unwrap_or_else(|_| "{}".to_string()), - } - }, - Err(e) => serde_json::to_string(&serde_json::json!({"success": false, "error": format!("Invalid JSON: {}", e)})).unwrap_or_else(|_| "{}".to_string()), - } -} - -pub fn delete_schedule_json(date: String) -> String { - let client = get_client(); - let rt = get_runtime(); - - match rt.block_on(client.delete_admin_schedule(&date)) { - Ok(_) => serde_json::to_string(&serde_json::json!({"success": true})).unwrap_or_else(|_| "{}".to_string()), - Err(e) => serde_json::to_string(&serde_json::json!({"success": false, "error": e.to_string()})).unwrap_or_else(|_| "{}".to_string()), - } -} - -// Admin Auth Functions -pub fn admin_login_json(email: String, password: String) -> String { - let client = get_client(); - let rt = get_runtime(); - - match rt.block_on(client.admin_login(&email, &password)) { - Ok(token) => serde_json::to_string(&serde_json::json!({"success": true, "token": token})).unwrap_or_else(|_| "{}".to_string()), - Err(e) => serde_json::to_string(&serde_json::json!({"success": false, "error": e.to_string()})).unwrap_or_else(|_| "{}".to_string()), - } -} - -pub fn validate_admin_token_json(token: String) -> String { - let client = get_client(); - let rt = get_runtime(); - - match rt.block_on(client.validate_admin_token(&token)) { - Ok(valid) => serde_json::to_string(&serde_json::json!({"success": true, "valid": valid})).unwrap_or_else(|_| "{}".to_string()), - Err(e) => serde_json::to_string(&serde_json::json!({"success": false, "error": e.to_string()})).unwrap_or_else(|_| "{}".to_string()), - } -} - -// Admin Events Functions -pub fn fetch_pending_events_json() -> String { - let client = get_client(); - let rt = get_runtime(); - - match rt.block_on(crate::client::admin::get_pending_events(client)) { - Ok(events) => serde_json::to_string(&events).unwrap_or_else(|_| "[]".to_string()), - Err(_) => "[]".to_string(), - } -} - -pub fn approve_pending_event_json(event_id: String) -> String { - let client = get_client(); - let rt = get_runtime(); - - match rt.block_on(crate::client::admin::approve_pending_event(client, &event_id)) { - Ok(_) => serde_json::to_string(&serde_json::json!({"success": true})).unwrap_or_else(|_| "{}".to_string()), - Err(e) => serde_json::to_string(&serde_json::json!({"success": false, "error": e.to_string()})).unwrap_or_else(|_| "{}".to_string()), - } -} - -pub fn reject_pending_event_json(event_id: String) -> String { - let client = get_client(); - let rt = get_runtime(); - - match rt.block_on(crate::client::admin::reject_pending_event(client, &event_id)) { - Ok(_) => serde_json::to_string(&serde_json::json!({"success": true})).unwrap_or_else(|_| "{}".to_string()), - Err(e) => serde_json::to_string(&serde_json::json!({"success": false, "error": e.to_string()})).unwrap_or_else(|_| "{}".to_string()), - } -} - -pub fn delete_pending_event_json(event_id: String) -> String { - let client = get_client(); - let rt = get_runtime(); - - match rt.block_on(crate::client::admin::delete_pending_event(client, &event_id)) { - Ok(_) => serde_json::to_string(&serde_json::json!({"success": true})).unwrap_or_else(|_| "{}".to_string()), - Err(e) => serde_json::to_string(&serde_json::json!({"success": false, "error": e.to_string()})).unwrap_or_else(|_| "{}".to_string()), - } -} - - -pub fn update_admin_event_json(event_id: String, update_json: String) -> String { - let client = get_client(); - let rt = get_runtime(); - - match serde_json::from_str::(&update_json) { - Ok(update) => { - match rt.block_on(crate::client::admin::update_admin_event(client, &event_id, update)) { - Ok(_) => serde_json::to_string(&serde_json::json!({"success": true})).unwrap_or_else(|_| "{}".to_string()), - Err(e) => serde_json::to_string(&serde_json::json!({"success": false, "error": e.to_string()})).unwrap_or_else(|_| "{}".to_string()), - } - }, - Err(e) => serde_json::to_string(&serde_json::json!({"success": false, "error": format!("Invalid JSON: {}", e)})).unwrap_or_else(|_| "{}".to_string()), - } -} - -pub fn delete_admin_event_json(event_id: String) -> String { - let client = get_client(); - let rt = get_runtime(); - - match rt.block_on(crate::client::admin::delete_admin_event(client, &event_id)) { - Ok(_) => serde_json::to_string(&serde_json::json!({"success": true})).unwrap_or_else(|_| "{}".to_string()), - Err(e) => serde_json::to_string(&serde_json::json!({"success": false, "error": e.to_string()})).unwrap_or_else(|_| "{}".to_string()), - } -} - -// Admin Bulletins Functions -pub fn create_bulletin_json(bulletin_json: String) -> String { - let client = get_client(); - let rt = get_runtime(); - - match serde_json::from_str::(&bulletin_json) { - Ok(bulletin) => { - match rt.block_on(crate::client::admin::create_bulletin(client, bulletin)) { - Ok(id) => serde_json::to_string(&serde_json::json!({"success": true, "id": id})).unwrap_or_else(|_| "{}".to_string()), - Err(e) => serde_json::to_string(&serde_json::json!({"success": false, "error": e.to_string()})).unwrap_or_else(|_| "{}".to_string()), - } - }, - Err(e) => serde_json::to_string(&serde_json::json!({"success": false, "error": format!("Invalid JSON: {}", e)})).unwrap_or_else(|_| "{}".to_string()), - } -} - -pub fn update_bulletin_json(bulletin_id: String, update_json: String) -> String { - let client = get_client(); - let rt = get_runtime(); - - match serde_json::from_str::(&update_json) { - Ok(update) => { - match rt.block_on(crate::client::admin::update_bulletin(client, &bulletin_id, update)) { - Ok(_) => serde_json::to_string(&serde_json::json!({"success": true})).unwrap_or_else(|_| "{}".to_string()), - Err(e) => serde_json::to_string(&serde_json::json!({"success": false, "error": e.to_string()})).unwrap_or_else(|_| "{}".to_string()), - } - }, - Err(e) => serde_json::to_string(&serde_json::json!({"success": false, "error": format!("Invalid JSON: {}", e)})).unwrap_or_else(|_| "{}".to_string()), - } -} - -pub fn delete_bulletin_json(bulletin_id: String) -> String { - let client = get_client(); - let rt = get_runtime(); - - match rt.block_on(crate::client::admin::delete_bulletin(client, &bulletin_id)) { - Ok(_) => serde_json::to_string(&serde_json::json!({"success": true})).unwrap_or_else(|_| "{}".to_string()), - Err(e) => serde_json::to_string(&serde_json::json!({"success": false, "error": e.to_string()})).unwrap_or_else(|_| "{}".to_string()), - } -} \ No newline at end of file diff --git a/church-core/src/auth/mod.rs b/church-core/src/auth/mod.rs deleted file mode 100644 index 81968e1..0000000 --- a/church-core/src/auth/mod.rs +++ /dev/null @@ -1,4 +0,0 @@ -// Authentication modules placeholder -// This contains authentication implementations - -pub use crate::models::AuthToken; \ No newline at end of file diff --git a/church-core/src/bin/test-date-submission.rs b/church-core/src/bin/test-date-submission.rs deleted file mode 100644 index 090076a..0000000 --- a/church-core/src/bin/test-date-submission.rs +++ /dev/null @@ -1,36 +0,0 @@ -use church_core::{ - client::{ChurchApiClient, events::submit_event}, - models::EventSubmission, - config::ChurchCoreConfig, -}; - -#[tokio::main] -async fn main() { - let config = ChurchCoreConfig::new(); - let client = ChurchApiClient::new(config).unwrap(); - - let submission = EventSubmission { - title: "Test Event".to_string(), - description: "Testing date submission".to_string(), - start_time: "2025-06-28T23:00".to_string(), // The problematic format - end_time: "2025-06-29T00:00".to_string(), - location: "Test Location".to_string(), - location_url: None, - category: "Other".to_string(), - is_featured: false, - recurring_type: None, - bulletin_week: None, - submitter_email: "test@example.com".to_string(), - }; - - println!("Testing date validation:"); - println!("Can parse start_time: {}", submission.parse_start_time().is_some()); - println!("Can parse end_time: {}", submission.parse_end_time().is_some()); - println!("Validation passes: {}", submission.validate_times()); - - println!("\nAttempting to submit event..."); - match submit_event(&client, submission).await { - Ok(id) => println!("✅ Success! Event ID: {}", id), - Err(e) => println!("❌ Error: {}", e), - } -} diff --git a/church-core/src/bin/test.rs b/church-core/src/bin/test.rs deleted file mode 100644 index 6d35d17..0000000 --- a/church-core/src/bin/test.rs +++ /dev/null @@ -1,94 +0,0 @@ -use church_core::{ChurchApiClient, ChurchCoreConfig, DeviceCapabilities, StreamingCapability}; -use chrono::TimeZone; - -#[tokio::main] -async fn main() -> Result<(), Box> { - // Initialize the client with default configuration - let config = ChurchCoreConfig::default(); - let client = ChurchApiClient::new(config)?; - - println!("Church Core API Client Test"); - println!("=========================="); - - // Test health check - match client.health_check().await { - Ok(true) => println!("✅ Health check passed"), - Ok(false) => println!("❌ Health check failed"), - Err(e) => println!("❌ Health check error: {}", e), - } - - // Test upcoming events - match client.get_upcoming_events(Some(5)).await { - Ok(events) => { - println!("✅ Retrieved {} upcoming events", events.len()); - for event in events.iter().take(3) { - println!(" - {}: {}", event.title, event.start_time.format("%Y-%m-%d %H:%M")); - } - } - Err(e) => println!("❌ Failed to get events: {}", e), - } - - // Test current bulletin - match client.get_current_bulletin().await { - Ok(Some(bulletin)) => { - println!("✅ Retrieved current bulletin: {}", bulletin.title); - } - Ok(None) => println!("ℹ️ No current bulletin found"), - Err(e) => println!("❌ Failed to get bulletin: {}", e), - } - - // Test configuration - match client.get_config().await { - Ok(config) => { - println!("✅ Retrieved church config"); - if let Some(name) = &config.church_name { - println!(" Church: {}", name); - } - } - Err(e) => println!("❌ Failed to get config: {}", e), - } - - // Test sermons - match client.get_recent_sermons(Some(5)).await { - Ok(sermons) => { - println!("✅ Retrieved {} recent sermons", sermons.len()); - for sermon in sermons.iter().take(2) { - println!(" - {}: {}", sermon.title, sermon.speaker); - } - } - Err(e) => println!("❌ Failed to get sermons: {}", e), - } - - // Test livestreams - match client.get_livestreams().await { - Ok(streams) => { - println!("✅ Retrieved {} livestream archives", streams.len()); - for stream in streams.iter().take(2) { - println!(" - {}: {}", stream.title, stream.speaker); - } - } - Err(e) => println!("❌ Failed to get livestreams: {}", e), - } - - // Test cache stats - let (cache_size, max_size) = client.get_cache_stats().await; - println!("📊 Cache: {}/{} items", cache_size, max_size); - - // Test streaming URL generation - println!("\n🎬 Testing Streaming URLs:"); - let media_id = "test-id-123"; - let base_url = "https://api.rockvilletollandsda.church"; - - let av1_url = DeviceCapabilities::get_streaming_url(base_url, media_id, StreamingCapability::AV1); - println!(" AV1: {}", av1_url.url); - - let hls_url = DeviceCapabilities::get_streaming_url(base_url, media_id, StreamingCapability::HLS); - println!(" HLS: {}", hls_url.url); - - let optimal_url = DeviceCapabilities::get_optimal_streaming_url(base_url, media_id); - println!(" Optimal: {} ({:?})", optimal_url.url, optimal_url.capability); - - println!("\nTest completed!"); - - Ok(()) -} \ No newline at end of file diff --git a/church-core/src/cache/mod.rs b/church-core/src/cache/mod.rs deleted file mode 100644 index 396fdb3..0000000 --- a/church-core/src/cache/mod.rs +++ /dev/null @@ -1,339 +0,0 @@ -use serde::{de::DeserializeOwned, Serialize, Deserialize}; -use std::{ - collections::HashMap, - sync::Arc, - time::{Duration, Instant}, - path::PathBuf, -}; -use tokio::sync::RwLock; -use tokio::fs; - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct CachedHttpResponse { - pub data: Vec, - pub content_type: String, - pub headers: HashMap, - pub status_code: u16, - #[serde(with = "instant_serde")] - pub cached_at: Instant, - #[serde(with = "instant_serde")] - pub expires_at: Instant, -} - -// Custom serializer for Instant (can't be serialized directly) -mod instant_serde { - use super::*; - use serde::{Deserializer, Serializer}; - - pub fn serialize(instant: &Instant, serializer: S) -> Result - where - S: Serializer, - { - // Convert to duration since app start - this is approximate but works for our use case - let duration_since_start = instant.elapsed(); - serializer.serialize_u64(duration_since_start.as_secs()) - } - - pub fn deserialize<'de, D>(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let secs = ::deserialize(deserializer)?; - // For loaded items, set as if they were cached "now" minus the stored duration - // This isn't perfect but works for expiration checking - Ok(Instant::now() - Duration::from_secs(secs)) - } -} - -// Simplified cache interface - removed trait object complexity -// Each cache type will implement these methods directly - -#[derive(Debug)] -struct CacheEntry { - data: Vec, - expires_at: Instant, -} - -impl CacheEntry { - fn new(data: Vec, ttl: Duration) -> Self { - Self { - data, - expires_at: Instant::now() + ttl, - } - } - - fn is_expired(&self) -> bool { - Instant::now() > self.expires_at - } -} - -pub struct MemoryCache { - store: Arc>>, - http_store: Arc>>, - max_size: usize, - cache_dir: Option, -} - -impl MemoryCache { - pub fn new(max_size: usize) -> Self { - Self { - store: Arc::new(RwLock::new(HashMap::new())), - http_store: Arc::new(RwLock::new(HashMap::new())), - max_size, - cache_dir: None, - } - } - - pub fn with_disk_cache(mut self, cache_dir: PathBuf) -> Self { - self.cache_dir = Some(cache_dir); - self - } - - fn get_cache_file_path(&self, url: &str) -> Option { - self.cache_dir.as_ref().map(|dir| { - // Create a safe filename from URL - let hash = { - use std::collections::hash_map::DefaultHasher; - use std::hash::{Hash, Hasher}; - let mut hasher = DefaultHasher::new(); - url.hash(&mut hasher); - hasher.finish() - }; - dir.join(format!("cache_{}.json", hash)) - }) - } - - async fn cleanup_expired(&self) { - let mut store = self.store.write().await; - let now = Instant::now(); - store.retain(|_, entry| entry.expires_at > now); - } - - async fn ensure_capacity(&self) { - let mut store = self.store.write().await; - - if store.len() >= self.max_size { - // Remove oldest entries if we're at capacity - // Collect keys to remove to avoid borrow issues - let mut to_remove: Vec = Vec::new(); - { - let entries: Vec<_> = store.iter().collect(); - let mut sorted_entries = entries; - sorted_entries.sort_by_key(|(_, entry)| entry.expires_at); - - let remove_count = sorted_entries.len().saturating_sub(self.max_size / 2); - for (key, _) in sorted_entries.into_iter().take(remove_count) { - to_remove.push(key.clone()); - } - } - - // Now remove the keys - for key in to_remove { - store.remove(&key); - } - } - } -} - -impl MemoryCache { - pub async fn get(&self, key: &str) -> Option - where - T: DeserializeOwned + Send + 'static, - { - // Clean up expired entries periodically - if rand::random::() < 0.1 { - self.cleanup_expired().await; - } - - let store = self.store.read().await; - if let Some(entry) = store.get(key) { - if !entry.is_expired() { - if let Ok(value) = serde_json::from_slice(&entry.data) { - return Some(value); - } - } - } - None - } - - pub async fn set(&self, key: &str, value: &T, ttl: Duration) - where - T: Serialize + Send + Sync, - { - if let Ok(data) = serde_json::to_vec(value) { - self.ensure_capacity().await; - - let mut store = self.store.write().await; - store.insert(key.to_string(), CacheEntry::new(data, ttl)); - } - } - - pub async fn remove(&self, key: &str) { - let mut store = self.store.write().await; - store.remove(key); - } - - pub async fn clear(&self) { - let mut store = self.store.write().await; - store.clear(); - } - - pub async fn len(&self) -> usize { - let store = self.store.read().await; - store.len() - } - - pub async fn invalidate_prefix(&self, prefix: &str) { - let mut store = self.store.write().await; - store.retain(|key, _| !key.starts_with(prefix)); - - let mut http_store = self.http_store.write().await; - http_store.retain(|key, _| !key.starts_with(prefix)); - } - - // HTTP Response Caching Methods - - pub async fn get_http_response(&self, url: &str) -> Option { - // Clean up expired entries periodically - if rand::random::() < 0.1 { - self.cleanup_expired_http().await; - } - - // 1. Check memory cache first (fastest) - { - let store = self.http_store.read().await; - println!("🔍 Memory cache lookup for: {}", url); - println!("🔍 Memory cache has {} entries", store.len()); - - if let Some(response) = store.get(url) { - if !response.is_expired() { - println!("🔍 Memory cache HIT - found valid entry"); - return Some(response.clone()); - } else { - println!("🔍 Memory cache entry expired"); - } - } - } - - // 2. Check disk cache (persistent) - if let Some(cache_path) = self.get_cache_file_path(url) { - println!("🔍 Checking disk cache at: {:?}", cache_path); - - if let Ok(file_content) = fs::read(&cache_path).await { - if let Ok(cached_response) = serde_json::from_slice::(&file_content) { - if !cached_response.is_expired() { - println!("🔍 Disk cache HIT - loading into memory"); - - // Load back into memory cache for faster future access - let mut store = self.http_store.write().await; - store.insert(url.to_string(), cached_response.clone()); - - return Some(cached_response); - } else { - println!("🔍 Disk cache entry expired, removing file"); - let _ = fs::remove_file(&cache_path).await; - } - } else { - println!("🔍 Failed to parse disk cache file"); - } - } else { - println!("🔍 No disk cache file found"); - } - } - - println!("🔍 Cache MISS - no valid entry found"); - None - } - - pub async fn set_http_response(&self, url: &str, response: CachedHttpResponse) { - self.ensure_http_capacity().await; - - // Store in memory cache - let mut store = self.http_store.write().await; - println!("🔍 Storing in memory cache: {}", url); - println!("🔍 Memory cache will have {} entries after insert", store.len() + 1); - store.insert(url.to_string(), response.clone()); - drop(store); // Release the lock before async disk operation - - // Store in disk cache (async, non-blocking) - if let Some(cache_path) = self.get_cache_file_path(url) { - println!("🔍 Storing to disk cache: {:?}", cache_path); - - // Ensure cache directory exists - if let Some(parent) = cache_path.parent() { - let _ = fs::create_dir_all(parent).await; - } - - // Serialize and save to disk - match serde_json::to_vec(&response) { - Ok(serialized) => { - if let Err(e) = fs::write(&cache_path, serialized).await { - println!("🔍 Failed to write disk cache: {}", e); - } else { - println!("🔍 Successfully saved to disk cache"); - } - } - Err(e) => { - println!("🔍 Failed to serialize for disk cache: {}", e); - } - } - } - } - - async fn cleanup_expired_http(&self) { - let mut store = self.http_store.write().await; - let now = Instant::now(); - store.retain(|_, response| response.expires_at > now); - } - - async fn ensure_http_capacity(&self) { - let mut store = self.http_store.write().await; - - if store.len() >= self.max_size { - // Remove oldest entries if we're at capacity - let mut to_remove: Vec = Vec::new(); - { - let entries: Vec<_> = store.iter().collect(); - let mut sorted_entries = entries; - sorted_entries.sort_by_key(|(_, response)| response.cached_at); - - let remove_count = sorted_entries.len().saturating_sub(self.max_size / 2); - for (key, _) in sorted_entries.into_iter().take(remove_count) { - to_remove.push(key.clone()); - } - } - - // Now remove the keys - for key in to_remove { - store.remove(&key); - } - } - } -} - -impl CachedHttpResponse { - pub fn new( - data: Vec, - content_type: String, - headers: HashMap, - status_code: u16, - ttl: Duration - ) -> Self { - let now = Instant::now(); - Self { - data, - content_type, - headers, - status_code, - cached_at: now, - expires_at: now + ttl, - } - } - - pub fn is_expired(&self) -> bool { - Instant::now() > self.expires_at - } -} - -// Add rand dependency for periodic cleanup -// This is a simple implementation - in production you might want to use a more sophisticated cache like moka \ No newline at end of file diff --git a/church-core/src/church_core.udl b/church-core/src/church_core.udl deleted file mode 100644 index 9254cae..0000000 --- a/church-core/src/church_core.udl +++ /dev/null @@ -1,78 +0,0 @@ -namespace church_core { - string fetch_events_json(); - string fetch_bulletins_json(); - string fetch_sermons_json(); - string fetch_bible_verse_json(string query); - string fetch_random_bible_verse_json(); - string fetch_scripture_verses_for_sermon_json(string sermon_id); - string fetch_config_json(); - string fetch_current_bulletin_json(); - string fetch_featured_events_json(); - string fetch_stream_status_json(); - boolean get_stream_live_status(); - string get_livestream_url(); - string fetch_live_stream_json(); - string fetch_livestream_archive_json(); - string submit_contact_json(string name, string email, string message); - string submit_contact_v2_json(string name, string email, string subject, string message, string phone); - string submit_contact_v2_json_legacy(string first_name, string last_name, string email, string subject, string message); - string fetch_cached_image_base64(string url); - string get_optimal_streaming_url(string media_id); - boolean device_supports_av1(); - string get_av1_streaming_url(string media_id); - string get_hls_streaming_url(string media_id); - - // Scripture formatting utilities - string format_scripture_text_json(string scripture_text); - string extract_scripture_references_string(string scripture_text); - string create_sermon_share_items_json(string title, string speaker, string? video_url, string? audio_url); - - // Form validation functions - string validate_contact_form_json(string form_json); - boolean validate_email_address(string email); - boolean validate_phone_number(string phone); - - // Event formatting functions - string format_event_for_display_json(string event_json); - string format_time_range_string(string start_time, string end_time); - boolean is_multi_day_event_check(string date); - - // Home feed aggregation - string generate_home_feed_json(string events_json, string sermons_json, string bulletins_json, string verse_json); - - // Media type management - string get_media_type_display_name(string media_type_str); - string get_media_type_icon(string media_type_str); - string filter_sermons_by_media_type(string sermons_json, string media_type_str); - - // Individual config getter functions (RTSDA architecture compliant) - string get_church_name(); - string get_contact_phone(); - string get_contact_email(); - string get_brand_color(); - string get_about_text(); - string get_donation_url(); - string get_church_address(); - string get_church_physical_address(); - string get_church_po_box(); - sequence get_coordinates(); - string get_website_url(); - string get_facebook_url(); - string get_youtube_url(); - string get_instagram_url(); - string get_mission_statement(); - - // Calendar event parsing (RTSDA architecture compliant) - string create_calendar_event_data(string event_json); - - // JSON parsing functions (RTSDA architecture compliance) - string parse_events_from_json(string events_json); - string parse_sermons_from_json(string sermons_json); - string parse_bulletins_from_json(string bulletins_json); - string parse_bible_verse_from_json(string verse_json); - string parse_contact_result_from_json(string result_json); - string generate_verse_description(string verses_json); - string extract_full_verse_text(string verses_json); - string extract_stream_url_from_status(string status_json); - string parse_calendar_event_data(string calendar_json); -}; \ No newline at end of file diff --git a/church-core/src/client/admin.rs b/church-core/src/client/admin.rs deleted file mode 100644 index faf5bfd..0000000 --- a/church-core/src/client/admin.rs +++ /dev/null @@ -1,107 +0,0 @@ -use crate::{ - client::ChurchApiClient, - error::Result, - models::{ - NewBulletin, BulletinUpdate, - NewEvent, EventUpdate, PendingEvent, - User, Schedule, NewSchedule, ScheduleUpdate, - ApiVersion, - }, -}; - -// Admin Bulletin Management -pub async fn create_bulletin(client: &ChurchApiClient, bulletin: NewBulletin) -> Result { - client.post_api_with_version("/admin/bulletins", &bulletin, ApiVersion::V1).await -} - -pub async fn update_bulletin(client: &ChurchApiClient, id: &str, update: BulletinUpdate) -> Result<()> { - let path = format!("/admin/bulletins/{}", id); - client.put_api(&path, &update).await -} - -pub async fn delete_bulletin(client: &ChurchApiClient, id: &str) -> Result<()> { - let path = format!("/admin/bulletins/{}", id); - client.delete_api(&path).await -} - -// Admin Event Management -// Note: Event creation must go through public submission form - -pub async fn update_admin_event(client: &ChurchApiClient, id: &str, update: EventUpdate) -> Result<()> { - let path = format!("/admin/events/{}", id); - client.put_api(&path, &update).await -} - -pub async fn delete_admin_event(client: &ChurchApiClient, id: &str) -> Result<()> { - let path = format!("/admin/events/{}", id); - client.delete_api(&path).await -} - -// Admin Pending Events Management -pub async fn get_pending_events(client: &ChurchApiClient) -> Result> { - client.get_api("/admin/events/pending").await -} - -pub async fn approve_pending_event(client: &ChurchApiClient, id: &str) -> Result<()> { - let path = format!("/admin/events/pending/{}/approve", id); - let response: crate::models::ApiResponse<()> = client.post(&path, &()).await?; - - if response.success { - Ok(()) - } else { - Err(crate::error::ChurchApiError::Api( - response.error - .or(response.message) - .unwrap_or_else(|| "Failed to approve pending event".to_string()) - )) - } -} - -pub async fn reject_pending_event(client: &ChurchApiClient, id: &str) -> Result<()> { - let path = format!("/admin/events/pending/{}/reject", id); - let response: crate::models::ApiResponse<()> = client.post(&path, &()).await?; - - if response.success { - Ok(()) - } else { - Err(crate::error::ChurchApiError::Api( - response.error - .or(response.message) - .unwrap_or_else(|| "Failed to reject pending event".to_string()) - )) - } -} - -pub async fn delete_pending_event(client: &ChurchApiClient, id: &str) -> Result<()> { - let path = format!("/admin/events/pending/{}", id); - client.delete_api(&path).await -} - -// Admin User Management -pub async fn get_users(client: &ChurchApiClient) -> Result> { - client.get_api("/admin/users").await -} - -// Admin Schedule Management -pub async fn create_schedule(client: &ChurchApiClient, schedule: NewSchedule) -> Result { - client.post_api("/admin/schedule", &schedule).await -} - -pub async fn update_schedule(client: &ChurchApiClient, date: &str, update: ScheduleUpdate) -> Result<()> { - let path = format!("/admin/schedule/{}", date); - client.put_api(&path, &update).await -} - -pub async fn delete_schedule(client: &ChurchApiClient, date: &str) -> Result<()> { - let path = format!("/admin/schedule/{}", date); - client.delete_api(&path).await -} - -pub async fn get_all_schedules(client: &ChurchApiClient) -> Result> { - client.get_api("/admin/schedule").await -} - -// Admin Config Management -pub async fn get_admin_config(client: &ChurchApiClient) -> Result { - client.get_api("/admin/config").await -} \ No newline at end of file diff --git a/church-core/src/client/bible.rs b/church-core/src/client/bible.rs deleted file mode 100644 index be32176..0000000 --- a/church-core/src/client/bible.rs +++ /dev/null @@ -1,169 +0,0 @@ -use crate::{ - client::ChurchApiClient, - error::Result, - models::{BibleVerse, VerseOfTheDay, VerseCategory, PaginationParams, ApiListResponse, ApiVersion}, -}; - -pub async fn get_random_verse(client: &ChurchApiClient) -> Result { - // The response format is {success: bool, data: Verse} - #[derive(serde::Deserialize, serde::Serialize)] - struct VerseResponse { - success: bool, - data: ApiVerse, - } - - #[derive(serde::Deserialize, serde::Serialize)] - struct ApiVerse { - id: String, - reference: String, - text: String, - #[serde(rename = "is_active")] - is_active: bool, - } - - let url = client.build_url("/bible_verses/random"); - let raw_response = client.client.get(&url).send().await?; - let response_text = raw_response.text().await?; - let response: VerseResponse = serde_json::from_str(&response_text) - .map_err(|e| crate::error::ChurchApiError::Json(e))?; - - if response.success { - Ok(BibleVerse::new(response.data.text, response.data.reference)) - } else { - Err(crate::error::ChurchApiError::Api("Bible verse API returned success=false".to_string())) - } -} - -pub async fn get_verse_of_the_day(client: &ChurchApiClient) -> Result { - client.get_api("/bible/verse-of-the-day").await -} - -pub async fn get_verse_by_reference(client: &ChurchApiClient, reference: &str) -> Result> { - let path = format!("/bible/verse?reference={}", urlencoding::encode(reference)); - - match client.get_api(&path).await { - Ok(verse) => Ok(Some(verse)), - Err(crate::error::ChurchApiError::NotFound) => Ok(None), - Err(e) => Err(e), - } -} - -pub async fn get_verses_by_category(client: &ChurchApiClient, category: VerseCategory, limit: Option) -> Result> { - let mut path = format!("/bible/category/{}", category.display_name().to_lowercase()); - - if let Some(limit) = limit { - path.push_str(&format!("?limit={}", limit)); - } - - let response: crate::models::ApiListResponse = client.get_api_list(&path).await?; - Ok(response.data.items) -} - -pub async fn search_verses(client: &ChurchApiClient, query: &str, limit: Option) -> Result> { - let mut path = format!("/bible_verses/search?q={}", urlencoding::encode(query)); - - if let Some(limit) = limit { - path.push_str(&format!("&limit={}", limit)); - } - - // The bible_verses/search endpoint returns a custom format with additional fields - #[derive(serde::Deserialize, serde::Serialize)] - struct ApiBibleVerse { - id: String, - reference: String, - text: String, - is_active: bool, - created_at: String, - updated_at: String, - } - - #[derive(serde::Deserialize, serde::Serialize)] - struct BibleSearchResponse { - success: bool, - data: Vec, - message: Option, - } - - let url = client.build_url(&path); - let raw_response = client.client.get(&url).send().await?; - let response_text = raw_response.text().await?; - let response: BibleSearchResponse = serde_json::from_str(&response_text) - .map_err(|e| crate::error::ChurchApiError::Json(e))?; - if response.success { - // Convert ApiBibleVerse to BibleVerse - let verses = response.data.into_iter() - .map(|api_verse| BibleVerse::new(api_verse.text, api_verse.reference)) - .collect(); - Ok(verses) - } else { - Ok(Vec::new()) - } -} - -// V2 API methods -pub async fn get_random_verse_v2(client: &ChurchApiClient) -> Result { - #[derive(serde::Deserialize, serde::Serialize)] - struct VerseResponse { - success: bool, - data: ApiVerse, - } - - #[derive(serde::Deserialize, serde::Serialize)] - struct ApiVerse { - id: String, - reference: String, - text: String, - #[serde(rename = "is_active")] - is_active: bool, - } - - let url = client.build_url_with_version("/bible_verses/random", ApiVersion::V2); - let raw_response = client.client.get(&url).send().await?; - let response_text = raw_response.text().await?; - let response: VerseResponse = serde_json::from_str(&response_text) - .map_err(|e| crate::error::ChurchApiError::Json(e))?; - - if response.success { - Ok(BibleVerse::new(response.data.text, response.data.reference)) - } else { - Err(crate::error::ChurchApiError::Api("Bible verse API returned success=false".to_string())) - } -} - -pub async fn get_bible_verses_v2(client: &ChurchApiClient, params: Option) -> Result> { - let mut path = "/bible_verses".to_string(); - - if let Some(params) = params { - let mut query_params = Vec::new(); - - if let Some(page) = params.page { - query_params.push(("page", page.to_string())); - } - - if let Some(per_page) = params.per_page { - query_params.push(("per_page", per_page.to_string())); - } - - if !query_params.is_empty() { - let query_string = query_params - .iter() - .map(|(k, v)| format!("{}={}", k, urlencoding::encode(v))) - .collect::>() - .join("&"); - path.push_str(&format!("?{}", query_string)); - } - } - - client.get_api_list_with_version(&path, ApiVersion::V2).await -} - -pub async fn search_verses_v2(client: &ChurchApiClient, query: &str, limit: Option) -> Result> { - let mut path = format!("/bible_verses/search?q={}", urlencoding::encode(query)); - - if let Some(limit) = limit { - path.push_str(&format!("&limit={}", limit)); - } - - let response: crate::models::ApiListResponse = client.get_api_list_with_version(&path, ApiVersion::V2).await?; - Ok(response.data.items) -} \ No newline at end of file diff --git a/church-core/src/client/bulletins.rs b/church-core/src/client/bulletins.rs deleted file mode 100644 index 427f2b1..0000000 --- a/church-core/src/client/bulletins.rs +++ /dev/null @@ -1,101 +0,0 @@ -use crate::{ - client::ChurchApiClient, - error::Result, - models::{Bulletin, NewBulletin, PaginationParams, ApiListResponse, ApiVersion}, -}; - -pub async fn get_bulletins(client: &ChurchApiClient, active_only: bool) -> Result> { - let path = if active_only { - "/bulletins?active=true" - } else { - "/bulletins" - }; - - let response: ApiListResponse = client.get_api_list(path).await?; - Ok(response.data.items) -} - -pub async fn get_current_bulletin(client: &ChurchApiClient) -> Result> { - match client.get_api("/bulletins/current").await { - Ok(bulletin) => Ok(Some(bulletin)), - Err(crate::error::ChurchApiError::NotFound) => Ok(None), - Err(e) => Err(e), - } -} - -pub async fn get_bulletin(client: &ChurchApiClient, id: &str) -> Result> { - let path = format!("/bulletins/{}", id); - - match client.get_api(&path).await { - Ok(bulletin) => Ok(Some(bulletin)), - Err(crate::error::ChurchApiError::NotFound) => Ok(None), - Err(e) => Err(e), - } -} - -pub async fn create_bulletin(client: &ChurchApiClient, bulletin: NewBulletin) -> Result { - client.post_api("/bulletins", &bulletin).await -} - -pub async fn get_next_bulletin(client: &ChurchApiClient) -> Result> { - match client.get_api("/bulletins/next").await { - Ok(bulletin) => Ok(Some(bulletin)), - Err(crate::error::ChurchApiError::NotFound) => Ok(None), - Err(e) => Err(e), - } -} - -// V2 API methods -pub async fn get_bulletins_v2(client: &ChurchApiClient, params: Option) -> Result> { - let mut path = "/bulletins".to_string(); - - if let Some(params) = params { - let mut query_params = Vec::new(); - - if let Some(page) = params.page { - query_params.push(("page", page.to_string())); - } - - if let Some(per_page) = params.per_page { - query_params.push(("per_page", per_page.to_string())); - } - - if !query_params.is_empty() { - let query_string = query_params - .iter() - .map(|(k, v)| format!("{}={}", k, urlencoding::encode(v))) - .collect::>() - .join("&"); - path.push_str(&format!("?{}", query_string)); - } - } - - let url = client.build_url_with_version(&path, ApiVersion::V2); - let response: ApiListResponse = client.get(&url).await?; - - if response.success { - Ok(response) - } else { - Err(crate::error::ChurchApiError::Api( - response.error - .or(response.message) - .unwrap_or_else(|| "Unknown API error".to_string()) - )) - } -} - -pub async fn get_current_bulletin_v2(client: &ChurchApiClient) -> Result> { - match client.get_api_with_version("/bulletins/current", ApiVersion::V2).await { - Ok(bulletin) => Ok(Some(bulletin)), - Err(crate::error::ChurchApiError::NotFound) => Ok(None), - Err(e) => Err(e), - } -} - -pub async fn get_next_bulletin_v2(client: &ChurchApiClient) -> Result> { - match client.get_api_with_version("/bulletins/next", ApiVersion::V2).await { - Ok(bulletin) => Ok(Some(bulletin)), - Err(crate::error::ChurchApiError::NotFound) => Ok(None), - Err(e) => Err(e), - } -} \ No newline at end of file diff --git a/church-core/src/client/config.rs b/church-core/src/client/config.rs deleted file mode 100644 index f95f861..0000000 --- a/church-core/src/client/config.rs +++ /dev/null @@ -1,50 +0,0 @@ -use crate::{ - client::ChurchApiClient, - error::Result, - models::{ChurchConfig, Schedule, ConferenceData, ApiVersion}, -}; - -pub async fn get_config(client: &ChurchApiClient) -> Result { - client.get("/config").await -} - -pub async fn get_config_by_id(client: &ChurchApiClient, record_id: &str) -> Result { - let path = format!("/config/records/{}", record_id); - client.get_api(&path).await -} - -pub async fn update_config(client: &ChurchApiClient, config: ChurchConfig) -> Result<()> { - client.put_api("/config", &config).await -} - -// V2 API methods -pub async fn get_config_v2(client: &ChurchApiClient) -> Result { - client.get_api_with_version("/config", ApiVersion::V2).await -} - -// Schedule endpoints -pub async fn get_schedule(client: &ChurchApiClient, date: Option<&str>) -> Result { - let path = if let Some(date) = date { - format!("/schedule?date={}", date) - } else { - "/schedule".to_string() - }; - client.get_api(&path).await -} - -pub async fn get_schedule_v2(client: &ChurchApiClient, date: Option<&str>) -> Result { - let path = if let Some(date) = date { - format!("/schedule?date={}", date) - } else { - "/schedule".to_string() - }; - client.get_api_with_version(&path, ApiVersion::V2).await -} - -pub async fn get_conference_data(client: &ChurchApiClient) -> Result { - client.get_api("/conference-data").await -} - -pub async fn get_conference_data_v2(client: &ChurchApiClient) -> Result { - client.get_api_with_version("/conference-data", ApiVersion::V2).await -} \ No newline at end of file diff --git a/church-core/src/client/contact.rs b/church-core/src/client/contact.rs deleted file mode 100644 index 042b2bc..0000000 --- a/church-core/src/client/contact.rs +++ /dev/null @@ -1,125 +0,0 @@ -use crate::{ - client::ChurchApiClient, - error::Result, - models::{ContactForm, ContactSubmission, ContactStatus, PaginationParams, ApiListResponse, ApiVersion}, -}; - -pub async fn submit_contact_form(client: &ChurchApiClient, form: ContactForm) -> Result { - // Create payload matching the expected format from iOS app - let payload = serde_json::json!({ - "first_name": form.name.split_whitespace().next().unwrap_or(&form.name), - "last_name": form.name.split_whitespace().nth(1).unwrap_or(""), - "email": form.email, - "phone": form.phone.unwrap_or_default(), - "message": form.message - }); - - // Use the main API subdomain for consistency - let contact_url = client.build_url("/contact"); - - let response = client.client - .post(contact_url) - .header("Content-Type", "application/json") - .json(&payload) - .send() - .await?; - - if response.status().is_success() { - Ok("Contact form submitted successfully".to_string()) - } else { - Err(crate::error::ChurchApiError::Api(format!("Contact form submission failed with status: {}", response.status()))) - } -} - -pub async fn get_contact_submissions(client: &ChurchApiClient, params: Option) -> Result> { - let mut path = "/contact/submissions".to_string(); - - if let Some(params) = params { - let mut query_params = Vec::new(); - - if let Some(page) = params.page { - query_params.push(("page", page.to_string())); - } - - if let Some(per_page) = params.per_page { - query_params.push(("per_page", per_page.to_string())); - } - - if let Some(sort) = ¶ms.sort { - query_params.push(("sort", sort.clone())); - } - - if let Some(filter) = ¶ms.filter { - query_params.push(("filter", filter.clone())); - } - - if !query_params.is_empty() { - let query_string = query_params - .iter() - .map(|(k, v)| format!("{}={}", k, urlencoding::encode(v))) - .collect::>() - .join("&"); - path.push_str(&format!("?{}", query_string)); - } - } - - client.get_api_list(&path).await -} - -pub async fn get_contact_submission(client: &ChurchApiClient, id: &str) -> Result> { - let path = format!("/contact/submissions/{}", id); - - match client.get_api(&path).await { - Ok(submission) => Ok(Some(submission)), - Err(crate::error::ChurchApiError::NotFound) => Ok(None), - Err(e) => Err(e), - } -} - -pub async fn update_contact_submission( - client: &ChurchApiClient, - id: &str, - status: ContactStatus, - response: Option -) -> Result<()> { - let path = format!("/contact/submissions/{}", id); - - let update_data = serde_json::json!({ - "status": status, - "response": response - }); - - client.put_api(&path, &update_data).await -} - -// V2 API methods -pub async fn submit_contact_form_v2(client: &ChurchApiClient, form: ContactForm) -> Result { - let mut payload = serde_json::json!({ - "name": form.name, - "email": form.email, - "subject": form.subject, - "message": form.message - }); - - // Add phone field if provided - if let Some(phone) = &form.phone { - if !phone.trim().is_empty() { - payload["phone"] = serde_json::json!(phone); - } - } - - let url = client.build_url_with_version("/contact", ApiVersion::V2); - - let response = client.client - .post(url) - .header("Content-Type", "application/json") - .json(&payload) - .send() - .await?; - - if response.status().is_success() { - Ok("Contact form submitted successfully".to_string()) - } else { - Err(crate::error::ChurchApiError::Api(format!("Contact form submission failed with status: {}", response.status()))) - } -} \ No newline at end of file diff --git a/church-core/src/client/events.rs b/church-core/src/client/events.rs deleted file mode 100644 index 0678941..0000000 --- a/church-core/src/client/events.rs +++ /dev/null @@ -1,192 +0,0 @@ -use crate::{ - client::ChurchApiClient, - error::Result, - models::{Event, NewEvent, EventUpdate, EventSubmission, PaginationParams, ApiListResponse, ApiVersion}, -}; - -pub async fn get_events(client: &ChurchApiClient, params: Option) -> Result> { - let mut path = "/events".to_string(); - - if let Some(params) = params { - let mut query_params = Vec::new(); - - if let Some(page) = params.page { - query_params.push(("page", page.to_string())); - } - - if let Some(per_page) = params.per_page { - query_params.push(("per_page", per_page.to_string())); - } - - if let Some(sort) = ¶ms.sort { - query_params.push(("sort", sort.clone())); - } - - if let Some(filter) = ¶ms.filter { - query_params.push(("filter", filter.clone())); - } - - if !query_params.is_empty() { - let query_string = query_params - .iter() - .map(|(k, v)| format!("{}={}", k, urlencoding::encode(v))) - .collect::>() - .join("&"); - path.push_str(&format!("?{}", query_string)); - } - } - - client.get_api_list(&path).await -} - -pub async fn get_upcoming_events(client: &ChurchApiClient, limit: Option) -> Result> { - let mut path = "/events/upcoming".to_string(); - - if let Some(limit) = limit { - path.push_str(&format!("?limit={}", limit)); - } - - client.get_api(&path).await -} - -pub async fn get_event(client: &ChurchApiClient, id: &str) -> Result> { - let path = format!("/events/{}", id); - - match client.get_api(&path).await { - Ok(event) => Ok(Some(event)), - Err(crate::error::ChurchApiError::NotFound) => Ok(None), - Err(e) => Err(e), - } -} - -pub async fn create_event(client: &ChurchApiClient, event: NewEvent) -> Result { - client.post_api("/events", &event).await -} - -pub async fn update_event(client: &ChurchApiClient, id: &str, update: EventUpdate) -> Result<()> { - let path = format!("/events/{}", id); - client.put_api(&path, &update).await -} - -pub async fn delete_event(client: &ChurchApiClient, id: &str) -> Result<()> { - let path = format!("/events/{}", id); - client.delete_api(&path).await -} - -pub async fn get_featured_events(client: &ChurchApiClient, limit: Option) -> Result> { - let mut path = "/events/featured".to_string(); - - if let Some(limit) = limit { - path.push_str(&format!("?limit={}", limit)); - } - - client.get_api(&path).await -} - -pub async fn get_events_by_category(client: &ChurchApiClient, category: &str, limit: Option) -> Result> { - let mut path = format!("/events/category/{}", category); - - if let Some(limit) = limit { - path.push_str(&format!("?limit={}", limit)); - } - - client.get_api(&path).await -} - -pub async fn get_events_by_date_range( - client: &ChurchApiClient, - start_date: &str, - end_date: &str -) -> Result> { - let path = format!("/events/range?start={}&end={}", - urlencoding::encode(start_date), - urlencoding::encode(end_date) - ); - - client.get_api(&path).await -} - -pub async fn search_events(client: &ChurchApiClient, query: &str, limit: Option) -> Result> { - let mut path = format!("/events/search?q={}", urlencoding::encode(query)); - - if let Some(limit) = limit { - path.push_str(&format!("&limit={}", limit)); - } - - client.get_api(&path).await -} - -pub async fn upload_event_image(client: &ChurchApiClient, event_id: &str, image_data: Vec, filename: String) -> Result { - let path = format!("/events/{}/image", event_id); - client.upload_file(&path, image_data, filename, "image".to_string()).await -} - -// V2 API methods -pub async fn get_events_v2(client: &ChurchApiClient, params: Option) -> Result> { - let mut path = "/events".to_string(); - - if let Some(params) = params { - let mut query_params = Vec::new(); - - if let Some(page) = params.page { - query_params.push(("page", page.to_string())); - } - - if let Some(per_page) = params.per_page { - query_params.push(("per_page", per_page.to_string())); - } - - if let Some(sort) = ¶ms.sort { - query_params.push(("sort", sort.clone())); - } - - if let Some(filter) = ¶ms.filter { - query_params.push(("filter", filter.clone())); - } - - if !query_params.is_empty() { - let query_string = query_params - .iter() - .map(|(k, v)| format!("{}={}", k, urlencoding::encode(v))) - .collect::>() - .join("&"); - path.push_str(&format!("?{}", query_string)); - } - } - - client.get_api_list_with_version(&path, ApiVersion::V2).await -} - -pub async fn get_upcoming_events_v2(client: &ChurchApiClient, limit: Option) -> Result> { - let mut path = "/events/upcoming".to_string(); - - if let Some(limit) = limit { - path.push_str(&format!("?limit={}", limit)); - } - - client.get_api_with_version(&path, ApiVersion::V2).await -} - -pub async fn get_featured_events_v2(client: &ChurchApiClient, limit: Option) -> Result> { - let mut path = "/events/featured".to_string(); - - if let Some(limit) = limit { - path.push_str(&format!("?limit={}", limit)); - } - - client.get_api_with_version(&path, ApiVersion::V2).await -} - -pub async fn get_event_v2(client: &ChurchApiClient, id: &str) -> Result> { - let path = format!("/events/{}", id); - - match client.get_api_with_version(&path, ApiVersion::V2).await { - Ok(event) => Ok(Some(event)), - Err(crate::error::ChurchApiError::NotFound) => Ok(None), - Err(e) => Err(e), - } -} - -pub async fn submit_event(client: &ChurchApiClient, submission: EventSubmission) -> Result { - client.post_api("/events/submit", &submission).await -} \ No newline at end of file diff --git a/church-core/src/client/http.rs b/church-core/src/client/http.rs deleted file mode 100644 index 8dfc08b..0000000 --- a/church-core/src/client/http.rs +++ /dev/null @@ -1,402 +0,0 @@ -use crate::{ - client::ChurchApiClient, - error::{ChurchApiError, Result}, - models::{ApiResponse, ApiListResponse, ApiVersion}, - cache::CachedHttpResponse, -}; -use serde::{de::DeserializeOwned, Serialize}; -use std::{collections::HashMap, time::Duration}; - -impl ChurchApiClient { - pub(crate) async fn get(&self, path: &str) -> Result - where - T: DeserializeOwned + Send + Sync + serde::Serialize + 'static, - { - self.get_with_version(path, ApiVersion::V1).await - } - - pub(crate) async fn get_with_version(&self, path: &str, version: ApiVersion) -> Result - where - T: DeserializeOwned + Send + Sync + serde::Serialize + 'static, - { - let cache_key = format!("GET:{}:{:?}", path, version); - - // Check cache first - if self.config.enable_offline_mode { - if let Some(cached) = self.cache.get::(&cache_key).await { - return Ok(cached); - } - } - - let url = self.build_url_with_version(path, version); - let request = self.client.get(&url); - let request = self.add_auth_header(request).await; - - let response = self.send_with_retry(request).await?; - - let status = response.status(); - - if !status.is_success() { - let error_text = response.text().await?; - return Err(crate::error::ChurchApiError::Api(format!("HTTP {}: {}", status, error_text))); - } - - let response_text = response.text().await?; - - let data: T = serde_json::from_str(&response_text).map_err(|e| { - crate::error::ChurchApiError::Json(e) - })?; - - // Cache the result - if self.config.enable_offline_mode { - self.cache.set(&cache_key, &data, self.config.cache_ttl).await; - } - - Ok(data) - } - - pub(crate) async fn get_api(&self, path: &str) -> Result - where - T: DeserializeOwned + Send + Sync + serde::Serialize + 'static, - { - self.get_api_with_version(path, ApiVersion::V1).await - } - - pub(crate) async fn get_api_with_version(&self, path: &str, version: ApiVersion) -> Result - where - T: DeserializeOwned + Send + Sync + serde::Serialize + 'static, - { - let response: ApiResponse = self.get_with_version(path, version).await?; - - if response.success { - response.data.ok_or_else(|| { - ChurchApiError::Api("API returned success but no data".to_string()) - }) - } else { - Err(ChurchApiError::Api( - response.error - .or(response.message) - .unwrap_or_else(|| "Unknown API error".to_string()) - )) - } - } - - pub(crate) async fn get_api_list(&self, path: &str) -> Result> - where - T: DeserializeOwned + Send + Sync + serde::Serialize + 'static, - { - self.get_api_list_with_version(path, ApiVersion::V1).await - } - - pub(crate) async fn get_api_list_with_version(&self, path: &str, version: ApiVersion) -> Result> - where - T: DeserializeOwned + Send + Sync + serde::Serialize + 'static, - { - let response: ApiListResponse = self.get_with_version(path, version).await?; - - if response.success { - Ok(response) - } else { - Err(ChurchApiError::Api( - response.error - .or(response.message) - .unwrap_or_else(|| "Unknown API error".to_string()) - )) - } - } - - pub(crate) async fn post(&self, path: &str, data: &T) -> Result - where - T: Serialize, - R: DeserializeOwned, - { - self.post_with_version(path, data, ApiVersion::V1).await - } - - pub(crate) async fn post_with_version(&self, path: &str, data: &T, version: ApiVersion) -> Result - where - T: Serialize, - R: DeserializeOwned, - { - let url = self.build_url_with_version(path, version); - let request = self.client.post(&url).json(data); - let request = self.add_auth_header(request).await; - - let response = self.send_with_retry(request).await?; - let result: R = response.json().await?; - - // Invalidate related cache entries - self.invalidate_cache_prefix(&format!("GET:{}", path.split('?').next().unwrap_or(path))).await; - - Ok(result) - } - - pub(crate) async fn post_api(&self, path: &str, data: &T) -> Result - where - T: Serialize, - R: DeserializeOwned, - { - self.post_api_with_version(path, data, ApiVersion::V1).await - } - - pub(crate) async fn post_api_with_version(&self, path: &str, data: &T, version: ApiVersion) -> Result - where - T: Serialize, - R: DeserializeOwned, - { - let response: ApiResponse = self.post_with_version(path, data, version).await?; - - if response.success { - response.data.ok_or_else(|| { - ChurchApiError::Api("API returned success but no data".to_string()) - }) - } else { - Err(ChurchApiError::Api( - response.error - .or(response.message) - .unwrap_or_else(|| "Unknown API error".to_string()) - )) - } - } - - pub(crate) async fn put(&self, path: &str, data: &T) -> Result - where - T: Serialize, - R: DeserializeOwned, - { - let url = self.build_url(path); - let request = self.client.put(&url).json(data); - let request = self.add_auth_header(request).await; - - let response = self.send_with_retry(request).await?; - let result: R = response.json().await?; - - // Invalidate related cache entries - self.invalidate_cache_prefix(&format!("GET:{}", path.split('?').next().unwrap_or(path))).await; - - Ok(result) - } - - pub(crate) async fn put_api(&self, path: &str, data: &T) -> Result<()> - where - T: Serialize, - { - let response: ApiResponse<()> = self.put(path, data).await?; - - if response.success { - Ok(()) - } else { - Err(ChurchApiError::Api( - response.error - .or(response.message) - .unwrap_or_else(|| "Unknown API error".to_string()) - )) - } - } - - pub(crate) async fn delete(&self, path: &str) -> Result<()> { - let url = self.build_url(path); - let request = self.client.delete(&url); - let request = self.add_auth_header(request).await; - - let response = self.send_with_retry(request).await?; - - if !response.status().is_success() { - return Err(ChurchApiError::Http( - reqwest::Error::from(response.error_for_status().unwrap_err()) - )); - } - - // Invalidate related cache entries - self.invalidate_cache_prefix(&format!("GET:{}", path.split('?').next().unwrap_or(path))).await; - - Ok(()) - } - - pub(crate) async fn delete_api(&self, path: &str) -> Result<()> { - let response: ApiResponse<()> = { - let url = self.build_url(path); - let request = self.client.delete(&url); - let request = self.add_auth_header(request).await; - - let response = self.send_with_retry(request).await?; - response.json().await? - }; - - if response.success { - Ok(()) - } else { - Err(ChurchApiError::Api( - response.error - .or(response.message) - .unwrap_or_else(|| "Unknown API error".to_string()) - )) - } - } - - async fn send_with_retry(&self, request: reqwest::RequestBuilder) -> Result { - let mut attempts = 0; - let max_attempts = self.config.retry_attempts; - - loop { - attempts += 1; - - // Clone the request for potential retry - let cloned_request = request.try_clone() - .ok_or_else(|| ChurchApiError::Internal("Failed to clone request".to_string()))?; - - match cloned_request.send().await { - Ok(response) => { - let status = response.status(); - - if status.is_success() { - return Ok(response); - } else if status == reqwest::StatusCode::UNAUTHORIZED { - return Err(ChurchApiError::Auth("Unauthorized".to_string())); - } else if status == reqwest::StatusCode::FORBIDDEN { - return Err(ChurchApiError::PermissionDenied); - } else if status == reqwest::StatusCode::NOT_FOUND { - return Err(ChurchApiError::NotFound); - } else if status == reqwest::StatusCode::TOO_MANY_REQUESTS { - if attempts < max_attempts { - // Exponential backoff for rate limiting - let delay = std::time::Duration::from_millis(100 * 2_u64.pow(attempts - 1)); - tokio::time::sleep(delay).await; - continue; - } else { - return Err(ChurchApiError::RateLimit); - } - } else if status.is_server_error() && attempts < max_attempts { - // Retry on server errors - let delay = std::time::Duration::from_millis(500 * attempts as u64); - tokio::time::sleep(delay).await; - continue; - } else { - return Err(ChurchApiError::Http( - reqwest::Error::from(response.error_for_status().unwrap_err()) - )); - } - } - Err(e) => { - if attempts < max_attempts && (e.is_timeout() || e.is_connect()) { - // Retry on timeout and connection errors - let delay = std::time::Duration::from_millis(500 * attempts as u64); - tokio::time::sleep(delay).await; - continue; - } else { - return Err(ChurchApiError::Http(e)); - } - } - } - } - } - - async fn invalidate_cache_prefix(&self, prefix: &str) { - self.cache.invalidate_prefix(prefix).await; - } - - pub(crate) fn build_query_string(&self, params: &[(&str, &str)]) -> String { - if params.is_empty() { - return String::new(); - } - - let query: Vec = params - .iter() - .map(|(key, value)| format!("{}={}", urlencoding::encode(key), urlencoding::encode(value))) - .collect(); - - format!("?{}", query.join("&")) - } - - pub(crate) async fn upload_file(&self, path: &str, file_data: Vec, filename: String, field_name: String) -> Result { - let url = self.build_url(path); - - let part = reqwest::multipart::Part::bytes(file_data) - .file_name(filename) - .mime_str("application/octet-stream") - .map_err(|e| ChurchApiError::Internal(format!("Failed to create multipart: {}", e)))?; - - let form = reqwest::multipart::Form::new() - .part(field_name, part); - - let request = self.client.post(&url).multipart(form); - let request = self.add_auth_header(request).await; - - let response = self.send_with_retry(request).await?; - let result: ApiResponse = response.json().await?; - - if result.success { - result.data.ok_or_else(|| { - ChurchApiError::Api("File upload succeeded but no URL returned".to_string()) - }) - } else { - Err(ChurchApiError::Api( - result.error - .or(result.message) - .unwrap_or_else(|| "File upload failed".to_string()) - )) - } - } - - /// Fetch an image with HTTP caching support - pub async fn get_cached_image(&self, url: &str) -> Result { - // Check cache first - if let Some(cached) = self.cache.get_http_response(url).await { - println!("📸 Cache HIT for image: {}", url); - return Ok(cached); - } - - println!("📸 Cache MISS for image: {}", url); - - // Make HTTP request - let request = self.client.get(url); - let response = self.send_with_retry(request).await?; - - let status = response.status(); - let headers = response.headers().clone(); - - if !status.is_success() { - return Err(ChurchApiError::Http( - reqwest::Error::from(response.error_for_status().unwrap_err()) - )); - } - - // Extract headers we care about - let mut header_map = HashMap::new(); - for (name, value) in headers.iter() { - if let Ok(value_str) = value.to_str() { - header_map.insert(name.to_string(), value_str.to_string()); - } - } - - let content_type = headers - .get("content-type") - .and_then(|v| v.to_str().ok()) - .unwrap_or("application/octet-stream") - .to_string(); - - // Get response body - let data = response.bytes().await?.to_vec(); - - // Determine cache TTL based on content type - let ttl = if content_type.starts_with("image/") { - Duration::from_secs(24 * 60 * 60) // 24 hours for images - } else { - Duration::from_secs(5 * 60) // 5 minutes for other content - }; - - // Create cached response - let cached_response = CachedHttpResponse::new( - data, - content_type, - header_map, - status.as_u16(), - ttl, - ); - - // Store in cache - self.cache.set_http_response(url, cached_response.clone()).await; - - Ok(cached_response) - } -} \ No newline at end of file diff --git a/church-core/src/client/livestream.rs b/church-core/src/client/livestream.rs deleted file mode 100644 index 30c2fc6..0000000 --- a/church-core/src/client/livestream.rs +++ /dev/null @@ -1,35 +0,0 @@ -use crate::{ - client::ChurchApiClient, - error::Result, -}; -use serde::{Deserialize, Serialize}; -use chrono::{DateTime, Utc}; - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct StreamStatus { - pub is_live: bool, - pub last_connect_time: Option>, - pub last_disconnect_time: Option>, - pub stream_title: Option, - pub stream_url: Option, - pub viewer_count: Option, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct LiveStream { - pub last_connect_time: Option>, - pub last_disconnect_time: Option>, - pub viewer_count: Option, - pub stream_title: Option, - pub is_live: bool, -} - -/// Get current stream status from Owncast -pub async fn get_stream_status(client: &ChurchApiClient) -> Result { - client.get("/stream/status").await -} - -/// Get live stream info from Owncast -pub async fn get_live_stream(client: &ChurchApiClient) -> Result { - client.get("/stream/live").await -} \ No newline at end of file diff --git a/church-core/src/client/mod.rs b/church-core/src/client/mod.rs deleted file mode 100644 index ae36afa..0000000 --- a/church-core/src/client/mod.rs +++ /dev/null @@ -1,446 +0,0 @@ -pub mod http; -pub mod events; -pub mod bulletins; -pub mod config; -pub mod contact; -pub mod sermons; -pub mod bible; -pub mod admin; -pub mod uploads; -pub mod livestream; - -use crate::{ - cache::MemoryCache, - config::ChurchCoreConfig, - error::Result, - models::*, -}; -use std::sync::Arc; -use tokio::sync::RwLock; - -pub struct ChurchApiClient { - pub(crate) client: reqwest::Client, - pub(crate) config: ChurchCoreConfig, - pub(crate) auth_token: Arc>>, - pub(crate) cache: Arc, -} - -impl ChurchApiClient { - pub fn new(config: ChurchCoreConfig) -> Result { - let client = reqwest::Client::builder() - .timeout(config.timeout) - .connect_timeout(config.connect_timeout) - .pool_idle_timeout(std::time::Duration::from_secs(90)) - .user_agent(&config.user_agent) - .build()?; - - let cache = Arc::new(MemoryCache::new(config.max_cache_size)); - - Ok(Self { - client, - config, - auth_token: Arc::new(RwLock::new(None)), - cache, - }) - } - - pub fn with_cache(mut self, cache: Arc) -> Self { - self.cache = cache; - self - } - - pub async fn set_auth_token(&self, token: AuthToken) { - let mut auth = self.auth_token.write().await; - *auth = Some(token); - } - - pub async fn clear_auth_token(&self) { - let mut auth = self.auth_token.write().await; - *auth = None; - } - - pub async fn get_auth_token(&self) -> Option { - let auth = self.auth_token.read().await; - auth.clone() - } - - pub async fn is_authenticated(&self) -> bool { - if let Some(token) = self.get_auth_token().await { - token.is_valid() - } else { - false - } - } - - pub(crate) fn build_url(&self, path: &str) -> String { - self.build_url_with_version(path, crate::models::ApiVersion::V1) - } - - pub(crate) fn build_url_with_version(&self, path: &str, version: crate::models::ApiVersion) -> String { - if path.starts_with("http") { - path.to_string() - } else { - let base = self.config.api_base_url.trim_end_matches('/'); - let path = path.trim_start_matches('/'); - let version_prefix = version.path_prefix(); - - if base.ends_with("/api") { - format!("{}/{}{}", base, version_prefix, path) - } else { - format!("{}/api/{}{}", base, version_prefix, path) - } - } - } - - pub(crate) async fn add_auth_header(&self, builder: reqwest::RequestBuilder) -> reqwest::RequestBuilder { - if let Some(token) = self.get_auth_token().await { - if token.is_valid() { - return builder.header("Authorization", format!("{} {}", token.token_type, token.token)); - } - } - builder - } - - // Event operations - pub async fn get_upcoming_events(&self, limit: Option) -> Result> { - events::get_upcoming_events(self, limit).await - } - - pub async fn get_events(&self, params: Option) -> Result> { - events::get_events(self, params).await - } - - pub async fn get_event(&self, id: &str) -> Result> { - events::get_event(self, id).await - } - - pub async fn create_event(&self, event: NewEvent) -> Result { - events::create_event(self, event).await - } - - pub async fn update_event(&self, id: &str, update: EventUpdate) -> Result<()> { - events::update_event(self, id, update).await - } - - pub async fn delete_event(&self, id: &str) -> Result<()> { - events::delete_event(self, id).await - } - - // Bulletin operations - pub async fn get_bulletins(&self, active_only: bool) -> Result> { - bulletins::get_bulletins(self, active_only).await - } - - pub async fn get_current_bulletin(&self) -> Result> { - bulletins::get_current_bulletin(self).await - } - - pub async fn get_next_bulletin(&self) -> Result> { - bulletins::get_next_bulletin(self).await - } - - pub async fn get_bulletin(&self, id: &str) -> Result> { - bulletins::get_bulletin(self, id).await - } - - pub async fn create_bulletin(&self, bulletin: NewBulletin) -> Result { - bulletins::create_bulletin(self, bulletin).await - } - - // V2 API methods - pub async fn get_bulletins_v2(&self, params: Option) -> Result> { - bulletins::get_bulletins_v2(self, params).await - } - - pub async fn get_current_bulletin_v2(&self) -> Result> { - bulletins::get_current_bulletin_v2(self).await - } - - pub async fn get_next_bulletin_v2(&self) -> Result> { - bulletins::get_next_bulletin_v2(self).await - } - - // Configuration - pub async fn get_config(&self) -> Result { - config::get_config(self).await - } - - pub async fn get_config_by_id(&self, record_id: &str) -> Result { - config::get_config_by_id(self, record_id).await - } - - pub async fn update_config(&self, config: ChurchConfig) -> Result<()> { - config::update_config(self, config).await - } - - // Contact operations - pub async fn submit_contact_form(&self, form: ContactForm) -> Result { - contact::submit_contact_form(self, form).await - } - - pub async fn get_contact_submissions(&self, params: Option) -> Result> { - contact::get_contact_submissions(self, params).await - } - - pub async fn get_contact_submission(&self, id: &str) -> Result> { - contact::get_contact_submission(self, id).await - } - - pub async fn update_contact_submission(&self, id: &str, status: ContactStatus, response: Option) -> Result<()> { - contact::update_contact_submission(self, id, status, response).await - } - - // Sermon operations - pub async fn get_sermons(&self, params: Option) -> Result> { - sermons::get_sermons(self, params).await - } - - pub async fn search_sermons(&self, search: SermonSearch, params: Option) -> Result> { - sermons::search_sermons(self, search, params).await - } - - pub async fn get_sermon(&self, id: &str) -> Result> { - sermons::get_sermon(self, id).await - } - - pub async fn get_featured_sermons(&self, limit: Option) -> Result> { - sermons::get_featured_sermons(self, limit).await - } - - pub async fn get_recent_sermons(&self, limit: Option) -> Result> { - sermons::get_recent_sermons(self, limit).await - } - - pub async fn create_sermon(&self, sermon: NewSermon) -> Result { - sermons::create_sermon(self, sermon).await - } - - // Bible verse operations - pub async fn get_random_verse(&self) -> Result { - bible::get_random_verse(self).await - } - - pub async fn get_verse_of_the_day(&self) -> Result { - bible::get_verse_of_the_day(self).await - } - - pub async fn get_verse_by_reference(&self, reference: &str) -> Result> { - bible::get_verse_by_reference(self, reference).await - } - - pub async fn get_verses_by_category(&self, category: VerseCategory, limit: Option) -> Result> { - bible::get_verses_by_category(self, category, limit).await - } - - pub async fn search_verses(&self, query: &str, limit: Option) -> Result> { - bible::search_verses(self, query, limit).await - } - - // V2 API methods - - // Events V2 - pub async fn get_events_v2(&self, params: Option) -> Result> { - events::get_events_v2(self, params).await - } - - pub async fn get_upcoming_events_v2(&self, limit: Option) -> Result> { - events::get_upcoming_events_v2(self, limit).await - } - - pub async fn get_featured_events_v2(&self, limit: Option) -> Result> { - events::get_featured_events_v2(self, limit).await - } - - pub async fn get_event_v2(&self, id: &str) -> Result> { - events::get_event_v2(self, id).await - } - - pub async fn submit_event(&self, submission: EventSubmission) -> Result { - events::submit_event(self, submission).await - } - - // Bible V2 - pub async fn get_random_verse_v2(&self) -> Result { - bible::get_random_verse_v2(self).await - } - - pub async fn get_bible_verses_v2(&self, params: Option) -> Result> { - bible::get_bible_verses_v2(self, params).await - } - - pub async fn search_verses_v2(&self, query: &str, limit: Option) -> Result> { - bible::search_verses_v2(self, query, limit).await - } - - // Contact V2 - pub async fn submit_contact_form_v2(&self, form: ContactForm) -> Result { - contact::submit_contact_form_v2(self, form).await - } - - // Config and Schedule V2 - pub async fn get_config_v2(&self) -> Result { - config::get_config_v2(self).await - } - - pub async fn get_schedule(&self, date: Option<&str>) -> Result { - config::get_schedule(self, date).await - } - - pub async fn get_schedule_v2(&self, date: Option<&str>) -> Result { - config::get_schedule_v2(self, date).await - } - - pub async fn get_conference_data(&self) -> Result { - config::get_conference_data(self).await - } - - pub async fn get_conference_data_v2(&self) -> Result { - config::get_conference_data_v2(self).await - } - -pub async fn get_livestreams(&self) -> Result> { - sermons::get_livestreams(self).await - } - - // Owncast Live Streaming - pub async fn get_stream_status(&self) -> Result { - livestream::get_stream_status(self).await - } - - pub async fn get_live_stream(&self) -> Result { - livestream::get_live_stream(self).await - } - - // Admin operations - - // Admin Bulletins - pub async fn create_admin_bulletin(&self, bulletin: NewBulletin) -> Result { - admin::create_bulletin(self, bulletin).await - } - - pub async fn update_admin_bulletin(&self, id: &str, update: BulletinUpdate) -> Result<()> { - admin::update_bulletin(self, id, update).await - } - - pub async fn delete_admin_bulletin(&self, id: &str) -> Result<()> { - admin::delete_bulletin(self, id).await - } - - // Admin Events - Edit/delete allowed, creation via submission form only - pub async fn update_admin_event(&self, id: &str, update: EventUpdate) -> Result<()> { - admin::update_admin_event(self, id, update).await - } - - pub async fn delete_admin_event(&self, id: &str) -> Result<()> { - admin::delete_admin_event(self, id).await - } - - // Admin Pending Events - pub async fn get_pending_events(&self) -> Result> { - admin::get_pending_events(self).await - } - - pub async fn approve_pending_event(&self, id: &str) -> Result<()> { - admin::approve_pending_event(self, id).await - } - - pub async fn reject_pending_event(&self, id: &str) -> Result<()> { - admin::reject_pending_event(self, id).await - } - - pub async fn delete_pending_event(&self, id: &str) -> Result<()> { - admin::delete_pending_event(self, id).await - } - - // Admin Users - pub async fn get_admin_users(&self) -> Result> { - admin::get_users(self).await - } - - // Admin Schedule - pub async fn create_admin_schedule(&self, schedule: NewSchedule) -> Result { - admin::create_schedule(self, schedule).await - } - - pub async fn update_admin_schedule(&self, date: &str, update: ScheduleUpdate) -> Result<()> { - admin::update_schedule(self, date, update).await - } - - pub async fn delete_admin_schedule(&self, date: &str) -> Result<()> { - admin::delete_schedule(self, date).await - } - - pub async fn get_all_admin_schedules(&self) -> Result> { - admin::get_all_schedules(self).await - } - - pub async fn get_admin_config(&self) -> Result { - admin::get_admin_config(self).await - } - - // File Upload operations - pub async fn upload_bulletin_pdf(&self, bulletin_id: &str, file_data: Vec, filename: String) -> Result { - uploads::upload_bulletin_pdf(self, bulletin_id, file_data, filename).await - } - - pub async fn upload_bulletin_cover(&self, bulletin_id: &str, file_data: Vec, filename: String) -> Result { - uploads::upload_bulletin_cover(self, bulletin_id, file_data, filename).await - } - - pub async fn upload_event_image(&self, event_id: &str, file_data: Vec, filename: String) -> Result { - uploads::upload_event_image(self, event_id, file_data, filename).await - } - - // Utility methods - pub async fn health_check(&self) -> Result { - let url = self.build_url("/health"); - let response = self.client.get(&url).send().await?; - Ok(response.status().is_success()) - } - - pub async fn clear_cache(&self) { - self.cache.clear().await; - } - - pub async fn get_cache_stats(&self) -> (usize, usize) { - (self.cache.len().await, self.config.max_cache_size) - } - - // Admin Auth operations - pub async fn admin_login(&self, email: &str, password: &str) -> Result { - let url = self.build_url("/auth/login"); - let request_body = serde_json::json!({ - "email": email, - "password": password - }); - - let response = self.client - .post(&url) - .json(&request_body) - .send() - .await?; - - if response.status().is_success() { - let auth_response: serde_json::Value = response.json().await?; - if let Some(token) = auth_response.get("token").and_then(|t| t.as_str()) { - Ok(token.to_string()) - } else { - Err(crate::error::ChurchApiError::Api("No token in response".to_string())) - } - } else { - let error_text = response.text().await.unwrap_or_else(|_| "Unknown error".to_string()); - Err(crate::error::ChurchApiError::Api(error_text)) - } - } - - pub async fn validate_admin_token(&self, token: &str) -> Result { - let url = self.build_url("/admin/events/pending"); - let response = self.client - .get(&url) - .header("Authorization", format!("Bearer {}", token)) - .send() - .await?; - - Ok(response.status().is_success()) - } -} \ No newline at end of file diff --git a/church-core/src/client/sermons.rs b/church-core/src/client/sermons.rs deleted file mode 100644 index 237264e..0000000 --- a/church-core/src/client/sermons.rs +++ /dev/null @@ -1,237 +0,0 @@ -use crate::{ - client::ChurchApiClient, - error::Result, - models::{Sermon, ApiSermon, NewSermon, SermonSearch, PaginationParams, ApiListResponse, DeviceCapabilities}, -}; - -pub async fn get_sermons(client: &ChurchApiClient, params: Option) -> Result> { - let mut path = "/sermons".to_string(); - - if let Some(params) = params { - let mut query_params = Vec::new(); - - if let Some(page) = params.page { - query_params.push(("page", page.to_string())); - } - - if let Some(per_page) = params.per_page { - query_params.push(("per_page", per_page.to_string())); - } - - if let Some(sort) = ¶ms.sort { - query_params.push(("sort", sort.clone())); - } - - if let Some(filter) = ¶ms.filter { - query_params.push(("filter", filter.clone())); - } - - if !query_params.is_empty() { - let query_string = query_params - .iter() - .map(|(k, v)| format!("{}={}", k, urlencoding::encode(v))) - .collect::>() - .join("&"); - path.push_str(&format!("?{}", query_string)); - } - } - - client.get_api_list(&path).await -} - -pub async fn search_sermons( - client: &ChurchApiClient, - search: SermonSearch, - params: Option -) -> Result> { - let mut path = "/sermons/search".to_string(); - let mut query_params = Vec::new(); - - if let Some(query) = &search.query { - query_params.push(("q", query.clone())); - } - - if let Some(speaker) = &search.speaker { - query_params.push(("speaker", speaker.clone())); - } - - if let Some(category) = &search.category { - query_params.push(("category", format!("{:?}", category).to_lowercase())); - } - - if let Some(series) = &search.series { - query_params.push(("series", series.clone())); - } - - if let Some(featured_only) = search.featured_only { - if featured_only { - query_params.push(("featured", "true".to_string())); - } - } - - if let Some(has_video) = search.has_video { - if has_video { - query_params.push(("has_video", "true".to_string())); - } - } - - if let Some(has_audio) = search.has_audio { - if has_audio { - query_params.push(("has_audio", "true".to_string())); - } - } - - if let Some(params) = params { - if let Some(page) = params.page { - query_params.push(("page", page.to_string())); - } - - if let Some(per_page) = params.per_page { - query_params.push(("per_page", per_page.to_string())); - } - } - - if !query_params.is_empty() { - let query_string = query_params - .iter() - .map(|(k, v)| format!("{}={}", k, urlencoding::encode(v))) - .collect::>() - .join("&"); - path.push_str(&format!("?{}", query_string)); - } - - client.get_api_list(&path).await -} - -pub async fn get_sermon(client: &ChurchApiClient, id: &str) -> Result> { - let path = format!("/sermons/{}", id); - - match client.get_api(&path).await { - Ok(sermon) => Ok(Some(sermon)), - Err(crate::error::ChurchApiError::NotFound) => Ok(None), - Err(e) => Err(e), - } -} - -pub async fn get_featured_sermons(client: &ChurchApiClient, limit: Option) -> Result> { - let mut path = "/sermons/featured".to_string(); - - if let Some(limit) = limit { - path.push_str(&format!("?limit={}", limit)); - } - - client.get_api(&path).await -} - -// Helper function to convert seconds to human readable duration -fn format_duration_seconds(seconds: u32) -> String { - let hours = seconds / 3600; - let minutes = (seconds % 3600) / 60; - let remaining_seconds = seconds % 60; - - if hours > 0 { - format!("{}:{:02}:{:02}", hours, minutes, remaining_seconds) - } else { - format!("{}:{:02}", minutes, remaining_seconds) - } -} - -// Shared function to convert API sermon/livestream data to Sermon model -fn convert_api_sermon_to_sermon(api_sermon: ApiSermon, category: crate::models::sermon::SermonCategory) -> Sermon { - // Parse date string to DateTime if available - let date = if let Some(date_str) = &api_sermon.date { - chrono::DateTime::parse_from_str(&format!("{} 00:00:00 +0000", date_str), "%Y-%m-%d %H:%M:%S %z") - .unwrap_or_else(|_| chrono::Utc::now().into()) - .with_timezone(&chrono::Utc) - } else { - chrono::Utc::now() - }; - - // Duration is already in string format from the API, so use it directly - let duration_string = Some(api_sermon.duration.clone()); - - // Generate optimal streaming URL for the device - let media_url = if !api_sermon.id.is_empty() { - let base_url = "https://api.rockvilletollandsda.church"; // TODO: Get from config - let streaming_url = DeviceCapabilities::get_optimal_streaming_url(base_url, &api_sermon.id); - Some(streaming_url.url) - } else { - api_sermon.video_url.clone() - }; - - Sermon { - id: api_sermon.id.clone(), - title: api_sermon.title, - speaker: api_sermon.speaker.unwrap_or("Unknown".to_string()), - description: api_sermon.description.unwrap_or_default(), - date, - scripture_reference: api_sermon.scripture_reading.unwrap_or_default(), - series: None, - duration_string, - media_url, - audio_url: api_sermon.audio_url, - video_url: api_sermon.video_url, - transcript: None, - thumbnail: api_sermon.thumbnail, - tags: None, - category, - is_featured: false, - view_count: 0, - download_count: 0, - created_at: date, - updated_at: date, - } -} - -pub async fn get_recent_sermons(client: &ChurchApiClient, limit: Option) -> Result> { - let mut path = "/sermons".to_string(); - - if let Some(limit) = limit { - path.push_str(&format!("?limit={}", limit)); - } - - // The new API returns a wrapper with "sermons" array - #[derive(serde::Deserialize, serde::Serialize)] - struct SermonsResponse { - success: bool, - data: Vec, - message: Option, - } - - let response: SermonsResponse = client.get(&path).await?; - - // Convert using shared logic - let sermons = response.data.into_iter() - .map(|api_sermon| convert_api_sermon_to_sermon(api_sermon, crate::models::sermon::SermonCategory::Regular)) - .collect(); - - Ok(sermons) -} - -pub async fn create_sermon(client: &ChurchApiClient, sermon: NewSermon) -> Result { - client.post_api("/sermons", &sermon).await -} - -// Livestreams endpoint - reuses ApiSermon since format is identical - -pub async fn get_livestreams(client: &ChurchApiClient) -> Result> { - // Use the new API endpoint for livestreams - let path = "/livestreams"; - - // The new API returns a wrapper with "data" array (same format as sermons endpoint) - #[derive(serde::Deserialize, serde::Serialize)] - struct LivestreamsResponse { - success: bool, - data: Vec, - message: Option, - } - - let response: LivestreamsResponse = client.get(path).await?; - - // Convert using shared logic - same as regular sermons but different category - let sermons = response.data.into_iter() - .map(|api_sermon| convert_api_sermon_to_sermon(api_sermon, crate::models::sermon::SermonCategory::LivestreamArchive)) - .collect(); - - Ok(sermons) -} \ No newline at end of file diff --git a/church-core/src/client/uploads.rs b/church-core/src/client/uploads.rs deleted file mode 100644 index 0919cd7..0000000 --- a/church-core/src/client/uploads.rs +++ /dev/null @@ -1,119 +0,0 @@ -use crate::{ - client::ChurchApiClient, - error::Result, - models::UploadResponse, -}; - -/// Upload PDF file for a bulletin -pub async fn upload_bulletin_pdf( - client: &ChurchApiClient, - bulletin_id: &str, - file_data: Vec, - filename: String, -) -> Result { - let path = format!("/upload/bulletins/{}/pdf", bulletin_id); - - let url = client.build_url(&path); - - let part = reqwest::multipart::Part::bytes(file_data) - .file_name(filename) - .mime_str("application/pdf") - .map_err(|e| crate::error::ChurchApiError::Internal(format!("Failed to create multipart: {}", e)))?; - - let form = reqwest::multipart::Form::new() - .part("file", part); - - let request = client.client.post(&url).multipart(form); - let request = client.add_auth_header(request).await; - - let response = request.send().await?; - let result: crate::models::ApiResponse = response.json().await?; - - if result.success { - result.data.ok_or_else(|| { - crate::error::ChurchApiError::Api("File upload succeeded but no response returned".to_string()) - }) - } else { - Err(crate::error::ChurchApiError::Api( - result.error - .or(result.message) - .unwrap_or_else(|| "File upload failed".to_string()) - )) - } -} - -/// Upload cover image for a bulletin -pub async fn upload_bulletin_cover( - client: &ChurchApiClient, - bulletin_id: &str, - file_data: Vec, - filename: String, -) -> Result { - let path = format!("/upload/bulletins/{}/cover", bulletin_id); - - let url = client.build_url(&path); - - let part = reqwest::multipart::Part::bytes(file_data) - .file_name(filename) - .mime_str("image/jpeg") - .map_err(|e| crate::error::ChurchApiError::Internal(format!("Failed to create multipart: {}", e)))?; - - let form = reqwest::multipart::Form::new() - .part("file", part); - - let request = client.client.post(&url).multipart(form); - let request = client.add_auth_header(request).await; - - let response = request.send().await?; - let result: crate::models::ApiResponse = response.json().await?; - - if result.success { - result.data.ok_or_else(|| { - crate::error::ChurchApiError::Api("File upload succeeded but no response returned".to_string()) - }) - } else { - Err(crate::error::ChurchApiError::Api( - result.error - .or(result.message) - .unwrap_or_else(|| "File upload failed".to_string()) - )) - } -} - -/// Upload image for an event -pub async fn upload_event_image( - client: &ChurchApiClient, - event_id: &str, - file_data: Vec, - filename: String, -) -> Result { - let path = format!("/upload/events/{}/image", event_id); - - let url = client.build_url(&path); - - let part = reqwest::multipart::Part::bytes(file_data) - .file_name(filename) - .mime_str("image/jpeg") - .map_err(|e| crate::error::ChurchApiError::Internal(format!("Failed to create multipart: {}", e)))?; - - let form = reqwest::multipart::Form::new() - .part("file", part); - - let request = client.client.post(&url).multipart(form); - let request = client.add_auth_header(request).await; - - let response = request.send().await?; - let result: crate::models::ApiResponse = response.json().await?; - - if result.success { - result.data.ok_or_else(|| { - crate::error::ChurchApiError::Api("File upload succeeded but no response returned".to_string()) - }) - } else { - Err(crate::error::ChurchApiError::Api( - result.error - .or(result.message) - .unwrap_or_else(|| "File upload failed".to_string()) - )) - } -} \ No newline at end of file diff --git a/church-core/src/config.rs b/church-core/src/config.rs deleted file mode 100644 index d57a0fa..0000000 --- a/church-core/src/config.rs +++ /dev/null @@ -1,69 +0,0 @@ -use std::time::Duration; - -#[derive(Debug, Clone)] -pub struct ChurchCoreConfig { - pub api_base_url: String, - pub cache_ttl: Duration, - pub timeout: Duration, - pub connect_timeout: Duration, - pub retry_attempts: u32, - pub enable_offline_mode: bool, - pub max_cache_size: usize, - pub user_agent: String, -} - -impl Default for ChurchCoreConfig { - fn default() -> Self { - Self { - api_base_url: "https://api.rockvilletollandsda.church".to_string(), - cache_ttl: Duration::from_secs(300), // 5 minutes - timeout: Duration::from_secs(10), - connect_timeout: Duration::from_secs(5), - retry_attempts: 3, - enable_offline_mode: true, - max_cache_size: 1000, - user_agent: format!("church-core/{}", env!("CARGO_PKG_VERSION")), - } - } -} - -impl ChurchCoreConfig { - pub fn new() -> Self { - Self::default() - } - - pub fn with_base_url(mut self, url: impl Into) -> Self { - self.api_base_url = url.into(); - self - } - - pub fn with_cache_ttl(mut self, ttl: Duration) -> Self { - self.cache_ttl = ttl; - self - } - - pub fn with_timeout(mut self, timeout: Duration) -> Self { - self.timeout = timeout; - self - } - - pub fn with_retry_attempts(mut self, attempts: u32) -> Self { - self.retry_attempts = attempts; - self - } - - pub fn with_offline_mode(mut self, enabled: bool) -> Self { - self.enable_offline_mode = enabled; - self - } - - pub fn with_max_cache_size(mut self, size: usize) -> Self { - self.max_cache_size = size; - self - } - - pub fn with_user_agent(mut self, agent: impl Into) -> Self { - self.user_agent = agent.into(); - self - } -} \ No newline at end of file diff --git a/church-core/src/error.rs b/church-core/src/error.rs deleted file mode 100644 index 2020826..0000000 --- a/church-core/src/error.rs +++ /dev/null @@ -1,62 +0,0 @@ -use thiserror::Error; - -pub type Result = std::result::Result; - -#[derive(Debug, Error)] -pub enum ChurchApiError { - #[error("HTTP request failed: {0}")] - Http(#[from] reqwest::Error), - - #[error("JSON parsing failed: {0}")] - Json(#[from] serde_json::Error), - - #[error("Date parsing failed: {0}")] - DateParse(String), - - #[error("API returned error: {0}")] - Api(String), - - #[error("Authentication failed: {0}")] - Auth(String), - - #[error("Cache error: {0}")] - Cache(String), - - #[error("Validation error: {0}")] - Validation(String), - - #[error("Configuration error: {0}")] - Config(String), - - #[error("Network error: {0}")] - Network(String), - - #[error("Timeout error: operation took too long")] - Timeout, - - #[error("Rate limit exceeded")] - RateLimit, - - #[error("Resource not found")] - NotFound, - - #[error("Permission denied")] - PermissionDenied, - - #[error("Internal error: {0}")] - Internal(String), -} - -impl ChurchApiError { - pub fn is_network_error(&self) -> bool { - matches!(self, Self::Http(_) | Self::Network(_) | Self::Timeout) - } - - pub fn is_auth_error(&self) -> bool { - matches!(self, Self::Auth(_) | Self::PermissionDenied) - } - - pub fn is_temporary(&self) -> bool { - matches!(self, Self::Timeout | Self::RateLimit | Self::Network(_)) - } -} \ No newline at end of file diff --git a/church-core/src/ffi.rs b/church-core/src/ffi.rs deleted file mode 100644 index fb839df..0000000 --- a/church-core/src/ffi.rs +++ /dev/null @@ -1,12 +0,0 @@ -// FFI module for church-core -// This module is only compiled when the ffi feature is enabled - -use crate::{ChurchApiClient, ChurchCoreConfig, ChurchApiError}; - -// Re-export for UniFFI -pub use crate::{ - models::*, - ChurchApiClient, - ChurchCoreConfig, - ChurchApiError, -}; \ No newline at end of file diff --git a/church-core/src/lib.rs b/church-core/src/lib.rs deleted file mode 100644 index 0c48f21..0000000 --- a/church-core/src/lib.rs +++ /dev/null @@ -1,18 +0,0 @@ -pub mod client; -pub mod models; -pub mod auth; -pub mod cache; -pub mod utils; -pub mod error; -pub mod config; -pub mod api; -pub use client::ChurchApiClient; -pub use config::ChurchCoreConfig; -pub use error::{ChurchApiError, Result}; -pub use models::*; -pub use cache::*; -pub use api::*; - -#[cfg(feature = "wasm")] -pub mod wasm; - diff --git a/church-core/src/models/admin.rs b/church-core/src/models/admin.rs deleted file mode 100644 index 47f6e34..0000000 --- a/church-core/src/models/admin.rs +++ /dev/null @@ -1,144 +0,0 @@ -use chrono::{DateTime, Utc}; -use serde::{Deserialize, Serialize}; - -/// User information for admin user management -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct User { - pub id: String, - pub username: String, - pub email: Option, - pub role: AdminUserRole, - pub is_active: bool, - pub created_at: DateTime, - pub updated_at: DateTime, - pub last_login: Option>, -} - -#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] -pub enum AdminUserRole { - #[serde(rename = "admin")] - Admin, - #[serde(rename = "moderator")] - Moderator, - #[serde(rename = "user")] - User, -} - -/// Schedule data -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct Schedule { - pub id: String, - pub date: String, // YYYY-MM-DD format - pub sabbath_school: Option, - pub divine_worship: Option, - pub scripture_reading: Option, - pub sunset: Option, - pub special_notes: Option, - // Personnel assignments - pub song_leader: Option, - pub ss_teacher: Option, - pub ss_leader: Option, - pub mission_story: Option, - pub special_program: Option, - pub sermon_speaker: Option, - pub scripture: Option, - pub offering: Option, - pub deacons: Option, - pub special_music: Option, - pub childrens_story: Option, - pub afternoon_program: Option, - pub created_at: DateTime, - pub updated_at: DateTime, -} - -/// Conference data -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct ConferenceData { - pub id: String, - pub name: String, - pub website: Option, - pub contact_info: Option, - pub leadership: Option>, - pub announcements: Option>, - pub updated_at: DateTime, -} - -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct ConferenceLeader { - pub name: String, - pub title: String, - pub email: Option, - pub phone: Option, -} - -/// New schedule creation -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct NewSchedule { - pub date: String, // YYYY-MM-DD format - pub sabbath_school: Option, - pub divine_worship: Option, - pub scripture_reading: Option, - pub sunset: Option, - pub special_notes: Option, - // Personnel assignments - pub song_leader: Option, - pub ss_teacher: Option, - pub ss_leader: Option, - pub mission_story: Option, - pub special_program: Option, - pub sermon_speaker: Option, - pub scripture: Option, - pub offering: Option, - pub deacons: Option, - pub special_music: Option, - pub childrens_story: Option, - pub afternoon_program: Option, -} - -/// Schedule update -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct ScheduleUpdate { - #[serde(skip_serializing_if = "Option::is_none")] - pub sabbath_school: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub divine_worship: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub scripture_reading: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub sunset: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub special_notes: Option, - // Personnel assignments - #[serde(skip_serializing_if = "Option::is_none")] - pub song_leader: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub ss_teacher: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub ss_leader: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub mission_story: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub special_program: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub sermon_speaker: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub scripture: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub offering: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub deacons: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub special_music: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub childrens_story: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub afternoon_program: Option, -} - -/// File upload response -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct UploadResponse { - pub file_path: String, - pub pdf_path: Option, // Full URL to the uploaded file - pub message: String, -} \ No newline at end of file diff --git a/church-core/src/models/auth.rs b/church-core/src/models/auth.rs deleted file mode 100644 index 6e22728..0000000 --- a/church-core/src/models/auth.rs +++ /dev/null @@ -1,276 +0,0 @@ -use chrono::{DateTime, Utc}; -use serde::{Deserialize, Serialize}; - -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct AuthToken { - pub token: String, - pub token_type: String, - pub expires_at: DateTime, - pub user_id: Option, - pub user_name: Option, - pub user_email: Option, - pub permissions: Vec, -} - -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct LoginRequest { - pub identity: String, // email or username - pub password: String, -} - -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct LoginResponse { - pub token: String, - pub user: AuthUser, - pub expires_at: DateTime, -} - -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct AuthUser { - pub id: String, - pub email: String, - pub name: String, - pub username: Option, - pub avatar: Option, - pub verified: bool, - pub role: UserRole, - pub permissions: Vec, - pub created_at: DateTime, - pub updated_at: DateTime, - pub last_login: Option>, -} - -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct RefreshTokenRequest { - pub refresh_token: String, -} - -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct RefreshTokenResponse { - pub token: String, - pub expires_at: DateTime, -} - -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct PasswordResetRequest { - pub email: String, -} - -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct PasswordResetConfirm { - pub token: String, - pub new_password: String, -} - -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct ChangePasswordRequest { - pub current_password: String, - pub new_password: String, -} - -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct RegisterRequest { - pub email: String, - pub password: String, - pub name: String, - pub username: Option, -} - -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct EmailVerificationRequest { - pub token: String, -} - -#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] -pub enum UserRole { - #[serde(rename = "admin")] - Admin, - #[serde(rename = "pastor")] - Pastor, - #[serde(rename = "elder")] - Elder, - #[serde(rename = "deacon")] - Deacon, - #[serde(rename = "ministry_leader")] - MinistryLeader, - #[serde(rename = "member")] - Member, - #[serde(rename = "visitor")] - Visitor, - #[serde(rename = "guest")] - Guest, -} - -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct PocketBaseAuthResponse { - pub token: String, - pub record: PocketBaseUser, -} - -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct PocketBaseUser { - pub id: String, - pub email: String, - pub name: String, - pub username: Option, - pub avatar: Option, - pub verified: bool, - pub created: DateTime, - pub updated: DateTime, -} - - - -impl AuthToken { - pub fn is_expired(&self) -> bool { - Utc::now() > self.expires_at - } - - pub fn is_valid(&self) -> bool { - !self.is_expired() && !self.token.is_empty() - } - - pub fn expires_in_seconds(&self) -> i64 { - (self.expires_at - Utc::now()).num_seconds().max(0) - } - - pub fn expires_in_minutes(&self) -> i64 { - (self.expires_at - Utc::now()).num_minutes().max(0) - } - - pub fn has_permission(&self, permission: &str) -> bool { - self.permissions.contains(&permission.to_string()) - } - - pub fn has_any_permission(&self, permissions: &[&str]) -> bool { - permissions.iter().any(|p| self.has_permission(p)) - } - - pub fn has_all_permissions(&self, permissions: &[&str]) -> bool { - permissions.iter().all(|p| self.has_permission(p)) - } -} - -impl AuthUser { - pub fn is_admin(&self) -> bool { - matches!(self.role, UserRole::Admin) - } - - pub fn is_pastor(&self) -> bool { - matches!(self.role, UserRole::Pastor) - } - - pub fn is_leadership(&self) -> bool { - matches!( - self.role, - UserRole::Admin | UserRole::Pastor | UserRole::Elder | UserRole::Deacon - ) - } - - pub fn is_member(&self) -> bool { - matches!( - self.role, - UserRole::Admin - | UserRole::Pastor - | UserRole::Elder - | UserRole::Deacon - | UserRole::MinistryLeader - | UserRole::Member - ) - } - - pub fn can_edit_content(&self) -> bool { - matches!( - self.role, - UserRole::Admin | UserRole::Pastor | UserRole::Elder | UserRole::MinistryLeader - ) - } - - pub fn can_moderate(&self) -> bool { - matches!( - self.role, - UserRole::Admin | UserRole::Pastor | UserRole::Elder | UserRole::Deacon - ) - } - - pub fn display_name(&self) -> String { - if !self.name.is_empty() { - self.name.clone() - } else if let Some(username) = &self.username { - username.clone() - } else { - self.email.clone() - } - } -} - -impl UserRole { - pub fn display_name(&self) -> &'static str { - match self { - UserRole::Admin => "Administrator", - UserRole::Pastor => "Pastor", - UserRole::Elder => "Elder", - UserRole::Deacon => "Deacon", - UserRole::MinistryLeader => "Ministry Leader", - UserRole::Member => "Member", - UserRole::Visitor => "Visitor", - UserRole::Guest => "Guest", - } - } - - pub fn permissions(&self) -> Vec<&'static str> { - match self { - UserRole::Admin => vec![ - "admin.*", - "events.*", - "bulletins.*", - "sermons.*", - "contacts.*", - "users.*", - "config.*", - ], - UserRole::Pastor => vec![ - "events.*", - "bulletins.*", - "sermons.*", - "contacts.read", - "contacts.respond", - "users.read", - ], - UserRole::Elder => vec![ - "events.read", - "events.create", - "bulletins.read", - "sermons.read", - "contacts.read", - "contacts.respond", - ], - UserRole::Deacon => vec![ - "events.read", - "bulletins.read", - "sermons.read", - "contacts.read", - ], - UserRole::MinistryLeader => vec![ - "events.read", - "events.create", - "bulletins.read", - "sermons.read", - ], - UserRole::Member => vec![ - "events.read", - "bulletins.read", - "sermons.read", - ], - UserRole::Visitor => vec![ - "events.read", - "bulletins.read", - "sermons.read", - ], - UserRole::Guest => vec![ - "events.read", - "bulletins.read", - ], - } - } -} \ No newline at end of file diff --git a/church-core/src/models/bible.rs b/church-core/src/models/bible.rs deleted file mode 100644 index 8b4b14e..0000000 --- a/church-core/src/models/bible.rs +++ /dev/null @@ -1,136 +0,0 @@ -use serde::{Deserialize, Serialize}; - -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct BibleVerse { - pub text: String, - pub reference: String, - #[serde(skip_serializing_if = "Option::is_none")] - pub version: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub book: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub chapter: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub verse: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub category: Option, -} - -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct VerseOfTheDay { - pub verse: BibleVerse, - pub date: chrono::NaiveDate, - #[serde(skip_serializing_if = "Option::is_none")] - pub commentary: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub theme: Option, -} - -#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] -pub enum VerseCategory { - #[serde(rename = "comfort")] - Comfort, - #[serde(rename = "hope")] - Hope, - #[serde(rename = "faith")] - Faith, - #[serde(rename = "love")] - Love, - #[serde(rename = "peace")] - Peace, - #[serde(rename = "strength")] - Strength, - #[serde(rename = "wisdom")] - Wisdom, - #[serde(rename = "guidance")] - Guidance, - #[serde(rename = "forgiveness")] - Forgiveness, - #[serde(rename = "salvation")] - Salvation, - #[serde(rename = "prayer")] - Prayer, - #[serde(rename = "praise")] - Praise, - #[serde(rename = "thanksgiving")] - Thanksgiving, - #[serde(rename = "other")] - Other, -} - -impl BibleVerse { - pub fn new(text: String, reference: String) -> Self { - Self { - text, - reference, - version: None, - book: None, - chapter: None, - verse: None, - category: None, - } - } - - pub fn with_version(mut self, version: String) -> Self { - self.version = Some(version); - self - } - - pub fn with_book(mut self, book: String) -> Self { - self.book = Some(book); - self - } - - pub fn with_location(mut self, chapter: u32, verse: u32) -> Self { - self.chapter = Some(chapter); - self.verse = Some(verse); - self - } - - pub fn with_category(mut self, category: VerseCategory) -> Self { - self.category = Some(category); - self - } -} - -impl VerseOfTheDay { - pub fn new(verse: BibleVerse, date: chrono::NaiveDate) -> Self { - Self { - verse, - date, - commentary: None, - theme: None, - } - } - - pub fn with_commentary(mut self, commentary: String) -> Self { - self.commentary = Some(commentary); - self - } - - pub fn with_theme(mut self, theme: String) -> Self { - self.theme = Some(theme); - self - } -} - -impl VerseCategory { - pub fn display_name(&self) -> &'static str { - match self { - VerseCategory::Comfort => "Comfort", - VerseCategory::Hope => "Hope", - VerseCategory::Faith => "Faith", - VerseCategory::Love => "Love", - VerseCategory::Peace => "Peace", - VerseCategory::Strength => "Strength", - VerseCategory::Wisdom => "Wisdom", - VerseCategory::Guidance => "Guidance", - VerseCategory::Forgiveness => "Forgiveness", - VerseCategory::Salvation => "Salvation", - VerseCategory::Prayer => "Prayer", - VerseCategory::Praise => "Praise", - VerseCategory::Thanksgiving => "Thanksgiving", - VerseCategory::Other => "Other", - } - } -} \ No newline at end of file diff --git a/church-core/src/models/bulletin.rs b/church-core/src/models/bulletin.rs deleted file mode 100644 index c18ad35..0000000 --- a/church-core/src/models/bulletin.rs +++ /dev/null @@ -1,240 +0,0 @@ -use chrono::{DateTime, NaiveDate, Utc}; -use serde::{Deserialize, Serialize}; - -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct Bulletin { - pub id: String, - pub title: String, - pub date: NaiveDate, - pub sabbath_school: String, - pub divine_worship: String, - pub scripture_reading: String, - pub sunset: String, - #[serde(skip_serializing_if = "Option::is_none")] - pub pdf_path: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub cover_image: Option, - #[serde(default)] - pub is_active: bool, - #[serde(skip_serializing_if = "Option::is_none")] - pub announcements: Option>, - #[serde(skip_serializing_if = "Option::is_none")] - pub hymns: Option>, - #[serde(skip_serializing_if = "Option::is_none")] - pub special_music: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub offering_type: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub sermon_title: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub speaker: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub liturgy: Option>, - pub created_at: DateTime, - pub updated_at: DateTime, -} - -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct NewBulletin { - pub title: String, - pub date: NaiveDate, - pub sabbath_school: String, - pub divine_worship: String, - pub scripture_reading: String, - pub sunset: String, - #[serde(skip_serializing_if = "Option::is_none")] - pub pdf_path: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub cover_image: Option, - #[serde(default)] - pub is_active: bool, - #[serde(skip_serializing_if = "Option::is_none")] - pub announcements: Option>, - #[serde(skip_serializing_if = "Option::is_none")] - pub hymns: Option>, - #[serde(skip_serializing_if = "Option::is_none")] - pub special_music: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub offering_type: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub sermon_title: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub speaker: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub liturgy: Option>, -} - -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct BulletinUpdate { - #[serde(skip_serializing_if = "Option::is_none")] - pub title: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub date: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub sabbath_school: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub divine_worship: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub scripture_reading: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub sunset: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub pdf_path: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub cover_image: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub is_active: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub announcements: Option>, - #[serde(skip_serializing_if = "Option::is_none")] - pub hymns: Option>, - #[serde(skip_serializing_if = "Option::is_none")] - pub special_music: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub offering_type: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub sermon_title: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub speaker: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub liturgy: Option>, -} - -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct Announcement { - pub id: Option, - pub title: String, - pub content: String, - #[serde(skip_serializing_if = "Option::is_none")] - pub category: Option, - #[serde(default)] - pub is_urgent: bool, - #[serde(skip_serializing_if = "Option::is_none")] - pub contact_info: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub expires_at: Option>, -} - -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct BulletinHymn { - pub number: u32, - pub title: String, - #[serde(skip_serializing_if = "Option::is_none")] - pub category: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub verses: Option>, -} - -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct LiturgyItem { - pub order: u32, - pub item_type: LiturgyType, - pub title: String, - #[serde(skip_serializing_if = "Option::is_none")] - pub description: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub leader: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub scripture_reference: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub hymn_number: Option, -} - -#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] -pub enum AnnouncementCategory { - #[serde(rename = "general")] - General, - #[serde(rename = "ministry")] - Ministry, - #[serde(rename = "social")] - Social, - #[serde(rename = "urgent")] - Urgent, - #[serde(rename = "prayer")] - Prayer, - #[serde(rename = "community")] - Community, -} - -#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] -pub enum HymnCategory { - #[serde(rename = "opening")] - Opening, - #[serde(rename = "closing")] - Closing, - #[serde(rename = "offertory")] - Offertory, - #[serde(rename = "communion")] - Communion, - #[serde(rename = "special")] - Special, -} - -#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] -pub enum LiturgyType { - #[serde(rename = "prelude")] - Prelude, - #[serde(rename = "welcome")] - Welcome, - #[serde(rename = "opening_hymn")] - OpeningHymn, - #[serde(rename = "prayer")] - Prayer, - #[serde(rename = "scripture")] - Scripture, - #[serde(rename = "children_story")] - ChildrenStory, - #[serde(rename = "hymn")] - Hymn, - #[serde(rename = "offertory")] - Offertory, - #[serde(rename = "sermon")] - Sermon, - #[serde(rename = "closing_hymn")] - ClosingHymn, - #[serde(rename = "benediction")] - Benediction, - #[serde(rename = "postlude")] - Postlude, - #[serde(rename = "announcements")] - Announcements, - #[serde(rename = "special_music")] - SpecialMusic, -} - -impl Bulletin { - pub fn has_pdf(&self) -> bool { - self.pdf_path.is_some() - } - - pub fn has_cover_image(&self) -> bool { - self.cover_image.is_some() - } - - pub fn active_announcements(&self) -> Vec<&Announcement> { - self.announcements - .as_ref() - .map(|announcements| { - announcements - .iter() - .filter(|announcement| { - announcement.expires_at - .map_or(true, |expires| expires > Utc::now()) - }) - .collect() - }) - .unwrap_or_default() - } - - pub fn urgent_announcements(&self) -> Vec<&Announcement> { - self.announcements - .as_ref() - .map(|announcements| { - announcements - .iter() - .filter(|announcement| announcement.is_urgent) - .collect() - }) - .unwrap_or_default() - } -} \ No newline at end of file diff --git a/church-core/src/models/client_models.rs b/church-core/src/models/client_models.rs deleted file mode 100644 index a3ff815..0000000 --- a/church-core/src/models/client_models.rs +++ /dev/null @@ -1,344 +0,0 @@ -use serde::{Deserialize, Serialize}; -use crate::models::event::{Event, RecurringType}; -use crate::models::bulletin::Bulletin; -use crate::models::sermon::Sermon; -use chrono::{DateTime, Utc, Local, Timelike}; - -/// Client-facing Event model with both raw timestamps and formatted display strings -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct ClientEvent { - pub id: String, - pub title: String, - pub description: String, - - // Raw ISO timestamps for calendar/system APIs - #[serde(rename = "start_time")] - pub start_time: String, // ISO timestamp like "2025-08-13T05:00:00-04:00" - #[serde(rename = "end_time")] - pub end_time: String, // ISO timestamp like "2025-08-13T06:00:00-04:00" - - // Formatted display strings for UI - #[serde(rename = "formatted_time")] - pub formatted_time: String, // "6:00 PM - 8:00 PM" - #[serde(rename = "formatted_date")] - pub formatted_date: String, // "Friday, August 15, 2025" - #[serde(rename = "formatted_date_time")] - pub formatted_date_time: String, // "Friday, August 15, 2025 at 6:00 PM" - - // Additional display fields for UI components - #[serde(rename = "day_of_month")] - pub day_of_month: String, // "15" - #[serde(rename = "month_abbreviation")] - pub month_abbreviation: String, // "AUG" - #[serde(rename = "time_string")] - pub time_string: String, // "6:00 PM - 8:00 PM" (alias for formatted_time) - #[serde(rename = "is_multi_day")] - pub is_multi_day: bool, // true if event spans multiple days - #[serde(rename = "detailed_time_display")] - pub detailed_time_display: String, // Full time range for detail views - - pub location: String, - #[serde(skip_serializing_if = "Option::is_none", rename = "location_url")] - pub location_url: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub image: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub thumbnail: Option, - pub category: String, - #[serde(rename = "is_featured")] - pub is_featured: bool, - #[serde(skip_serializing_if = "Option::is_none", rename = "recurring_type")] - pub recurring_type: Option, - #[serde(rename = "created_at")] - pub created_at: String, // ISO timestamp - #[serde(rename = "updated_at")] - pub updated_at: String, // ISO timestamp -} - -/// Helper function to format time range from DateTime objects in local timezone -fn format_time_range_from_datetime(start_time: &DateTime, end_time: &DateTime) -> String { - // Convert UTC to local timezone for display - let start_local = start_time.with_timezone(&Local); - let end_local = end_time.with_timezone(&Local); - - // Use consistent formatting: always show hour without leading zero, include minutes, use PM/AM - let start_formatted = if start_local.minute() == 0 { - start_local.format("%l %p").to_string().trim().to_string() - } else { - start_local.format("%l:%M %p").to_string().trim().to_string() - }; - - let end_formatted = if end_local.minute() == 0 { - end_local.format("%l %p").to_string().trim().to_string() - } else { - end_local.format("%l:%M %p").to_string().trim().to_string() - }; - - // If start and end times are the same, just show one time - if start_formatted == end_formatted { - start_formatted - } else { - format!("{} - {}", start_formatted, end_formatted) - } -} - -impl From for ClientEvent { - fn from(event: Event) -> Self { - let description = event.clean_description(); - let category = event.category.to_string(); - let recurring_type = event.recurring_type.as_ref().map(|rt| rt.to_string()); - - // Raw ISO timestamps for calendar/system APIs - let start_time = event.start_time.format("%Y-%m-%dT%H:%M:%S%z").to_string(); - let end_time = event.end_time.format("%Y-%m-%dT%H:%M:%S%z").to_string(); - - // Generate formatted display strings in local timezone - let start_local = event.start_time.with_timezone(&Local); - let end_local = event.end_time.with_timezone(&Local); - - // Check if event spans multiple days - let is_multi_day = start_local.date_naive() != end_local.date_naive(); - - let (formatted_time, formatted_date, formatted_date_time, day_of_month, month_abbreviation, time_string, detailed_time_display) = if is_multi_day { - // Multi-day event: show date range for formatted_date, but start time for simplified views - let start_date = start_local.format("%B %d, %Y").to_string(); - let end_date = end_local.format("%B %d, %Y").to_string(); - let formatted_date = format!("{} - {}", start_date, end_date); - - // For detailed view: show full date range with full time range - let time_range = format_time_range_from_datetime(&event.start_time, &event.end_time); - let formatted_time = format!("{} - {}, {}", - start_local.format("%b %d").to_string(), - end_local.format("%b %d").to_string(), - time_range - ); - - // For HomeFeed simplified view: just show start time - let start_time_formatted = if start_local.minute() == 0 { - start_local.format("%l %p").to_string().trim().to_string() - } else { - start_local.format("%l:%M %p").to_string().trim().to_string() - }; - let time_string = start_time_formatted; - - // For detail views: use the same time_range that eliminates redundancy - let detailed_time_display = time_range.clone(); - - let formatted_date_time = format!("{} - {}", start_date, end_date); - - // Use start date for calendar display - let day_of_month = start_local.format("%d").to_string().trim_start_matches('0').to_string(); - let month_abbreviation = start_local.format("%b").to_string().to_uppercase(); - - (formatted_time, formatted_date, formatted_date_time, day_of_month, month_abbreviation, time_string, detailed_time_display) - } else { - // Single day event: show time range - let formatted_time = format_time_range_from_datetime(&event.start_time, &event.end_time); - let formatted_date = start_local.format("%B %d, %Y").to_string(); - // Use consistent time formatting for single events too - let time_formatted = if start_local.minute() == 0 { - start_local.format("%l %p").to_string().trim().to_string() - } else { - start_local.format("%l:%M %p").to_string().trim().to_string() - }; - let formatted_date_time = format!("{} at {}", formatted_date, time_formatted); - - let day_of_month = start_local.format("%d").to_string().trim_start_matches('0').to_string(); - let month_abbreviation = start_local.format("%b").to_string().to_uppercase(); - - // For single events, time_string should just be start time for HomeFeed - let time_string = time_formatted; - - // For single events, detailed_time_display is same as formatted_time - let detailed_time_display = formatted_time.clone(); - - (formatted_time, formatted_date, formatted_date_time, day_of_month, month_abbreviation, time_string, detailed_time_display) - }; - - let created_at = event.created_at.format("%Y-%m-%dT%H:%M:%S%z").to_string(); - let updated_at = event.updated_at.format("%Y-%m-%dT%H:%M:%S%z").to_string(); - - Self { - id: event.id, - title: event.title, - description, - start_time, - end_time, - formatted_time, - formatted_date, - formatted_date_time, - day_of_month, - month_abbreviation, - time_string, - is_multi_day, - detailed_time_display, - location: event.location, - location_url: event.location_url, - image: event.image, - thumbnail: event.thumbnail, - category, - is_featured: event.is_featured, - recurring_type, - created_at, - updated_at, - } - } -} - -/// Client-facing Bulletin model with formatted dates -/// Serializes to camelCase JSON for iOS compatibility -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct ClientBulletin { - pub id: String, - pub title: String, - pub date: String, // Pre-formatted date string - #[serde(rename = "sabbathSchool")] - pub sabbath_school: String, - #[serde(rename = "divineWorship")] - pub divine_worship: String, - #[serde(rename = "scriptureReading")] - pub scripture_reading: String, - pub sunset: String, - #[serde(skip_serializing_if = "Option::is_none", rename = "pdfPath")] - pub pdf_path: Option, - #[serde(skip_serializing_if = "Option::is_none", rename = "coverImage")] - pub cover_image: Option, - #[serde(rename = "isActive")] - pub is_active: bool, - // Add other fields as needed -} - -impl From for ClientBulletin { - fn from(bulletin: Bulletin) -> Self { - Self { - id: bulletin.id, - title: bulletin.title, - date: bulletin.date.format("%A, %B %d, %Y").to_string(), // Format NaiveDate to string - sabbath_school: bulletin.sabbath_school, - divine_worship: bulletin.divine_worship, - scripture_reading: bulletin.scripture_reading, - sunset: bulletin.sunset, - pdf_path: bulletin.pdf_path, - cover_image: bulletin.cover_image, - is_active: bulletin.is_active, - } - } -} - -/// Client-facing Sermon model with pre-formatted dates and cleaned data -/// Serializes to camelCase JSON for iOS compatibility -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct ClientSermon { - pub id: String, - pub title: String, - pub speaker: String, - #[serde(skip_serializing_if = "Option::is_none")] - pub description: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub date: Option, // Pre-formatted date string - #[serde(skip_serializing_if = "Option::is_none", rename = "audioUrl")] - pub audio_url: Option, - #[serde(skip_serializing_if = "Option::is_none", rename = "videoUrl")] - pub video_url: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub duration: Option, // Pre-formatted duration - #[serde(skip_serializing_if = "Option::is_none", rename = "mediaType")] - pub media_type: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub thumbnail: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub image: Option, - #[serde(skip_serializing_if = "Option::is_none", rename = "scriptureReading")] - pub scripture_reading: Option, -} - -impl ClientSermon { - /// Create a ClientSermon from a Sermon with URL conversion using base API URL - pub fn from_sermon_with_base_url(sermon: Sermon, base_url: &str) -> Self { - let date = sermon.date.format("%B %d, %Y").to_string(); - let media_type = if sermon.has_video() { - Some("Video".to_string()) - } else if sermon.has_audio() { - Some("Audio".to_string()) - } else { - None - }; - - // Helper function to convert relative URLs to full URLs - let make_full_url = |url: Option| -> Option { - url.map(|u| { - if u.starts_with("http://") || u.starts_with("https://") { - // Already a full URL - u - } else if u.starts_with("/") { - // Relative URL starting with / - let base = base_url.trim_end_matches('/'); - format!("{}{}", base, u) - } else { - // Relative URL not starting with / - let base = base_url.trim_end_matches('/'); - format!("{}/{}", base, u) - } - }) - }; - - Self { - id: sermon.id, - title: sermon.title, - speaker: sermon.speaker, - description: Some(sermon.description), - date: Some(date), - audio_url: make_full_url(sermon.audio_url), - video_url: make_full_url(sermon.video_url), - duration: sermon.duration_string, // Use raw duration string from API - media_type, - thumbnail: make_full_url(sermon.thumbnail), - image: None, // Sermons don't have separate image field - scripture_reading: if sermon.scripture_reference.is_empty() { None } else { Some(sermon.scripture_reference.clone()) }, - } - } -} - -impl From for ClientSermon { - fn from(sermon: Sermon) -> Self { - let date = sermon.date.format("%B %d, %Y").to_string(); - let media_type = if sermon.has_video() { - Some("Video".to_string()) - } else if sermon.has_audio() { - Some("Audio".to_string()) - } else { - None - }; - - Self { - id: sermon.id, - title: sermon.title, - speaker: sermon.speaker, - description: Some(sermon.description), - date: Some(date), - audio_url: sermon.audio_url, - video_url: sermon.video_url, - duration: sermon.duration_string, // Use raw duration string from API - media_type, - thumbnail: sermon.thumbnail, - image: None, // Sermons don't have separate image field - scripture_reading: if sermon.scripture_reference.is_empty() { None } else { Some(sermon.scripture_reference.clone()) }, - } - } -} - -// Add ToString implementations for enums if not already present -impl ToString for RecurringType { - fn to_string(&self) -> String { - match self { - RecurringType::Daily => "Daily".to_string(), - RecurringType::Weekly => "Weekly".to_string(), - RecurringType::Biweekly => "Bi-weekly".to_string(), - RecurringType::Monthly => "Monthly".to_string(), - RecurringType::FirstTuesday => "First Tuesday".to_string(), - RecurringType::FirstSabbath => "First Sabbath".to_string(), - RecurringType::LastSabbath => "Last Sabbath".to_string(), - RecurringType::SecondThirdSaturday => "2nd/3rd Saturday Monthly".to_string(), - } - } -} \ No newline at end of file diff --git a/church-core/src/models/common.rs b/church-core/src/models/common.rs deleted file mode 100644 index eff2d62..0000000 --- a/church-core/src/models/common.rs +++ /dev/null @@ -1,73 +0,0 @@ -use serde::{Deserialize, Serialize}; - -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct ApiResponse { - pub success: bool, - pub data: Option, - pub message: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub error: Option, -} - -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct ApiListResponse { - pub success: bool, - pub data: ApiListData, - pub message: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub error: Option, -} - -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct ApiListData { - pub items: Vec, - pub total: u32, - pub page: u32, - pub per_page: u32, - pub has_more: bool, -} - -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct PaginationParams { - pub page: Option, - pub per_page: Option, - pub sort: Option, - pub filter: Option, -} - -impl Default for PaginationParams { - fn default() -> Self { - Self { - page: Some(1), - per_page: Some(50), - sort: None, - filter: None, - } - } -} - -impl PaginationParams { - pub fn new() -> Self { - Self::default() - } - - pub fn with_page(mut self, page: u32) -> Self { - self.page = Some(page); - self - } - - pub fn with_per_page(mut self, per_page: u32) -> Self { - self.per_page = Some(per_page); - self - } - - pub fn with_sort(mut self, sort: impl Into) -> Self { - self.sort = Some(sort.into()); - self - } - - pub fn with_filter(mut self, filter: impl Into) -> Self { - self.filter = Some(filter.into()); - self - } -} \ No newline at end of file diff --git a/church-core/src/models/config.rs b/church-core/src/models/config.rs deleted file mode 100644 index 1af7eeb..0000000 --- a/church-core/src/models/config.rs +++ /dev/null @@ -1,253 +0,0 @@ -use serde::{Deserialize, Serialize}; - -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct Coordinates { - pub lat: f64, - pub lng: f64, -} - -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct ChurchConfig { - pub church_name: Option, - pub church_address: Option, - pub po_box: Option, - pub contact_phone: Option, - pub contact_email: Option, - pub website_url: Option, - pub google_maps_url: Option, - pub facebook_url: Option, - pub youtube_url: Option, - pub instagram_url: Option, - pub about_text: Option, - pub mission_statement: Option, - pub tagline: Option, - pub brand_color: Option, - pub donation_url: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub service_times: Option>, - #[serde(skip_serializing_if = "Option::is_none")] - pub pastoral_staff: Option>, - #[serde(skip_serializing_if = "Option::is_none")] - pub ministries: Option>, - #[serde(skip_serializing_if = "Option::is_none")] - pub app_settings: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub emergency_contacts: Option>, - #[serde(skip_serializing_if = "Option::is_none")] - pub coordinates: Option, -} - -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct ServiceTime { - pub day: String, - pub service: String, - pub time: String, -} - -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct ServiceTimes { - pub sabbath_school: Option, - pub divine_worship: Option, - pub prayer_meeting: Option, - pub youth_service: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub special_services: Option>, -} - -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct SpecialService { - pub name: String, - pub time: String, - pub frequency: String, - #[serde(skip_serializing_if = "Option::is_none")] - pub description: Option, -} - -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct StaffMember { - pub name: String, - pub title: String, - #[serde(skip_serializing_if = "Option::is_none")] - pub email: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub phone: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub photo: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub bio: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub responsibilities: Option>, -} - -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct Ministry { - pub name: String, - pub description: String, - #[serde(skip_serializing_if = "Option::is_none")] - pub leader: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub contact_email: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub contact_phone: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub meeting_time: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub meeting_location: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub website: Option, - pub category: MinistryCategory, - #[serde(default)] - pub is_active: bool, -} - -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct EmergencyContact { - pub name: String, - pub title: String, - pub phone: String, - #[serde(skip_serializing_if = "Option::is_none")] - pub email: Option, - pub priority: u32, - #[serde(skip_serializing_if = "Option::is_none")] - pub availability: Option, -} - -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct AppSettings { - pub enable_notifications: bool, - pub enable_calendar_sync: bool, - pub enable_offline_mode: bool, - pub theme: AppTheme, - pub default_language: String, -#[serde(skip_serializing_if = "Option::is_none")] - pub owncast_server: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub bible_version: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub hymnal_version: Option, - pub cache_duration_minutes: u32, - pub auto_refresh_interval_minutes: u32, -} - -#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] -pub enum MinistryCategory { - #[serde(rename = "worship")] - Worship, - #[serde(rename = "education")] - Education, - #[serde(rename = "youth")] - Youth, - #[serde(rename = "children")] - Children, - #[serde(rename = "outreach")] - Outreach, - #[serde(rename = "health")] - Health, - #[serde(rename = "music")] - Music, - #[serde(rename = "fellowship")] - Fellowship, - #[serde(rename = "prayer")] - Prayer, - #[serde(rename = "stewardship")] - Stewardship, - #[serde(rename = "other")] - Other, -} - -#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] -pub enum AppTheme { - #[serde(rename = "light")] - Light, - #[serde(rename = "dark")] - Dark, - #[serde(rename = "system")] - System, -} - -impl Default for AppSettings { - fn default() -> Self { - Self { - enable_notifications: true, - enable_calendar_sync: true, - enable_offline_mode: true, - theme: AppTheme::System, - default_language: "en".to_string(), -owncast_server: None, - bible_version: Some("KJV".to_string()), - hymnal_version: Some("1985".to_string()), - cache_duration_minutes: 60, - auto_refresh_interval_minutes: 15, - } - } -} - -impl ChurchConfig { - pub fn get_display_name(&self) -> String { - self.church_name - .as_ref() - .cloned() - .unwrap_or_else(|| "Church".to_string()) - } - - pub fn has_social_media(&self) -> bool { - self.facebook_url.is_some() || self.youtube_url.is_some() || self.instagram_url.is_some() - } - - pub fn get_contact_info(&self) -> Vec<(String, String)> { - let mut contacts = Vec::new(); - - if let Some(phone) = &self.contact_phone { - contacts.push(("Phone".to_string(), phone.clone())); - } - - if let Some(email) = &self.contact_email { - contacts.push(("Email".to_string(), email.clone())); - } - - if let Some(address) = &self.church_address { - contacts.push(("Address".to_string(), address.clone())); - } - - if let Some(po_box) = &self.po_box { - contacts.push(("PO Box".to_string(), po_box.clone())); - } - - contacts - } - - pub fn active_ministries(&self) -> Vec<&Ministry> { - self.ministries - .as_ref() - .map(|ministries| { - ministries - .iter() - .filter(|ministry| ministry.is_active) - .collect() - }) - .unwrap_or_default() - } - - pub fn ministries_by_category(&self, category: MinistryCategory) -> Vec<&Ministry> { - self.ministries - .as_ref() - .map(|ministries| { - ministries - .iter() - .filter(|ministry| ministry.category == category && ministry.is_active) - .collect() - }) - .unwrap_or_default() - } - - pub fn emergency_contacts_by_priority(&self) -> Vec<&EmergencyContact> { - self.emergency_contacts - .as_ref() - .map(|contacts| { - let mut sorted = contacts.iter().collect::>(); - sorted.sort_by_key(|contact| contact.priority); - sorted - }) - .unwrap_or_default() - } -} \ No newline at end of file diff --git a/church-core/src/models/contact.rs b/church-core/src/models/contact.rs deleted file mode 100644 index fe3c441..0000000 --- a/church-core/src/models/contact.rs +++ /dev/null @@ -1,339 +0,0 @@ -use chrono::{DateTime, Utc}; -use serde::{Deserialize, Serialize}; - -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct ContactForm { - pub name: String, - pub email: String, - #[serde(skip_serializing_if = "Option::is_none")] - pub phone: Option, - pub subject: String, - pub message: String, - #[serde(skip_serializing_if = "Option::is_none")] - pub category: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub preferred_contact_method: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub urgent: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub visitor_info: Option, -} - -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct ContactSubmission { - pub id: String, - pub name: String, - pub email: String, - #[serde(skip_serializing_if = "Option::is_none")] - pub phone: Option, - pub subject: String, - pub message: String, - pub category: ContactCategory, - #[serde(skip_serializing_if = "Option::is_none")] - pub preferred_contact_method: Option, - #[serde(default)] - pub urgent: bool, - #[serde(skip_serializing_if = "Option::is_none")] - pub visitor_info: Option, - pub status: ContactStatus, - #[serde(skip_serializing_if = "Option::is_none")] - pub assigned_to: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub response: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub tags: Option>, - pub created_at: DateTime, - pub updated_at: DateTime, - #[serde(skip_serializing_if = "Option::is_none")] - pub responded_at: Option>, -} - -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct VisitorInfo { - pub is_first_time: bool, - #[serde(skip_serializing_if = "Option::is_none")] - pub how_heard_about_us: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub interests: Option>, - #[serde(skip_serializing_if = "Option::is_none")] - pub family_members: Option>, - #[serde(skip_serializing_if = "Option::is_none")] - pub prayer_requests: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub address: Option
, - #[serde(default)] - pub wants_follow_up: bool, - #[serde(default)] - pub wants_newsletter: bool, -} - -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct FamilyMember { - pub name: String, - pub relationship: String, - #[serde(skip_serializing_if = "Option::is_none")] - pub age_group: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub interests: Option>, -} - -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct Address { - pub street: String, - #[serde(skip_serializing_if = "Option::is_none")] - pub city: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub state: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub zip_code: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub country: Option, -} - -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct PrayerRequest { - pub id: String, - pub name: String, - #[serde(skip_serializing_if = "Option::is_none")] - pub email: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub phone: Option, - pub request: String, - pub category: PrayerCategory, - #[serde(default)] - pub is_public: bool, - #[serde(default)] - pub is_urgent: bool, - #[serde(default)] - pub is_confidential: bool, - #[serde(skip_serializing_if = "Option::is_none")] - pub follow_up_requested: Option, - pub status: PrayerStatus, - #[serde(skip_serializing_if = "Option::is_none")] - pub assigned_to: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub notes: Option, - pub created_at: DateTime, - pub updated_at: DateTime, - #[serde(skip_serializing_if = "Option::is_none")] - pub answered_at: Option>, -} - -#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] -pub enum ContactCategory { - #[serde(rename = "general")] - General, - #[serde(rename = "pastoral_care")] - PastoralCare, - #[serde(rename = "prayer_request")] - PrayerRequest, - #[serde(rename = "visitor")] - Visitor, - #[serde(rename = "ministry")] - Ministry, - #[serde(rename = "event")] - Event, - #[serde(rename = "technical")] - Technical, - #[serde(rename = "feedback")] - Feedback, - #[serde(rename = "donation")] - Donation, - #[serde(rename = "membership")] - Membership, - #[serde(rename = "baptism")] - Baptism, - #[serde(rename = "wedding")] - Wedding, - #[serde(rename = "funeral")] - Funeral, - #[serde(rename = "other")] - Other, -} - -#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] -pub enum ContactMethod { - #[serde(rename = "email")] - Email, - #[serde(rename = "phone")] - Phone, - #[serde(rename = "text")] - Text, - #[serde(rename = "mail")] - Mail, - #[serde(rename = "no_preference")] - NoPreference, -} - -#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] -pub enum ContactStatus { - #[serde(rename = "new")] - New, - #[serde(rename = "assigned")] - Assigned, - #[serde(rename = "in_progress")] - InProgress, - #[serde(rename = "responded")] - Responded, - #[serde(rename = "follow_up")] - FollowUp, - #[serde(rename = "completed")] - Completed, - #[serde(rename = "closed")] - Closed, -} - -#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] -pub enum PrayerCategory { - #[serde(rename = "health")] - Health, - #[serde(rename = "family")] - Family, - #[serde(rename = "finances")] - Finances, - #[serde(rename = "relationships")] - Relationships, - #[serde(rename = "spiritual")] - Spiritual, - #[serde(rename = "work")] - Work, - #[serde(rename = "travel")] - Travel, - #[serde(rename = "community")] - Community, - #[serde(rename = "praise")] - Praise, - #[serde(rename = "other")] - Other, -} - -#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] -pub enum PrayerStatus { - #[serde(rename = "new")] - New, - #[serde(rename = "praying")] - Praying, - #[serde(rename = "answered")] - Answered, - #[serde(rename = "ongoing")] - Ongoing, - #[serde(rename = "closed")] - Closed, -} - -#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] -pub enum AgeGroup { - #[serde(rename = "infant")] - Infant, - #[serde(rename = "toddler")] - Toddler, - #[serde(rename = "child")] - Child, - #[serde(rename = "youth")] - Youth, - #[serde(rename = "adult")] - Adult, - #[serde(rename = "senior")] - Senior, -} - -impl ContactForm { - pub fn new(name: String, email: String, subject: String, message: String) -> Self { - Self { - name, - email, - phone: None, - subject, - message, - category: None, - preferred_contact_method: None, - urgent: None, - visitor_info: None, - } - } - - pub fn with_category(mut self, category: ContactCategory) -> Self { - self.category = Some(category); - self - } - - pub fn with_phone(mut self, phone: String) -> Self { - self.phone = Some(phone); - self - } - - pub fn with_preferred_method(mut self, method: ContactMethod) -> Self { - self.preferred_contact_method = Some(method); - self - } - - pub fn mark_urgent(mut self) -> Self { - self.urgent = Some(true); - self - } - - pub fn with_visitor_info(mut self, visitor_info: VisitorInfo) -> Self { - self.visitor_info = Some(visitor_info); - self - } - - pub fn is_urgent(&self) -> bool { - self.urgent.unwrap_or(false) - } - - pub fn is_visitor(&self) -> bool { - self.visitor_info.is_some() - } -} - -impl ContactSubmission { - pub fn is_urgent(&self) -> bool { - self.urgent - } - - pub fn is_visitor(&self) -> bool { - self.visitor_info.is_some() - } - - pub fn is_open(&self) -> bool { - !matches!(self.status, ContactStatus::Completed | ContactStatus::Closed) - } - - pub fn needs_response(&self) -> bool { - matches!(self.status, ContactStatus::New | ContactStatus::Assigned) - } - - pub fn response_time(&self) -> Option { - self.responded_at.map(|responded| responded - self.created_at) - } - - pub fn age_days(&self) -> i64 { - (Utc::now() - self.created_at).num_days() - } -} - -impl PrayerRequest { - pub fn is_urgent(&self) -> bool { - self.is_urgent - } - - pub fn is_confidential(&self) -> bool { - self.is_confidential - } - - pub fn is_public(&self) -> bool { - self.is_public && !self.is_confidential - } - - pub fn is_open(&self) -> bool { - !matches!(self.status, PrayerStatus::Answered | PrayerStatus::Closed) - } - - pub fn is_answered(&self) -> bool { - matches!(self.status, PrayerStatus::Answered) - } - - pub fn age_days(&self) -> i64 { - (Utc::now() - self.created_at).num_days() - } -} \ No newline at end of file diff --git a/church-core/src/models/event.rs b/church-core/src/models/event.rs deleted file mode 100644 index 9ee7686..0000000 --- a/church-core/src/models/event.rs +++ /dev/null @@ -1,349 +0,0 @@ -use chrono::{DateTime, Utc}; -use serde::{Deserialize, Serialize, Deserializer}; -use std::fmt; - -/// Timezone-aware timestamp from v2 API -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct TimezoneTimestamp { - pub utc: DateTime, - pub local: String, // "2025-08-13T05:00:00-04:00" - pub timezone: String, // "America/New_York" -} - -/// Custom deserializer that handles both v1 (simple string) and v2 (timezone object) formats -fn deserialize_flexible_datetime<'de, D>(deserializer: D) -> Result, D::Error> -where - D: Deserializer<'de>, -{ - use serde::de::{self, Visitor}; - - struct FlexibleDateTimeVisitor; - - impl<'de> Visitor<'de> for FlexibleDateTimeVisitor { - type Value = DateTime; - - fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { - formatter.write_str("a string timestamp or timezone object") - } - - fn visit_str(self, value: &str) -> Result - where - E: de::Error, - { - // v1 format: simple ISO string - DateTime::parse_from_rfc3339(value) - .map(|dt| dt.with_timezone(&Utc)) - .map_err(de::Error::custom) - } - - fn visit_map(self, mut map: M) -> Result - where - M: de::MapAccess<'de>, - { - // v2 format: timezone object - extract UTC field - let mut utc_value: Option> = None; - - while let Some(key) = map.next_key::()? { - match key.as_str() { - "utc" => { - utc_value = Some(map.next_value()?); - } - _ => { - // Skip other fields (local, timezone) - let _: serde_json::Value = map.next_value()?; - } - } - } - - utc_value.ok_or_else(|| de::Error::missing_field("utc")) - } - } - - deserializer.deserialize_any(FlexibleDateTimeVisitor) -} - -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct Event { - pub id: String, - pub title: String, - pub description: String, - #[serde(deserialize_with = "deserialize_flexible_datetime")] - pub start_time: DateTime, - #[serde(deserialize_with = "deserialize_flexible_datetime")] - pub end_time: DateTime, - pub location: String, - #[serde(skip_serializing_if = "Option::is_none")] - pub location_url: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub image: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub thumbnail: Option, - pub category: EventCategory, - #[serde(default)] - pub is_featured: bool, - #[serde(skip_serializing_if = "Option::is_none")] - pub recurring_type: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub tags: Option>, - #[serde(skip_serializing_if = "Option::is_none")] - pub contact_email: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub contact_phone: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub registration_url: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub max_attendees: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub current_attendees: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub timezone: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub approved_from: Option, - #[serde(deserialize_with = "deserialize_flexible_datetime")] - pub created_at: DateTime, - #[serde(deserialize_with = "deserialize_flexible_datetime")] - pub updated_at: DateTime, -} - -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct NewEvent { - pub title: String, - pub description: String, - pub start_time: DateTime, - pub end_time: DateTime, - pub location: String, - #[serde(skip_serializing_if = "Option::is_none")] - pub location_url: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub image: Option, - pub category: EventCategory, - #[serde(default)] - pub is_featured: bool, - #[serde(skip_serializing_if = "Option::is_none")] - pub recurring_type: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub tags: Option>, - #[serde(skip_serializing_if = "Option::is_none")] - pub contact_email: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub contact_phone: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub registration_url: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub max_attendees: Option, -} - -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct EventUpdate { - #[serde(skip_serializing_if = "Option::is_none")] - pub title: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub description: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub start_time: Option>, - #[serde(skip_serializing_if = "Option::is_none")] - pub end_time: Option>, - #[serde(skip_serializing_if = "Option::is_none")] - pub location: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub location_url: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub image: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub category: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub is_featured: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub recurring_type: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub tags: Option>, - #[serde(skip_serializing_if = "Option::is_none")] - pub contact_email: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub contact_phone: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub registration_url: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub max_attendees: Option, -} - -#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] -pub enum EventCategory { - #[serde(rename = "service", alias = "Service")] - Service, - #[serde(rename = "ministry", alias = "Ministry")] - Ministry, - #[serde(rename = "social", alias = "Social")] - Social, - #[serde(rename = "education", alias = "Education")] - Education, - #[serde(rename = "outreach", alias = "Outreach")] - Outreach, - #[serde(rename = "youth", alias = "Youth")] - Youth, - #[serde(rename = "music", alias = "Music")] - Music, - #[serde(rename = "other", alias = "Other")] - Other, -} - -#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] -pub enum RecurringType { - #[serde(rename = "daily", alias = "DAILY")] - Daily, - #[serde(rename = "weekly", alias = "WEEKLY")] - Weekly, - #[serde(rename = "biweekly", alias = "BIWEEKLY")] - Biweekly, - #[serde(rename = "monthly", alias = "MONTHLY")] - Monthly, - #[serde(rename = "first_tuesday", alias = "FIRST_TUESDAY")] - FirstTuesday, - #[serde(rename = "first_sabbath", alias = "FIRST_SABBATH")] - FirstSabbath, - #[serde(rename = "last_sabbath", alias = "LAST_SABBATH")] - LastSabbath, - #[serde(rename = "2nd/3rd Saturday Monthly")] - SecondThirdSaturday, -} - -impl Event { - pub fn duration_minutes(&self) -> i64 { - (self.end_time - self.start_time).num_minutes() - } - - pub fn has_registration(&self) -> bool { - self.registration_url.is_some() - } - - pub fn is_full(&self) -> bool { - match (self.max_attendees, self.current_attendees) { - (Some(max), Some(current)) => current >= max, - _ => false, - } - } - - pub fn spots_remaining(&self) -> Option { - match (self.max_attendees, self.current_attendees) { - (Some(max), Some(current)) => Some(max.saturating_sub(current)), - _ => None, - } - } -} - -impl fmt::Display for EventCategory { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - EventCategory::Service => write!(f, "Service"), - EventCategory::Ministry => write!(f, "Ministry"), - EventCategory::Social => write!(f, "Social"), - EventCategory::Education => write!(f, "Education"), - EventCategory::Outreach => write!(f, "Outreach"), - EventCategory::Youth => write!(f, "Youth"), - EventCategory::Music => write!(f, "Music"), - EventCategory::Other => write!(f, "Other"), - } - } -} - -impl Event { - pub fn formatted_date(&self) -> String { - self.start_time.format("%A, %B %d, %Y").to_string() - } - - /// Returns formatted date range for multi-day events, single date for same-day events - pub fn formatted_date_range(&self) -> String { - let start_date = self.start_time.date_naive(); - let end_date = self.end_time.date_naive(); - - if start_date == end_date { - // Same day event - self.start_time.format("%A, %B %d, %Y").to_string() - } else { - // Multi-day event - let start_formatted = self.start_time.format("%A, %B %d, %Y").to_string(); - let end_formatted = self.end_time.format("%A, %B %d, %Y").to_string(); - format!("{} - {}", start_formatted, end_formatted) - } - } - - pub fn formatted_start_time(&self) -> String { - // Convert UTC to user's local timezone automatically - let local_time = self.start_time.with_timezone(&chrono::Local); - local_time.format("%I:%M %p").to_string().trim_start_matches('0').to_string() - } - - pub fn formatted_end_time(&self) -> String { - // Convert UTC to user's local timezone automatically - let local_time = self.end_time.with_timezone(&chrono::Local); - local_time.format("%I:%M %p").to_string().trim_start_matches('0').to_string() - } - - pub fn clean_description(&self) -> String { - html2text::from_read(self.description.as_bytes(), 80) - .replace('\n', " ") - .split_whitespace() - .collect::>() - .join(" ") - } -} - -/// Event submission for public submission endpoint -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct EventSubmission { - pub title: String, - pub description: String, - pub start_time: String, // ISO string format - pub end_time: String, // ISO string format - pub location: String, - #[serde(skip_serializing_if = "Option::is_none")] - pub location_url: Option, - pub category: String, // String to match API exactly - #[serde(default)] - pub is_featured: bool, - #[serde(skip_serializing_if = "Option::is_none")] - pub recurring_type: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub bulletin_week: Option, // Date string in YYYY-MM-DD format - pub submitter_email: String, -} - -impl EventSubmission { - /// Parse start_time string to DateTime - pub fn parse_start_time(&self) -> Option> { - crate::utils::parse_datetime_flexible(&self.start_time) - } - - /// Parse end_time string to DateTime - pub fn parse_end_time(&self) -> Option> { - crate::utils::parse_datetime_flexible(&self.end_time) - } - - /// Validate that both start and end times can be parsed - pub fn validate_times(&self) -> bool { - self.parse_start_time().is_some() && self.parse_end_time().is_some() - } -} - -/// Pending event for admin management -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct PendingEvent { - pub id: String, - pub title: String, - pub description: String, - pub start_time: DateTime, - pub end_time: DateTime, - pub location: String, - #[serde(skip_serializing_if = "Option::is_none")] - pub location_url: Option, - pub category: EventCategory, - #[serde(default)] - pub is_featured: bool, - #[serde(skip_serializing_if = "Option::is_none")] - pub recurring_type: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub bulletin_week: Option, - pub submitter_email: String, - pub created_at: DateTime, - pub updated_at: DateTime, -} \ No newline at end of file diff --git a/church-core/src/models/mod.rs b/church-core/src/models/mod.rs deleted file mode 100644 index de0fefa..0000000 --- a/church-core/src/models/mod.rs +++ /dev/null @@ -1,28 +0,0 @@ -pub mod common; -pub mod event; -pub mod bulletin; -pub mod config; -pub mod contact; -pub mod sermon; -pub mod streaming; -pub mod auth; -pub mod bible; -pub mod client_models; -pub mod v2; -pub mod admin; - -pub use common::*; -pub use event::*; -pub use bulletin::*; -pub use config::*; -pub use contact::*; -pub use sermon::*; -pub use streaming::*; -pub use auth::*; -pub use bible::*; -pub use client_models::*; -pub use v2::*; -pub use admin::*; - -// Re-export livestream types from client module for convenience -pub use crate::client::livestream::{StreamStatus, LiveStream}; \ No newline at end of file diff --git a/church-core/src/models/sermon.rs b/church-core/src/models/sermon.rs deleted file mode 100644 index 8badb6b..0000000 --- a/church-core/src/models/sermon.rs +++ /dev/null @@ -1,376 +0,0 @@ -use chrono::{DateTime, Utc}; -use serde::{Deserialize, Serialize}; - -/// API response structure for sermons from the external API -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct ApiSermon { - pub id: String, - pub title: String, - pub speaker: Option, - pub date: Option, - pub duration: String, // Duration as string like "1:13:01" - pub description: Option, - pub audio_url: Option, - pub video_url: Option, - pub media_type: Option, - pub thumbnail: Option, - pub scripture_reading: Option, -} - -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct Sermon { - pub id: String, - pub title: String, - pub speaker: String, - pub description: String, - pub date: DateTime, - pub scripture_reference: String, - pub series: Option, - pub duration_string: Option, // Raw duration from API (e.g., "2:34:49") - pub media_url: Option, - pub audio_url: Option, - pub video_url: Option, - pub transcript: Option, - pub thumbnail: Option, - pub tags: Option>, - pub category: SermonCategory, - pub is_featured: bool, - pub view_count: u32, - pub download_count: u32, - pub created_at: DateTime, - pub updated_at: DateTime, -} - -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct NewSermon { - pub title: String, - pub speaker: String, - pub description: String, - pub date: DateTime, - pub scripture_reference: String, - pub series: Option, - pub duration_string: Option, - pub media_url: Option, - pub audio_url: Option, - pub video_url: Option, - pub transcript: Option, - pub thumbnail: Option, - pub tags: Option>, - pub category: SermonCategory, - pub is_featured: bool, -} - -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct SermonSeries { - pub id: String, - pub title: String, - pub description: String, - pub speaker: String, - pub start_date: DateTime, - pub end_date: Option>, - pub thumbnail: Option, - pub sermons: Vec, - pub is_active: bool, - pub created_at: DateTime, - pub updated_at: DateTime, -} - -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct SermonNote { - pub id: String, - pub sermon_id: String, - pub user_id: String, - pub content: String, - pub timestamp_seconds: Option, - pub is_private: bool, - pub created_at: DateTime, - pub updated_at: DateTime, -} - -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct SermonFeedback { - pub id: String, - pub sermon_id: String, - pub user_name: Option, - pub user_email: Option, - pub rating: Option, // 1-5 stars - pub comment: Option, - pub is_public: bool, - pub created_at: DateTime, -} - -#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] -pub enum SermonCategory { - #[serde(rename = "regular")] - Regular, - #[serde(rename = "evangelistic")] - Evangelistic, - #[serde(rename = "youth")] - Youth, - #[serde(rename = "children")] - Children, - #[serde(rename = "special")] - Special, - #[serde(rename = "prophecy")] - Prophecy, - #[serde(rename = "health")] - Health, - #[serde(rename = "stewardship")] - Stewardship, - #[serde(rename = "testimony")] - Testimony, - #[serde(rename = "holiday")] - Holiday, - #[serde(rename = "communion")] - Communion, - #[serde(rename = "baptism")] - Baptism, - #[serde(rename = "wedding")] - Wedding, - #[serde(rename = "funeral")] - Funeral, - #[serde(rename = "other")] - Other, - #[serde(rename = "livestream_archive")] - LivestreamArchive, -} - -#[derive(Debug, Serialize, Deserialize, Clone)] -pub struct SermonSearch { - pub query: Option, - pub speaker: Option, - pub category: Option, - pub series: Option, - pub date_from: Option>, - pub date_to: Option>, - pub tags: Option>, - pub featured_only: Option, - pub has_video: Option, - pub has_audio: Option, - pub has_transcript: Option, - pub min_duration: Option, - pub max_duration: Option, -} - -impl Default for SermonSearch { - fn default() -> Self { - Self { - query: None, - speaker: None, - category: None, - series: None, - date_from: None, - date_to: None, - tags: None, - featured_only: None, - has_video: None, - has_audio: None, - has_transcript: None, - min_duration: None, - max_duration: None, - } - } -} - -impl SermonSearch { - pub fn new() -> Self { - Self::default() - } - - pub fn with_query(mut self, query: String) -> Self { - self.query = Some(query); - self - } - - pub fn with_speaker(mut self, speaker: String) -> Self { - self.speaker = Some(speaker); - self - } - - pub fn with_category(mut self, category: SermonCategory) -> Self { - self.category = Some(category); - self - } - - pub fn with_series(mut self, series: String) -> Self { - self.series = Some(series); - self - } - - pub fn with_date_range(mut self, from: DateTime, to: DateTime) -> Self { - self.date_from = Some(from); - self.date_to = Some(to); - self - } - - pub fn featured_only(mut self) -> Self { - self.featured_only = Some(true); - self - } - - pub fn with_video(mut self) -> Self { - self.has_video = Some(true); - self - } - - pub fn with_audio(mut self) -> Self { - self.has_audio = Some(true); - self - } - - pub fn with_transcript(mut self) -> Self { - self.has_transcript = Some(true); - self - } -} - -impl Sermon { - pub fn duration_formatted(&self) -> String { - self.duration_string.clone().unwrap_or_else(|| "Unknown".to_string()) - } - - pub fn has_media(&self) -> bool { - self.media_url.is_some() || self.audio_url.is_some() || self.video_url.is_some() - } - - pub fn has_video(&self) -> bool { - self.video_url.is_some() || self.media_url.is_some() - } - - pub fn has_audio(&self) -> bool { - self.audio_url.is_some() || self.media_url.is_some() - } - - pub fn has_transcript(&self) -> bool { - self.transcript.is_some() - } - - pub fn is_recent(&self) -> bool { - let thirty_days_ago = Utc::now() - chrono::Duration::days(30); - self.date > thirty_days_ago - } - - pub fn is_popular(&self) -> bool { - self.view_count > 100 || self.download_count > 50 - } - - pub fn get_tags(&self) -> Vec { - self.tags.clone().unwrap_or_default() - } - - pub fn matches_search(&self, search: &SermonSearch) -> bool { - if let Some(query) = &search.query { - let query_lower = query.to_lowercase(); - if !self.title.to_lowercase().contains(&query_lower) - && !self.description.to_lowercase().contains(&query_lower) - && !self.speaker.to_lowercase().contains(&query_lower) - && !self.scripture_reference.to_lowercase().contains(&query_lower) { - return false; - } - } - - if let Some(speaker) = &search.speaker { - if !self.speaker.to_lowercase().contains(&speaker.to_lowercase()) { - return false; - } - } - - if let Some(category) = &search.category { - if self.category != *category { - return false; - } - } - - if let Some(series) = &search.series { - match &self.series { - Some(sermon_series) => { - if !sermon_series.to_lowercase().contains(&series.to_lowercase()) { - return false; - } - } - None => return false, - } - } - - if let Some(date_from) = search.date_from { - if self.date < date_from { - return false; - } - } - - if let Some(date_to) = search.date_to { - if self.date > date_to { - return false; - } - } - - if let Some(true) = search.featured_only { - if !self.is_featured { - return false; - } - } - - if let Some(true) = search.has_video { - if !self.has_video() { - return false; - } - } - - if let Some(true) = search.has_audio { - if !self.has_audio() { - return false; - } - } - - if let Some(true) = search.has_transcript { - if !self.has_transcript() { - return false; - } - } - - true - } -} - -impl SermonSeries { - pub fn is_active(&self) -> bool { - self.is_active && self.end_date.map_or(true, |end| end > Utc::now()) - } - - pub fn sermon_count(&self) -> usize { - self.sermons.len() - } - - pub fn total_duration(&self) -> Option { - if self.sermons.is_empty() { - return None; - } - - // Since we now use duration_string, we can't easily sum durations - // Return None for now - this would need proper duration parsing if needed - None - } - - pub fn latest_sermon(&self) -> Option<&Sermon> { - self.sermons - .iter() - .max_by_key(|s| s.date) - } - - pub fn duration_formatted(&self) -> String { - match self.total_duration() { - Some(seconds) => { - let minutes = seconds / 60; - let hours = minutes / 60; - let remaining_minutes = minutes % 60; - - if hours > 0 { - format!("{}h {}m", hours, remaining_minutes) - } else { - format!("{}m", minutes) - } - } - None => "Unknown".to_string(), - } - } -} \ No newline at end of file diff --git a/church-core/src/models/streaming.rs b/church-core/src/models/streaming.rs deleted file mode 100644 index 25703fe..0000000 --- a/church-core/src/models/streaming.rs +++ /dev/null @@ -1,157 +0,0 @@ -use serde::{Deserialize, Serialize}; - -/// Device streaming capabilities -#[derive(Debug, Clone, PartialEq)] -pub enum StreamingCapability { - /// Device supports AV1 codec (direct stream) - AV1, - /// Device needs HLS H.264 fallback - HLS, -} - -/// Streaming URL configuration -#[derive(Debug, Clone)] -pub struct StreamingUrl { - pub url: String, - pub capability: StreamingCapability, -} - -/// Device capability detection -pub struct DeviceCapabilities; - -impl DeviceCapabilities { - /// Detect device streaming capability - /// For now, this is a simple implementation that can be expanded - #[cfg(target_os = "ios")] - pub fn detect_capability() -> StreamingCapability { - // Use sysctlbyname to get device model on iOS - use std::ffi::{CStr, CString}; - use std::mem; - - unsafe { - let name = CString::new("hw.model").unwrap(); - let mut size: libc::size_t = 0; - - // First call to get the size - if libc::sysctlbyname( - name.as_ptr(), - std::ptr::null_mut(), - &mut size, - std::ptr::null_mut(), - 0, - ) != 0 { - println!("🎬 DEBUG: Failed to get model size, defaulting to HLS"); - return StreamingCapability::HLS; - } - - // Allocate buffer and get the actual value - let mut buffer = vec![0u8; size]; - if libc::sysctlbyname( - name.as_ptr(), - buffer.as_mut_ptr() as *mut libc::c_void, - &mut size, - std::ptr::null_mut(), - 0, - ) != 0 { - println!("🎬 DEBUG: Failed to get model value, defaulting to HLS"); - return StreamingCapability::HLS; - } - - // Convert to string - if let Ok(model_cstr) = CStr::from_bytes_with_nul(&buffer[..size]) { - if let Ok(model) = model_cstr.to_str() { - let model = model.to_lowercase(); - println!("🎬 DEBUG: Detected device model: {}", model); - - // iPhone models with AV1 hardware decoding support: - // Marketing names: iPhone16,x = iPhone 15 Pro/Pro Max, iPhone17,x = iPhone 16 series - // Internal codenames: d9xap = iPhone 16 series, d8xap = iPhone 15 Pro series - if model.starts_with("iphone16,") || model.starts_with("iphone17,") || - model.starts_with("d94ap") || model.starts_with("d93ap") || - model.starts_with("d84ap") || model.starts_with("d83ap") { - println!("🎬 DEBUG: Device {} supports AV1 hardware decoding", model); - return StreamingCapability::AV1; - } - - println!("🎬 DEBUG: Device {} does not support AV1, using HLS fallback", model); - return StreamingCapability::HLS; - } - } - - println!("🎬 DEBUG: Failed to parse model string, defaulting to HLS"); - StreamingCapability::HLS - } - } - - #[cfg(not(target_os = "ios"))] - pub fn detect_capability() -> StreamingCapability { - // Default to HLS for other platforms for now - StreamingCapability::HLS - } - - /// Generate streaming URL based on capability and media ID - pub fn get_streaming_url(base_url: &str, media_id: &str, capability: StreamingCapability) -> StreamingUrl { - // Add timestamp for cache busting to ensure fresh streams - let timestamp = std::time::SystemTime::now() - .duration_since(std::time::UNIX_EPOCH) - .unwrap() - .as_secs(); - - let url = match capability { - StreamingCapability::AV1 => { - format!("{}/api/media/stream/{}?t={}", base_url.trim_end_matches('/'), media_id, timestamp) - } - StreamingCapability::HLS => { - format!("{}/api/media/stream/{}/playlist.m3u8?t={}", base_url.trim_end_matches('/'), media_id, timestamp) - } - }; - - StreamingUrl { url, capability } - } - - /// Get optimal streaming URL for current device - pub fn get_optimal_streaming_url(base_url: &str, media_id: &str) -> StreamingUrl { - let capability = Self::detect_capability(); - Self::get_streaming_url(base_url, media_id, capability) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_av1_url_generation() { - let url = DeviceCapabilities::get_streaming_url( - "https://api.rockvilletollandsda.church", - "test-id-123", - StreamingCapability::AV1 - ); - - assert_eq!(url.url, "https://api.rockvilletollandsda.church/api/media/stream/test-id-123"); - assert_eq!(url.capability, StreamingCapability::AV1); - } - - #[test] - fn test_hls_url_generation() { - let url = DeviceCapabilities::get_streaming_url( - "https://api.rockvilletollandsda.church", - "test-id-123", - StreamingCapability::HLS - ); - - assert_eq!(url.url, "https://api.rockvilletollandsda.church/api/media/stream/test-id-123/playlist.m3u8"); - assert_eq!(url.capability, StreamingCapability::HLS); - } - - #[test] - fn test_base_url_trimming() { - let url = DeviceCapabilities::get_streaming_url( - "https://api.rockvilletollandsda.church/", - "test-id-123", - StreamingCapability::HLS - ); - - assert_eq!(url.url, "https://api.rockvilletollandsda.church/api/media/stream/test-id-123/playlist.m3u8"); - } -} \ No newline at end of file diff --git a/church-core/src/models/v2.rs b/church-core/src/models/v2.rs deleted file mode 100644 index f3ec572..0000000 --- a/church-core/src/models/v2.rs +++ /dev/null @@ -1,15 +0,0 @@ -/// API version enum to specify which API version to use -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum ApiVersion { - V1, - V2, -} - -impl ApiVersion { - pub fn path_prefix(&self) -> &'static str { - match self { - ApiVersion::V1 => "", - ApiVersion::V2 => "v2/", - } - } -} \ No newline at end of file diff --git a/church-core/src/utils/feed.rs b/church-core/src/utils/feed.rs deleted file mode 100644 index cb12590..0000000 --- a/church-core/src/utils/feed.rs +++ /dev/null @@ -1,310 +0,0 @@ -use crate::models::{ClientEvent, Sermon, Bulletin, BibleVerse}; -use chrono::{DateTime, Utc, NaiveDateTime}; -use serde::{Deserialize, Serialize}; - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct FeedItem { - pub id: String, - pub feed_type: FeedItemType, - pub timestamp: String, // ISO8601 format - pub priority: i32, -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -#[serde(tag = "type")] -pub enum FeedItemType { - #[serde(rename = "event")] - Event { - event: ClientEvent, - }, - #[serde(rename = "sermon")] - Sermon { - sermon: Sermon, - }, - #[serde(rename = "bulletin")] - Bulletin { - bulletin: Bulletin, - }, - #[serde(rename = "verse")] - Verse { - verse: BibleVerse, - }, -} - -/// Parse date string to DateTime, with fallback to current time -fn parse_date_with_fallback(date_str: &str) -> DateTime { - // Try ISO8601 format first - if let Ok(dt) = DateTime::parse_from_rfc3339(date_str) { - return dt.with_timezone(&Utc); - } - - // Try naive datetime parsing - if let Ok(naive) = NaiveDateTime::parse_from_str(date_str, "%Y-%m-%d %H:%M:%S") { - return DateTime::from_naive_utc_and_offset(naive, Utc); - } - - // Fallback to current time - Utc::now() -} - -/// Calculate priority for feed items based on type and recency -fn calculate_priority(feed_type: &FeedItemType, timestamp: &DateTime) -> i32 { - let now = Utc::now(); - let age_days = (now - *timestamp).num_days().max(0); - - match feed_type { - FeedItemType::Event { .. } => { - // Events get highest priority, especially upcoming ones - if *timestamp > now { - 1000 // Future events (upcoming) - } else { - 800 - (age_days as i32) // Recent past events - } - }, - FeedItemType::Sermon { .. } => { - // Sermons get high priority when recent - 600 - (age_days as i32) - }, - FeedItemType::Bulletin { .. } => { - // Bulletins get medium priority - 400 - (age_days as i32) - }, - FeedItemType::Verse { .. } => { - // Daily verse always gets consistent priority - 300 - }, - } -} - -/// Aggregate and sort home feed items -pub fn aggregate_home_feed( - events: &[ClientEvent], - sermons: &[Sermon], - bulletins: &[Bulletin], - daily_verse: Option<&BibleVerse> -) -> Vec { - let mut feed_items = Vec::new(); - - // Add recent sermons (limit to 3) - for sermon in sermons.iter().take(3) { - let timestamp = sermon.date; // Already a DateTime - let feed_type = FeedItemType::Sermon { sermon: sermon.clone() }; - let priority = calculate_priority(&feed_type, ×tamp); - - feed_items.push(FeedItem { - id: format!("sermon_{}", sermon.id), - feed_type, - timestamp: timestamp.to_rfc3339(), - priority, - }); - } - - // Add upcoming events (limit to 2) - for event in events.iter().take(2) { - let timestamp = parse_date_with_fallback(&event.created_at); - let feed_type = FeedItemType::Event { event: event.clone() }; - let priority = calculate_priority(&feed_type, ×tamp); - - feed_items.push(FeedItem { - id: format!("event_{}", event.id), - feed_type, - timestamp: timestamp.to_rfc3339(), - priority, - }); - } - - // Add most recent bulletin - if let Some(bulletin) = bulletins.first() { - let timestamp = parse_date_with_fallback(&bulletin.date.to_string()); - let feed_type = FeedItemType::Bulletin { bulletin: bulletin.clone() }; - let priority = calculate_priority(&feed_type, ×tamp); - - feed_items.push(FeedItem { - id: format!("bulletin_{}", bulletin.id), - feed_type, - timestamp: timestamp.to_rfc3339(), - priority, - }); - } - - // Add daily verse - if let Some(verse) = daily_verse { - let timestamp = Utc::now(); - let feed_type = FeedItemType::Verse { verse: verse.clone() }; - let priority = calculate_priority(&feed_type, ×tamp); - - feed_items.push(FeedItem { - id: format!("verse_{}", verse.reference), - feed_type, - timestamp: timestamp.to_rfc3339(), - priority, - }); - } - - // Sort by priority (highest first), then by timestamp (newest first) - feed_items.sort_by(|a, b| { - b.priority.cmp(&a.priority) - .then_with(|| b.timestamp.cmp(&a.timestamp)) - }); - - feed_items -} - -/// Media type enumeration for content categorization -#[derive(Debug, Clone, Serialize, Deserialize)] -pub enum MediaType { - Sermons, - LiveStreams, -} - -impl MediaType { - pub fn display_name(&self) -> &'static str { - match self { - MediaType::Sermons => "Sermons", - MediaType::LiveStreams => "Live Archives", - } - } - - pub fn icon_name(&self) -> &'static str { - match self { - MediaType::Sermons => "play.rectangle.fill", - MediaType::LiveStreams => "dot.radiowaves.left.and.right", - } - } -} - -/// Get sermons or livestreams based on media type -pub fn get_media_content(sermons: &[Sermon], media_type: &MediaType) -> Vec { - match media_type { - MediaType::Sermons => { - // Filter for regular sermons (non-livestream) - sermons.iter() - .filter(|sermon| !sermon.title.to_lowercase().contains("livestream")) - .cloned() - .collect() - }, - MediaType::LiveStreams => { - // Filter for livestream archives - sermons.iter() - .filter(|sermon| sermon.title.to_lowercase().contains("livestream")) - .cloned() - .collect() - }, - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::models::{ClientEvent, Sermon, Bulletin, BibleVerse}; - - fn create_sample_event(id: &str, title: &str) -> ClientEvent { - ClientEvent { - id: id.to_string(), - title: title.to_string(), - description: "Sample description".to_string(), - date: "2025-01-15".to_string(), - start_time: "6:00 PM".to_string(), - end_time: "8:00 PM".to_string(), - location: "Sample Location".to_string(), - location_url: None, - image: None, - thumbnail: None, - category: "Social".to_string(), - is_featured: false, - recurring_type: None, - tags: None, - contact_email: None, - contact_phone: None, - registration_url: None, - max_attendees: None, - current_attendees: None, - created_at: "2025-01-10T10:00:00Z".to_string(), - updated_at: "2025-01-10T10:00:00Z".to_string(), - duration_minutes: 120, - has_registration: false, - is_full: false, - spots_remaining: None, - } - } - - fn create_sample_sermon(id: &str, title: &str) -> Sermon { - Sermon { - id: id.to_string(), - title: title.to_string(), - description: Some("Sample sermon".to_string()), - date: Some("2025-01-10T10:00:00Z".to_string()), - video_url: Some("https://example.com/video".to_string()), - audio_url: None, - thumbnail_url: None, - duration: None, - speaker: Some("Pastor Smith".to_string()), - series: None, - scripture_references: None, - tags: None, - } - } - - #[test] - fn test_aggregate_home_feed() { - let events = vec![ - create_sample_event("1", "Event 1"), - create_sample_event("2", "Event 2"), - ]; - - let sermons = vec![ - create_sample_sermon("1", "Sermon 1"), - create_sample_sermon("2", "Sermon 2"), - ]; - - let bulletins = vec![ - Bulletin { - id: "1".to_string(), - title: "Weekly Bulletin".to_string(), - date: "2025-01-12T10:00:00Z".to_string(), - pdf_url: "https://example.com/bulletin.pdf".to_string(), - description: Some("This week's bulletin".to_string()), - thumbnail_url: None, - } - ]; - - let verse = BibleVerse { - text: "For God so loved the world...".to_string(), - reference: "John 3:16".to_string(), - version: Some("KJV".to_string()), - }; - - let feed = aggregate_home_feed(&events, &sermons, &bulletins, Some(&verse)); - - assert!(feed.len() >= 4); // Should have events, sermons, bulletin, and verse - - // Check that items are sorted by priority - for i in 1..feed.len() { - assert!(feed[i-1].priority >= feed[i].priority); - } - } - - #[test] - fn test_media_type_display() { - assert_eq!(MediaType::Sermons.display_name(), "Sermons"); - assert_eq!(MediaType::LiveStreams.display_name(), "Live Archives"); - assert_eq!(MediaType::Sermons.icon_name(), "play.rectangle.fill"); - assert_eq!(MediaType::LiveStreams.icon_name(), "dot.radiowaves.left.and.right"); - } - - #[test] - fn test_get_media_content() { - let sermons = vec![ - create_sample_sermon("1", "Regular Sermon"), - create_sample_sermon("2", "Livestream Service"), - create_sample_sermon("3", "Another Sermon"), - ]; - - let regular_sermons = get_media_content(&sermons, &MediaType::Sermons); - assert_eq!(regular_sermons.len(), 2); - - let livestreams = get_media_content(&sermons, &MediaType::LiveStreams); - assert_eq!(livestreams.len(), 1); - assert!(livestreams[0].title.contains("Livestream")); - } -} \ No newline at end of file diff --git a/church-core/src/utils/formatting.rs b/church-core/src/utils/formatting.rs deleted file mode 100644 index ad8ddc4..0000000 --- a/church-core/src/utils/formatting.rs +++ /dev/null @@ -1,159 +0,0 @@ -use crate::models::ClientEvent; -use serde::{Deserialize, Serialize}; - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct FormattedEvent { - pub formatted_time: String, - pub formatted_date_time: String, - pub is_multi_day: bool, - pub formatted_date_range: String, -} - -/// Format time range for display -pub fn format_time_range(start_time: &str, end_time: &str) -> String { - format!("{} - {}", start_time, end_time) -} - -/// Check if event appears to be multi-day based on date format -pub fn is_multi_day_event(date: &str) -> bool { - date.contains(" - ") -} - -/// Format date and time for display, handling multi-day events -pub fn format_date_time(date: &str, start_time: &str, end_time: &str) -> String { - if is_multi_day_event(date) { - // For multi-day events, integrate times with their respective dates - let components: Vec<&str> = date.split(" - ").collect(); - if components.len() == 2 { - format!("{} at {} - {} at {}", components[0], start_time, components[1], end_time) - } else { - date.to_string() // Fallback to original date - } - } else { - // Single day events: return just the date (time displayed separately) - date.to_string() - } -} - -/// Format a client event with all display formatting logic -pub fn format_event_for_display(event: &ClientEvent) -> FormattedEvent { - let start_time = &event.start_time; - let end_time = &event.end_time; - - // Derive formatted date from start_time since ClientEvent no longer has date field - let formatted_date = format_date_from_timestamp(start_time); - - FormattedEvent { - formatted_time: format_time_range(start_time, end_time), - formatted_date_time: format_date_time(&formatted_date, start_time, end_time), - is_multi_day: is_multi_day_event(&formatted_date), - formatted_date_range: formatted_date, - } -} - -/// Extract formatted date from ISO timestamp -fn format_date_from_timestamp(timestamp: &str) -> String { - use chrono::{DateTime, FixedOffset}; - - if let Ok(dt) = DateTime::parse_from_str(timestamp, "%Y-%m-%dT%H:%M:%S%z") { - dt.format("%A, %B %d, %Y").to_string() - } else { - "Date TBD".to_string() - } -} - -/// Format duration in minutes to human readable format -pub fn format_duration_minutes(minutes: i64) -> String { - if minutes < 60 { - format!("{} min", minutes) - } else { - let hours = minutes / 60; - let remaining_minutes = minutes % 60; - if remaining_minutes == 0 { - format!("{} hr", hours) - } else { - format!("{} hr {} min", hours, remaining_minutes) - } - } -} - -/// Format spots remaining for events -pub fn format_spots_remaining(current: Option, max: Option) -> Option { - match (current, max) { - (Some(current), Some(max)) => { - let remaining = max.saturating_sub(current); - if remaining == 0 { - Some("Event Full".to_string()) - } else { - Some(format!("{} spots remaining", remaining)) - } - } - _ => None, - } -} - -/// Check if event registration is full -pub fn is_event_full(current: Option, max: Option) -> bool { - match (current, max) { - (Some(current), Some(max)) => current >= max, - _ => false, - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_format_time_range() { - assert_eq!(format_time_range("9:00 AM", "5:00 PM"), "9:00 AM - 5:00 PM"); - assert_eq!(format_time_range("", ""), " - "); - } - - #[test] - fn test_is_multi_day_event() { - assert!(is_multi_day_event("Saturday, Aug 30, 2025 - Sunday, Aug 31, 2025")); - assert!(!is_multi_day_event("Saturday, Aug 30, 2025")); - assert!(!is_multi_day_event("")); - } - - #[test] - fn test_format_date_time() { - // Single day event - let result = format_date_time("Saturday, Aug 30, 2025", "6:00 PM", "8:00 PM"); - assert_eq!(result, "Saturday, Aug 30, 2025"); - - // Multi-day event - let result = format_date_time( - "Saturday, Aug 30, 2025 - Sunday, Aug 31, 2025", - "6:00 PM", - "6:00 AM" - ); - assert_eq!(result, "Saturday, Aug 30, 2025 at 6:00 PM - Sunday, Aug 31, 2025 at 6:00 AM"); - } - - #[test] - fn test_format_duration_minutes() { - assert_eq!(format_duration_minutes(30), "30 min"); - assert_eq!(format_duration_minutes(60), "1 hr"); - assert_eq!(format_duration_minutes(90), "1 hr 30 min"); - assert_eq!(format_duration_minutes(120), "2 hr"); - } - - #[test] - fn test_format_spots_remaining() { - assert_eq!(format_spots_remaining(Some(8), Some(10)), Some("2 spots remaining".to_string())); - assert_eq!(format_spots_remaining(Some(10), Some(10)), Some("Event Full".to_string())); - assert_eq!(format_spots_remaining(None, Some(10)), None); - assert_eq!(format_spots_remaining(Some(5), None), None); - } - - #[test] - fn test_is_event_full() { - assert!(is_event_full(Some(10), Some(10))); - assert!(is_event_full(Some(11), Some(10))); // Over capacity - assert!(!is_event_full(Some(9), Some(10))); - assert!(!is_event_full(None, Some(10))); - assert!(!is_event_full(Some(5), None)); - } -} \ No newline at end of file diff --git a/church-core/src/utils/mod.rs b/church-core/src/utils/mod.rs deleted file mode 100644 index 07c6c91..0000000 --- a/church-core/src/utils/mod.rs +++ /dev/null @@ -1,9 +0,0 @@ -pub mod scripture; -pub mod validation; -pub mod formatting; -pub mod feed; - -pub use scripture::*; -pub use validation::*; -pub use formatting::*; -pub use feed::*; \ No newline at end of file diff --git a/church-core/src/utils/scripture.rs b/church-core/src/utils/scripture.rs deleted file mode 100644 index 5a1fa0f..0000000 --- a/church-core/src/utils/scripture.rs +++ /dev/null @@ -1,164 +0,0 @@ -use regex::Regex; -use serde::{Deserialize, Serialize}; - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct ScriptureSection { - pub verse: String, - pub reference: String, -} - -/// Format raw scripture text into structured sections with verses and references -pub fn format_scripture_text(text: &str) -> Vec { - // Handle single-line format where verse and reference are together - if text.contains(" KJV") && !text.contains('\n') { - // Single line format: "verse text. Book chapter:verse KJV" - if let Some(kjv_pos) = text.rfind(" KJV") { - let before_kjv = &text[..kjv_pos]; - // Find the last period or other punctuation that separates verse from reference - if let Some(last_period) = before_kjv.rfind('.') { - if let Some(reference_start) = before_kjv[last_period..].find(char::is_alphabetic) { - let actual_start = last_period + reference_start; - let verse_text = format!("{}.", &before_kjv[..last_period]); - let reference = format!("{} KJV", &before_kjv[actual_start..]); - return vec![ScriptureSection { - verse: verse_text.trim().to_string(), - reference: reference.trim().to_string(), - }]; - } - } - } - // Fallback: treat entire text as verse with no separate reference - return vec![ScriptureSection { - verse: text.to_string(), - reference: String::new(), - }]; - } - - // Multi-line format (original logic) - let sections: Vec<&str> = text.split('\n').collect(); - let mut formatted_sections = Vec::new(); - let mut current_verse = String::new(); - - for section in sections { - let trimmed = section.trim(); - if trimmed.is_empty() { - continue; - } - - // Check if this line is a reference (contains "KJV" at the end) - if trimmed.ends_with("KJV") { - // This is a reference for the verse we just accumulated - if !current_verse.is_empty() { - formatted_sections.push(ScriptureSection { - verse: current_verse.clone(), - reference: trimmed.to_string(), - }); - current_verse.clear(); // Reset for next verse - } - } else { - // This is verse text - if !current_verse.is_empty() { - current_verse.push(' '); - } - current_verse.push_str(trimmed); - } - } - - // Add any remaining verse without a reference - if !current_verse.is_empty() { - formatted_sections.push(ScriptureSection { - verse: current_verse, - reference: String::new(), - }); - } - - formatted_sections -} - -/// Extract scripture references from text (e.g., "Joel 2:28 KJV" patterns) -pub fn extract_scripture_references(text: &str) -> String { - let pattern = r"([1-3]?\s*[A-Za-z]+\s+\d+:\d+(?:-\d+)?)\s+KJV"; - - match Regex::new(pattern) { - Ok(regex) => { - let references: Vec = regex - .captures_iter(text) - .filter_map(|cap| cap.get(1).map(|m| m.as_str().trim().to_string())) - .collect(); - - if references.is_empty() { - "Scripture Reading".to_string() - } else { - references.join(", ") - } - } - Err(_) => "Scripture Reading".to_string(), - } -} - -/// Create standardized share text for sermons -pub fn create_sermon_share_text(title: &str, speaker: &str, video_url: Option<&str>, audio_url: Option<&str>) -> Vec { - let mut items = Vec::new(); - - // Create share text - let share_text = format!("Check out this sermon: \"{}\" by {}", title, speaker); - items.push(share_text); - - // Add video URL if available, otherwise audio URL - if let Some(url) = video_url { - items.push(url.to_string()); - } else if let Some(url) = audio_url { - items.push(url.to_string()); - } - - items -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_single_line_scripture_format() { - let input = "And it shall come to pass afterward, that I will pour out my spirit upon all flesh. Joel 2:28 KJV"; - let result = format_scripture_text(input); - - assert_eq!(result.len(), 1); - assert_eq!(result[0].verse, "And it shall come to pass afterward, that I will pour out my spirit upon all flesh."); - assert_eq!(result[0].reference, "Joel 2:28 KJV"); - } - - #[test] - fn test_multi_line_scripture_format() { - let input = "And it shall come to pass afterward, that I will pour out my spirit upon all flesh\nJoel 2:28 KJV\nQuench not the Spirit. Despise not prophesyings.\n1 Thessalonians 5:19-21 KJV"; - let result = format_scripture_text(input); - - assert_eq!(result.len(), 2); - assert_eq!(result[0].verse, "And it shall come to pass afterward, that I will pour out my spirit upon all flesh"); - assert_eq!(result[0].reference, "Joel 2:28 KJV"); - assert_eq!(result[1].verse, "Quench not the Spirit. Despise not prophesyings."); - assert_eq!(result[1].reference, "1 Thessalonians 5:19-21 KJV"); - } - - #[test] - fn test_extract_scripture_references() { - let input = "Some text with Joel 2:28 KJV and 1 Thessalonians 5:19-21 KJV references"; - let result = extract_scripture_references(input); - - assert_eq!(result, "Joel 2:28, 1 Thessalonians 5:19-21"); - } - - #[test] - fn test_create_sermon_share_text() { - let result = create_sermon_share_text( - "Test Sermon", - "John Doe", - Some("https://example.com/video"), - Some("https://example.com/audio") - ); - - assert_eq!(result.len(), 2); - assert_eq!(result[0], "Check out this sermon: \"Test Sermon\" by John Doe"); - assert_eq!(result[1], "https://example.com/video"); - } -} \ No newline at end of file diff --git a/church-core/src/utils/validation.rs b/church-core/src/utils/validation.rs deleted file mode 100644 index af22e52..0000000 --- a/church-core/src/utils/validation.rs +++ /dev/null @@ -1,249 +0,0 @@ -use regex::Regex; -use serde::{Deserialize, Serialize}; -use chrono::{DateTime, Utc, NaiveDateTime}; - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct ValidationResult { - pub is_valid: bool, - pub errors: Vec, -} - -impl ValidationResult { - pub fn valid() -> Self { - Self { - is_valid: true, - errors: Vec::new(), - } - } - - pub fn invalid(errors: Vec) -> Self { - Self { - is_valid: false, - errors, - } - } - - pub fn add_error(&mut self, error: String) { - self.errors.push(error); - self.is_valid = false; - } -} - -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct ContactFormData { - pub name: String, - pub email: String, - pub phone: String, - pub message: String, - pub subject: String, -} - -/// Validate email address using regex -pub fn is_valid_email(email: &str) -> bool { - let email_regex = Regex::new(r"^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$").unwrap(); - email_regex.is_match(email) -} - -/// Validate phone number - must be exactly 10 digits -pub fn is_valid_phone(phone: &str) -> bool { - let digits_only: String = phone.chars().filter(|c| c.is_ascii_digit()).collect(); - digits_only.len() == 10 -} - -/// Validate contact form with all business rules -pub fn validate_contact_form(form_data: &ContactFormData) -> ValidationResult { - let mut errors = Vec::new(); - - let trimmed_name = form_data.name.trim(); - let trimmed_email = form_data.email.trim(); - let trimmed_phone = form_data.phone.trim(); - let trimmed_message = form_data.message.trim(); - - // Name validation - if trimmed_name.is_empty() || trimmed_name.len() < 2 { - errors.push("Name must be at least 2 characters".to_string()); - } - - // Email validation - if trimmed_email.is_empty() { - errors.push("Email is required".to_string()); - } else if !is_valid_email(trimmed_email) { - errors.push("Please enter a valid email address".to_string()); - } - - // Phone validation (optional, but if provided must be valid) - if !trimmed_phone.is_empty() && !is_valid_phone(trimmed_phone) { - errors.push("Please enter a valid phone number".to_string()); - } - - // Message validation - if trimmed_message.is_empty() { - errors.push("Message is required".to_string()); - } else if trimmed_message.len() < 10 { - errors.push("Message must be at least 10 characters".to_string()); - } - - if errors.is_empty() { - ValidationResult::valid() - } else { - ValidationResult::invalid(errors) - } -} - -/// Sanitize and trim form input -pub fn sanitize_form_input(input: &str) -> String { - input.trim().to_string() -} - -/// Parse date from various frontend formats -/// Supports: "2025-06-28T23:00", "2025-06-28 23:00", and RFC3339 formats -pub fn parse_datetime_flexible(date_str: &str) -> Option> { - let trimmed = date_str.trim(); - - // First try RFC3339/ISO 8601 with timezone info - if trimmed.contains('Z') || trimmed.contains('+') || trimmed.contains('-') && trimmed.rfind('-').map_or(false, |i| i > 10) { - if let Ok(dt) = DateTime::parse_from_rfc3339(trimmed) { - return Some(dt.with_timezone(&Utc)); - } - - // Try ISO 8601 with 'Z' suffix - if let Ok(dt) = chrono::DateTime::parse_from_str(trimmed, "%Y-%m-%dT%H:%M:%S%.3fZ") { - return Some(dt.with_timezone(&Utc)); - } - - // Try ISO 8601 without milliseconds but with Z - if let Ok(dt) = chrono::DateTime::parse_from_str(trimmed, "%Y-%m-%dT%H:%M:%SZ") { - return Some(dt.with_timezone(&Utc)); - } - } - - // Try local datetime formats (no timezone info) - treat as UTC - let local_formats = [ - "%Y-%m-%dT%H:%M:%S%.3f", // ISO 8601 with milliseconds, no timezone - "%Y-%m-%dT%H:%M:%S", // ISO 8601 no milliseconds, no timezone - "%Y-%m-%dT%H:%M", // ISO 8601 no seconds, no timezone (frontend format) - "%Y-%m-%d %H:%M:%S", // Space separated with seconds - "%Y-%m-%d %H:%M", // Space separated no seconds - "%m/%d/%Y %H:%M:%S", // US format with seconds - "%m/%d/%Y %H:%M", // US format no seconds - ]; - - for format in &local_formats { - if let Ok(naive_dt) = NaiveDateTime::parse_from_str(trimmed, format) { - return Some(DateTime::from_naive_utc_and_offset(naive_dt, Utc)); - } - } - - // Try date-only formats (no time) - set time to midnight UTC - let date_formats = [ - "%Y-%m-%d", // ISO date - "%m/%d/%Y", // US date - "%d/%m/%Y", // European date - ]; - - for format in &date_formats { - if let Ok(naive_date) = chrono::NaiveDate::parse_from_str(trimmed, format) { - return Some(DateTime::from_naive_utc_and_offset( - naive_date.and_hms_opt(0, 0, 0).unwrap(), - Utc, - )); - } - } - - None -} - -/// Validate datetime string can be parsed -pub fn is_valid_datetime(datetime_str: &str) -> bool { - parse_datetime_flexible(datetime_str).is_some() -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_valid_email() { - assert!(is_valid_email("test@example.com")); - assert!(is_valid_email("user.name+tag@domain.co.uk")); - assert!(!is_valid_email("invalid.email")); - assert!(!is_valid_email("@domain.com")); - assert!(!is_valid_email("user@")); - } - - #[test] - fn test_valid_phone() { - assert!(is_valid_phone("1234567890")); - assert!(is_valid_phone("(123) 456-7890")); - assert!(is_valid_phone("123-456-7890")); - assert!(!is_valid_phone("12345")); - assert!(!is_valid_phone("12345678901")); - assert!(!is_valid_phone("abc1234567")); - } - - #[test] - fn test_contact_form_validation() { - let valid_form = ContactFormData { - name: "John Doe".to_string(), - email: "john@example.com".to_string(), - phone: "1234567890".to_string(), - message: "This is a test message with enough characters.".to_string(), - subject: "Test Subject".to_string(), - }; - - let result = validate_contact_form(&valid_form); - assert!(result.is_valid); - assert!(result.errors.is_empty()); - - let invalid_form = ContactFormData { - name: "A".to_string(), // Too short - email: "invalid-email".to_string(), // Invalid email - phone: "123".to_string(), // Invalid phone - message: "Short".to_string(), // Too short message - subject: "".to_string(), - }; - - let result = validate_contact_form(&invalid_form); - assert!(!result.is_valid); - assert_eq!(result.errors.len(), 4); - } - - #[test] - fn test_parse_datetime_flexible() { - // Test frontend format (the main case we're solving) - assert!(parse_datetime_flexible("2025-06-28T23:00").is_some()); - - // Test RFC3339 with Z - assert!(parse_datetime_flexible("2024-01-15T14:30:00Z").is_some()); - - // Test RFC3339 with timezone offset - assert!(parse_datetime_flexible("2024-01-15T14:30:00-05:00").is_some()); - - // Test ISO 8601 without timezone (should work as local time) - assert!(parse_datetime_flexible("2024-01-15T14:30:00").is_some()); - - // Test with milliseconds - assert!(parse_datetime_flexible("2024-01-15T14:30:00.000Z").is_some()); - - // Test space separated - assert!(parse_datetime_flexible("2024-01-15 14:30:00").is_some()); - - // Test date only - assert!(parse_datetime_flexible("2024-01-15").is_some()); - - // Test US format - assert!(parse_datetime_flexible("01/15/2024 14:30").is_some()); - - // Test invalid format - assert!(parse_datetime_flexible("invalid-date").is_none()); - assert!(parse_datetime_flexible("").is_none()); - } - - #[test] - fn test_is_valid_datetime() { - assert!(is_valid_datetime("2025-06-28T23:00")); - assert!(is_valid_datetime("2024-01-15T14:30:00Z")); - assert!(!is_valid_datetime("invalid-date")); - assert!(!is_valid_datetime("")); - } -} \ No newline at end of file diff --git a/astro-church-website/cpp/church_core.cpp b/cpp/church_core.cpp similarity index 100% rename from astro-church-website/cpp/church_core.cpp rename to cpp/church_core.cpp diff --git a/astro-church-website/cpp/church_core.hpp b/cpp/church_core.hpp similarity index 100% rename from astro-church-website/cpp/church_core.hpp rename to cpp/church_core.hpp diff --git a/astro-church-website/linux/index.js b/linux/index.js similarity index 100% rename from astro-church-website/linux/index.js rename to linux/index.js diff --git a/astro-church-website/package.json b/package.json similarity index 100% rename from astro-church-website/package.json rename to package.json diff --git a/astro-church-website/public/favicon.svg b/public/favicon.svg similarity index 100% rename from astro-church-website/public/favicon.svg rename to public/favicon.svg diff --git a/astro-church-website/public/icons/app-icon.svg b/public/icons/app-icon.svg similarity index 100% rename from astro-church-website/public/icons/app-icon.svg rename to public/icons/app-icon.svg diff --git a/astro-church-website/public/images/app-store-badge.svg b/public/images/app-store-badge.svg similarity index 100% rename from astro-church-website/public/images/app-store-badge.svg rename to public/images/app-store-badge.svg diff --git a/astro-church-website/public/live-status-updater.js b/public/live-status-updater.js similarity index 100% rename from astro-church-website/public/live-status-updater.js rename to public/live-status-updater.js diff --git a/astro-church-website/public/manifest.json b/public/manifest.json similarity index 100% rename from astro-church-website/public/manifest.json rename to public/manifest.json diff --git a/astro-church-website/public/sw.js b/public/sw.js similarity index 100% rename from astro-church-website/public/sw.js rename to public/sw.js diff --git a/astro-church-website/src/components/Footer.astro b/src/components/Footer.astro similarity index 100% rename from astro-church-website/src/components/Footer.astro rename to src/components/Footer.astro diff --git a/astro-church-website/src/components/Navbar.astro b/src/components/Navbar.astro similarity index 100% rename from astro-church-website/src/components/Navbar.astro rename to src/components/Navbar.astro diff --git a/astro-church-website/src/components/admin/Login.astro b/src/components/admin/Login.astro similarity index 100% rename from astro-church-website/src/components/admin/Login.astro rename to src/components/admin/Login.astro diff --git a/astro-church-website/src/layouts/AdminLayout.astro b/src/layouts/AdminLayout.astro similarity index 100% rename from astro-church-website/src/layouts/AdminLayout.astro rename to src/layouts/AdminLayout.astro diff --git a/astro-church-website/src/layouts/BaseLayout.astro b/src/layouts/BaseLayout.astro similarity index 100% rename from astro-church-website/src/layouts/BaseLayout.astro rename to src/layouts/BaseLayout.astro diff --git a/astro-church-website/src/layouts/MainLayout.astro b/src/layouts/MainLayout.astro similarity index 100% rename from astro-church-website/src/layouts/MainLayout.astro rename to src/layouts/MainLayout.astro diff --git a/astro-church-website/src/lib.rs b/src/lib.rs similarity index 100% rename from astro-church-website/src/lib.rs rename to src/lib.rs diff --git a/astro-church-website/src/lib/bindings.js b/src/lib/bindings.js similarity index 96% rename from astro-church-website/src/lib/bindings.js rename to src/lib/bindings.js index 8c448e7..24ab299 100644 --- a/astro-church-website/src/lib/bindings.js +++ b/src/lib/bindings.js @@ -30,6 +30,7 @@ export const { fetchCurrentBulletinJson, fetchBibleVerseJson, submitEventJson, + submitEventWithImageJson, // Admin functions fetchAllSchedulesJson, createScheduleJson, diff --git a/astro-church-website/src/lib/constants.js b/src/lib/constants.js similarity index 100% rename from astro-church-website/src/lib/constants.js rename to src/lib/constants.js diff --git a/astro-church-website/src/pages/about.astro b/src/pages/about.astro similarity index 100% rename from astro-church-website/src/pages/about.astro rename to src/pages/about.astro diff --git a/astro-church-website/src/pages/admin/api/auth/login.ts b/src/pages/admin/api/auth/login.ts similarity index 100% rename from astro-church-website/src/pages/admin/api/auth/login.ts rename to src/pages/admin/api/auth/login.ts diff --git a/astro-church-website/src/pages/admin/api/bulletins.ts b/src/pages/admin/api/bulletins.ts similarity index 100% rename from astro-church-website/src/pages/admin/api/bulletins.ts rename to src/pages/admin/api/bulletins.ts diff --git a/astro-church-website/src/pages/admin/api/bulletins/[id].ts b/src/pages/admin/api/bulletins/[id].ts similarity index 100% rename from astro-church-website/src/pages/admin/api/bulletins/[id].ts rename to src/pages/admin/api/bulletins/[id].ts diff --git a/astro-church-website/src/pages/admin/api/events.ts b/src/pages/admin/api/events.ts similarity index 100% rename from astro-church-website/src/pages/admin/api/events.ts rename to src/pages/admin/api/events.ts diff --git a/astro-church-website/src/pages/admin/api/events/[id].ts b/src/pages/admin/api/events/[id].ts similarity index 100% rename from astro-church-website/src/pages/admin/api/events/[id].ts rename to src/pages/admin/api/events/[id].ts diff --git a/astro-church-website/src/pages/admin/api/events/[id]/approve.ts b/src/pages/admin/api/events/[id]/approve.ts similarity index 100% rename from astro-church-website/src/pages/admin/api/events/[id]/approve.ts rename to src/pages/admin/api/events/[id]/approve.ts diff --git a/astro-church-website/src/pages/admin/api/events/[id]/reject.ts b/src/pages/admin/api/events/[id]/reject.ts similarity index 100% rename from astro-church-website/src/pages/admin/api/events/[id]/reject.ts rename to src/pages/admin/api/events/[id]/reject.ts diff --git a/astro-church-website/src/pages/admin/api/schedules.ts b/src/pages/admin/api/schedules.ts similarity index 100% rename from astro-church-website/src/pages/admin/api/schedules.ts rename to src/pages/admin/api/schedules.ts diff --git a/astro-church-website/src/pages/admin/api/schedules/[date].ts b/src/pages/admin/api/schedules/[date].ts similarity index 100% rename from astro-church-website/src/pages/admin/api/schedules/[date].ts rename to src/pages/admin/api/schedules/[date].ts diff --git a/astro-church-website/src/pages/admin/bulletins.astro b/src/pages/admin/bulletins.astro similarity index 100% rename from astro-church-website/src/pages/admin/bulletins.astro rename to src/pages/admin/bulletins.astro diff --git a/astro-church-website/src/pages/admin/events.astro b/src/pages/admin/events.astro similarity index 100% rename from astro-church-website/src/pages/admin/events.astro rename to src/pages/admin/events.astro diff --git a/astro-church-website/src/pages/admin/index.astro b/src/pages/admin/index.astro similarity index 100% rename from astro-church-website/src/pages/admin/index.astro rename to src/pages/admin/index.astro diff --git a/astro-church-website/src/pages/admin/schedules.astro b/src/pages/admin/schedules.astro similarity index 100% rename from astro-church-website/src/pages/admin/schedules.astro rename to src/pages/admin/schedules.astro diff --git a/astro-church-website/src/pages/api/config.ts b/src/pages/api/config.ts similarity index 100% rename from astro-church-website/src/pages/api/config.ts rename to src/pages/api/config.ts diff --git a/astro-church-website/src/pages/api/contact.ts b/src/pages/api/contact.ts similarity index 100% rename from astro-church-website/src/pages/api/contact.ts rename to src/pages/api/contact.ts diff --git a/astro-church-website/src/pages/api/contact/validate.ts b/src/pages/api/contact/validate.ts similarity index 100% rename from astro-church-website/src/pages/api/contact/validate.ts rename to src/pages/api/contact/validate.ts diff --git a/astro-church-website/src/pages/api/events.ts b/src/pages/api/events.ts similarity index 100% rename from astro-church-website/src/pages/api/events.ts rename to src/pages/api/events.ts diff --git a/astro-church-website/src/pages/api/livestreams.ts b/src/pages/api/livestreams.ts similarity index 100% rename from astro-church-website/src/pages/api/livestreams.ts rename to src/pages/api/livestreams.ts diff --git a/astro-church-website/src/pages/api/sermons.ts b/src/pages/api/sermons.ts similarity index 100% rename from astro-church-website/src/pages/api/sermons.ts rename to src/pages/api/sermons.ts diff --git a/astro-church-website/src/pages/bulletin.astro b/src/pages/bulletin.astro similarity index 100% rename from astro-church-website/src/pages/bulletin.astro rename to src/pages/bulletin.astro diff --git a/astro-church-website/src/pages/bulletin/[id].astro b/src/pages/bulletin/[id].astro similarity index 100% rename from astro-church-website/src/pages/bulletin/[id].astro rename to src/pages/bulletin/[id].astro diff --git a/astro-church-website/src/pages/bulletin/archive.astro b/src/pages/bulletin/archive.astro similarity index 100% rename from astro-church-website/src/pages/bulletin/archive.astro rename to src/pages/bulletin/archive.astro diff --git a/astro-church-website/src/pages/contact.astro b/src/pages/contact.astro similarity index 100% rename from astro-church-website/src/pages/contact.astro rename to src/pages/contact.astro diff --git a/astro-church-website/src/pages/events.astro b/src/pages/events.astro similarity index 100% rename from astro-church-website/src/pages/events.astro rename to src/pages/events.astro diff --git a/astro-church-website/src/pages/events/[id].astro b/src/pages/events/[id].astro similarity index 95% rename from astro-church-website/src/pages/events/[id].astro rename to src/pages/events/[id].astro index fbb4c7d..046bd86 100644 --- a/astro-church-website/src/pages/events/[id].astro +++ b/src/pages/events/[id].astro @@ -2,23 +2,20 @@ import MainLayout from '../../layouts/MainLayout.astro'; import { getChurchName, fetchEventsJson } from '../../lib/bindings.js'; -export async function getStaticPaths() { - try { - const eventsJson = fetchEventsJson(); - const parsedEvents = JSON.parse(eventsJson); - const events = Array.isArray(parsedEvents) ? parsedEvents : (parsedEvents.items || []); - - return events.map((event) => ({ - params: { id: event.id }, - props: { event } - })); - } catch (e) { - console.error('Failed to generate event paths:', e); - return []; - } -} +const { id } = Astro.params; -const { event } = Astro.props; +const eventsJson = fetchEventsJson(); +const parsedEvents = JSON.parse(eventsJson); +const events = Array.isArray(parsedEvents) ? parsedEvents : (parsedEvents.items || []); + +const event = events.find(e => e.id === id); + +if (!event) { + return new Response(null, { + status: 404, + statusText: 'Not found' + }); +} let churchName = 'Church'; try { diff --git a/astro-church-website/src/pages/events/submit.astro b/src/pages/events/submit.astro similarity index 94% rename from astro-church-website/src/pages/events/submit.astro rename to src/pages/events/submit.astro index d21f339..fab2a95 100644 --- a/astro-church-website/src/pages/events/submit.astro +++ b/src/pages/events/submit.astro @@ -1,6 +1,6 @@ --- import MainLayout from '../../layouts/MainLayout.astro'; -import { getChurchName } from '../../lib/bindings.js'; +import { getChurchName, submitEventJson } from '../../lib/bindings.js'; let churchName = 'Church'; @@ -361,6 +361,8 @@ try {