diff --git a/ENV_SETUP.md b/ENV_SETUP.md index d5a39aa..0a5e01f 100644 --- a/ENV_SETUP.md +++ b/ENV_SETUP.md @@ -1,98 +1,151 @@ -# Environment Configuration +# Environment Setup -Copy `.env.example` files and configure for your environment: +Notely uses three different environment-file locations depending on how you run the app. -## Backend (.env) +## 1. Root `.env` -```env -# MongoDB -MONGODB_URI=mongodb://admin:password@localhost:27017/noteapp?authSource=admin +Use the root `.env` file when running `docker compose` from the repository root. -# JWT Configuration -JWT_SECRET=your-super-secret-jwt-key-minimum-32-characters -JWT_ISSUER=noteapp - -# Encryption (32 bytes = 32 characters) -ENCRYPTION_KEY=00000000000000000000000000000000 - -# Server -PORT=8080 -ENV=development -LOG_LEVEL=info - -# CORS (comma-separated for multiple origins) -CORS_ALLOWED_ORIGINS=http://localhost:5173,http://localhost:3000 - -# Rate Limiting -RATE_LIMIT_REQUESTS=50 -RATE_LIMIT_WINDOW=1s -``` - -## Frontend (.env) - -```env -VITE_API_BASE_URL=http://localhost:8080 -VITE_ENV=development -``` - -## Development vs Production - -### Development (.env.development) - -- Less strict security (for easier testing) -- Localhost CORS allowed -- JWT secrets can be simple -- Logging more verbose - -### Production (.env.production) - -- Strict security requirements -- Specific CORS origins only -- Strong random JWT secrets -- Limited logging (performance) -- All environment variables must be set - -## Generating Secrets +Start from: + +```bash +cp .env.example .env +``` + +### Variables Used By Docker Compose + +Required or commonly used: + +- `MONGODB_URI` +- `BACKEND_PORT` +- `JWT_SECRET` +- `ENCRYPTION_KEY` +- `FRONTEND_URL` +- `VITE_API_BASE_URL` +- `DEFAULT_ADMIN_EMAIL` +- `DEFAULT_ADMIN_USERNAME` +- `DEFAULT_ADMIN_PASSWORD` +- `NGINX_HTTP_PORT` +- `NGINX_HTTPS_PORT` + +Optional backend runtime values that Docker Compose will also pass through if present: + +- `REDIS_ADDR` +- `REDIS_USER` +- `REDIS_PASSWORD` +- `REDIS_DB` +- `SESSION_TTL_HOURS` + +### Current Defaults In The Checked-In Example + +- MongoDB container: `mongodb://admin:password@mongodb:27017/noteapp?authSource=admin` +- Backend port: `8080` +- Public frontend URL: `http://localhost` +- Browser API base URL for container builds: `http://localhost` + +## 2. `backend/.env` + +Use `backend/.env` for local backend development. + +Start from: + +```bash +cd backend +cp .env.example .env +``` + +### Variables Currently Read By The Backend Runtime + +Read in `backend/cmd/server/main.go` or other active handlers: + +- `MONGODB_URI` +- `JWT_SECRET` +- `ENCRYPTION_KEY` +- `PORT` +- `REDIS_ADDR` +- `REDIS_USER` +- `REDIS_PASSWORD` +- `REDIS_DB` +- `SESSION_TTL_HOURS` +- `DEFAULT_ADMIN_EMAIL` +- `DEFAULT_ADMIN_USERNAME` +- `DEFAULT_ADMIN_PASSWORD` +- `FRONTEND_URL` + +### Variables Present In `backend/.env.example` But Not Currently Consumed By Runtime Code + +These values exist in the example file, but the current code path does not read them yet: + +- `JWT_ISSUER` +- `ENV` +- `LOG_LEVEL` +- `CORS_ALLOWED_ORIGINS` +- `RATE_LIMIT_REQUESTS` +- `RATE_LIMIT_WINDOW` + +### Backend Defaults If A Variable Is Missing + +- `MONGODB_URI`: `mongodb://localhost:27017` +- `JWT_SECRET`: `your-secret-key-change-in-production` +- `ENCRYPTION_KEY`: `00000000000000000000000000000000` +- `PORT`: `8080` +- `REDIS_ADDR`: `localhost:6379` +- `REDIS_DB`: `0` +- `SESSION_TTL_HOURS`: `168` +- `FRONTEND_URL`: falls back to `http://localhost:5173` for login redirects + +## 3. `frontend/.env` + +Use `frontend/.env` for local frontend development. + +Start from: + +```bash +cd frontend +cp .env.example .env +``` + +### Frontend Variables In `frontend/.env.example` + +- `VITE_API_BASE_URL` +- `VITE_ENV` +- `VITE_ENABLE_ANALYTICS` + +### Variables Currently Relevant To The Frontend App + +- `VITE_API_BASE_URL`: used by the API client + +The other example values are safe to keep, but the current checked-in frontend code does not actively consume them. + +## Secret Generation + +Examples: ```bash -# JWT Secret (32+ characters) openssl rand -base64 32 - -# Encryption Key (32 bytes) -openssl rand -hex 16 # outputs 32 characters - -# Random token +openssl rand -hex 16 openssl rand -hex 32 ``` -## Docker Compose +Use generated values for: -Environment variables are defined in `docker-compose.yml` and will override `.env` files. Update the file for your deployment: +- `JWT_SECRET` +- `ENCRYPTION_KEY` +- provider secrets or other sensitive credentials stored through admin settings -```yaml -environment: - MONGODB_URI: mongodb://admin:password@mongodb:27017/noteapp?authSource=admin - JWT_SECRET: your-secret-key-change-in-production - # ... other vars -``` +## Compose Vs Local Development -## Kubernetes +Use the right env file for the right mode: -Use `kubectl create secret` for sensitive data: +- root `.env`: Docker Compose +- `backend/.env`: local backend +- `frontend/.env`: local frontend -```bash -# Create secret from literal values -kubectl create secret generic app-secrets \ - --from-literal=mongodb-uri="..." \ - --from-literal=jwt-secret="..." \ - -n noteapp +Do not assume values from one location are automatically shared with the others. -# Or use ConfigMap for non-sensitive config -kubectl create configmap app-config \ - --from-file=config.yaml \ - -n noteapp -``` +## Important Notes ---- - -**IMPORTANT**: Never commit .env files or secrets to version control! +- Do not commit real secrets +- Keep `ENCRYPTION_KEY` at 32 characters for the current AES-256 usage +- If OAuth login is enabled, set `FRONTEND_URL` correctly so callback redirects go to the intended UI +- If Redis settings are omitted, the backend assumes a local Redis instance at `localhost:6379` diff --git a/PERMISSIONS.md b/PERMISSIONS.md index 50bb034..286e984 100644 --- a/PERMISSIONS.md +++ b/PERMISSIONS.md @@ -14,7 +14,7 @@ This file lists the permissions currently checked by the application. - space.edit - Global space edit capability (used as fallback alongside space-scoped settings edit) - space.delete - - Global space delete capability (used as fallback alongside space-scoped delete) + - Global space delete capability (used as fallback alongside space-scoped settings.delete) ## Space-Scoped Permission Format @@ -30,7 +30,7 @@ space.. ### Space Management - settings.edit -- delete +- settings.delete ### Member Management diff --git a/QUICKSTART.md b/QUICKSTART.md index cd00180..671fc70 100644 --- a/QUICKSTART.md +++ b/QUICKSTART.md @@ -1,304 +1,151 @@ -# ๐Ÿš€ Quick Start Guide +# Quick Start -## Prerequisites +This guide covers the fastest way to run Notely and the current local-development workflow. -- Docker and Docker Compose (recommended for quickest setup) -- OR: Go 1.21+, Node.js 18+, MongoDB 7.0+ +## Option 1: Docker Compose -## Option 1: Docker Compose (Recommended - 1 Command) +From the repository root: ```bash -# Clone/navigate to project -cd noteapp - -# Start everything -docker-compose up - -# Wait for services to initialize (~30 seconds) -# Then open: http://localhost +cp .env.example .env +docker compose up -d --build ``` -**Services running**: +Open: -- Notely: http://localhost:8080 -- MongoDB: localhost:27017 -- Nginx Reverse Proxy: http://localhost:80 +- App UI: `http://localhost` +- Backend health endpoint: `http://localhost:8080/health` +- MongoDB: `localhost:27017` +- Redis: `localhost:6379` -**Test user (after startup)**: +Compose starts four services: -- Register a new account at http://localhost/register -- Login and create a Space -- Add Categories and Notes +- `mongodb` +- `redis` +- `notely` +- `nginx` ## Option 2: Local Development -### Backend Setup +### Prerequisites + +- Go 1.25+ +- Node.js 18+ +- MongoDB +- Redis + +If you do not already have MongoDB and Redis running locally, you can start just those services with Docker Compose: + +```bash +docker compose up -d mongodb redis +``` + +### Backend ```bash cd backend - -# Copy environment file cp .env.example .env - -# Install dependencies go mod download - -# Ensure MongoDB is running -# Docker: docker run -d -p 27017:27017 -e MONGO_INITDB_ROOT_USERNAME=admin \ -# -e MONGO_INITDB_ROOT_PASSWORD=password mongo:7.0-alpine - -# Run backend go run ./cmd/server/main.go - -# Logs should show: "Server starting on port 8080" ``` -### Frontend Setup +The backend listens on `http://localhost:8080` by default. + +### Frontend ```bash cd frontend - -# Copy environment file cp .env.example .env - -# Install dependencies npm install - -# Start dev server npm run dev - -# Open: http://localhost:5173 in browser ``` -## ๐Ÿงช Testing +The Vite dev server listens on `http://localhost:5173` and proxies `/api` to `http://localhost:8080`. -### Backend Tests +## Day-To-Day Commands + +### Backend ```bash cd backend - -# Run all tests go test ./... - -# Run with verbose output -go test -v ./... - -# Run specific test -go test -v -run TestRegisterUser ./tests/unit/... - -# With coverage -go test -cover ./... +go test -v ./tests/unit/... +go test -v ./tests/integration/... ``` -### Frontend Tests +### Frontend ```bash cd frontend - -# Run tests +npm run build +npm run lint npm run test - -# Watch mode -npm run test:watch - -# Coverage -npm run test:coverage ``` -## ๐Ÿ“ Key API Endpoints +## First Run Checklist -### Authentication +1. Register a user or set `DEFAULT_ADMIN_*` values in your env file before startup. +2. Sign in. +3. Create a space. +4. Create categories and notes. +5. Use the top search bar to verify note search. -```bash -# Register -curl -X POST http://localhost:8080/api/v1/auth/register \ - -H "Content-Type: application/json" \ - -d '{ - "email": "user@example.com", - "username": "myuser", - "password": "SecurePassword123", - "password_confirm": "SecurePassword123", - "first_name": "John", - "last_name": "Doe" - }' +## Useful Endpoints -# Login -curl -X POST http://localhost:8080/api/v1/auth/login \ - -H "Content-Type: application/json" \ - -d '{ - "email": "user@example.com", - "password": "SecurePassword123" - }' +Authentication: -# Response contains: access_token, refresh_token, user data -``` +- `POST /api/v1/auth/register` +- `POST /api/v1/auth/login` +- `POST /api/v1/auth/refresh` +- `GET /api/v1/auth/me` -### Create Space +Spaces: -```bash -curl -X POST http://localhost:8080/api/v1/spaces \ - -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \ - -H "Content-Type: application/json" \ - -d '{ - "name": "My First Space", - "description": "Notes for my project", - "icon": "๐Ÿ“š", - "is_public": false - }' -``` +- `GET /api/v1/spaces` +- `POST /api/v1/spaces` +- `PUT /api/v1/spaces/{spaceId}` +- `DELETE /api/v1/spaces/{spaceId}` -### Create Note +Notes: -```bash -curl -X POST http://localhost:8080/api/v1/spaces/{spaceId}/notes \ - -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \ - -H "Content-Type: application/json" \ - -d '{ - "title": "My First Note", - "content": "# Markdown Heading\n\nThis is a note", - "tags": ["important", "work"], - "category_id": null, - "is_pinned": false, - "is_favorite": true - }' -``` +- `GET /api/v1/spaces/{spaceId}/notes` +- `POST /api/v1/spaces/{spaceId}/notes` +- `GET /api/v1/spaces/{spaceId}/notes/search?q=` +- `POST /api/v1/spaces/{spaceId}/notes/{noteId}/unlock` -### Search Notes +Public access: -```bash -curl "http://localhost:8080/api/v1/spaces/{spaceId}/notes/search?q=important" \ - -H "Authorization: Bearer YOUR_ACCESS_TOKEN" -``` +- `GET /api/v1/public/spaces` +- `GET /api/v1/public/spaces/{spaceId}/notes` -## ๐Ÿ” Troubleshooting +## Troubleshooting -### MongoDB Connection Error +### Backend cannot connect to MongoDB -``` -Error: Failed to connect to database +Check `MONGODB_URI` in your selected env file and make sure MongoDB is reachable. -Solution: -docker run -d -p 27017:27017 \ - -e MONGO_INITDB_ROOT_USERNAME=admin \ - -e MONGO_INITDB_ROOT_PASSWORD=password \ - mongo:7.0-alpine -``` +### Backend cannot connect to Redis -### Port Already in Use +Check `REDIS_ADDR`, `REDIS_PASSWORD`, and `REDIS_DB`. For local defaults, Redis should usually be reachable at `localhost:6379`. -```bash -# Find process on port 8080 -lsof -i :8080 +### The browser cannot reach the API in local dev -# Kill it -kill -9 +Check: -# Or use different port -PORT=8081 go run ./cmd/server/main.go -``` +- backend is running on port `8080` +- frontend `VITE_API_BASE_URL` +- Vite proxy settings in `frontend/vite.config.js` -### CORS Errors +### OAuth callback redirects to the wrong URL -Make sure frontend and backend URLs match in: +Check `FRONTEND_URL` in your selected env file. -- Frontend: `VITE_API_BASE_URL` in `.env` -- Backend: `CORS_ALLOWED_ORIGINS` in `.env` +### Permission-denied behavior is unclear -### MongoDB Auth Failed +Read `PERMISSIONS.md` and then inspect the relevant backend service in `backend/internal/application/services/`. -If using Docker Compose: +## Related Docs -- Username: `admin` -- Password: `password` -- Connection string includes `?authSource=admin` - -## ๐Ÿ“š Project Structure - -``` -noteapp/ -โ”œโ”€โ”€ backend/ # Go REST API -โ”‚ โ”œโ”€โ”€ cmd/server/ # Entry point -โ”‚ โ”œโ”€โ”€ internal/ -โ”‚ โ”‚ โ”œโ”€โ”€ domain/ # Business logic -โ”‚ โ”‚ โ”œโ”€โ”€ application/ # Services & DTOs -โ”‚ โ”‚ โ”œโ”€โ”€ infrastructure/ # DB, auth, security -โ”‚ โ”‚ โ””โ”€โ”€ interfaces/ # HTTP handlers -โ”‚ โ”œโ”€โ”€ tests/ # Test files -โ”‚ โ”œโ”€โ”€ go.mod & go.sum # Dependencies -โ”‚ โ””โ”€โ”€ README.md -โ”‚ -โ”œโ”€โ”€ frontend/ # Vue 3 SPA -โ”‚ โ”œโ”€โ”€ src/ -โ”‚ โ”‚ โ”œโ”€โ”€ components/ # UI components -โ”‚ โ”‚ โ”œโ”€โ”€ pages/ # Page components -โ”‚ โ”‚ โ”œโ”€โ”€ stores/ # Pinia state -โ”‚ โ”‚ โ”œโ”€โ”€ services/ # API client -โ”‚ โ”‚ โ”œโ”€โ”€ router/ # Vue Router -โ”‚ โ”‚ โ”œโ”€โ”€ assets/ # Styles & images -โ”‚ โ”‚ โ””โ”€โ”€ main.js # Entry point -โ”‚ โ”œโ”€โ”€ tests/ # Test files -โ”‚ โ”œโ”€โ”€ package.json # Dependencies -โ”‚ โ””โ”€โ”€ vite.config.js # Vite configuration -โ”‚ -โ”œโ”€โ”€ devops/ -โ”‚ โ”œโ”€โ”€ docker/ -โ”‚ โ”‚ โ”œโ”€โ”€ Dockerfile.backend -โ”‚ โ”‚ โ”œโ”€โ”€ Dockerfile.frontend -โ”‚ โ”‚ โ””โ”€โ”€ nginx.conf -โ”‚ โ””โ”€โ”€ kubernetes/ -โ”‚ โ””โ”€โ”€ deployment.yaml -โ”‚ -โ”œโ”€โ”€ docker-compose.yml # Local development setup -โ”œโ”€โ”€ README.md # Project docs -โ”œโ”€โ”€ ARCHITECTURE.md # Architecture overview -โ”œโ”€โ”€ SECURITY.md # Security implementation -โ””โ”€โ”€ ENV_SETUP.md # Environment configuration -``` - -## ๐ŸŽ“ Learning Resources - -### Understanding the Code - -1. **Start here**: `ARCHITECTURE.md` - Clean architecture pattern -2. **Then read**: Backend `domain/entities/*.go` - Core models -3. **Next**: Backend `application/services/*.go` - Business logic -4. **UI**: Frontend `src/stores/authStore.js` - State management -5. **API**: Backend `interfaces/handlers/*.go` - HTTP layer - -### Security Deep Dive - -See `SECURITY.md` for: - -- Password hashing (Argon2id) -- JWT authentication -- Authorization (RBAC) -- Input validation -- XSS prevention -- CSRF protection - -## ๐Ÿš€ Next Steps - -1. **Explore the UI**: Create spaces, notes, categories -2. **Read the code**: Start with `index ARCHITECTURE.md` -3. **Run tests**: `go test ./...` and `npm test` -4. **Deploy**: Use `docker-compose.yml` or Kubernetes -5. **Extend**: Add OAuth2, WebSockets, more features - -## ๐Ÿ’ก Quick Tips - -- **Hot reload**: Changes auto-reload in dev mode -- **Network tab**: Check API calls in browser DevTools -- **Logs**: Docker: `docker-compose logs -f service-name` -- **Database GUI**: MongoDB Compass (free tool to browse data) -- **API testing**: Postman or `curl` commands - -## ๐Ÿ“ž Support - -- Check logs: `docker-compose logs` -- Review `SECURITY.md` for auth issues -- Check `ENV_SETUP.md` for config problems -- See `ARCHITECTURE.md` for code structure - ---- - -**Now you're ready to explore and extend Notely! ๐ŸŽ‰** +- `README.md` +- `ENV_SETUP.md` +- `PERMISSIONS.md` diff --git a/README.md b/README.md index ffd9787..3cd6f95 100644 --- a/README.md +++ b/README.md @@ -1,306 +1,174 @@ -# Notely - Secure Multi-Space Note-Taking Application +# Notely -A production-ready, secure multi-tenant note-taking platform built with Go, Vue 3, and MongoDB. +Notely is a multi-space note application built with Go, Vue 3, MongoDB, and Redis. -## ๐Ÿš€ Quick Start +The repository contains a Go backend, a Vue frontend, Docker Compose assets for local deployment, and Kubernetes manifests for cluster deployment. In containerized environments, the frontend is built into the backend image and served by the Go server. Docker Compose also places Nginx in front of the app for HTTP and HTTPS entry points. -### Prerequisites +## What Is In This Repo -- Docker & Docker Compose -- Go 1.21+ (for local development) -- Node.js 18+ (for frontend development) -- MongoDB 7.0+ (for local development) +- Backend API in `backend/` +- Frontend SPA in `frontend/` +- Docker and Nginx assets in `devops/docker/` +- Kubernetes manifests in `devops/kubernetes/` +- Root documentation in `README.md`, `QUICKSTART.md`, `ENV_SETUP.md`, and `PERMISSIONS.md` -### Development with Docker Compose +## Core Features + +- Email/password authentication +- Session cookies backed by Redis, with bearer-token fallback for API clients +- Admin bootstrap from environment variables +- Permission-based authorization with wildcard support +- Spaces, categories, and notes +- Full-text note search +- Public spaces and public notes +- Password-protected notes +- OAuth/OIDC provider support +- Feature flags for registration, provider login, public sharing, and file explorer support +- Optional S3-compatible file explorer when enabled through feature flags + +## Architecture Overview + +### Backend + +- Language: Go +- Module: `gitea.hostxtra.co.uk/mrhid6/notely/backend` +- Entry point: `backend/cmd/server/main.go` +- Architecture style: domain/application/infrastructure/interfaces split +- Storage: MongoDB +- Session store: Redis + +### Frontend + +- Framework: Vue 3 +- Router: Vue Router +- State: Pinia +- Build tool: Vite + +### Container Layout + +- `devops/docker/Dockerfile` builds the frontend and backend into a single app image +- `docker-compose.yml` starts: + - `mongodb` + - `redis` + - `notely` (combined app image) + - `nginx` + +## Documentation Map + +- `README.md`: project overview and current architecture +- `QUICKSTART.md`: fast setup and day-to-day development commands +- `ENV_SETUP.md`: environment-variable reference and configuration layout +- `PERMISSIONS.md`: enforced permission model and naming + +## Getting Started + +### Docker Compose + +1. Copy the root environment file: ```bash -# Start all services -docker-compose up - -# Backend: http://localhost:8080 -# Frontend: http://localhost:5173 -# MongoDB: localhost:27017 -# Nginx: http://localhost:80 +cp .env.example .env ``` -### Local Development Setup +2. Start the stack: -#### Backend +```bash +docker compose up -d --build +``` + +3. Open the app: + +- UI through Nginx: `http://localhost` +- Backend health check: `http://localhost:8080/health` +- MongoDB: `localhost:27017` +- Redis: `localhost:6379` + +### Local Development + +Prerequisites: + +- Go 1.25+ +- Node.js 18+ +- MongoDB +- Redis + +Backend: ```bash cd backend - -# Install dependencies +cp .env.example .env go mod download - -# Set environment variables -export MONGODB_URI=mongodb://admin:password@localhost:27017/noteapp?authSource=admin -export JWT_SECRET=your-secret-key -export ENCRYPTION_KEY=00000000000000000000000000000000 - -# Run migrations and server go run ./cmd/server/main.go ``` -#### Frontend +Frontend: ```bash cd frontend - -# Install dependencies +cp .env.example .env npm install - -# Start development server npm run dev ``` -## ๐Ÿ“š Architecture +Local frontend development runs at `http://localhost:5173` and proxies `/api` requests to `http://localhost:8080`. -### Backend (GoClean Architecture) +## API Surface -``` -backend/ -โ”œโ”€โ”€ cmd/server/ # Entry point -โ”œโ”€โ”€ internal/ -โ”‚ โ”œโ”€โ”€ domain/ # Business logic (entities, interfaces) -โ”‚ โ”œโ”€โ”€ application/ # Use cases (services, DTOs) -โ”‚ โ”œโ”€โ”€ infrastructure/ # External dependencies (DB, auth) -โ”‚ โ””โ”€โ”€ interfaces/ # API handlers & middleware -โ”œโ”€โ”€ pkg/ # Public packages -โ””โ”€โ”€ tests/ # Test suites -``` +The router in `backend/cmd/server/main.go` currently exposes these endpoint groups. -### Frontend (Vue 3 Composition API) +### Public Endpoints -``` -frontend/ -โ”œโ”€โ”€ src/ -โ”‚ โ”œโ”€โ”€ components/ # Reusable Vue components -โ”‚ โ”œโ”€โ”€ pages/ # Page components -โ”‚ โ”œโ”€โ”€ stores/ # Pinia state management -โ”‚ โ”œโ”€โ”€ services/ # API client -โ”‚ โ”œโ”€โ”€ router/ # Vue Router config -โ”‚ โ”œโ”€โ”€ assets/ # Styles and assets -โ”‚ โ””โ”€โ”€ main.js # Entry point -โ”œโ”€โ”€ index.html -โ””โ”€โ”€ vite.config.js -``` +- `GET /health` +- `POST /api/v1/auth/register` +- `POST /api/v1/auth/login` +- `POST /api/v1/auth/refresh` +- `POST /api/v1/auth/logout` +- `GET /api/v1/auth/providers` +- `GET /api/v1/auth/providers/{providerId}/start` +- `GET /api/v1/auth/providers/{providerId}/callback` +- `GET /api/v1/settings/feature-flags` +- `GET /api/v1/public/spaces` +- `GET /api/v1/public/spaces/{spaceId}` +- `GET /api/v1/public/spaces/{spaceId}/notes` +- `GET /api/v1/public/spaces/{spaceId}/notes/{noteId}` +- `POST /api/v1/public/spaces/{spaceId}/notes/{noteId}/unlock` -## ๐Ÿ” Security Features +### Authenticated User Endpoints -### Authentication +- `GET /api/v1/auth/me` +- Space CRUD under `/api/v1/spaces` +- Space member management under `/api/v1/spaces/{spaceId}/members` +- Note CRUD, search, and unlock under `/api/v1/spaces/{spaceId}/notes` +- Category CRUD and move under `/api/v1/spaces/{spaceId}/categories` +- File explorer operations under `/api/v1/spaces/{spaceId}/files` -- **Argon2id password hashing** - Industry-standard PBKDF2 -- **JWT tokens** with short expiration (1 hour) -- **HTTP-only secure cookies** for refresh tokens -- **CSRF protection** via SameSite cookies -- **Brute-force protection** via login attempt tracking +### Admin Endpoints -### Authorization +Admin routes live under `/api/v1/admin` and cover: -- **Role-based access control (RBAC)** per space: - - Owner: Full control - - Editor: Edit notes and categories - - Viewer: Read-only access -- **Space-level data isolation** - all queries include space_id -- **IDOR prevention** - middleware enforces ownership verification +- users +- groups +- spaces +- feature flags +- auth providers -### Data Security +## Permissions -- **Encryption at rest** for sensitive fields (OAuth secrets) -- **HTTPS/TLS** in production (Nginx reverse proxy) -- **Content Security Policy (CSP)** headers -- **XSS protection** - DOMPurify for markdown sanitization -- **SQL injection prevention** - parameterized queries (MongoDB) +Notely uses permission-based authorization, not fixed owner/editor/viewer roles. -### API Security +- Global permissions include `space.create`, `space.edit`, and `space.delete` +- Space-scoped permissions follow `space..` +- Example: `space.product_docs.note.create` +- Example: `space.product_docs.settings.delete` +- Space deletion requires either: + - global `space.delete`, or + - space-scoped `space..settings.delete` -- **Rate limiting** - IP-based and user-based -- **Security headers** - HSTS, X-Frame-Options, X-Content-Type-Options -- **CORS properly configured** - whitelist origin domains -- **Input validation** on all endpoints +See `PERMISSIONS.md` for the current enforced permission set. -## ๐Ÿ“ฆ API Endpoints +## Testing And Quality Checks -### Authentication - -``` -POST /api/v1/auth/register - Register new user -POST /api/v1/auth/login - Login user -POST /api/v1/auth/refresh - Refresh access token -POST /api/v1/auth/logout - Logout user -GET /health - Health check -``` - -### Spaces - -``` -GET /api/v1/spaces - List user's spaces -POST /api/v1/spaces - Create space -GET /api/v1/spaces/{spaceId} - Get space details -PUT /api/v1/spaces/{spaceId} - Update space -DELETE /api/v1/spaces/{spaceId} - Delete space -``` - -### Notes - -``` -GET /api/v1/spaces/{spaceId}/notes - List notes -POST /api/v1/spaces/{spaceId}/notes - Create note -GET /api/v1/spaces/{spaceId}/notes/{noteId} - Get note -PUT /api/v1/spaces/{spaceId}/notes/{noteId} - Update note -DELETE /api/v1/spaces/{spaceId}/notes/{noteId} - Delete note -GET /api/v1/spaces/{spaceId}/notes/search?q= - Search notes -``` - -### Categories - -``` -GET /api/v1/spaces/{spaceId}/categories - List categories -POST /api/v1/spaces/{spaceId}/categories - Create category -PUT /api/v1/spaces/{spaceId}/categories/{id} - Update category -DELETE /api/v1/spaces/{spaceId}/categories/{id} - Delete category -``` - -## ๐Ÿ—„๏ธ Database Design - -### MongoDB Collections - -#### users - -```javascript -{ - _id: ObjectId, - email: String (unique), - username: String (unique), - password_hash: String, - first_name: String, - last_name: String, - avatar: String, - is_active: Boolean, - email_verified: Boolean, - created_at: Date, - updated_at: Date, - last_login_at: Date -} -``` - -#### spaces - -```javascript -{ - _id: ObjectId, - name: String, - description: String, - icon: String, - owner_id: ObjectId, - is_public: Boolean, - created_at: Date, - updated_at: Date -} -``` - -#### memberships - -```javascript -{ - _id: ObjectId, - user_id: ObjectId, - space_id: ObjectId, - role: String (owner|editor|viewer), - joined_at: Date, - invited_by: ObjectId, - invited_at: Date -} -``` - -#### notes - -```javascript -{ - _id: ObjectId, - space_id: ObjectId, - category_id: ObjectId, - title: String, - content: String (Markdown), - tags: [String], - is_pinned: Boolean, - is_favorite: Boolean, - created_by: ObjectId, - updated_by: ObjectId, - created_at: Date, - updated_at: Date, - viewed_at: Date -} -``` - -#### categories - -```javascript -{ - _id: ObjectId, - space_id: ObjectId, - name: String, - description: String, - parent_id: ObjectId (for hierarchical structure), - icon: String, - order: Number, - created_by: ObjectId, - updated_by: ObjectId, - created_at: Date, - updated_at: Date -} -``` - -#### Indexes - -``` -users: { email: 1 (unique), username: 1 (unique) } -spaces: { owner_id: 1, created_at: -1 } -memberships: { user_id: 1, space_id: 1 (unique), space_id: 1 } -notes: { space_id: 1, category_id: 1, updated_at: -1, text: "text" } -categories: { space_id: 1, parent_id: 1, order: 1 } -``` - -## ๐Ÿณ Deployment - -### Docker Compose (Development/Testing) - -```bash -docker-compose up -d -``` - -Services: - -- **MongoDB** (port 27017) -- **Backend API** (port 8080) -- **Frontend** (port 5173) -- **Nginx Reverse Proxy** (port 80) - -### Kubernetes (Production) - -```bash -# Create namespace and secrets -kubectl apply -f devops/kubernetes/deployment.yaml - -# Verify deployment -kubectl get pods -n noteapp -kubectl port-forward svc/frontend 5173:5173 -n noteapp -kubectl port-forward svc/backend 8080:8080 -n noteapp -``` - -Features: - -- **StatefulSet** for MongoDB with persistent storage -- **Deployments** for backend and frontend with horizontal scaling -- **Ingress** for routing (requires ingress controller) -- **HPA** (Horizontal Pod Autoscaler) for automatic scaling -- **Liveness & readiness probes** for health checks -- **Resource limits** for fair resource allocation - -## ๐Ÿงช Testing - -### Backend Tests +Backend: ```bash cd backend @@ -309,118 +177,73 @@ go test -v ./tests/unit/... go test -v ./tests/integration/... ``` -### Frontend Tests +Frontend: ```bash cd frontend +npm run build +npm run lint npm run test -npm run test:watch ``` -## ๐Ÿ”ง Configuration +## Deployment Notes -### Environment Variables +### Docker Compose -#### Backend (.env) +Docker Compose uses the combined application image plus Nginx, MongoDB, and Redis. Configuration is driven by the root `.env` file. -``` -MONGODB_URI=mongodb://admin:password@localhost:27017/noteapp -JWT_SECRET=your-secret-key-min-32-chars -ENCRYPTION_KEY=32-char-encryption-key-for-secrets -PORT=8080 -LOG_LEVEL=info -ENV=development -``` +### Kubernetes -#### Frontend (.env) +The manifest at `devops/kubernetes/deployment.yaml` currently provisions: -``` -VITE_API_BASE_URL=http://localhost:8080 -``` +- `noteapp` namespace +- MongoDB StatefulSet and PVC +- single `noteapp` Deployment for the combined app image +- ClusterIP services +- Ingress +- HorizontalPodAutoscaler -## ๐Ÿ“ Development Guidelines - -### Code Structure - -- Follow clean architecture principles -- Separate concerns: domain, application, infrastructure -- Use interfaces for dependency injection -- Keep services testable and focused - -### Security Best Practices - -1. **Never store secrets in code** - use environment variables -2. **Validate all inputs** on backend -3. **Sanitize outputs** before rendering -4. **Use HTTPS in production** -5. **Implement rate limiting** on APIs -6. **Log security events** (login attempts, permission denied) -7. **Audit trail** for sensitive operations - -### Commit Message Format - -``` -[TYPE] Description - -types: feat, fix, docs, style, refactor, test, chore -``` - -## ๐Ÿ“– API Documentation - -### Request/Response Format - -All API requests and responses use JSON. +Apply it with: ```bash -# Example: Create Note -curl -X POST http://localhost:8080/api/v1/spaces/{spaceId}/notes \ - -H "Authorization: Bearer {accessToken}" \ - -H "Content-Type: application/json" \ - -d '{ - "title": "My Note", - "content": "# Markdown content", - "tags": ["tag1", "tag2"], - "category_id": null, - "is_pinned": false, - "is_favorite": false - }' +kubectl apply -f devops/kubernetes/deployment.yaml ``` -## ๐Ÿšจ Error Handling +## Current Repo Layout -All errors return appropriate HTTP status codes: +```text +noteapp/ +โ”œโ”€โ”€ backend/ +โ”‚ โ”œโ”€โ”€ cmd/server/ +โ”‚ โ”œโ”€โ”€ internal/ +โ”‚ โ”œโ”€โ”€ pkg/ +โ”‚ โ”œโ”€โ”€ tests/ +โ”‚ โ””โ”€โ”€ .env.example +โ”œโ”€โ”€ frontend/ +โ”‚ โ”œโ”€โ”€ src/ +โ”‚ โ”œโ”€โ”€ tests/ +โ”‚ โ”œโ”€โ”€ package.json +โ”‚ โ”œโ”€โ”€ vite.config.js +โ”‚ โ”œโ”€โ”€ vitest.config.js +โ”‚ โ””โ”€โ”€ .env.example +โ”œโ”€โ”€ devops/ +โ”‚ โ”œโ”€โ”€ docker/ +โ”‚ โ”‚ โ”œโ”€โ”€ Dockerfile +โ”‚ โ”‚ โ”œโ”€โ”€ nginx.conf +โ”‚ โ”‚ โ””โ”€โ”€ ssl/ +โ”‚ โ””โ”€โ”€ kubernetes/ +โ”‚ โ””โ”€โ”€ deployment.yaml +โ”œโ”€โ”€ docker-compose.yml +โ”œโ”€โ”€ .env.example +โ”œโ”€โ”€ ENV_SETUP.md +โ”œโ”€โ”€ PERMISSIONS.md +โ”œโ”€โ”€ QUICKSTART.md +โ””โ”€โ”€ README.md +``` -- `400` - Bad Request -- `401` - Unauthorized -- `403` - Forbidden (insufficient permissions) -- `404` - Not Found -- `409` - Conflict (e.g., duplicate email) -- `429` - Too Many Requests (rate limit exceeded) -- `500` - Internal Server Error +## Notes For Contributors -## ๐ŸŽฏ Future Enhancements - -- [ ] OAuth2/OIDC integration -- [ ] Email notifications -- [ ] Real-time collaboration (WebSockets) -- [ ] Full-text search with Elasticsearch -- [ ] Export to PDF/Markdown -- [ ] Mobile applications -- [ ] Plugin system -- [ ] Advanced permissions management - -## ๐Ÿ“„ License - -MIT License - See LICENSE file - -## ๐Ÿ‘ฅ Contributing - -1. Fork the repository -2. Create a feature branch -3. Commit your changes -4. Push to the branch -5. Create a Pull Request - ---- - -**Built with โค๏ธ for secure, collaborative note-taking** +- Check `PERMISSIONS.md` when changing authorization behavior +- Check `ENV_SETUP.md` when adding or changing configuration +- Check `backend/cmd/server/main.go` before documenting routes +- Keep docs aligned with actual package scripts and checked-in files diff --git a/SECURITY.md b/SECURITY.md deleted file mode 100644 index 69e55b3..0000000 --- a/SECURITY.md +++ /dev/null @@ -1,284 +0,0 @@ -# Security Implementation Guide - -This document outlines the security measures implemented in Notely. - -## ๐Ÿ” Authentication Security - -### Password Hashing - -- **Algorithm**: Argon2id (memory-hard, resistant to GPU attacks) -- **Configuration**: - - Memory: 64 MB - - Time: 1 iteration - - Parallelism: 4 threads - - Salt: 16 random bytes (cryptographically secure) - -```go -// Generated hash format: -$argon2id$v=19$m=65536,t=1,p=4$salt_hex$hash_hex -``` - -### JWT Tokens - -- **Algorithm**: HS256 (HMAC-SHA256) -- **Access Token TTL**: 1 hour -- **Refresh Token TTL**: 7 days (HTTP-only secure cookie) -- **Claims**: - - `user_id`: User's MongoDB ObjectID - - `email`: User's email address - - `username`: User's username - - `iat`: Issued at timestamp - - `exp`: Expiration timestamp - - `iss`: Issuer (verified against hardcoded value) - -### Brute-Force Protection - -- Track failed login attempts in `login_attempts` collection -- Rate limit: Max 5 failed attempts per IP per 15 minutes -- Account lockout: 15 minutes after 5 consecutive failures -- Cleanup: Expired records auto-deleted via TTL index - -## ๐Ÿ›ก๏ธ Authorization Security - -### Role-Based Access Control (RBAC) - -``` -Space Roles: -โ”œโ”€โ”€ Owner (all permissions) -โ”œโ”€โ”€ Editor (create/edit/delete notes) -โ””โ”€โ”€ Viewer (read-only) -``` - -### Space-Level Data Isolation - -**ALL queries include mandatory `space_id` filter** - -```go -// Correct query pattern: -db.notes.find({ space_id: spaceID, ... }) - -// Never allow: -db.notes.find({ user_id: userID }) // โŒ Cross-space leak possible -``` - -### Middleware Authorization Flow - -``` -1. Extract JWT token โ†’ Verify signature & expiration -2. Load user credentials โ†’ Verify user is active -3. Check space membership โ†’ Verify user_id + space_id + role -4. Execute request โ†’ With space_id context -``` - -## ๐Ÿ”‘ Data Encryption - -### At Rest - -- OAuth client secrets encrypted with AES-256-GCM -- Stored in MongoDB with encryption key in environment variables -- Decryption happens only when reading from database - -```go -plaintext, err := encryptor.Encrypt(clientSecret) // Stores encrypted blob -recovered, err := encryptor.Decrypt(plaintext) // Decrypts on retrieval -``` - -### In Transit - -- HTTPS/TLS required in production (enforced via Nginx) -- Secure cookies: `Secure`, `HttpOnly`, `SameSite=Lax` flags -- All sensitive data transmitted over encrypted channels - -## ๐Ÿšจ Input Validation - -### Backend Validation (MANDATORY) - -Every endpoint validates: - -1. **Type validation** - JSON schema validation -2. **Length limits** - min/max string lengths -3. **Format validation** - email, ObjectID, URL formats -4. **Range validation** - pagination limits - -```go -type CreateNoteRequest struct { - Title string `validate:"required,min=1,max=255"` - Content string `validate:"max=50000"` - Tags []string `validate:"max=100,dive,max=50"` -} -``` - -### Frontend Validation - -- **Input sanitization** - trim whitespace -- **Format validation** - regex patterns -- **Debounced searches** - prevent query spam -- **Client-side feedback** - improve UX - -### Output Sanitization - -Markdown โ†’ HTML conversion sanitized with DOMPurify: - -```javascript -// XSS prevention -const dirty = marked.parse(userMarkdown); -const clean = DOMPurify.sanitize(dirty); - -// Blocks: scripts, event handlers, dangerous attributes -``` - -## ๐ŸŒ Web Security Headers - -Implemented via Nginx and Go middleware: - -| Header | Value | Purpose | -| --------------------------- | --------------------------------- | ------------------------------- | -| `Strict-Transport-Security` | `max-age=31536000` | Force HTTPS | -| `X-Content-Type-Options` | `nosniff` | Prevent MIME sniffing | -| `X-Frame-Options` | `DENY` | Prevent clickjacking | -| `X-XSS-Protection` | `1; mode=block` | XSS protection (older browsers) | -| `Content-Security-Policy` | Restrictive policy | Prevent XSS attacks | -| `Referrer-Policy` | `strict-origin-when-cross-origin` | Referrer control | - -**CSP Policy:** - -``` -default-src 'self' -script-src 'self' 'unsafe-inline' (for development only) -style-src 'self' 'unsafe-inline' -img-src 'self' data: https: -font-src 'self' -connect-src 'self' -frame-ancestors 'none' -``` - -## ๐Ÿช Cookie Security - -### Access Token (via Authorization header) - -- Stored in **memory** (not localStorage) -- Passed via `Authorization: Bearer {token}` - -### Refresh Token (HTTP-only cookie) - -```go -http.SetCookie(w, &http.Cookie{ - Name: "refresh_token", - Value: token, - Path: "/", - MaxAge: 7 * 24 * 60 * 60, // 7 days - HttpOnly: true, // โœ… Cannot access from JavaScript - Secure: true, // โœ… HTTPS only - SameSite: http.SameSiteLaxMode, // โœ… CSRF protection -}) -``` - -## ๐Ÿ”„ Rate Limiting - -### API Rate Limiting - -- **General**: 50 requests / second per IP -- **Login**: 10 requests / second per IP -- **Burst allowance**: 20 additional requests - -```nginx -limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s; -limit_req zone=api_limit burst=20 nodelay; -``` - -### Login Attempt Tracking - -- Track per email + IP combination -- Maximum 5 attempts per 15 minutes -- Exponential backoff on repeated failures - -## ๐Ÿ”’ Database Security - -### MongoDB - -- **Authentication**: Username/password with role-based access -- **Network**: Runs in secure Docker network (not exposed) -- **Admin credentials**: Stored in Kubernetes Secrets (not in code) -- **Backups**: TBD - use MongoDB Atlas or encrypted backups - -### Connection String - -``` -mongodb://admin:password@mongodb:27017/dbname?authSource=admin -``` - -## ๐Ÿšจ Logging & Monitoring - -### Security Events Logged - -- โœ… User registration attempts -- โœ… Login attempts (success/failure) -- โœ… Authorization failures -- โœ… Permission denied events -- โœ… Sensitive data access - -### Data NOT logged - -- โŒ Passwords/hashes -- โŒ JWT tokens -- โŒ Encryption keys -- โŒ OAuth secrets - -## ๐Ÿงช Security Testing - -### What to Test - -1. **Authentication**: Register, login, token refresh, logout -2. **Authorization**: RBAC enforcement, space isolation -3. **Input validation**: Invalid data rejection -4. **XSS prevention**: Markdown sanitization -5. **CSRF protection**: Token validation -6. **Rate limiting**: Too many requests blocked -7. **SQL Injection**: MongoDB-specific (parameterized queries safe) - -### Manual Testing Commands - -```bash -# Test invalid input -curl -X POST http://localhost:8080/api/v1/auth/login \ - -d '{"email":"not-an-email","password":""}' - -# Test expired token -curl -H "Authorization: Bearer expired.token.here" \ - http://localhost:8080/api/v1/spaces - -# Test rate limiting -for i in {1..100}; do - curl http://localhost:8080/api/v1/auth/login & -done -``` - -## ๐Ÿ› ๏ธ Production Checklist - -- [ ] Change default JWT_SECRET (min 32 characters) -- [ ] Change default ENCRYPTION_KEY (32 bytes) -- [ ] Generate TLS certificates (Let's Encrypt recommended) -- [ ] Configure Nginx SSL/TLS -- [ ] Enable HTTPS redirect -- [ ] Set up database backups -- [ ] Configure logging & monitoring -- [ ] Implement CORS whitelist (specific domains) -- [ ] Set up rate limiting (tuned to your traffic) -- [ ] Enable database authentication -- [ ] Use Kubernetes Network Policies -- [ ] Set up Pod Security Policies -- [ ] Enable audit logging -- [ ] Configure Secrets encryption at rest - -## ๐Ÿ“š References - -- [OWASP Top 10](https://owasp.org/www-project-top-ten/) -- [MongoDB Security](https://docs.mongodb.com/manual/security/) -- [JWT Best Practices](https://tools.ietf.org/html/rfc8949) -- [Argon2 Specification](https://github.com/P-H-C/phc-winner-argon2) -- [Content Security Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP) - ---- - -**Last Updated**: March 2026 -**Security Level**: Production-Grade diff --git a/backend/cmd/server/main.go b/backend/cmd/server/main.go index 80a9583..8fb190b 100644 --- a/backend/cmd/server/main.go +++ b/backend/cmd/server/main.go @@ -10,16 +10,16 @@ import ( "strings" "time" + "gitea.hostxtra.co.uk/mrhid6/notely/backend/internal/application/services" + "gitea.hostxtra.co.uk/mrhid6/notely/backend/internal/domain/entities" + "gitea.hostxtra.co.uk/mrhid6/notely/backend/internal/domain/repositories" + "gitea.hostxtra.co.uk/mrhid6/notely/backend/internal/infrastructure/auth" + "gitea.hostxtra.co.uk/mrhid6/notely/backend/internal/infrastructure/database" + "gitea.hostxtra.co.uk/mrhid6/notely/backend/internal/infrastructure/security" + "gitea.hostxtra.co.uk/mrhid6/notely/backend/internal/interfaces/handlers" + "gitea.hostxtra.co.uk/mrhid6/notely/backend/internal/interfaces/middleware" "github.com/gorilla/mux" "github.com/joho/godotenv" - "github.com/noteapp/backend/internal/application/services" - "github.com/noteapp/backend/internal/domain/entities" - "github.com/noteapp/backend/internal/domain/repositories" - "github.com/noteapp/backend/internal/infrastructure/auth" - "github.com/noteapp/backend/internal/infrastructure/database" - "github.com/noteapp/backend/internal/infrastructure/security" - "github.com/noteapp/backend/internal/interfaces/handlers" - "github.com/noteapp/backend/internal/interfaces/middleware" "github.com/redis/go-redis/v9" "go.mongodb.org/mongo-driver/v2/bson" ) @@ -316,6 +316,7 @@ func main() { admin.HandleFunc("/feature-flags", adminHandler.GetFeatureFlags).Methods("GET") admin.HandleFunc("/feature-flags", adminHandler.UpdateFeatureFlags).Methods("PUT") // manage identity providers โ€” admin-only + admin.HandleFunc("/auth/providers", authHandler.ListProvidersForAdmin).Methods("GET") admin.HandleFunc("/auth/providers", authHandler.CreateProvider).Methods("POST") admin.HandleFunc("/auth/providers/{providerId}", authHandler.UpdateProvider).Methods("PUT") admin.HandleFunc("/auth/providers/{providerId}", adminHandler.DeleteProvider).Methods("DELETE") diff --git a/backend/go.mod b/backend/go.mod index b8afd3d..4a11797 100644 --- a/backend/go.mod +++ b/backend/go.mod @@ -1,4 +1,4 @@ -module github.com/noteapp/backend +module gitea.hostxtra.co.uk/mrhid6/notely/backend go 1.25.0 diff --git a/backend/internal/application/dto/dto.go b/backend/internal/application/dto/dto.go index e35877d..ad8ea4f 100644 --- a/backend/internal/application/dto/dto.go +++ b/backend/internal/application/dto/dto.go @@ -1,7 +1,7 @@ package dto import ( - "github.com/noteapp/backend/internal/domain/entities" + "gitea.hostxtra.co.uk/mrhid6/notely/backend/internal/domain/entities" ) // ========== AUTH DTOs ========== diff --git a/backend/internal/application/services/admin_service.go b/backend/internal/application/services/admin_service.go index dabbd02..79807eb 100644 --- a/backend/internal/application/services/admin_service.go +++ b/backend/internal/application/services/admin_service.go @@ -7,10 +7,10 @@ import ( "go.mongodb.org/mongo-driver/v2/bson" - "github.com/noteapp/backend/internal/application/dto" - "github.com/noteapp/backend/internal/domain/entities" - "github.com/noteapp/backend/internal/domain/repositories" - "github.com/noteapp/backend/internal/infrastructure/security" + "gitea.hostxtra.co.uk/mrhid6/notely/backend/internal/application/dto" + "gitea.hostxtra.co.uk/mrhid6/notely/backend/internal/domain/entities" + "gitea.hostxtra.co.uk/mrhid6/notely/backend/internal/domain/repositories" + "gitea.hostxtra.co.uk/mrhid6/notely/backend/internal/infrastructure/security" ) // AdminService handles admin-level operations diff --git a/backend/internal/application/services/auth_service.go b/backend/internal/application/services/auth_service.go index 1195849..de0310d 100644 --- a/backend/internal/application/services/auth_service.go +++ b/backend/internal/application/services/auth_service.go @@ -12,11 +12,11 @@ import ( "strings" "time" - "github.com/noteapp/backend/internal/application/dto" - "github.com/noteapp/backend/internal/domain/entities" - "github.com/noteapp/backend/internal/domain/repositories" - "github.com/noteapp/backend/internal/infrastructure/auth" - "github.com/noteapp/backend/internal/infrastructure/security" + "gitea.hostxtra.co.uk/mrhid6/notely/backend/internal/application/dto" + "gitea.hostxtra.co.uk/mrhid6/notely/backend/internal/domain/entities" + "gitea.hostxtra.co.uk/mrhid6/notely/backend/internal/domain/repositories" + "gitea.hostxtra.co.uk/mrhid6/notely/backend/internal/infrastructure/auth" + "gitea.hostxtra.co.uk/mrhid6/notely/backend/internal/infrastructure/security" "go.mongodb.org/mongo-driver/v2/bson" "golang.org/x/oauth2" ) @@ -259,6 +259,25 @@ func (s *AuthService) ListProviders(ctx context.Context) ([]*dto.AuthProviderDTO return result, nil } +// ListProvidersForAdmin returns all OAuth/OIDC providers, including inactive ones. +func (s *AuthService) ListProvidersForAdmin(ctx context.Context) ([]*dto.AuthProviderDTO, error) { + if s.providerRepo == nil { + return []*dto.AuthProviderDTO{}, nil + } + + providers, err := s.providerRepo.GetAllProvidersForAdmin(ctx) + if err != nil { + return nil, err + } + + result := make([]*dto.AuthProviderDTO, 0, len(providers)) + for _, provider := range providers { + result = append(result, dto.NewAuthProviderDTO(provider)) + } + + return result, nil +} + // GetFeatureFlags returns current app-wide feature flags. func (s *AuthService) GetFeatureFlags(ctx context.Context) (*dto.FeatureFlagsDTO, error) { if s.featureFlagRepo == nil { diff --git a/backend/internal/application/services/category_service.go b/backend/internal/application/services/category_service.go index 87c55fb..a31a5cc 100644 --- a/backend/internal/application/services/category_service.go +++ b/backend/internal/application/services/category_service.go @@ -7,9 +7,9 @@ import ( "go.mongodb.org/mongo-driver/v2/bson" - "github.com/noteapp/backend/internal/application/dto" - "github.com/noteapp/backend/internal/domain/entities" - "github.com/noteapp/backend/internal/domain/repositories" + "gitea.hostxtra.co.uk/mrhid6/notely/backend/internal/application/dto" + "gitea.hostxtra.co.uk/mrhid6/notely/backend/internal/domain/entities" + "gitea.hostxtra.co.uk/mrhid6/notely/backend/internal/domain/repositories" ) // CategoryService handles category operations diff --git a/backend/internal/application/services/file_service.go b/backend/internal/application/services/file_service.go index 288dd66..8943336 100644 --- a/backend/internal/application/services/file_service.go +++ b/backend/internal/application/services/file_service.go @@ -15,8 +15,8 @@ import ( "github.com/aws/aws-sdk-go-v2/service/s3/types" "go.mongodb.org/mongo-driver/v2/bson" - "github.com/noteapp/backend/internal/domain/repositories" - "github.com/noteapp/backend/internal/infrastructure/security" + "gitea.hostxtra.co.uk/mrhid6/notely/backend/internal/domain/repositories" + "gitea.hostxtra.co.uk/mrhid6/notely/backend/internal/infrastructure/security" ) // S3Object represents a file or folder entry with key relative to the space root. diff --git a/backend/internal/application/services/note_service.go b/backend/internal/application/services/note_service.go index 3514ccc..6cfaa8b 100644 --- a/backend/internal/application/services/note_service.go +++ b/backend/internal/application/services/note_service.go @@ -6,10 +6,10 @@ import ( "strings" "time" - "github.com/noteapp/backend/internal/application/dto" - "github.com/noteapp/backend/internal/domain/entities" - "github.com/noteapp/backend/internal/domain/repositories" - "github.com/noteapp/backend/internal/infrastructure/security" + "gitea.hostxtra.co.uk/mrhid6/notely/backend/internal/application/dto" + "gitea.hostxtra.co.uk/mrhid6/notely/backend/internal/domain/entities" + "gitea.hostxtra.co.uk/mrhid6/notely/backend/internal/domain/repositories" + "gitea.hostxtra.co.uk/mrhid6/notely/backend/internal/infrastructure/security" "go.mongodb.org/mongo-driver/v2/bson" ) diff --git a/backend/internal/application/services/permission_service.go b/backend/internal/application/services/permission_service.go index 19720e7..c909ace 100644 --- a/backend/internal/application/services/permission_service.go +++ b/backend/internal/application/services/permission_service.go @@ -5,8 +5,8 @@ import ( "errors" "strings" - "github.com/noteapp/backend/internal/domain/entities" - "github.com/noteapp/backend/internal/domain/repositories" + "gitea.hostxtra.co.uk/mrhid6/notely/backend/internal/domain/entities" + "gitea.hostxtra.co.uk/mrhid6/notely/backend/internal/domain/repositories" "go.mongodb.org/mongo-driver/v2/bson" ) diff --git a/backend/internal/application/services/space_service.go b/backend/internal/application/services/space_service.go index 4f36291..07f281a 100644 --- a/backend/internal/application/services/space_service.go +++ b/backend/internal/application/services/space_service.go @@ -5,9 +5,9 @@ import ( "errors" "time" - "github.com/noteapp/backend/internal/application/dto" - "github.com/noteapp/backend/internal/domain/entities" - "github.com/noteapp/backend/internal/domain/repositories" + "gitea.hostxtra.co.uk/mrhid6/notely/backend/internal/application/dto" + "gitea.hostxtra.co.uk/mrhid6/notely/backend/internal/domain/entities" + "gitea.hostxtra.co.uk/mrhid6/notely/backend/internal/domain/repositories" "go.mongodb.org/mongo-driver/v2/bson" ) diff --git a/backend/internal/domain/repositories/additional.go b/backend/internal/domain/repositories/additional.go index 9346c2a..808af9f 100644 --- a/backend/internal/domain/repositories/additional.go +++ b/backend/internal/domain/repositories/additional.go @@ -3,7 +3,7 @@ package repositories import ( "context" - "github.com/noteapp/backend/internal/domain/entities" + "gitea.hostxtra.co.uk/mrhid6/notely/backend/internal/domain/entities" "go.mongodb.org/mongo-driver/v2/bson" ) diff --git a/backend/internal/domain/repositories/interfaces.go b/backend/internal/domain/repositories/interfaces.go index eca8de0..62137a6 100644 --- a/backend/internal/domain/repositories/interfaces.go +++ b/backend/internal/domain/repositories/interfaces.go @@ -3,7 +3,7 @@ package repositories import ( "context" - "github.com/noteapp/backend/internal/domain/entities" + "gitea.hostxtra.co.uk/mrhid6/notely/backend/internal/domain/entities" "go.mongodb.org/mongo-driver/v2/bson" ) @@ -174,6 +174,9 @@ type AuthProviderRepository interface { // GetAllProviders retrieves all active providers GetAllProviders(ctx context.Context) ([]*entities.AuthProvider, error) + // GetAllProvidersForAdmin retrieves all providers, including inactive ones + GetAllProvidersForAdmin(ctx context.Context) ([]*entities.AuthProvider, error) + // UpdateProvider updates a provider UpdateProvider(ctx context.Context, provider *entities.AuthProvider) error diff --git a/backend/internal/infrastructure/database/additional_repositories.go b/backend/internal/infrastructure/database/additional_repositories.go index e1aebf3..9d306c0 100644 --- a/backend/internal/infrastructure/database/additional_repositories.go +++ b/backend/internal/infrastructure/database/additional_repositories.go @@ -9,7 +9,7 @@ import ( "go.mongodb.org/mongo-driver/v2/mongo" "go.mongodb.org/mongo-driver/v2/mongo/options" - "github.com/noteapp/backend/internal/domain/entities" + "gitea.hostxtra.co.uk/mrhid6/notely/backend/internal/domain/entities" ) // AccountRecoveryRepository implements account recovery operations @@ -222,6 +222,23 @@ func (r *AuthProviderRepository) GetAllProviders(ctx context.Context) ([]*entiti return providers, nil } +// GetAllProvidersForAdmin retrieves all providers, including inactive ones +func (r *AuthProviderRepository) GetAllProvidersForAdmin(ctx context.Context) ([]*entities.AuthProvider, error) { + var providers []*entities.AuthProvider + + cursor, err := r.collection.Find(ctx, bson.M{}) + if err != nil { + return nil, err + } + defer cursor.Close(ctx) + + if err = cursor.All(ctx, &providers); err != nil { + return nil, err + } + + return providers, nil +} + // UpdateProvider updates a provider func (r *AuthProviderRepository) UpdateProvider(ctx context.Context, provider *entities.AuthProvider) error { provider.UpdatedAt = time.Now() diff --git a/backend/internal/infrastructure/database/group_repository.go b/backend/internal/infrastructure/database/group_repository.go index 26a1734..3f3a65c 100644 --- a/backend/internal/infrastructure/database/group_repository.go +++ b/backend/internal/infrastructure/database/group_repository.go @@ -6,7 +6,7 @@ import ( "strings" "time" - "github.com/noteapp/backend/internal/domain/entities" + "gitea.hostxtra.co.uk/mrhid6/notely/backend/internal/domain/entities" "go.mongodb.org/mongo-driver/v2/bson" "go.mongodb.org/mongo-driver/v2/mongo" "go.mongodb.org/mongo-driver/v2/mongo/options" diff --git a/backend/internal/infrastructure/database/note_repository.go b/backend/internal/infrastructure/database/note_repository.go index 6804aa0..b6c2d1c 100644 --- a/backend/internal/infrastructure/database/note_repository.go +++ b/backend/internal/infrastructure/database/note_repository.go @@ -9,7 +9,7 @@ import ( "go.mongodb.org/mongo-driver/v2/mongo" "go.mongodb.org/mongo-driver/v2/mongo/options" - "github.com/noteapp/backend/internal/domain/entities" + "gitea.hostxtra.co.uk/mrhid6/notely/backend/internal/domain/entities" ) // NoteRepository implements the note repository interface diff --git a/backend/internal/infrastructure/database/space_repository.go b/backend/internal/infrastructure/database/space_repository.go index c7fdc4b..d142763 100644 --- a/backend/internal/infrastructure/database/space_repository.go +++ b/backend/internal/infrastructure/database/space_repository.go @@ -9,7 +9,7 @@ import ( "go.mongodb.org/mongo-driver/v2/mongo" "go.mongodb.org/mongo-driver/v2/mongo/options" - "github.com/noteapp/backend/internal/domain/entities" + "gitea.hostxtra.co.uk/mrhid6/notely/backend/internal/domain/entities" ) // SpaceRepository implements the space repository interface diff --git a/backend/internal/infrastructure/database/user_repository.go b/backend/internal/infrastructure/database/user_repository.go index 1ed368b..845a3b5 100644 --- a/backend/internal/infrastructure/database/user_repository.go +++ b/backend/internal/infrastructure/database/user_repository.go @@ -9,7 +9,7 @@ import ( "go.mongodb.org/mongo-driver/v2/mongo" "go.mongodb.org/mongo-driver/v2/mongo/options" - "github.com/noteapp/backend/internal/domain/entities" + "gitea.hostxtra.co.uk/mrhid6/notely/backend/internal/domain/entities" ) // UserRepository implements the user repository interface diff --git a/backend/internal/interfaces/handlers/admin_handler.go b/backend/internal/interfaces/handlers/admin_handler.go index 1f8e986..77cfecf 100644 --- a/backend/internal/interfaces/handlers/admin_handler.go +++ b/backend/internal/interfaces/handlers/admin_handler.go @@ -4,12 +4,12 @@ import ( "encoding/json" "net/http" + "gitea.hostxtra.co.uk/mrhid6/notely/backend/internal/interfaces/middleware" "github.com/gorilla/mux" - "github.com/noteapp/backend/internal/interfaces/middleware" "go.mongodb.org/mongo-driver/v2/bson" - "github.com/noteapp/backend/internal/application/dto" - "github.com/noteapp/backend/internal/application/services" + "gitea.hostxtra.co.uk/mrhid6/notely/backend/internal/application/dto" + "gitea.hostxtra.co.uk/mrhid6/notely/backend/internal/application/services" ) // AdminHandler handles admin-level HTTP requests diff --git a/backend/internal/interfaces/handlers/auth_handler.go b/backend/internal/interfaces/handlers/auth_handler.go index f7747c0..d871a02 100644 --- a/backend/internal/interfaces/handlers/auth_handler.go +++ b/backend/internal/interfaces/handlers/auth_handler.go @@ -7,10 +7,10 @@ import ( "os" "strings" + "gitea.hostxtra.co.uk/mrhid6/notely/backend/internal/application/dto" + "gitea.hostxtra.co.uk/mrhid6/notely/backend/internal/application/services" + "gitea.hostxtra.co.uk/mrhid6/notely/backend/internal/infrastructure/auth" "github.com/gorilla/mux" - "github.com/noteapp/backend/internal/application/dto" - "github.com/noteapp/backend/internal/application/services" - "github.com/noteapp/backend/internal/infrastructure/auth" "go.mongodb.org/mongo-driver/v2/bson" ) @@ -121,6 +121,18 @@ func (h *AuthHandler) ListProviders(w http.ResponseWriter, r *http.Request) { json.NewEncoder(w).Encode(map[string]interface{}{"providers": providers}) } +// ListProvidersForAdmin returns all OAuth/OIDC providers, including inactive ones. +func (h *AuthHandler) ListProvidersForAdmin(w http.ResponseWriter, r *http.Request) { + providers, err := h.authService.ListProvidersForAdmin(r.Context()) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(map[string]interface{}{"providers": providers}) +} + // CreateProvider stores a new OAuth/OIDC provider configuration. func (h *AuthHandler) CreateProvider(w http.ResponseWriter, r *http.Request) { var req dto.CreateAuthProviderRequest diff --git a/backend/internal/interfaces/handlers/category_handler.go b/backend/internal/interfaces/handlers/category_handler.go index 56f2729..ec4fc38 100644 --- a/backend/internal/interfaces/handlers/category_handler.go +++ b/backend/internal/interfaces/handlers/category_handler.go @@ -7,9 +7,9 @@ import ( "github.com/gorilla/mux" "go.mongodb.org/mongo-driver/v2/bson" - "github.com/noteapp/backend/internal/application/dto" - "github.com/noteapp/backend/internal/application/services" - "github.com/noteapp/backend/internal/interfaces/middleware" + "gitea.hostxtra.co.uk/mrhid6/notely/backend/internal/application/dto" + "gitea.hostxtra.co.uk/mrhid6/notely/backend/internal/application/services" + "gitea.hostxtra.co.uk/mrhid6/notely/backend/internal/interfaces/middleware" ) // CategoryHandler handles category endpoints diff --git a/backend/internal/interfaces/handlers/file_handler.go b/backend/internal/interfaces/handlers/file_handler.go index 8250abd..7054ceb 100644 --- a/backend/internal/interfaces/handlers/file_handler.go +++ b/backend/internal/interfaces/handlers/file_handler.go @@ -9,9 +9,9 @@ import ( "path" "strings" + "gitea.hostxtra.co.uk/mrhid6/notely/backend/internal/application/services" + "gitea.hostxtra.co.uk/mrhid6/notely/backend/internal/interfaces/middleware" "github.com/gorilla/mux" - "github.com/noteapp/backend/internal/application/services" - "github.com/noteapp/backend/internal/interfaces/middleware" ) const maxUploadSize = 100 << 20 // 100 MB diff --git a/backend/internal/interfaces/handlers/note_handler.go b/backend/internal/interfaces/handlers/note_handler.go index 36f1981..d14bb55 100644 --- a/backend/internal/interfaces/handlers/note_handler.go +++ b/backend/internal/interfaces/handlers/note_handler.go @@ -8,9 +8,9 @@ import ( "github.com/gorilla/mux" "go.mongodb.org/mongo-driver/v2/bson" - "github.com/noteapp/backend/internal/application/dto" - "github.com/noteapp/backend/internal/application/services" - "github.com/noteapp/backend/internal/interfaces/middleware" + "gitea.hostxtra.co.uk/mrhid6/notely/backend/internal/application/dto" + "gitea.hostxtra.co.uk/mrhid6/notely/backend/internal/application/services" + "gitea.hostxtra.co.uk/mrhid6/notely/backend/internal/interfaces/middleware" ) // NoteHandler handles note endpoints diff --git a/backend/internal/interfaces/handlers/public_handler.go b/backend/internal/interfaces/handlers/public_handler.go index c5e853e..228a171 100644 --- a/backend/internal/interfaces/handlers/public_handler.go +++ b/backend/internal/interfaces/handlers/public_handler.go @@ -8,8 +8,8 @@ import ( "github.com/gorilla/mux" "go.mongodb.org/mongo-driver/v2/bson" - "github.com/noteapp/backend/internal/application/dto" - "github.com/noteapp/backend/internal/application/services" + "gitea.hostxtra.co.uk/mrhid6/notely/backend/internal/application/dto" + "gitea.hostxtra.co.uk/mrhid6/notely/backend/internal/application/services" ) // PublicHandler handles unauthenticated public read-only requests diff --git a/backend/internal/interfaces/handlers/settings_handler.go b/backend/internal/interfaces/handlers/settings_handler.go index bc3f0e8..04de093 100644 --- a/backend/internal/interfaces/handlers/settings_handler.go +++ b/backend/internal/interfaces/handlers/settings_handler.go @@ -4,7 +4,7 @@ import ( "encoding/json" "net/http" - "github.com/noteapp/backend/internal/application/services" + "gitea.hostxtra.co.uk/mrhid6/notely/backend/internal/application/services" ) // SettingsHandler handles public app settings endpoints. diff --git a/backend/internal/interfaces/handlers/space_handler.go b/backend/internal/interfaces/handlers/space_handler.go index dff5489..ba14710 100644 --- a/backend/internal/interfaces/handlers/space_handler.go +++ b/backend/internal/interfaces/handlers/space_handler.go @@ -4,10 +4,10 @@ import ( "encoding/json" "net/http" + "gitea.hostxtra.co.uk/mrhid6/notely/backend/internal/application/dto" + "gitea.hostxtra.co.uk/mrhid6/notely/backend/internal/application/services" + "gitea.hostxtra.co.uk/mrhid6/notely/backend/internal/interfaces/middleware" "github.com/gorilla/mux" - "github.com/noteapp/backend/internal/application/dto" - "github.com/noteapp/backend/internal/application/services" - "github.com/noteapp/backend/internal/interfaces/middleware" "go.mongodb.org/mongo-driver/v2/bson" ) diff --git a/backend/internal/interfaces/middleware/auth.go b/backend/internal/interfaces/middleware/auth.go index dc06724..083a3a8 100644 --- a/backend/internal/interfaces/middleware/auth.go +++ b/backend/internal/interfaces/middleware/auth.go @@ -6,7 +6,7 @@ import ( "net/http" "strings" - "github.com/noteapp/backend/internal/infrastructure/auth" + "gitea.hostxtra.co.uk/mrhid6/notely/backend/internal/infrastructure/auth" ) // ContextKey is a custom type for context keys diff --git a/backend/tests/integration/integration_test.go b/backend/tests/integration/integration_test.go index c05cd30..6a598d9 100644 --- a/backend/tests/integration/integration_test.go +++ b/backend/tests/integration/integration_test.go @@ -8,7 +8,7 @@ import ( "testing" "time" - "github.com/noteapp/backend/internal/infrastructure/database" + "gitea.hostxtra.co.uk/mrhid6/notely/backend/internal/infrastructure/database" ) // TestDatabaseConnection tests MongoDB connection diff --git a/backend/tests/unit/auth_service_test.go b/backend/tests/unit/auth_service_test.go index f810488..820409a 100644 --- a/backend/tests/unit/auth_service_test.go +++ b/backend/tests/unit/auth_service_test.go @@ -4,11 +4,11 @@ import ( "context" "testing" - "github.com/noteapp/backend/internal/application/dto" - "github.com/noteapp/backend/internal/application/services" - "github.com/noteapp/backend/internal/domain/entities" - "github.com/noteapp/backend/internal/infrastructure/auth" - "github.com/noteapp/backend/internal/infrastructure/security" + "gitea.hostxtra.co.uk/mrhid6/notely/backend/internal/application/dto" + "gitea.hostxtra.co.uk/mrhid6/notely/backend/internal/application/services" + "gitea.hostxtra.co.uk/mrhid6/notely/backend/internal/domain/entities" + "gitea.hostxtra.co.uk/mrhid6/notely/backend/internal/infrastructure/auth" + "gitea.hostxtra.co.uk/mrhid6/notely/backend/internal/infrastructure/security" "go.mongodb.org/mongo-driver/v2/bson" ) diff --git a/frontend/eslint.config.js b/frontend/eslint.config.js new file mode 100644 index 0000000..10a0db4 --- /dev/null +++ b/frontend/eslint.config.js @@ -0,0 +1,33 @@ +import js from "@eslint/js"; +import globals from "globals"; +import pluginVue from "eslint-plugin-vue"; + +export default [ + { + ignores: ["dist/**", "node_modules/**"], + }, + js.configs.recommended, + ...pluginVue.configs["flat/essential"], + { + files: ["**/*.{js,mjs,cjs,vue}"], + languageOptions: { + ecmaVersion: "latest", + sourceType: "module", + globals: { + ...globals.browser, + ...globals.node, + }, + }, + rules: { + "no-unused-vars": [ + "warn", + { + argsIgnorePattern: "^_", + varsIgnorePattern: "^_", + }, + ], + "no-console": "off", + "vue/multi-word-component-names": "off", + }, + }, +]; diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 092c029..df7e34f 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -21,12 +21,78 @@ "vue-router": "^4.2.0" }, "devDependencies": { + "@eslint/js": "^9.22.0", "@vitejs/plugin-vue": "^4.2.0", "@vue/test-utils": "^2.4.0", + "eslint": "^9.22.0", + "eslint-plugin-vue": "^9.32.0", + "globals": "^16.0.0", + "jsdom": "^29.0.1", "vite": "^4.3.0", "vitest": "^0.34.0" } }, + "node_modules/@asamuzakjp/css-color": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-5.0.1.tgz", + "integrity": "sha512-2SZFvqMyvboVV1d15lMf7XiI3m7SDqXUuKaTymJYLN6dSGadqp+fVojqJlVoMlbZnlTmu3S0TLwLTJpvBMO1Aw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@csstools/css-calc": "^3.1.1", + "@csstools/css-color-parser": "^4.0.2", + "@csstools/css-parser-algorithms": "^4.0.0", + "@csstools/css-tokenizer": "^4.0.0", + "lru-cache": "^11.2.6" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/@asamuzakjp/css-color/node_modules/lru-cache": { + "version": "11.2.7", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.7.tgz", + "integrity": "sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@asamuzakjp/dom-selector": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-7.0.4.tgz", + "integrity": "sha512-jXR6x4AcT3eIrS2fSNAwJpwirOkGcd+E7F7CP3zjdTqz9B/2huHOL8YJZBgekKwLML+u7qB/6P1LXQuMScsx0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@asamuzakjp/nwsapi": "^2.3.9", + "bidi-js": "^1.0.3", + "css-tree": "^3.2.1", + "is-potential-custom-element-name": "^1.0.1", + "lru-cache": "^11.2.7" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/@asamuzakjp/dom-selector/node_modules/lru-cache": { + "version": "11.2.7", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.7.tgz", + "integrity": "sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@asamuzakjp/nwsapi": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/@asamuzakjp/nwsapi/-/nwsapi-2.3.9.tgz", + "integrity": "sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==", + "dev": true, + "license": "MIT" + }, "node_modules/@babel/helper-string-parser": { "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", @@ -73,6 +139,159 @@ "node": ">=6.9.0" } }, + "node_modules/@bramus/specificity": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/@bramus/specificity/-/specificity-2.4.2.tgz", + "integrity": "sha512-ctxtJ/eA+t+6q2++vj5j7FYX3nRu311q1wfYH3xjlLOsczhlhxAg2FWNUXhpGvAw3BWo1xBcvOV6/YLc2r5FJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "css-tree": "^3.0.0" + }, + "bin": { + "specificity": "bin/cli.js" + } + }, + "node_modules/@csstools/color-helpers": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-6.0.2.tgz", + "integrity": "sha512-LMGQLS9EuADloEFkcTBR3BwV/CGHV7zyDxVRtVDTwdI2Ca4it0CCVTT9wCkxSgokjE5Ho41hEPgb8OEUwoXr6Q==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": ">=20.19.0" + } + }, + "node_modules/@csstools/css-calc": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-3.1.1.tgz", + "integrity": "sha512-HJ26Z/vmsZQqs/o3a6bgKslXGFAungXGbinULZO3eMsOyNJHeBBZfup5FiZInOghgoM4Hwnmw+OgbJCNg1wwUQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^4.0.0", + "@csstools/css-tokenizer": "^4.0.0" + } + }, + "node_modules/@csstools/css-color-parser": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-4.0.2.tgz", + "integrity": "sha512-0GEfbBLmTFf0dJlpsNU7zwxRIH0/BGEMuXLTCvFYxuL1tNhqzTbtnFICyJLTNK4a+RechKP75e7w42ClXSnJQw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "dependencies": { + "@csstools/color-helpers": "^6.0.2", + "@csstools/css-calc": "^3.1.1" + }, + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^4.0.0", + "@csstools/css-tokenizer": "^4.0.0" + } + }, + "node_modules/@csstools/css-parser-algorithms": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-4.0.0.tgz", + "integrity": "sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "@csstools/css-tokenizer": "^4.0.0" + } + }, + "node_modules/@csstools/css-syntax-patches-for-csstree": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.1.1.tgz", + "integrity": "sha512-BvqN0AMWNAnLk9G8jnUT77D+mUbY/H2b3uDTvg2isJkHaOufUE2R3AOwxWo7VBQKT1lOdwdvorddo2B/lk64+w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "peerDependencies": { + "css-tree": "^3.2.1" + }, + "peerDependenciesMeta": { + "css-tree": { + "optional": true + } + } + }, + "node_modules/@csstools/css-tokenizer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-4.0.0.tgz", + "integrity": "sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=20.19.0" + } + }, "node_modules/@esbuild/android-arm": { "version": "0.18.20", "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz", @@ -447,6 +666,281 @@ "node": ">=12" } }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.21.2", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz", + "integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.7", + "debug": "^4.3.1", + "minimatch": "^3.1.5" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-array/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@eslint/config-array/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", + "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", + "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.5.tgz", + "integrity": "sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.14.0", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.1", + "minimatch": "^3.1.5", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/eslintrc/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@eslint/js": { + "version": "9.39.4", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.4.tgz", + "integrity": "sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", + "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", + "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@exodus/bytes": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@exodus/bytes/-/bytes-1.15.0.tgz", + "integrity": "sha512-UY0nlA+feH81UGSHv92sLEPLCeZFjXOuHhrIo0HQydScuQc8s0A7kL/UdgwgDq8g8ilksmuoF35YVTNphV2aBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + }, + "peerDependencies": { + "@noble/hashes": "^1.8.0 || ^2.0.0" + }, + "peerDependenciesMeta": { + "@noble/hashes": { + "optional": true + } + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.7", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", + "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", @@ -549,6 +1043,20 @@ "@types/chai": "<5.2.0" } }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/node": { "version": "25.5.0", "resolved": "https://registry.npmjs.org/@types/node/-/node-25.5.0.tgz", @@ -793,6 +1301,16 @@ "node": ">=0.4.0" } }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, "node_modules/acorn-walk": { "version": "8.3.5", "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.5.tgz", @@ -806,6 +1324,23 @@ "node": ">=0.4.0" } }, + "node_modules/ajv": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", + "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, "node_modules/ansi-regex": { "version": "6.2.2", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", @@ -832,6 +1367,13 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, "node_modules/assertion-error": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", @@ -866,6 +1408,23 @@ "dev": true, "license": "MIT" }, + "node_modules/bidi-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.3.tgz", + "integrity": "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==", + "dev": true, + "license": "MIT", + "dependencies": { + "require-from-string": "^2.0.2" + } + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "dev": true, + "license": "ISC" + }, "node_modules/bootstrap": { "version": "5.3.8", "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.8.tgz", @@ -918,6 +1477,16 @@ "node": ">= 0.4" } }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/chai": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/chai/-/chai-4.5.0.tgz", @@ -937,6 +1506,39 @@ "node": ">=4" } }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chalk/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/check-error": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", @@ -992,6 +1594,13 @@ "node": ">=14" } }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, "node_modules/confbox": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz", @@ -1025,12 +1634,53 @@ "node": ">= 8" } }, + "node_modules/css-tree": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.2.1.tgz", + "integrity": "sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "mdn-data": "2.27.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/csstype": { "version": "3.2.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", "license": "MIT" }, + "node_modules/data-urls": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-7.0.0.tgz", + "integrity": "sha512-23XHcCF+coGYevirZceTVD7NdJOqVn+49IHyxgszm+JIiHLoB2TkmPtsYkNWT1pvRSGkc35L6NHs0yHkN2SumA==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-mimetype": "^5.0.0", + "whatwg-url": "^16.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, "node_modules/debug": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", @@ -1049,6 +1699,13 @@ } } }, + "node_modules/decimal.js": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz", + "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", + "dev": true, + "license": "MIT" + }, "node_modules/deep-eql": { "version": "4.1.4", "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.4.tgz", @@ -1062,6 +1719,13 @@ "node": ">=6" } }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -1232,12 +1896,324 @@ "@esbuild/win32-x64": "0.18.20" } }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.39.4", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.4.tgz", + "integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.2", + "@eslint/config-helpers": "^0.4.2", + "@eslint/core": "^0.17.0", + "@eslint/eslintrc": "^3.3.5", + "@eslint/js": "9.39.4", + "@eslint/plugin-kit": "^0.4.1", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.14.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.5", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-vue": { + "version": "9.33.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-vue/-/eslint-plugin-vue-9.33.0.tgz", + "integrity": "sha512-174lJKuNsuDIlLpjeXc5E2Tss8P44uIimAfGD0b90k0NoirJqpG7stLuU9Vp/9ioTOrQdWVREc4mRd1BD+CvGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "globals": "^13.24.0", + "natural-compare": "^1.4.0", + "nth-check": "^2.1.1", + "postcss-selector-parser": "^6.0.15", + "semver": "^7.6.3", + "vue-eslint-parser": "^9.4.3", + "xml-name-validator": "^4.0.0" + }, + "engines": { + "node": "^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.2.0 || ^7.0.0 || ^8.0.0 || ^9.0.0" + } + }, + "node_modules/eslint-plugin-vue/node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint-plugin-vue/node_modules/xml-name-validator": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz", + "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12" + } + }, + "node_modules/eslint-scope": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/eslint/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/espree": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, "node_modules/estree-walker": { "version": "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/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", + "dev": true, + "license": "ISC" + }, "node_modules/follow-redirects": { "version": "1.15.11", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", @@ -1384,6 +2360,32 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "16.5.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.5.0.tgz", + "integrity": "sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/gopd": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", @@ -1396,6 +2398,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/has-symbols": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", @@ -1444,6 +2456,56 @@ "node": ">=12.0.0" } }, + "node_modules/html-encoding-sniffer": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-6.0.0.tgz", + "integrity": "sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@exodus/bytes": "^1.6.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, "node_modules/ini": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", @@ -1451,6 +2513,16 @@ "dev": true, "license": "ISC" }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", @@ -1461,6 +2533,26 @@ "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "dev": true, + "license": "MIT" + }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -1516,6 +2608,115 @@ "node": ">=14" } }, + "node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsdom": { + "version": "29.0.1", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-29.0.1.tgz", + "integrity": "sha512-z6JOK5gRO7aMybVq/y/MlIpKh8JIi68FBKMUtKkK2KH/wMSRlCxQ682d08LB9fYXplyY/UXG8P4XXTScmdjApg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@asamuzakjp/css-color": "^5.0.1", + "@asamuzakjp/dom-selector": "^7.0.3", + "@bramus/specificity": "^2.4.2", + "@csstools/css-syntax-patches-for-csstree": "^1.1.1", + "@exodus/bytes": "^1.15.0", + "css-tree": "^3.2.1", + "data-urls": "^7.0.0", + "decimal.js": "^10.6.0", + "html-encoding-sniffer": "^6.0.0", + "is-potential-custom-element-name": "^1.0.1", + "lru-cache": "^11.2.7", + "parse5": "^8.0.0", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^6.0.1", + "undici": "^7.24.5", + "w3c-xmlserializer": "^5.0.0", + "webidl-conversions": "^8.0.1", + "whatwg-mimetype": "^5.0.0", + "whatwg-url": "^16.0.1", + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24.0.0" + }, + "peerDependencies": { + "canvas": "^3.0.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/jsdom/node_modules/lru-cache": { + "version": "11.2.7", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.7.tgz", + "integrity": "sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/local-pkg": { "version": "0.4.3", "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-0.4.3.tgz", @@ -1529,6 +2730,36 @@ "url": "https://github.com/sponsors/antfu" } }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.17.23", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", + "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, "node_modules/loupe": { "version": "2.3.7", "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", @@ -1585,6 +2816,13 @@ "node": ">= 0.4" } }, + "node_modules/mdn-data": { + "version": "2.27.1", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.27.1.tgz", + "integrity": "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==", + "dev": true, + "license": "CC0-1.0" + }, "node_modules/mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", @@ -1677,6 +2915,13 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, "node_modules/nopt": { "version": "7.2.1", "resolved": "https://registry.npmjs.org/nopt/-/nopt-7.2.1.tgz", @@ -1693,6 +2938,37 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/p-limit": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", @@ -1709,6 +2985,51 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate/node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate/node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "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", @@ -1716,6 +3037,55 @@ "dev": true, "license": "BlueOak-1.0.0" }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse5": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.0.tgz", + "integrity": "sha512-9m4m5GSgXjL4AjumKzq1Fgfp3Z8rsvjRNbnkVwfu2ImRqE5D0LnY2QfDen18FSY9C573YU5XxSapdHZTZ2WolA==", + "dev": true, + "license": "MIT", + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5/node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", @@ -1835,6 +3205,30 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/pretty-format": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", @@ -1863,6 +3257,16 @@ "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", "license": "MIT" }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/react-is": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", @@ -1870,6 +3274,26 @@ "dev": true, "license": "MIT" }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/rollup": { "version": "3.30.0", "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.30.0.tgz", @@ -1887,6 +3311,19 @@ "fsevents": "~2.3.2" } }, + "node_modules/saxes": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "dev": true, + "license": "ISC", + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=v12.22.7" + } + }, "node_modules/semver": { "version": "7.7.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", @@ -2070,6 +3507,19 @@ "node": ">=8" } }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/strip-literal": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-1.3.0.tgz", @@ -2083,6 +3533,26 @@ "url": "https://github.com/sponsors/antfu" } }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true, + "license": "MIT" + }, "node_modules/tinybench": { "version": "2.9.0", "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", @@ -2110,6 +3580,65 @@ "node": ">=14.0.0" } }, + "node_modules/tldts": { + "version": "7.0.27", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.0.27.tgz", + "integrity": "sha512-I4FZcVFcqCRuT0ph6dCDpPuO4Xgzvh+spkcTr1gK7peIvxWauoloVO0vuy1FQnijT63ss6AsHB6+OIM4aXHbPg==", + "dev": true, + "license": "MIT", + "dependencies": { + "tldts-core": "^7.0.27" + }, + "bin": { + "tldts": "bin/cli.js" + } + }, + "node_modules/tldts-core": { + "version": "7.0.27", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.0.27.tgz", + "integrity": "sha512-YQ7uPjgWUibIK6DW5lrKujGwUKhLevU4hcGbP5O6TcIUb+oTjJYJVWPS4nZsIHrEEEG6myk/oqAJUEQmpZrHsg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tough-cookie": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-6.0.1.tgz", + "integrity": "sha512-LktZQb3IeoUWB9lqR5EWTHgW/VTITCXg4D21M+lvybRVdylLrRMnqaIONLVb5mav8vM19m44HIcGq4qASeu2Qw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tldts": "^7.0.5" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/tr46": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-6.0.0.tgz", + "integrity": "sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==", + "dev": true, + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, "node_modules/type-detect": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.1.0.tgz", @@ -2120,6 +3649,19 @@ "node": ">=4" } }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/ufo": { "version": "1.6.3", "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.3.tgz", @@ -2127,6 +3669,16 @@ "dev": true, "license": "MIT" }, + "node_modules/undici": { + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.24.6.tgz", + "integrity": "sha512-Xi4agocCbRzt0yYMZGMA6ApD7gvtUFaxm4ZmeacWI4cZxaF6C+8I8QfofC20NAePiB/IcvZmzkJ7XPa471AEtA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.18.1" + } + }, "node_modules/undici-types": { "version": "7.18.2", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", @@ -2134,6 +3686,23 @@ "dev": true, "license": "MIT" }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT" + }, "node_modules/vite": { "version": "4.5.14", "resolved": "https://registry.npmjs.org/vite/-/vite-4.5.14.tgz", @@ -2346,6 +3915,79 @@ } } }, + "node_modules/vue-eslint-parser": { + "version": "9.4.3", + "resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-9.4.3.tgz", + "integrity": "sha512-2rYRLWlIpaiN8xbPiDyXZXRgLGOtWxERV7ND5fFAv5qo1D2N9Fu9MNajBNc6o13lZ+24DAWCkQCvj4klgmcITg==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.4", + "eslint-scope": "^7.1.1", + "eslint-visitor-keys": "^3.3.0", + "espree": "^9.3.1", + "esquery": "^1.4.0", + "lodash": "^4.17.21", + "semver": "^7.3.6" + }, + "engines": { + "node": "^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=6.0.0" + } + }, + "node_modules/vue-eslint-parser/node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/vue-eslint-parser/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/vue-eslint-parser/node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, "node_modules/vue-router": { "version": "4.6.4", "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.6.4.tgz", @@ -2361,6 +4003,54 @@ "vue": "^3.5.0" } }, + "node_modules/w3c-xmlserializer": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", + "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/webidl-conversions": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-8.0.1.tgz", + "integrity": "sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=20" + } + }, + "node_modules/whatwg-mimetype": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-5.0.0.tgz", + "integrity": "sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + } + }, + "node_modules/whatwg-url": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-16.0.1.tgz", + "integrity": "sha512-1to4zXBxmXHV3IiSSEInrreIlu02vUOvrhxJJH5vcxYTBDAx51cqZiKdyTxlecdKNSjj8EcxGBxNf6Vg+945gw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@exodus/bytes": "^1.11.0", + "tr46": "^6.0.0", + "webidl-conversions": "^8.0.1" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -2394,6 +4084,16 @@ "node": ">=8" } }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/wrap-ansi": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", @@ -2505,6 +4205,23 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/xml-name-validator": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", + "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true, + "license": "MIT" + }, "node_modules/yocto-queue": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.2.2.tgz", diff --git a/frontend/package.json b/frontend/package.json index 54684f3..b8aa5d4 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -7,7 +7,9 @@ "dev": "vite", "build": "vite build", "preview": "vite preview", - "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs" + "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs", + "test": "vitest run", + "test:watch": "vitest" }, "dependencies": { "@mdi/font": "^7.4.47", @@ -23,8 +25,13 @@ "vue-router": "^4.2.0" }, "devDependencies": { + "@eslint/js": "^9.22.0", "@vitejs/plugin-vue": "^4.2.0", "@vue/test-utils": "^2.4.0", + "eslint": "^9.22.0", + "eslint-plugin-vue": "^9.32.0", + "globals": "^16.0.0", + "jsdom": "^29.0.1", "vite": "^4.3.0", "vitest": "^0.34.0" } diff --git a/frontend/src/App.vue b/frontend/src/App.vue index 59c6e7d..4b9a00b 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -808,11 +808,6 @@ const createSpace = async (spaceData) => { await spaceStore.createSpace(spaceData); }; -const createCategory = async (categoryData) => { - showCreateCategoryModal.value = false; - await spaceStore.createCategory(currentSpace.value.id, categoryData); -}; - const openCreateCategoryModal = () => { if (!canCreateCategories.value) { return; diff --git a/frontend/src/components/AdminProviderModal.vue b/frontend/src/components/AdminProviderModal.vue index bb57a37..51cfbe7 100644 --- a/frontend/src/components/AdminProviderModal.vue +++ b/frontend/src/components/AdminProviderModal.vue @@ -60,6 +60,20 @@ + +
+
+
+
+
Danger Zone
+
Permanently delete this provider configuration.
+
+ +
+
+