diff --git a/ANDROID_BUILD_GUIDE..md b/ANDROID_BUILD_GUIDE..md new file mode 100644 index 0000000..1b8dcdc --- /dev/null +++ b/ANDROID_BUILD_GUIDE..md @@ -0,0 +1,201 @@ +# 🗺️ Hướng Dẫn Đóng Gói & Kiểm Thử Ứng Dụng Android (YoTrip) + +Tài liệu này hướng dẫn chi tiết quy trình thiết lập môi trường máy tính Windows Local để biên dịch, kiểm thử dự án Frontend (React + Vite) qua Capacitor, kết nối tới hệ thống Docker Production (`https://yotrip.labz.io.vn`) và chuẩn bị phát hành lên Google Play Store. + +--- + +## 📋 1. Điều Kiện Tiên Quyết (Môi Trường Windows) + +Trước khi chạy lệnh, đảm bảo máy tính local đã cài đặt và cấu hình đầy đủ các công cụ sau: + +* **Node.js:** Phiên bản v18 hoặc v20+. +* **Java JDK:** Phiên bản 17 hoặc 21 (Temurin hoặc Microsoft OpenJDK). + * Biến môi trường hệ thống: `JAVA_HOME` trỏ tới thư mục cài đặt JDK. + * Biến `Path` hệ thống: Bổ sung `%JAVA_HOME%\bin`. +* **Android Studio:** * Đã cài đặt **Android SDK**, **Android SDK Command-line Tools**. + * Biến môi trường hệ thống: `ANDROID_HOME` trỏ tới `AppData\Local\Android\Sdk`. + * Đã khởi tạo 1 thiết bị ảo (Android Simulator) qua *Virtual Device Manager*. + +--- + +## ⚙️ 2. Cấu Hình Mã Nguồn Frontend (Local) + +### 2.1 Cấu hình file `.env.production` +Tạo hoặc cập nhật file `.env.production` nằm tại thư mục gốc của `frontend/`: + +```text +VITE_BACKEND_URL=[https://yotrip.labz.io.vn](https://yotrip.labz.io.vn) + +### 2.2 Cấu hình Axios / API Instance (src/api/axios.ts) +Cập nhật logic baseURL để tự động phân tách môi trường chạy Web Dev (sử dụng Proxy của Vite) và môi trường chạy App Native (gọi trực tiếp URL tuyệt đối): + +import axios from 'axios'; +import { Capacitor } from '@capacitor/core'; + +const API = axios.create({ + baseURL: Capacitor.isNativePlatform() + ? import.meta.env.VITE_BACKEND_URL + : '', + timeout: 15000, + headers: { + 'Content-Type': 'application/json', + }, +}); + +export default API; + +## 3. Cấu Hình Nền Tảng Android Native + +### 3.1 Thiết lập Biểu tượng Ứng dụng (App Icon) + +Để sử dụng frontend/public/favicon.ico làm icon của app trên Android, chúng ta cần chuyển đổi nó sang định dạng .png độ phân giải cao và sử dụng công cụ của Capacitor để tự động tạo các kích thước cần thiết cho Android. + +Chuẩn bị ảnh: Chuyển đổi file favicon.ico của bạn thành file .png (khuyên dùng độ phân giải ít nhất 1024x1024 pixel để có chất lượng tốt nhất trên các thiết bị đời mới) và lưu tên là icon-only.png. + +Cài đặt công cụ: Chạy lệnh sau tại thư mục frontend/ để cài đặt công cụ quản lý tài nguyên của Capacitor: + +Bash +npm install @capacitor/assets --save-dev +Khởi tạo thư mục: Tạo thư mục assets ở thư mục gốc của frontend/ (cùng cấp với src) và đặt file icon-only.png vào đó. + +Bash +mkdir assets +# Sau đó di chuyển file icon-only.png của bạn vào thư mục assets/ +Tạo Icon: Chạy lệnh sau để tự động tạo và đặt các icon vào đúng vị trí trong dự án Android: + +Bash +npx capacitor-assets generate --android + +### 3.2 File capacitor.config.json + +Định danh chính xác gói ứng dụng (App ID) dùng để đăng ký trên Google Play Console: + +{ + "appId": "com.yotrip.app", + "appName": "YoTrip", + "webDir": "dist", + "plugins": { + "SplashScreen": { + "launchShowDuration": 2000 + } + } +} + +### 3.3 File capacitor.config.json +Định danh chính xác gói ứng dụng (App ID) dùng để đăng ký trên Google Play Console: + +JSON +{ + "appId": "com.yotrip.app", + "appName": "YoTrip", + "webDir": "dist", + "plugins": { + "SplashScreen": { + "launchShowDuration": 2000 + } + } +} +### 3.4 Cấu hình quyền trong AndroidManifest.xml +Mở đường dẫn android/app/src/main/AndroidManifest.xml, thêm các quyền truy cập Internet, định vị GPS, và các quyền cần thiết cho tính năng chụp ảnh và lưu ảnh vào bộ nhớ máy: + +XML + + + + + + + + + + + + + + + + + + ... + + + +## 4. Tích Hợp Tính Năng Chụp Ảnh và Lưu Ảnh (React Code) +Sử dụng Plugin của Capacitor để tích hợp trực tiếp vào code React của bạn. + +Cài đặt Plugin Camera: Chạy lệnh sau tại thư mục frontend/: + +Bash +npm install @capacitor/camera +npx cap update +Ví dụ code: Dưới đây là cách implement tính năng chụp ảnh và tự động lưu ảnh gốc vào thư viện ảnh của điện thoại trong một React component (ví dụ src/components/PhotoTaker.tsx): + +JavaScript +import React, { useState } from 'react'; +import { IonButton, IonIcon, IonContent, IonPage } from '@ionic/react'; +import { camera } from 'ionicons/icons'; +import { Camera, CameraResultType, CameraSource } from '@capacitor/camera'; + +const PhotoTaker: React.FC = () => { + const [photoUri, setPhotoUri] = useState(); + + const takeAndSavePhoto = async () => { + try { + const image = await Camera.getPhoto({ + quality: 90, + allowEditing: false, // Giữ nguyên ảnh gốc, không qua chỉnh sửa + resultType: CameraResultType.Uri, + source: CameraSource.Camera, // Mở camera trực tiếp + saveToGallery: true, // YÊU CẦU MỚI: Tự động lưu ảnh gốc vào thư viện điện thoại + }); + + // Bạn có thể sử dụng image.webPath để hiển thị xem trước + setPhotoUri(image.webPath); + console.log('Ảnh đã được chụp và lưu tại:', image.path); + + } catch (error) { + console.error('Lỗi khi chụp hoặc lưu ảnh:', error); + } + }; + + return ( + + +
+

Tính năng Chụp ảnh

+ + + + Chụp và Lưu Ảnh Gốc + + + {photoUri && ( +
+

Xem trước ảnh vừa chụp:

+ Xem trước ảnh chụp +
+ )} +
+
+
+ ); +}; + +export default PhotoTaker; + +## 5. Quy Trình Biên Dịch & Kiểm Thử (Simulator) +Mỗi lần cập nhật code giao diện ở máy local, chạy chuỗi lệnh sau tại Terminal của VS Code để đẩy app lên máy ảo: + +# Bước 1: Cài đặt các thư viện phụ thuộc tại local +npm install + +# Bước 2: Build code React + Vite thành file tĩnh +npm run build + +# Bước 3: Đồng bộ mã nguồn tĩnh vào thư mục Android mã nguồn mở +npx cap sync + +# Bước 4: Khởi chạy máy ảo và nạp ứng dụng tự động +npx cap run android \ No newline at end of file diff --git a/client_secret_639044397050-7hair384u6lle941h033trut1s3q47l4.apps.googleusercontent.com.json b/client_secret_639044397050-7hair384u6lle941h033trut1s3q47l4.apps.googleusercontent.com.json new file mode 100644 index 0000000..1a682d1 --- /dev/null +++ b/client_secret_639044397050-7hair384u6lle941h033trut1s3q47l4.apps.googleusercontent.com.json @@ -0,0 +1 @@ +{"web":{"client_id":"639044397050-7hair384u6lle941h033trut1s3q47l4.apps.googleusercontent.com","project_id":"yotrip-web","auth_uri":"https://accounts.google.com/o/oauth2/auth","token_uri":"https://oauth2.googleapis.com/token","auth_provider_x509_cert_url":"https://www.googleapis.com/oauth2/v1/certs","client_secret":"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"}} \ No newline at end of file diff --git a/docker_compose_plan.md b/docker_compose_plan.md new file mode 100644 index 0000000..596952e --- /dev/null +++ b/docker_compose_plan.md @@ -0,0 +1,87 @@ +# Kế hoạch triển khai: Cấu hình Docker để Chạy Server & Phát triển (Hot-reload) + +Kế hoạch này phác thảo cách cấu hình Docker và Docker Compose cho dự án YoTrip. Cấu hình này sẽ đáp ứng đồng thời hai nhu cầu: +1. **Môi trường Phát triển (Development)**: Đồng bộ mã nguồn trực tiếp (bind mounts) từ máy local vào container, hỗ trợ hot-reload cho cả backend (NestJS watch) và frontend (Vite HMR). +2. **Môi trường Triển khai (Production)**: Đóng gói tối ưu thành các image độc lập, sử dụng Nginx để phục vụ frontend tĩnh và tối ưu hóa hiệu năng NestJS backend. + +--- + +## User Review Required + +> [!IMPORTANT] +> - **Biến môi trường trong Docker**: Khi chạy trong Docker Compose, địa chỉ kết nối cơ sở dữ liệu (`DATABASE_URL`) và Redis (`REDIS_URL`) phải trỏ đến tên các service của container (ví dụ: `postgres` thay vì `localhost`). Chúng tôi sẽ cấu hình Docker Compose ghi đè (override) các biến này một cách tự động để tránh làm hỏng cấu hình chạy trực tiếp bằng `npm run start:dev` trên máy local của bạn. +> - **Cổng mạng (Ports)**: +> - Backend: Cổng `3001` được mở ra ngoài. +> - Frontend: Cổng `5173` (cho Dev) và cổng `3002` (cho Production thông qua Nginx). +> - Database: Cổng `5432` (mở để truy cập quản trị nếu cần). +> - Redis: Cổng `6379`. + +--- + +## Proposed Changes + +### 1. Dockerfile cho Backend + +#### [NEW] [Dockerfile](file:///home/locpham/travelplanning/backend/Dockerfile) +- Thiết lập môi trường chạy Node.js (phiên bản `20-alpine`). +- Cài đặt các gói phụ thuộc hệ thống cần thiết (như openssl cho Prisma). +- Cấu hình chạy chế độ phát triển (sử dụng volume mounts để hot-reload) và chế độ production (build code JS). +- Chạy Prisma client generation lúc build. + +--- + +### 2. Dockerfile cho Frontend + +#### [NEW] [Dockerfile](file:///home/locpham/travelplanning/frontend/Dockerfile) +- Sử dụng chiến lược Multi-stage build để tối ưu hóa dung lượng: + - **Stage 1 (Build)**: Cài đặt dependencies và build mã nguồn React/Vite thành thư mục tĩnh `dist`. + - **Stage 2 (Nginx)**: Copy thư mục `dist` vào container chạy Nginx để phục vụ các file tĩnh ở cổng `80` (phục vụ môi trường production). +- Hỗ trợ chạy Node.js trực tiếp cho môi trường phát triển để dùng Vite dev server và HMR. + +--- + +### 3. Docker Compose cho Phát triển & Sửa Code (Hot-reload) + +#### [NEW] [docker-compose.yml](file:///home/locpham/travelplanning/docker-compose.yml) +- Khởi tạo 4 services chính: + 1. `postgres`: Cơ sở dữ liệu PostgreSQL 15, lưu trữ dữ liệu bền vững qua volume `pg_data`. + 2. `redis`: Caching và WebSockets. + 3. `backend`: Mount thư mục `backend/` vào container, chạy lệnh `npm run start:dev` để tự động reload khi sửa code trên máy host. + 4. `frontend`: Mount thư mục `frontend/` vào container, chạy lệnh `npm run dev -- --host` để phục vụ Vite dev server hỗ trợ HMR (Hot Module Replacement). +- Đồng bộ hóa các volume ẩn `node_modules` để tránh xung đột hệ điều hành giữa máy host và container. + +--- + +### 4. Docker Compose cho Triển khai lên Server (Production) + +#### [NEW] [docker-compose.prod.yml](file:///home/locpham/travelplanning/docker-compose.prod.yml) +- Cấu hình tối ưu để triển khai lên server cloud: + - Builds production image cho `backend` và chạy trực tiếp file JS đã build (`dist/src/main.js`). + - Builds production image cho `frontend` sử dụng Nginx để phục vụ client, tối ưu hóa tốc độ tải trang và bảo mật. + - Tự động restart dịch vụ nếu gặp sự cố (`restart: always`). + +--- + +## Verification Plan + +### Automated Tests +- Kiểm tra tính hợp lệ của cấu hình docker-compose: + ```bash + docker compose config + ``` + +### Manual Verification +1. **Kiểm tra Môi trường Phát triển (Sửa code trực tiếp)**: + - Chạy lệnh khởi động môi trường dev: + ```bash + docker compose up --build + ``` + - Truy cập giao diện tại `http://localhost:5173`. + - Sửa đổi một dòng văn bản trong frontend (ví dụ: nhãn nút ở `LandingPage.tsx`) hoặc backend và kiểm tra xem container có tự động tải lại (hot-reload) tức thì hay không. +2. **Kiểm tra Môi trường Production (Triển khai server)**: + - Chạy lệnh khởi động môi trường prod: + ```bash + docker compose -f docker-compose.prod.yml up --build -d + ``` + - Xác nhận mọi service khởi chạy ngầm thành công. + - Truy cập ứng dụng qua cổng `80` (http://localhost) và xác nhận hoạt động bình thường. diff --git a/frontend/.env b/frontend/.env index b626b69..5426a2f 100644 --- a/frontend/.env +++ b/frontend/.env @@ -1,3 +1,2 @@ -VITE_API_URL=http://localhost:3001 -DATABASE_URL=postgresql://postgres:password@localhost:5432/travel_db?schema=public -VITE_GOOGLE_CLIENT_ID=639044397050-7hair384u6lle941h033trut1s3q47l4.apps.googleusercontent.com +VITE_BACKEND_URL=https://yotrip.labz.io.vn +VITE_GOOGLE_CLIENT_ID=639044397050-7hair384u6lle941h033trut1s3q47l4.apps.googleusercontent.com \ No newline at end of file diff --git a/frontend/.env.production b/frontend/.env.production new file mode 100644 index 0000000..19fab17 --- /dev/null +++ b/frontend/.env.production @@ -0,0 +1,2 @@ +VITE_BACKEND_URL=https://yotrip.labz.io.vn +VITE_GOOGLE_CLIENT_ID=639044397050-7hair384u6lle941h033trut1s3q47l4.apps.googleusercontent.com diff --git a/frontend/android/app/capacitor.build.gradle b/frontend/android/app/capacitor.build.gradle index c78c0f4..c164c2d 100644 --- a/frontend/android/app/capacitor.build.gradle +++ b/frontend/android/app/capacitor.build.gradle @@ -9,7 +9,10 @@ android { apply from: "../capacitor-cordova-android-plugins/cordova.variables.gradle" dependencies { + implementation project(':capacitor-camera') implementation project(':capacitor-geolocation') + implementation project(':capacitor-local-notifications') + implementation project(':codetrix-studio-capacitor-google-auth') } diff --git a/frontend/android/app/src/main/AndroidManifest.xml b/frontend/android/app/src/main/AndroidManifest.xml index 340e7df..e7aa2dd 100644 --- a/frontend/android/app/src/main/AndroidManifest.xml +++ b/frontend/android/app/src/main/AndroidManifest.xml @@ -1,14 +1,13 @@ - + - - + android:theme="@style/AppTheme" + android:usesCleartextTraffic="true"> - - - + + + + + + + + + + + + + + + diff --git a/frontend/android/app/src/main/res/drawable-land-hdpi/splash.png b/frontend/android/app/src/main/res/drawable-land-hdpi/splash.png index e31573b..47dcf33 100644 Binary files a/frontend/android/app/src/main/res/drawable-land-hdpi/splash.png and b/frontend/android/app/src/main/res/drawable-land-hdpi/splash.png differ diff --git a/frontend/android/app/src/main/res/drawable-land-ldpi/splash.png b/frontend/android/app/src/main/res/drawable-land-ldpi/splash.png new file mode 100644 index 0000000..da7d4cf Binary files /dev/null and b/frontend/android/app/src/main/res/drawable-land-ldpi/splash.png differ diff --git a/frontend/android/app/src/main/res/drawable-land-mdpi/splash.png b/frontend/android/app/src/main/res/drawable-land-mdpi/splash.png index f7a6492..af83328 100644 Binary files a/frontend/android/app/src/main/res/drawable-land-mdpi/splash.png and b/frontend/android/app/src/main/res/drawable-land-mdpi/splash.png differ diff --git a/frontend/android/app/src/main/res/drawable-land-night-hdpi/splash.png b/frontend/android/app/src/main/res/drawable-land-night-hdpi/splash.png new file mode 100644 index 0000000..fbfeb02 Binary files /dev/null and b/frontend/android/app/src/main/res/drawable-land-night-hdpi/splash.png differ diff --git a/frontend/android/app/src/main/res/drawable-land-night-ldpi/splash.png b/frontend/android/app/src/main/res/drawable-land-night-ldpi/splash.png new file mode 100644 index 0000000..75ee966 Binary files /dev/null and b/frontend/android/app/src/main/res/drawable-land-night-ldpi/splash.png differ diff --git a/frontend/android/app/src/main/res/drawable-land-night-mdpi/splash.png b/frontend/android/app/src/main/res/drawable-land-night-mdpi/splash.png new file mode 100644 index 0000000..3da0d24 Binary files /dev/null and b/frontend/android/app/src/main/res/drawable-land-night-mdpi/splash.png differ diff --git a/frontend/android/app/src/main/res/drawable-land-night-xhdpi/splash.png b/frontend/android/app/src/main/res/drawable-land-night-xhdpi/splash.png new file mode 100644 index 0000000..b60c888 Binary files /dev/null and b/frontend/android/app/src/main/res/drawable-land-night-xhdpi/splash.png differ diff --git a/frontend/android/app/src/main/res/drawable-land-night-xxhdpi/splash.png b/frontend/android/app/src/main/res/drawable-land-night-xxhdpi/splash.png new file mode 100644 index 0000000..615ebd5 Binary files /dev/null and b/frontend/android/app/src/main/res/drawable-land-night-xxhdpi/splash.png differ diff --git a/frontend/android/app/src/main/res/drawable-land-night-xxxhdpi/splash.png b/frontend/android/app/src/main/res/drawable-land-night-xxxhdpi/splash.png new file mode 100644 index 0000000..75104a9 Binary files /dev/null and b/frontend/android/app/src/main/res/drawable-land-night-xxxhdpi/splash.png differ diff --git a/frontend/android/app/src/main/res/drawable-land-xhdpi/splash.png b/frontend/android/app/src/main/res/drawable-land-xhdpi/splash.png index 8077255..35a7438 100644 Binary files a/frontend/android/app/src/main/res/drawable-land-xhdpi/splash.png and b/frontend/android/app/src/main/res/drawable-land-xhdpi/splash.png differ diff --git a/frontend/android/app/src/main/res/drawable-land-xxhdpi/splash.png b/frontend/android/app/src/main/res/drawable-land-xxhdpi/splash.png index 14c6c8f..5f66dcf 100644 Binary files a/frontend/android/app/src/main/res/drawable-land-xxhdpi/splash.png and b/frontend/android/app/src/main/res/drawable-land-xxhdpi/splash.png differ diff --git a/frontend/android/app/src/main/res/drawable-land-xxxhdpi/splash.png b/frontend/android/app/src/main/res/drawable-land-xxxhdpi/splash.png index 244ca25..af9ec88 100644 Binary files a/frontend/android/app/src/main/res/drawable-land-xxxhdpi/splash.png and b/frontend/android/app/src/main/res/drawable-land-xxxhdpi/splash.png differ diff --git a/frontend/android/app/src/main/res/drawable-night/splash.png b/frontend/android/app/src/main/res/drawable-night/splash.png new file mode 100644 index 0000000..75ee966 Binary files /dev/null and b/frontend/android/app/src/main/res/drawable-night/splash.png differ diff --git a/frontend/android/app/src/main/res/drawable-port-hdpi/splash.png b/frontend/android/app/src/main/res/drawable-port-hdpi/splash.png index 74faaa5..9e55aa3 100644 Binary files a/frontend/android/app/src/main/res/drawable-port-hdpi/splash.png and b/frontend/android/app/src/main/res/drawable-port-hdpi/splash.png differ diff --git a/frontend/android/app/src/main/res/drawable-port-ldpi/splash.png b/frontend/android/app/src/main/res/drawable-port-ldpi/splash.png new file mode 100644 index 0000000..1ab6e44 Binary files /dev/null and b/frontend/android/app/src/main/res/drawable-port-ldpi/splash.png differ diff --git a/frontend/android/app/src/main/res/drawable-port-mdpi/splash.png b/frontend/android/app/src/main/res/drawable-port-mdpi/splash.png index e944f4a..03ee3e6 100644 Binary files a/frontend/android/app/src/main/res/drawable-port-mdpi/splash.png and b/frontend/android/app/src/main/res/drawable-port-mdpi/splash.png differ diff --git a/frontend/android/app/src/main/res/drawable-port-night-hdpi/splash.png b/frontend/android/app/src/main/res/drawable-port-night-hdpi/splash.png new file mode 100644 index 0000000..1539555 Binary files /dev/null and b/frontend/android/app/src/main/res/drawable-port-night-hdpi/splash.png differ diff --git a/frontend/android/app/src/main/res/drawable-port-night-ldpi/splash.png b/frontend/android/app/src/main/res/drawable-port-night-ldpi/splash.png new file mode 100644 index 0000000..8d8f92f Binary files /dev/null and b/frontend/android/app/src/main/res/drawable-port-night-ldpi/splash.png differ diff --git a/frontend/android/app/src/main/res/drawable-port-night-mdpi/splash.png b/frontend/android/app/src/main/res/drawable-port-night-mdpi/splash.png new file mode 100644 index 0000000..a45d4a6 Binary files /dev/null and b/frontend/android/app/src/main/res/drawable-port-night-mdpi/splash.png differ diff --git a/frontend/android/app/src/main/res/drawable-port-night-xhdpi/splash.png b/frontend/android/app/src/main/res/drawable-port-night-xhdpi/splash.png new file mode 100644 index 0000000..deef156 Binary files /dev/null and b/frontend/android/app/src/main/res/drawable-port-night-xhdpi/splash.png differ diff --git a/frontend/android/app/src/main/res/drawable-port-night-xxhdpi/splash.png b/frontend/android/app/src/main/res/drawable-port-night-xxhdpi/splash.png new file mode 100644 index 0000000..e16c049 Binary files /dev/null and b/frontend/android/app/src/main/res/drawable-port-night-xxhdpi/splash.png differ diff --git a/frontend/android/app/src/main/res/drawable-port-night-xxxhdpi/splash.png b/frontend/android/app/src/main/res/drawable-port-night-xxxhdpi/splash.png new file mode 100644 index 0000000..df39518 Binary files /dev/null and b/frontend/android/app/src/main/res/drawable-port-night-xxxhdpi/splash.png differ diff --git a/frontend/android/app/src/main/res/drawable-port-xhdpi/splash.png b/frontend/android/app/src/main/res/drawable-port-xhdpi/splash.png index 564a82f..d50de64 100644 Binary files a/frontend/android/app/src/main/res/drawable-port-xhdpi/splash.png and b/frontend/android/app/src/main/res/drawable-port-xhdpi/splash.png differ diff --git a/frontend/android/app/src/main/res/drawable-port-xxhdpi/splash.png b/frontend/android/app/src/main/res/drawable-port-xxhdpi/splash.png index bfabe68..cc03e12 100644 Binary files a/frontend/android/app/src/main/res/drawable-port-xxhdpi/splash.png and b/frontend/android/app/src/main/res/drawable-port-xxhdpi/splash.png differ diff --git a/frontend/android/app/src/main/res/drawable-port-xxxhdpi/splash.png b/frontend/android/app/src/main/res/drawable-port-xxxhdpi/splash.png index 6929071..3aa3b4a 100644 Binary files a/frontend/android/app/src/main/res/drawable-port-xxxhdpi/splash.png and b/frontend/android/app/src/main/res/drawable-port-xxxhdpi/splash.png differ diff --git a/frontend/android/app/src/main/res/drawable/splash.png b/frontend/android/app/src/main/res/drawable/splash.png index f7a6492..03ee3e6 100644 Binary files a/frontend/android/app/src/main/res/drawable/splash.png and b/frontend/android/app/src/main/res/drawable/splash.png differ diff --git a/frontend/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/frontend/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml index 036d09b..0aa82aa 100644 --- a/frontend/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +++ b/frontend/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -1,5 +1,9 @@ - - + + + + + + \ No newline at end of file diff --git a/frontend/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/frontend/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml index 036d09b..0aa82aa 100644 --- a/frontend/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +++ b/frontend/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -1,5 +1,9 @@ - - + + + + + + \ No newline at end of file diff --git a/frontend/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/frontend/android/app/src/main/res/mipmap-hdpi/ic_launcher.png index c023e50..d94f58c 100644 Binary files a/frontend/android/app/src/main/res/mipmap-hdpi/ic_launcher.png and b/frontend/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/frontend/android/app/src/main/res/mipmap-hdpi/ic_launcher_background.png b/frontend/android/app/src/main/res/mipmap-hdpi/ic_launcher_background.png new file mode 100644 index 0000000..c092285 Binary files /dev/null and b/frontend/android/app/src/main/res/mipmap-hdpi/ic_launcher_background.png differ diff --git a/frontend/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png b/frontend/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png index 2127973..ddd6b80 100644 Binary files a/frontend/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png and b/frontend/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png differ diff --git a/frontend/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/frontend/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png index b441f37..e5f9b40 100644 Binary files a/frontend/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png and b/frontend/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ diff --git a/frontend/android/app/src/main/res/mipmap-ldpi/ic_launcher.png b/frontend/android/app/src/main/res/mipmap-ldpi/ic_launcher.png new file mode 100644 index 0000000..2243405 Binary files /dev/null and b/frontend/android/app/src/main/res/mipmap-ldpi/ic_launcher.png differ diff --git a/frontend/android/app/src/main/res/mipmap-ldpi/ic_launcher_background.png b/frontend/android/app/src/main/res/mipmap-ldpi/ic_launcher_background.png new file mode 100644 index 0000000..adc7b13 Binary files /dev/null and b/frontend/android/app/src/main/res/mipmap-ldpi/ic_launcher_background.png differ diff --git a/frontend/android/app/src/main/res/mipmap-ldpi/ic_launcher_foreground.png b/frontend/android/app/src/main/res/mipmap-ldpi/ic_launcher_foreground.png new file mode 100644 index 0000000..a696372 Binary files /dev/null and b/frontend/android/app/src/main/res/mipmap-ldpi/ic_launcher_foreground.png differ diff --git a/frontend/android/app/src/main/res/mipmap-ldpi/ic_launcher_round.png b/frontend/android/app/src/main/res/mipmap-ldpi/ic_launcher_round.png new file mode 100644 index 0000000..d6bcc04 Binary files /dev/null and b/frontend/android/app/src/main/res/mipmap-ldpi/ic_launcher_round.png differ diff --git a/frontend/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/frontend/android/app/src/main/res/mipmap-mdpi/ic_launcher.png index 72905b8..c945d92 100644 Binary files a/frontend/android/app/src/main/res/mipmap-mdpi/ic_launcher.png and b/frontend/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/frontend/android/app/src/main/res/mipmap-mdpi/ic_launcher_background.png b/frontend/android/app/src/main/res/mipmap-mdpi/ic_launcher_background.png new file mode 100644 index 0000000..8c57e54 Binary files /dev/null and b/frontend/android/app/src/main/res/mipmap-mdpi/ic_launcher_background.png differ diff --git a/frontend/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png b/frontend/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png index 8ed0605..8bee5be 100644 Binary files a/frontend/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png and b/frontend/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png differ diff --git a/frontend/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/frontend/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png index 9502e47..68d689c 100644 Binary files a/frontend/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png and b/frontend/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ diff --git a/frontend/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/frontend/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png index 4d1e077..078397d 100644 Binary files a/frontend/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png and b/frontend/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/frontend/android/app/src/main/res/mipmap-xhdpi/ic_launcher_background.png b/frontend/android/app/src/main/res/mipmap-xhdpi/ic_launcher_background.png new file mode 100644 index 0000000..704f168 Binary files /dev/null and b/frontend/android/app/src/main/res/mipmap-xhdpi/ic_launcher_background.png differ diff --git a/frontend/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png b/frontend/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png index df0f158..9699fa9 100644 Binary files a/frontend/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png and b/frontend/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png differ diff --git a/frontend/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/frontend/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png index 853db04..defcb62 100644 Binary files a/frontend/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png and b/frontend/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ diff --git a/frontend/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/frontend/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png index 6cdf97c..ba55bbf 100644 Binary files a/frontend/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png and b/frontend/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/frontend/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_background.png b/frontend/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_background.png new file mode 100644 index 0000000..af19909 Binary files /dev/null and b/frontend/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_background.png differ diff --git a/frontend/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png b/frontend/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png index 2960cbb..d06e8f2 100644 Binary files a/frontend/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png and b/frontend/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png differ diff --git a/frontend/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/frontend/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png index 8e3093a..b44e6d7 100644 Binary files a/frontend/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and b/frontend/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ diff --git a/frontend/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/frontend/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png index 46de6e2..d7e926b 100644 Binary files a/frontend/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png and b/frontend/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/frontend/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_background.png b/frontend/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_background.png new file mode 100644 index 0000000..27a3773 Binary files /dev/null and b/frontend/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_background.png differ diff --git a/frontend/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png b/frontend/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png index d2ea9ab..8245228 100644 Binary files a/frontend/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png and b/frontend/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png differ diff --git a/frontend/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/frontend/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png index a40d73e..b584611 100644 Binary files a/frontend/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and b/frontend/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ diff --git a/frontend/android/app/src/main/res/values/strings.xml b/frontend/android/app/src/main/res/values/strings.xml index a3da462..9cecbc3 100644 --- a/frontend/android/app/src/main/res/values/strings.xml +++ b/frontend/android/app/src/main/res/values/strings.xml @@ -4,4 +4,5 @@ YoTrip com.yotrip.app com.yotrip.app + 639044397050-7hair384u6lle941h033trut1s3q47l4.apps.googleusercontent.com diff --git a/frontend/android/build.gradle b/frontend/android/build.gradle index f1b3b0e..78e3f9a 100644 --- a/frontend/android/build.gradle +++ b/frontend/android/build.gradle @@ -24,6 +24,15 @@ allprojects { } } +subprojects { + configurations.all { + resolutionStrategy { + force "androidx.core:core-ktx:${rootProject.ext.androidxCoreVersion}" + force "androidx.core:core:${rootProject.ext.androidxCoreVersion}" + } + } +} + task clean(type: Delete) { delete rootProject.buildDir } diff --git a/frontend/android/capacitor.settings.gradle b/frontend/android/capacitor.settings.gradle index e365d3d..5c823c6 100644 --- a/frontend/android/capacitor.settings.gradle +++ b/frontend/android/capacitor.settings.gradle @@ -2,5 +2,14 @@ include ':capacitor-android' project(':capacitor-android').projectDir = new File('../../node_modules/@capacitor/android/capacitor') +include ':capacitor-camera' +project(':capacitor-camera').projectDir = new File('../../node_modules/@capacitor/camera/android') + include ':capacitor-geolocation' project(':capacitor-geolocation').projectDir = new File('../../node_modules/@capacitor/geolocation/android') + +include ':capacitor-local-notifications' +project(':capacitor-local-notifications').projectDir = new File('../../node_modules/@capacitor/local-notifications/android') + +include ':codetrix-studio-capacitor-google-auth' +project(':codetrix-studio-capacitor-google-auth').projectDir = new File('../../node_modules/@codetrix-studio/capacitor-google-auth/android') diff --git a/frontend/android/variables.gradle b/frontend/android/variables.gradle index 2c8e408..1bb4164 100644 --- a/frontend/android/variables.gradle +++ b/frontend/android/variables.gradle @@ -1,5 +1,5 @@ ext { - minSdkVersion = 23 + minSdkVersion = 24 compileSdkVersion = 35 targetSdkVersion = 35 androidxActivityVersion = '1.9.2' diff --git a/frontend/assets/icon-background.png b/frontend/assets/icon-background.png new file mode 100644 index 0000000..f1465a6 Binary files /dev/null and b/frontend/assets/icon-background.png differ diff --git a/frontend/assets/icon-only.png b/frontend/assets/icon-only.png new file mode 100644 index 0000000..59bb216 Binary files /dev/null and b/frontend/assets/icon-only.png differ diff --git a/frontend/assets/icon.png b/frontend/assets/icon.png new file mode 100644 index 0000000..cce0e9b Binary files /dev/null and b/frontend/assets/icon.png differ diff --git a/frontend/assets/splash-dark.png b/frontend/assets/splash-dark.png new file mode 100644 index 0000000..34bce9e Binary files /dev/null and b/frontend/assets/splash-dark.png differ diff --git a/frontend/assets/splash.png b/frontend/assets/splash.png new file mode 100644 index 0000000..5fd6834 Binary files /dev/null and b/frontend/assets/splash.png differ diff --git a/frontend/capacitor.config.ts b/frontend/capacitor.config.ts index 2fb4398..f26b2da 100644 --- a/frontend/capacitor.config.ts +++ b/frontend/capacitor.config.ts @@ -3,7 +3,17 @@ import type { CapacitorConfig } from '@capacitor/cli'; const config: CapacitorConfig = { appId: 'com.yotrip.app', appName: 'YoTrip', - webDir: 'dist' + webDir: 'dist', + plugins: { + SplashScreen: { + launchShowDuration: 2000 + }, + GoogleAuth: { + scopes: ['profile', 'email'], + serverClientId: '639044397050-7hair384u6lle941h033trut1s3q47l4.apps.googleusercontent.com', + forceCodeForRefreshToken: true + } + } }; export default config; diff --git a/frontend/dist/index.html b/frontend/dist/index.html index 4dcf971..624c270 100644 --- a/frontend/dist/index.html +++ b/frontend/dist/index.html @@ -1,30 +1,38 @@ - - - - - - - - Travel Planner - - - - - - - - - - - - - - - - - - - -
- + + + + + + + + Travel Planner + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ \ No newline at end of file diff --git a/frontend/package.json b/frontend/package.json index c47b672..8e62372 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -11,9 +11,12 @@ }, "dependencies": { "@capacitor/android": "^8.4.1", + "@capacitor/camera": "^8.2.0", "@capacitor/cli": "^7.6.7", "@capacitor/core": "^8.4.1", "@capacitor/geolocation": "^8.2.0", + "@capacitor/local-notifications": "^8.2.0", + "@codetrix-studio/capacitor-google-auth": "^3.4.0-rc.4", "date-fns": "^4.4.0", "jspdf": "^4.2.1", "jspdf-autotable": "^5.0.8", @@ -29,6 +32,7 @@ "zustand": "^5.0.1" }, "devDependencies": { + "@capacitor/assets": "^3.0.5", "@tailwindcss/postcss": "^4.3.1", "@types/leaflet": "^1.9.12", "@types/react": "^18.3.12", diff --git a/frontend/src/components/CommentModal.tsx b/frontend/src/components/CommentModal.tsx index 06c3d28..e4e6ac0 100644 --- a/frontend/src/components/CommentModal.tsx +++ b/frontend/src/components/CommentModal.tsx @@ -1,6 +1,7 @@ import React, { useState, useEffect } from 'react'; import { X, Send, MessageSquare, User, Loader2, Trash2 } from 'lucide-react'; import { io } from 'socket.io-client'; +import { Capacitor } from '@capacitor/core'; import { useTourStore } from '@/store/useTourStore'; import { ConfirmModal } from './ConfirmModal'; @@ -69,7 +70,9 @@ export const CommentModal: React.FC = ({ isOpen, onClose, loc fetchComments(); // Lắng nghe bình luận mới qua Proxy (không cần hardcode URL) - const socket = io(); + const socket = Capacitor.isNativePlatform() + ? io(import.meta.env.VITE_BACKEND_URL || 'https://yotrip.labz.io.vn') + : io(); socket.emit('joinTour', 'global'); // Hoặc logic join cụ thể socket.on('commentAdded', (newCommentData: any) => { diff --git a/frontend/src/components/JoinTourLoginModal.tsx b/frontend/src/components/JoinTourLoginModal.tsx index 6cef8b3..db8742c 100644 --- a/frontend/src/components/JoinTourLoginModal.tsx +++ b/frontend/src/components/JoinTourLoginModal.tsx @@ -1,6 +1,8 @@ import React, { useState, useEffect, useRef } from 'react'; import { X, Mail, Lock, ArrowRight, Loader2, LogIn } from 'lucide-react'; import { useNotification } from '@/hooks/useNotification'; +import { Capacitor } from '@capacitor/core'; +import { GoogleAuth } from '@codetrix-studio/capacitor-google-auth'; interface JoinTourLoginModalProps { isOpen: boolean; @@ -119,6 +121,88 @@ export const JoinTourLoginModal: React.FC = ({ } }; + const handleNativeGoogleLogin = async () => { + setError(''); + setIsLoading(true); + try { + const user = await GoogleAuth.signIn(); + console.log('[JoinTourLogin] Native Google user received:', user); + + const idToken = user.authentication.idToken; + if (!idToken) { + throw new Error('Không nhận được token từ Google.'); + } + + const response = await fetch(`/api/v1/auth/google`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ credential: idToken }), + }); + + const data = await response.json(); + if (!response.ok) { + throw new Error(data.message || 'Đăng nhập Google thất bại'); + } + + localStorage.setItem('token', data.access_token); + localStorage.setItem('user', JSON.stringify(data.user)); + + let currentToken = inviteTokenRef.current || localStorage.getItem('pendingInviteToken'); + if (!currentToken) { + const params = new URLSearchParams(window.location.search); + const urlToken = params.get('token'); + if (urlToken) { + currentToken = urlToken; + localStorage.setItem('pendingInviteToken', urlToken); + } + } + + if (!currentToken) { + throw new Error('Không có mã lời mời để tham gia tour'); + } + + try { + const joinRes = await fetch(`/api/v1/tours/join-by-token`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${data.access_token}`, + }, + body: JSON.stringify({ token: currentToken }), + }); + + const joinData = await joinRes.json().catch(() => ({})); + + if (joinRes.ok) { + notify({ + title: 'Thành công', + message: joinData.message || 'Bạn đã gia nhập tour!', + type: 'success' + }); + localStorage.removeItem('pendingInviteToken'); + if (onJoinSuccess) { + onJoinSuccess(data.user, joinData); + } + onClose(); + } else { + localStorage.removeItem('pendingInviteToken'); + const errorMessage = joinData.message || `Không thể gia nhập tour (HTTP ${joinRes.status})`; + setError(`Lỗi gia nhập tour: ${errorMessage}`); + setIsLoading(false); + } + } catch (e: any) { + setError(`Lỗi khi gia nhập: ${e.message}`); + setIsLoading(false); + } + } catch (err: any) { + console.error('[JoinTourLogin] Native Google login error:', err); + if (err?.message !== 'Sign in window was closed' && err?.message !== '12501') { + setError(err.message || 'Đăng nhập Google thất bại'); + } + setIsLoading(false); + } + }; + const handleEmailPasswordJoin = async (e: React.FormEvent) => { e.preventDefault(); setError(''); @@ -182,7 +266,7 @@ export const JoinTourLoginModal: React.FC = ({ }; useEffect(() => { - if (!isOpen) return; + if (!isOpen || Capacitor.isNativePlatform()) return; const timer = setTimeout(() => { if (typeof window !== 'undefined' && (window as any).google) { @@ -241,10 +325,27 @@ export const JoinTourLoginModal: React.FC = ({ )} - {/* Google OAuth Button */} -
-
-
+ {Capacitor.isNativePlatform() ? ( +
+ +
+ ) : ( +
+
+
+ )}
diff --git a/frontend/src/components/LoginModal.tsx b/frontend/src/components/LoginModal.tsx index 4974729..9c9cce3 100644 --- a/frontend/src/components/LoginModal.tsx +++ b/frontend/src/components/LoginModal.tsx @@ -1,5 +1,7 @@ import React, { useState, useEffect } from 'react'; import { X, Mail, Lock, ArrowRight, Loader2 } from 'lucide-react'; +import { Capacitor } from '@capacitor/core'; +import { GoogleAuth } from '@codetrix-studio/capacitor-google-auth'; interface LoginModalProps { isOpen: boolean; @@ -101,8 +103,70 @@ export const LoginModal: React.FC = ({ isOpen, onClose, onSwitc } }; + const handleNativeGoogleLogin = async () => { + setError(''); + setIsLoading(true); + try { + const user = await GoogleAuth.signIn(); + console.log('[Native OAuth] Google user received:', user); + + const idToken = user.authentication.idToken; + if (!idToken) { + throw new Error('Không nhận được token từ Google.'); + } + + const response = await fetch(`/api/v1/auth/google`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ credential: idToken }), + }); + + const data = await response.json(); + if (!response.ok) { + throw new Error(data.message || 'Đăng nhập Google thất bại'); + } + + localStorage.setItem('token', data.access_token); + localStorage.setItem('user', JSON.stringify(data.user)); + localStorage.removeItem('guest_token'); + localStorage.removeItem('guest_user'); + console.log('[Native OAuth] Google login successful'); + + let pendingInviteToken = localStorage.getItem('pendingInviteToken'); + if (pendingInviteToken) { + try { + const joinRes = await fetch(`/api/v1/tours/join-by-token`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${data.access_token}`, + }, + body: JSON.stringify({ token: pendingInviteToken }), + }); + if (joinRes.ok) { + localStorage.removeItem('pendingInviteToken'); + } + } catch (joinErr) { + console.error('[Native OAuth] Join tour after login failed:', joinErr); + } + } + + if (onLoginSuccess) { + onLoginSuccess(data.user); + } + onClose(); + } catch (err: any) { + console.error('[Native OAuth] Error:', err); + if (err?.message !== 'Sign in window was closed' && err?.message !== '12501') { + setError(err.message || 'Đăng nhập Google thất bại'); + } + } finally { + setIsLoading(false); + } + }; + useEffect(() => { - if (!isOpen) return; + if (!isOpen || Capacitor.isNativePlatform()) return; const timer = setTimeout(() => { if (typeof window !== 'undefined' && (window as any).google) { @@ -244,7 +308,23 @@ export const LoginModal: React.FC = ({ isOpen, onClose, onSwitc Hoặc
-
+ {Capacitor.isNativePlatform() ? ( + + ) : ( +
+ )}

diff --git a/frontend/src/components/PhotoTaker.tsx b/frontend/src/components/PhotoTaker.tsx new file mode 100644 index 0000000..fa8049c --- /dev/null +++ b/frontend/src/components/PhotoTaker.tsx @@ -0,0 +1,73 @@ +import React, { useState } from 'react'; +import { Camera as CameraIcon, X } from 'lucide-react'; +import { Camera, CameraResultType, CameraSource } from '@capacitor/camera'; +import { useNotification } from '@/hooks/useNotification'; + +interface PhotoTakerProps { + onPhotoTaken?: (webPath: string) => void; + onClose?: () => void; +} + +export const PhotoTaker: React.FC = ({ onPhotoTaken, onClose }) => { + const [photoUri, setPhotoUri] = useState(); + const notify = useNotification(); + + const takeAndSavePhoto = async () => { + try { + const image = await Camera.getPhoto({ + quality: 90, + allowEditing: false, // Giữ nguyên ảnh gốc, không qua chỉnh sửa + resultType: CameraResultType.Uri, + source: CameraSource.Camera, // Mở camera trực tiếp + saveToGallery: true, // Tự động lưu ảnh gốc vào thư viện điện thoại + }); + + setPhotoUri(image.webPath); + notify({ + title: 'Thành công', + message: 'Ảnh đã được chụp và tự động lưu vào thư viện điện thoại.', + type: 'success' + }); + if (onPhotoTaken && image.webPath) { + onPhotoTaken(image.webPath); + } + } catch (error: any) { + console.error('Lỗi khi chụp hoặc lưu ảnh:', error); + if (error?.message !== 'User cancelled photos app') { + notify({ + title: 'Lỗi chụp ảnh', + message: 'Không thể truy cập camera hoặc lưu ảnh.', + type: 'error' + }); + } + } + }; + + return ( +

+
+

Chụp ảnh hành trình

+ {onClose && ( + + )} +
+ + + + {photoUri && ( +
+

Xem trước ảnh vừa chụp:

+ Xem trước ảnh chụp +
+ )} +
+ ); +}; diff --git a/frontend/src/components/PublicPhotoModal.tsx b/frontend/src/components/PublicPhotoModal.tsx index 130e158..da0ffa3 100644 --- a/frontend/src/components/PublicPhotoModal.tsx +++ b/frontend/src/components/PublicPhotoModal.tsx @@ -1,6 +1,7 @@ import React, { useState, useEffect, useRef } from 'react'; import { X, Send, MessageSquare, User, Loader2, Calendar, MapPin, Download, Edit, Heart, Trash2 } from 'lucide-react'; import { io } from 'socket.io-client'; +import { Capacitor } from '@capacitor/core'; import { CoordinateSelectModal } from './CoordinateSelectModal'; import { useTranslation } from '../hooks/useTranslation'; import { useConfirm } from '../hooks/useConfirm'; @@ -246,7 +247,9 @@ export const PublicPhotoModal: React.FC = ({ fetchComments(); - const socket = io(); + const socket = Capacitor.isNativePlatform() + ? io(import.meta.env.VITE_BACKEND_URL || 'https://yotrip.labz.io.vn') + : io(); socket.emit('joinPhoto', photo.id); socket.on('photoCommentAdded', (newCommentData: any) => { diff --git a/frontend/src/components/TourChat.tsx b/frontend/src/components/TourChat.tsx index 2a3e8c2..d69b0db 100644 --- a/frontend/src/components/TourChat.tsx +++ b/frontend/src/components/TourChat.tsx @@ -1,5 +1,7 @@ import React, { useState, useEffect, useRef } from 'react'; import { io, Socket } from 'socket.io-client'; +import { Capacitor } from '@capacitor/core'; +import { Camera, CameraResultType, CameraSource } from '@capacitor/camera'; import { Send, MessageSquare, Loader2, Image as ImageIcon, MapPin, Download, X } from 'lucide-react'; import { useNotification } from '@/hooks/useNotification'; @@ -87,7 +89,7 @@ export const TourChat: React.FC = ({ tourId, embedded = false }) }, []); const filteredParticipants = participants.filter(p => - p.name.toLowerCase().includes(mentionSearch.toLowerCase()) + p && p.name && p.name.toLowerCase().includes(mentionSearch.toLowerCase()) ); const handleInputChange = (e: React.ChangeEvent) => { @@ -179,7 +181,9 @@ export const TourChat: React.FC = ({ tourId, embedded = false }) // Connect to socket and listen for tour messages useEffect(() => { - const socket = io(); + const socket = Capacitor.isNativePlatform() + ? io(import.meta.env.VITE_BACKEND_URL || 'https://yotrip.labz.io.vn') + : io(); socketRef.current = socket; socket.on('connect', () => { @@ -252,6 +256,41 @@ export const TourChat: React.FC = ({ tourId, embedded = false }) }); }; + // Handle Native Image Selection (Camera or Library) + const handleNativePhotoSelect = async () => { + try { + const image = await Camera.getPhoto({ + quality: 90, + allowEditing: false, + resultType: CameraResultType.Uri, + source: CameraSource.Prompt, // Prompt selection + saveToGallery: true, // Auto-save original photo to device gallery + promptLabelHeader: 'Chọn hình ảnh', + promptLabelPhoto: 'Chọn từ thư viện', + promptLabelPicture: 'Chụp ảnh mới (Camera)' + }); + + if (image && image.webPath) { + setImagePreview(image.webPath); + + // Convert Capacitor webPath resource back to standard File instance + const response = await fetch(image.webPath); + const blob = await response.blob(); + const file = new File([blob], `photo.${image.format}`, { type: `image/${image.format}` }); + setSelectedImage(file); + } + } catch (error: any) { + console.error('Lỗi chọn ảnh native:', error); + if (error?.message !== 'User cancelled photos app' && error?.message !== 'User cancelled camera') { + notify({ + title: 'Lỗi', + message: 'Không thể truy cập máy ảnh hoặc thư viện ảnh.', + type: 'error' + }); + } + } + }; + // Handle Image Selection const handleImageChange = (e: React.ChangeEvent) => { const file = e.target.files?.[0]; @@ -613,7 +652,7 @@ export const TourChat: React.FC = ({ tourId, embedded = false }) {/* Attach photo button */} - - + +
{/* Emergency Share Button - moved to bottom */} @@ -1502,31 +1501,28 @@ return (
)} {activeChatUser.avatar ? ( - {activeChatUser.name} ) : ( @@ -1931,20 +1924,19 @@ return ( chatMessages.map((msg) => { const isMe = msg.senderId === user.id; return ( -
-
+ }`}> {msg.attachmentUrl && (
- Đính kèm -
- - {/* Modal Body */} -
-
-

{sharingTour.title}

-

{t('emergencyShareTooltip')}

-
- - {loadingShare ? ( -
- - {t('loading')} -
- ) : ( - <> - {/* Share Activation Toggle */} -
-
- Kích hoạt đường dẫn cứu hộ + {/* Emergency Share Configuration Modal */} + {sharingTour && ( +
+
+ {/* Modal Header */} +
+
+ +

{t('emergencyShare')}

- {shareStatus && ( - + +
+ + {/* Modal Body */} +
+
+

{sharingTour.title}

+

{t('emergencyShareTooltip')}

+
+ + {loadingShare ? ( +
+ + {t('loading')} +
+ ) : ( + <> + {/* Share Activation Toggle */} +
+
+ Kích hoạt đường dẫn cứu hộ +
+ {shareStatus && ( + + )} +
+ + {shareStatus?.isEnabled && ( + <> + {/* Configuration: Language and Theme selectors */} +
+
+ + +
+ +
+ + +
+
+ + {/* Shareable Link Input with Copy button */} +
+
Đường dẫn khẩn cấp:
+
+ + +
+
+ + )} + )}
- {shareStatus?.isEnabled && ( - <> - {/* Configuration: Language and Theme selectors */} -
-
- - -
- -
- - -
-
- - {/* Shareable Link Input with Copy button */} -
-
Đường dẫn khẩn cấp:
-
- - -
-
- - )} - - )} -
- - {/* Modal Footer */} -
- -
-
-
- )} + {/* Modal Footer */} +
+ +
+
+
+ )}
diff --git a/frontend/src/pages/SignupPage.tsx b/frontend/src/pages/SignupPage.tsx index f1d2e08..3b6ca8a 100644 --- a/frontend/src/pages/SignupPage.tsx +++ b/frontend/src/pages/SignupPage.tsx @@ -1,5 +1,7 @@ import React, { useState, useEffect } from 'react'; import { User, Mail, Lock, ArrowRight, ChevronLeft, Phone, MapPin, ShieldCheck } from 'lucide-react'; +import { Capacitor } from '@capacitor/core'; +import { GoogleAuth } from '@codetrix-studio/capacitor-google-auth'; interface SignupPageProps { onBack: () => void; @@ -69,8 +71,67 @@ const SignupPage: React.FC = ({ onBack, onSuccess }) => { } }; + const handleNativeGoogleLogin = async () => { + setError(''); + setIsLoading(true); + try { + const user = await GoogleAuth.signIn(); + console.log('[SignupPage] Native Google user received:', user); + + const idToken = user.authentication.idToken; + if (!idToken) { + throw new Error('Không nhận được token từ Google.'); + } + + const response = await fetch(`/api/v1/auth/google`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ credential: idToken }), + }); + + const data = await response.json(); + if (!response.ok) { + throw new Error(data.message || 'Đăng ký bằng Google thất bại'); + } + + localStorage.removeItem('guest_token'); + localStorage.removeItem('guest_user'); + localStorage.setItem('token', data.access_token); + localStorage.setItem('user', JSON.stringify(data.user)); + + const pendingInviteToken = localStorage.getItem('pendingInviteToken'); + if (pendingInviteToken) { + console.log('[SignupPage] Auto-joining tour with pending token after Google signup'); + await fetch(`/api/v1/tours/join-by-token`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${data.access_token}`, + }, + body: JSON.stringify({ token: pendingInviteToken }), + }).then(res => { + if (res.ok) { + localStorage.removeItem('pendingInviteToken'); + console.log('[SignupPage] Successfully joined tour after Google signup'); + } else { + console.warn('[SignupPage] Failed to join tour after Google signup:', res.status); + } + }).catch((e) => console.error('Lỗi tự động gia nhập:', e)); + } + + onSuccess(); + } catch (err: any) { + console.error('[SignupPage] Native Google signup error:', err); + if (err?.message !== 'Sign in window was closed' && err?.message !== '12501') { + setError(err.message || 'Đăng ký bằng Google thất bại'); + } + } finally { + setIsLoading(false); + } + }; + useEffect(() => { - if (step !== 'form') return; + if (step !== 'form' || Capacitor.isNativePlatform()) return; const timer = setTimeout(() => { if (typeof window !== 'undefined' && (window as any).google) { @@ -313,7 +374,23 @@ const SignupPage: React.FC = ({ onBack, onSuccess }) => { Hoặc
-
+ {Capacitor.isNativePlatform() ? ( + + ) : ( +
+ )} )}
diff --git a/frontend/src/pages/TourDetailPage.tsx b/frontend/src/pages/TourDetailPage.tsx index 60bfeeb..eebea28 100644 --- a/frontend/src/pages/TourDetailPage.tsx +++ b/frontend/src/pages/TourDetailPage.tsx @@ -1,5 +1,6 @@ import React, { useState, useEffect, useMemo, useRef } from 'react'; import { io } from 'socket.io-client'; +import { Capacitor } from '@capacitor/core'; import { jsPDF } from 'jspdf'; import autoTable from 'jspdf-autotable'; import { robotoBase64, robotoBoldBase64 } from '../utils/pdfFont'; @@ -1571,7 +1572,9 @@ export const TourDetailPage = ({ useEffect(() => { if (!currentTour) return; - const socket = io(); // Kết nối qua Proxy của Vite (cùng origin) + const socket = Capacitor.isNativePlatform() + ? io(import.meta.env.VITE_BACKEND_URL || 'https://yotrip.labz.io.vn') + : io(); // Kết nối qua Proxy của Vite (cùng origin) socket.on('connect', () => { socket.emit('joinTour', currentTour.id); @@ -1910,7 +1913,7 @@ export const TourDetailPage = ({
{/* Top Navigation Bar */}
-

@@ -2210,7 +2213,7 @@ export const TourDetailPage = ({ {/* Main Content Area - Điều chỉnh max-width khi ở chế độ bản đồ */}
{/* Tab Switcher */} - {!(activeTab === 'plan' && viewMode === 'map') && ( + {!(activeTab === 'plan' && viewMode === 'map') && activeTab !== 'chat' && (
{tabs.map((tab) => ( +``` + +--- + +#### [MODIFY] [SignupPage.tsx](file:///home/locpham/travelplanning/frontend/src/pages/SignupPage.tsx) +- Thêm nút "Đăng ký nhanh bằng Google" tương tự ở phần `step === 'form'`. +- Cùng hành động: redirect sang `/api/v1/auth/google`. + +--- + +## Sơ đồ kiến trúc + +```mermaid +sequenceDiagram + participant U as Người dùng + participant FE as Frontend + participant BE as Backend + participant G as Google OAuth + + U->>FE: Nhấn "Đăng nhập bằng Google" + FE->>BE: GET /api/v1/auth/google + BE->>G: Redirect → Google Consent Screen + G->>U: Hiển thị chọn tài khoản + U->>G: Chọn tài khoản + G->>BE: Callback với authorization code + BE->>G: Exchange code → access_token + BE->>G: GET profile (googleId, email, name, avatar) + BE->>BE: Upsert User (tìm theo googleId hoặc email) + BE->>BE: Tạo JWT + BE->>FE: Redirect về /auth/callback?token=...&user=... + FE->>FE: Lưu token/user vào localStorage + FE->>U: Điều hướng vào Explore Map +``` + +--- + +## Verification Plan + +### Automated Tests +- `npm run build -w frontend` để kiểm tra TypeScript. + +### Manual Verification +1. Nhấn "Đăng nhập bằng Google" → chọn tài khoản Google → kiểm tra được điều hướng vào app. +2. Đăng xuất → đăng nhập lại bằng cùng tài khoản Google → kiểm tra không bị tạo user mới. +3. Người dùng đã có tài khoản email trùng → kiểm tra được hợp nhất (merge) với tài khoản hiện có. +4. Kiểm tra `googleId` được lưu vào DB. diff --git a/implementation_plan.md b/implementation_plan.md new file mode 100644 index 0000000..813f0b0 --- /dev/null +++ b/implementation_plan.md @@ -0,0 +1,78 @@ +# Plan to Implement Like-Based Photo Selection, Displays, and Mobile UI Enhancements + +Highlight photos with more likes across different displays and optimize the public photo modal's mobile UI layout. + +--- + +## Proposed Changes + +### 1. Public Photo Modal Mobile UI Layout & Fixes + +#### [MODIFY] [PublicPhotoModal.tsx](file:///home/locpham/travelplanning/frontend/src/components/PublicPhotoModal.tsx) +- **Fixed Photo and Scrollable Comments**: + - Remove parent modal container vertical scrolling on mobile (`overflow-y-auto` -> `overflow-hidden`). + - Set the parent container layout to `flex flex-col h-[90vh] md:h-[85vh] overflow-hidden`. + - The photo container (`h-[45vh]`) and mobile uploader details container will remain static/fixed at the top. + - Set the comments panel container to `flex-1 min-h-0 flex flex-col` and the comments list to `flex-1 overflow-y-auto` so only the comments scroll on mobile. +- **Remove Comments Header on Mobile**: + - Add responsive classes to the comment header (`hidden md:block`) to hide the "Bình luận cộng đồng" / "bình luận từ cộng đồng" title banner on mobile, maximizing space. +- **Style Uploader Details**: + - Reduce font size and remove bold style for the "Người đăng" (Uploader name) elements. + - Desktop uploader text: change from `font-semibold` to `text-xs`. + - Mobile uploader text: change from `text-sm font-semibold text-white` to `text-xs text-slate-200`. +- **Safe Area Like Button**: + - Shift the absolute top position of the Like button to account for safe area insets: `top-[calc(1rem+env(safe-area-inset-top,0px))]`. This prevents browser address bars or notches from clipping it. + +--- + +### 2. Landing Page Like-Based Sorting + +#### [MODIFY] [LandingPage.tsx](file:///home/locpham/travelplanning/frontend/src/pages/LandingPage.tsx) +- In `fetchPublicPhotos`, sort the fetched `publicPhotos` array based on the number of likes (`likedUserIds.length`) in descending order. +- This ensures: + 1. The cycling background images naturally present the most-liked public photos first. + 2. The gallery previews at the bottom display the top 8 most-liked photos from the community. + +--- + +### 3. Tour Banner Image Selection + +#### [MODIFY] [TourDetailPage.tsx](file:///home/locpham/travelplanning/frontend/src/pages/TourDetailPage.tsx) +- Replace the simple first-photo check for `coverImage` with a function that retrieves the photo with the most likes in the tour. +- If there are no photos in the tour, fall back to the default Unsplash placeholder cover image. + +--- + +### 4. Auto-Select Most Liked Photo for Display + +#### [MODIFY] [ExploreMap.tsx](file:///home/locpham/travelplanning/frontend/src/pages/ExploreMap.tsx) +- In `groupedPhotos` memo, sort each location's photo array so that photos with the most likes appear first. +- This ensures the map pin thumbnail bubble displays the most-liked photo at that coordinate, and clicking it displays that photo as the default. + +#### [MODIFY] [MyPhotosPage.tsx](file:///home/locpham/travelplanning/frontend/src/pages/MyPhotosPage.tsx) +- Define a helper function `getMostLikedPhoto(photos)` that returns the photo with the most likes. +- In the `useEffect` that updates `selectedPhotoForDisplay`, use this helper to default-select the most liked photo from the filtered list. + +#### [MODIFY] [TourDetailPage.tsx](file:///home/locpham/travelplanning/frontend/src/pages/TourDetailPage.tsx) +- Define the same helper function `getMostLikedPhoto(photos)`. +- Update the default photo selection inside both `useEffect` hooks (the one listening to `filteredPhotos` and the one listening to `currentTour`/`legs` changes) to select the photo with the most likes using this helper. + +--- + +## Verification Plan + +### Automated Tests +- Run `npm run build -w frontend` to verify that there are no compilation or syntax errors. + +### Manual Verification +- **Mobile Public Photo Modal**: + 1. View a photo on mobile browser/emulator. + 2. Verify only the comments scroll, and the header label is hidden. + 3. Verify the Like button position is adjusted. + 4. Verify the Uploader text size is reduced and normal. +- **Landing Page background & gallery**: + 1. Open the landing page. Verify that the previews show the most liked photos first. +- **Tour Banner**: + 1. Verify the tour banner updates to show the photo with the most likes. +- **Map Marker / Photo Timeline default**: + 1. Click map marker and verify most liked photo is active by default. diff --git a/implementation_plan_11features.md b/implementation_plan_11features.md new file mode 100644 index 0000000..a158eff --- /dev/null +++ b/implementation_plan_11features.md @@ -0,0 +1,166 @@ +# Kế hoạch triển khai: 11 Tính năng mới (Bổ sung Đa ngôn ngữ & Giao diện Sáng/Tối) + +Kế hoạch này phác thảo các thay đổi về cơ sở dữ liệu (Prisma Schema), các API Endpoint ở Backend (NestJS), các Component và trang ở Frontend (React) để hiện thực hóa 11 tính năng mới theo yêu cầu của bạn. + +--- + +## User Review Required + +> [!IMPORTANT] +> - **Hệ thống đa ngôn ngữ (Tiếng Việt, Tiếng Anh, Tiếng Trung)**: Chúng tôi sẽ triển khai một hook dịch thuật tinh gọn `useTranslation` dạng từ điển lưu trữ cục bộ ở Frontend. Toàn bộ các tiêu đề, nhãn (labels), nút bấm, thông báo toast và văn bản tĩnh trong dự án sẽ được ánh xạ qua hook này dựa trên ngôn ngữ được lựa chọn. Lựa chọn ngôn ngữ sẽ được lưu trữ vào `localStorage` để giữ trạng thái sau khi tải lại trang. +> - **Cơ chế Giao diện Sáng / Tối (Light / Dark Theme)**: Hệ thống sẽ hỗ trợ 3 tùy chọn: Sáng (Light), Tối (Dark), và theo Hệ thống (System default, sử dụng `prefers-color-scheme` để tự động đổi màu theo hệ điều hành). Chúng tôi sẽ tích hợp các class tiện ích Tailwind CSS v4 (như `dark:bg-slate-900`, `dark:text-slate-100`, `dark:border-slate-800`) vào tất cả các component chính để giao diện đồng bộ hoàn hảo. +> - **Cơ chế lọc ảnh khiêu dâm & Làm mờ khuôn mặt**: Xử lý trực tiếp trên trình duyệt (Client-side) qua TensorFlow.js trước khi tải lên để đảm bảo tối ưu hiệu năng backend. +> - **Tài khoản Admin quản trị hệ thống bằng Secret Key**: Đọc `ADMIN_SECRET_KEY` từ `.env`. Ở Frontend, người dùng có thể kích hoạt quyền Admin tức thời bằng cách nhập chuỗi khóa này để được promote lên `isAdmin: true` trong Database. + +--- + +## Open Questions + +> [!WARNING] +> - Bạn có muốn lưu tùy chọn ngôn ngữ trực tiếp vào tài khoản User ở Database (để khi đăng nhập trên thiết bị khác thì tự động áp dụng ngôn ngữ đó) hay chỉ cần lưu ở `localStorage` trình duyệt là đủ? +> - Ngoài các ngôn ngữ Tiếng Việt, Tiếng Anh, Tiếng Trung, bạn có dự định mở rộng thêm ngôn ngữ nào khác trong tương lai không? + +--- + +## Proposed Changes + +### 1. Thay đổi Cơ sở dữ liệu (`backend/prisma/schema.prisma`) + +Chúng ta sẽ bổ sung các Model mới để lưu trữ: Bộ lọc từ khóa, Cấu hình bộ lọc hình ảnh, Đánh giá Owner, và Mã chia sẻ hành trình. + +#### [MODIFY] [schema.prisma](file:///home/locpham/travelplanning/backend/prisma/schema.prisma) +- Thêm model `WordFilter` để cấu hình bộ lọc từ khóa cấm. +- Thêm model `ModerationSetting` để lưu trạng thái cấu hình lọc của hệ thống. +- Thêm model `TourRating` để lưu đánh giá 5 sao cho Owner/Manager từ các thành viên. +- Thêm model `TourShare` để quản lý liên kết chia sẻ hành trình khẩn cấp. + +```prisma +model WordFilter { + id String @id @default(uuid()) + word String @unique + replacement String + createdAt DateTime @default(now()) +} + +model ModerationSetting { + id String @id @default(uuid()) + blockNsfw Boolean @default(false) + blurFaces Boolean @default(false) +} + +model TourRating { + id String @id @default(uuid()) + tourId String + targetUserId String // Owner/Manager được đánh giá + raterUserId String // Người dùng thực hiện đánh giá + honesty Int @default(5) + transparency Int @default(5) + enthusiasm Int @default(5) + cheerfulness Int @default(5) + seriousness Int @default(5) + planning Int @default(5) + survival Int @default(5) + averageScore Float @default(5.0) + comment String? @db.Text + createdAt DateTime @default(now()) + + tour Tour @relation(fields: [tourId], references: [id], onDelete: Cascade) + targetUser User @relation("RatedUser", fields: [targetUserId], references: [id], onDelete: Cascade) + raterUser User @relation("RatingUser", fields: [raterUserId], references: [id], onDelete: Cascade) + + @@unique([tourId, targetUserId, raterUserId]) +} + +model TourShare { + id String @id @default(uuid()) + tourId String @unique + token String @unique @default(uuid()) + isEnabled Boolean @default(true) + createdAt DateTime @default(now()) + + tour Tour @relation(fields: [tourId], references: [id], onDelete: Cascade) +} +``` + +--- + +### 2. Backend API (`backend/src/main.ts`) + +#### [MODIFY] [main.ts](file:///home/locpham/travelplanning/backend/src/main.ts) +- Triển khai đầy đủ các endpoint như trong kế hoạch trước đó: + - `DELETE /api/v1/public-photos/comments/:id` + - Tích hợp bộ lọc từ khóa cấm ở các API tạo bài đăng/tin nhắn/bình luận. + - API cài đặt bộ lọc ảnh và text của Admin (`/api/v1/admin/moderation`, `/api/v1/admin/word-filters`). + - API Đánh giá Owner/Manager (`/api/v1/tours/:tourId/ratings`) và Leaderboard uy tín (`/api/v1/users/trusted`). + - API kích hoạt Admin bằng secret key (`/api/v1/auth/promote-admin`). + - API chia sẻ hành trình khẩn cấp (`/api/v1/tours/:tourId/share`, `/api/v1/tours/share/:token`). + +--- + +### 3. Giao diện Frontend (`frontend/src/...`) + +#### [NEW] [useTranslation.ts](file:///home/locpham/travelplanning/frontend/src/hooks/useTranslation.ts) +- Tạo hook `useTranslation` để quản lý đa ngôn ngữ: + - Khai báo từ điển ngôn ngữ `translations` chứa các bản dịch Tiếng Việt (VI), Tiếng Anh (EN), Tiếng Trung (ZH). + - Hỗ trợ hàm dịch `t(key: string)` và các hàm chuyển đổi ngôn ngữ, tự động đồng bộ giá trị với `localStorage.getItem('language')`. + - Dịch các tiêu đề, thông báo, nhãn, nút bấm trong toàn bộ ứng dụng sang 3 ngôn ngữ tương ứng. + +#### [NEW] [useTheme.ts](file:///home/locpham/travelplanning/frontend/src/hooks/useTheme.ts) +- Tạo hook `useTheme` để quản lý giao diện Sáng/Tối: + - Quản lý trạng thái theme (`light`, `dark`, `system`). + - Nếu chọn `system`, sử dụng `window.matchMedia('(prefers-color-scheme: dark)')` để lắng nghe thiết lập hệ điều hành của người dùng. + - Thêm hoặc xóa class `dark` khỏi thẻ `` hoặc `` của ứng dụng. + +#### [MODIFY] [index.css](file:///home/locpham/travelplanning/frontend/src/index.css) +- Bổ sung cấu hình màu sắc cơ bản cho dark mode: định nghĩa các màu nền và chữ chính cho chế độ tối để đảm bảo độ tương phản tốt nhất và đồng bộ thiết kế. + +#### [MODIFY] [ExploreMap.tsx](file:///home/locpham/travelplanning/frontend/src/pages/ExploreMap.tsx), [MemberDashboard.tsx](file:///home/locpham/travelplanning/frontend/src/pages/MemberDashboard.tsx), [LandingPage.tsx](file:///home/locpham/travelplanning/frontend/src/pages/LandingPage.tsx) +- **Tích hợp Dark Mode**: Thêm các lớp CSS Tailwind như `dark:bg-slate-900`, `dark:text-slate-100`, `dark:border-slate-800` vào tất cả các card, danh sách và thanh điều hướng chính. +- **Thanh công cụ**: Thêm dropdown lựa chọn ngôn ngữ (Tiếng Việt, Tiếng Anh, Tiếng Trung) và nút chuyển đổi giao diện (Sáng/Tối/Hệ thống) ở Sidebar/Header cạnh nút Đăng xuất hoặc trong Menu Cài đặt của Admin. +- **Áp dụng Đa ngôn ngữ**: Thay thế toàn bộ text hiển thị tĩnh bằng hàm dịch `t('key')` (ví dụ: `t('explore')`, `t('logout')`, `t('trusted_members')`,...). + +#### [MODIFY] [ItineraryTimeline.tsx](file:///home/locpham/travelplanning/frontend/src/components/ItineraryTimeline.tsx) +- Bổ sung click vào địa điểm để xem trên bản đồ (gọi `onNavigate`). +- Áp dụng các lớp Tailwind chế độ tối (`dark:bg-slate-900`, `dark:border-slate-800`, `dark:text-gray-100`) để các thẻ chặng và chi tiết điểm dừng hiển thị hoàn hảo ở Dark Mode. +- Thay các đoạn text tĩnh bằng đa ngôn ngữ. + +#### [MODIFY] [TourDetailPage.tsx](file:///home/locpham/travelplanning/frontend/src/pages/TourDetailPage.tsx) +- Thêm nút "Xuất PDF" (không kèm chi phí) sử dụng `html2pdf.js` từ CDN. +- Tích hợp Đánh giá ban tổ chức 5 sao. +- Tích hợp Đa ngôn ngữ và Dark Mode vào toàn bộ trang chi tiết hành trình. + +#### [MODIFY] [PublicPhotoModal.tsx](file:///home/locpham/travelplanning/frontend/src/components/PublicPhotoModal.tsx) +- Cho phép xóa bình luận nếu thỏa mãn điều kiện quyền hạn. +- Tích hợp Đa ngôn ngữ và Dark Mode. + +#### [MODIFY] [CoordinateSelectModal.tsx](file:///home/locpham/travelplanning/frontend/src/components/CoordinateSelectModal.tsx) +- Ẩn liên kết Leaflet bằng `attributionControl={false}`. +- Bổ sung ô tìm kiếm địa điểm tích hợp Nominatim API. +- Tích hợp Dark Mode (bảng kết quả tìm kiếm và các khung modal chuyển sang màu tối phù hợp). + +#### [NEW] [ShareJourneyPage.tsx](file:///home/locpham/travelplanning/frontend/src/pages/ShareJourneyPage.tsx) +- Giao diện chia sẻ khẩn cấp, tích hợp Đa ngôn ngữ và hỗ trợ cả 2 chế độ Sáng / Tối. + +#### [MODIFY] [App.tsx](file:///home/locpham/travelplanning/frontend/src/App.tsx) +- Cập nhật logic route `/journey/:token`. +- Bao bọc ứng dụng để khởi chạy ngôn ngữ và theme mặc định dựa trên cấu hình hệ thống / localStorage khi ứng dụng khởi chạy. + +--- + +## Verification Plan + +### Automated Tests +- Chạy biên dịch toàn bộ hệ thống để phát hiện lỗi TypeScript: + - Backend: `npm run build -w backend` + - Frontend: `npm run build -w frontend` hoặc chạy bộ kiểm tra TypeScript: `npx tsc --noEmit` ở thư mục frontend. + +### Manual Verification +1. **Kiểm tra Đa ngôn ngữ**: + - Chuyển đổi ngôn ngữ sang Tiếng Anh / Tiếng Trung. + - Xác nhận tất cả tiêu đề, nhãn, nút bấm, thông báo toast cập nhật sang ngôn ngữ mới. +2. **Kiểm tra Dark Mode**: + - Thay đổi cài đặt sang "Tối". Xác nhận toàn bộ giao diện (kể cả các card trắng ở dashboard, timeline, map modal) chuyển thành màu tối. + - Thay đổi cài đặt sang "Hệ thống". Đổi cấu hình hệ điều hành từ Light sang Dark và ngược lại để xác nhận trang web đổi giao diện tương ứng theo thời gian thực. +3. **Kiểm tra click địa điểm xem bản đồ**: Xác nhận click vào địa điểm timeline tự động nhảy sang tab bản đồ và hiển thị lộ trình. +4. **Kiểm tra xuất PDF**: Xác nhận xuất lịch trình ra file PDF không chứa thông tin chi phí. +5. **Kiểm tra xóa bình luận công khai, lọc ảnh & text, chia sẻ khẩn cấp, admin secret key**. diff --git a/package-lock.json b/package-lock.json index 5905daa..a907088 100644 --- a/package-lock.json +++ b/package-lock.json @@ -190,12 +190,6 @@ "fsevents": "2.3.3" } }, - "backend/node_modules/reflect-metadata": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz", - "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==", - "license": "Apache-2.0" - }, "backend/node_modules/undici-types": { "version": "6.21.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", @@ -207,9 +201,12 @@ "version": "1.0.0", "dependencies": { "@capacitor/android": "^8.4.1", + "@capacitor/camera": "^8.2.0", "@capacitor/cli": "^7.6.7", "@capacitor/core": "^8.4.1", "@capacitor/geolocation": "^8.2.0", + "@capacitor/local-notifications": "^8.2.0", + "@codetrix-studio/capacitor-google-auth": "^3.4.0-rc.4", "date-fns": "^4.4.0", "jspdf": "^4.2.1", "jspdf-autotable": "^5.0.8", @@ -225,6 +222,7 @@ "zustand": "^5.0.1" }, "devDependencies": { + "@capacitor/assets": "^3.0.5", "@tailwindcss/postcss": "^4.3.1", "@types/leaflet": "^1.9.12", "@types/react": "^18.3.12", @@ -509,6 +507,437 @@ "@capacitor/core": "^8.4.0" } }, + "node_modules/@capacitor/assets": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@capacitor/assets/-/assets-3.0.5.tgz", + "integrity": "sha512-ohz/OUq61Y1Fc6aVSt0uDrUdeOA7oTH4pkWDbv/8I3UrPjH7oPkzYhShuDRUjekNp9RBi198VSFdt0CetpEOzw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@capacitor/cli": "^5.3.0", + "@ionic/utils-array": "2.1.6", + "@ionic/utils-fs": "3.1.7", + "@trapezedev/project": "^7.0.10", + "commander": "8.3.0", + "debug": "4.3.4", + "fs-extra": "10.1.0", + "node-fetch": "2.7.0", + "node-html-parser": "5.4.2", + "sharp": "0.32.6", + "tslib": "2.6.2", + "yargs": "17.7.2" + }, + "bin": { + "capacitor-assets": "bin/capacitor-assets" + }, + "engines": { + "node": ">=10.3.0" + } + }, + "node_modules/@capacitor/assets/node_modules/@capacitor/cli": { + "version": "5.7.8", + "resolved": "https://registry.npmjs.org/@capacitor/cli/-/cli-5.7.8.tgz", + "integrity": "sha512-qN8LDlREMhrYhOvVXahoJVNkP8LP55/YPRJrzTAFrMqlNJC18L3CzgWYIblFPnuwfbH/RxbfoZT/ydkwgVpMrw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ionic/cli-framework-output": "^2.2.5", + "@ionic/utils-fs": "^3.1.6", + "@ionic/utils-subprocess": "^2.1.11", + "@ionic/utils-terminal": "^2.3.3", + "commander": "^9.3.0", + "debug": "^4.3.4", + "env-paths": "^2.2.0", + "kleur": "^4.1.4", + "native-run": "^2.0.0", + "open": "^8.4.0", + "plist": "^3.0.5", + "prompts": "^2.4.2", + "rimraf": "^4.4.1", + "semver": "^7.3.7", + "tar": "^6.1.11", + "tslib": "^2.4.0", + "xml2js": "^0.5.0" + }, + "bin": { + "cap": "bin/capacitor", + "capacitor": "bin/capacitor" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@capacitor/assets/node_modules/@capacitor/cli/node_modules/commander": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", + "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || >=14" + } + }, + "node_modules/@capacitor/assets/node_modules/@ionic/utils-process": { + "version": "2.1.11", + "resolved": "https://registry.npmjs.org/@ionic/utils-process/-/utils-process-2.1.11.tgz", + "integrity": "sha512-Uavxn+x8j3rDlZEk1X7YnaN6wCgbCwYQOeIjv/m94i1dzslqWhqIHEqxEyeE8HsT5Negboagg7GtQiABy+BLbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ionic/utils-object": "2.1.6", + "@ionic/utils-terminal": "2.3.4", + "debug": "^4.0.0", + "signal-exit": "^3.0.3", + "tree-kill": "^1.2.2", + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@capacitor/assets/node_modules/@ionic/utils-stream": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/@ionic/utils-stream/-/utils-stream-3.1.6.tgz", + "integrity": "sha512-4+Kitey1lTA1yGtnigeYNhV/0tggI3lWBMjC7tBs1K9GXa/q7q4CtOISppdh8QgtOhrhAXS2Igp8rbko/Cj+lA==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.0.0", + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@capacitor/assets/node_modules/@ionic/utils-subprocess": { + "version": "2.1.14", + "resolved": "https://registry.npmjs.org/@ionic/utils-subprocess/-/utils-subprocess-2.1.14.tgz", + "integrity": "sha512-nGYvyGVjU0kjPUcSRFr4ROTraT3w/7r502f5QJEsMRKTqa4eEzCshtwRk+/mpASm0kgBN5rrjYA5A/OZg8ahqg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ionic/utils-array": "2.1.6", + "@ionic/utils-fs": "3.1.7", + "@ionic/utils-process": "2.1.11", + "@ionic/utils-stream": "3.1.6", + "@ionic/utils-terminal": "2.3.4", + "cross-spawn": "^7.0.3", + "debug": "^4.0.0", + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@capacitor/assets/node_modules/@ionic/utils-terminal": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/@ionic/utils-terminal/-/utils-terminal-2.3.4.tgz", + "integrity": "sha512-cEiMFl3jklE0sW60r8JHH3ijFTwh/jkdEKWbylSyExQwZ8pPuwoXz7gpkWoJRLuoRHHSvg+wzNYyPJazIHfoJA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/slice-ansi": "^4.0.0", + "debug": "^4.0.0", + "signal-exit": "^3.0.3", + "slice-ansi": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "tslib": "^2.0.1", + "untildify": "^4.0.0", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@capacitor/assets/node_modules/brace-expansion": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.1.tgz", + "integrity": "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@capacitor/assets/node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/@capacitor/assets/node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/@capacitor/assets/node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@capacitor/assets/node_modules/glob": { + "version": "9.3.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-9.3.5.tgz", + "integrity": "sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "minimatch": "^8.0.2", + "minipass": "^4.2.4", + "path-scurry": "^1.6.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@capacitor/assets/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/@capacitor/assets/node_modules/minimatch": { + "version": "8.0.7", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-8.0.7.tgz", + "integrity": "sha512-V+1uQNdzybxa14e/p00HZnQNNcTjnRJjDxg2V8wtkjFctq4M7hXFws4oekyTP0Jebeq7QYtpFyOeBAjc88zvYg==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@capacitor/assets/node_modules/minipass": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.2.8.tgz", + "integrity": "sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=8" + } + }, + "node_modules/@capacitor/assets/node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@capacitor/assets/node_modules/minizlib/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@capacitor/assets/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@capacitor/assets/node_modules/node-addon-api": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-6.1.0.tgz", + "integrity": "sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@capacitor/assets/node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@capacitor/assets/node_modules/path-scurry/node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/@capacitor/assets/node_modules/rimraf": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-4.4.1.tgz", + "integrity": "sha512-Gk8NlF062+T9CqNGn6h4tls3k6T1+/nXdOcSZVikNVtlRdYpA7wRJJMoXmuvOnLW844rPjdQ7JgXCYM6PPC/og==", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^9.2.0" + }, + "bin": { + "rimraf": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@capacitor/assets/node_modules/sharp": { + "version": "0.32.6", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.32.6.tgz", + "integrity": "sha512-KyLTWwgcR9Oe4d9HwCwNM2l7+J0dUQwn/yf7S0EnTtb0eVS4RxO0eUSvxPtzT4F3SY+C4K6fqdv/DO27sJ/v/w==", + "dev": true, + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "color": "^4.2.3", + "detect-libc": "^2.0.2", + "node-addon-api": "^6.1.0", + "prebuild-install": "^7.1.1", + "semver": "^7.5.4", + "simple-get": "^4.0.1", + "tar-fs": "^3.0.4", + "tunnel-agent": "^0.6.0" + }, + "engines": { + "node": ">=14.15.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@capacitor/assets/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/@capacitor/assets/node_modules/tar": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", + "deprecated": "Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@capacitor/assets/node_modules/tar/node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=8" + } + }, + "node_modules/@capacitor/assets/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "dev": true, + "license": "0BSD" + }, + "node_modules/@capacitor/assets/node_modules/xml2js": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", + "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/@capacitor/assets/node_modules/xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/@capacitor/camera": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@capacitor/camera/-/camera-8.2.0.tgz", + "integrity": "sha512-hYfrT6xpL936qoEkIpJzSnb0fQCaTkOux1cXzGBfH8QLOGqr6gSLiWZlZz/fqMPmMKJMNRBqlTQkj5fuMhVZog==", + "license": "MIT", + "peerDependencies": { + "@capacitor/core": ">=8.0.0" + } + }, "node_modules/@capacitor/cli": { "version": "7.6.7", "resolved": "https://registry.npmjs.org/@capacitor/cli/-/cli-7.6.7.tgz", @@ -585,12 +1014,30 @@ "@capacitor/core": ">=8.0.0" } }, + "node_modules/@capacitor/local-notifications": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@capacitor/local-notifications/-/local-notifications-8.2.0.tgz", + "integrity": "sha512-fvLY0w2w4MiX+DD4+Wv4DOwOLdzKZsMDwAcRv/Juudd+QbKbn69s6cM3xVqPwAiDqfnqsY4/S8xtQD6M73wY2A==", + "license": "MIT", + "peerDependencies": { + "@capacitor/core": ">=8.0.0" + } + }, "node_modules/@capacitor/synapse": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/@capacitor/synapse/-/synapse-1.0.4.tgz", "integrity": "sha512-/C1FUo8/OkKuAT4nCIu/34ny9siNHr9qtFezu4kxm6GY1wNFxrCFWjfYx5C1tUhVGz3fxBABegupkpjXvjCHrw==", "license": "ISC" }, + "node_modules/@codetrix-studio/capacitor-google-auth": { + "version": "3.4.0-rc.4", + "resolved": "https://registry.npmjs.org/@codetrix-studio/capacitor-google-auth/-/capacitor-google-auth-3.4.0-rc.4.tgz", + "integrity": "sha512-d548/xKrbMHHbzMYSWnlgZCLZYo2zzY7Onrh3x8ujojrb7atkNK68I6su5WgmaBt4E+R4gxBsWams4554aOFjA==", + "license": "MIT", + "peerDependencies": { + "@capacitor/core": "^6.0.0" + } + }, "node_modules/@colors/colors": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", @@ -673,7 +1120,6 @@ "os": [ "aix" ], - "peer": true, "engines": { "node": ">=18" } @@ -691,7 +1137,6 @@ "os": [ "android" ], - "peer": true, "engines": { "node": ">=18" } @@ -709,7 +1154,6 @@ "os": [ "android" ], - "peer": true, "engines": { "node": ">=18" } @@ -727,7 +1171,6 @@ "os": [ "android" ], - "peer": true, "engines": { "node": ">=18" } @@ -745,7 +1188,6 @@ "os": [ "darwin" ], - "peer": true, "engines": { "node": ">=18" } @@ -763,7 +1205,6 @@ "os": [ "darwin" ], - "peer": true, "engines": { "node": ">=18" } @@ -781,7 +1222,6 @@ "os": [ "freebsd" ], - "peer": true, "engines": { "node": ">=18" } @@ -799,7 +1239,6 @@ "os": [ "freebsd" ], - "peer": true, "engines": { "node": ">=18" } @@ -817,7 +1256,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">=18" } @@ -835,7 +1273,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">=18" } @@ -853,7 +1290,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">=18" } @@ -871,7 +1307,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">=18" } @@ -889,7 +1324,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">=18" } @@ -907,7 +1341,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">=18" } @@ -925,7 +1358,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">=18" } @@ -943,7 +1375,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">=18" } @@ -961,7 +1392,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">=18" } @@ -979,7 +1409,6 @@ "os": [ "netbsd" ], - "peer": true, "engines": { "node": ">=18" } @@ -997,7 +1426,6 @@ "os": [ "netbsd" ], - "peer": true, "engines": { "node": ">=18" } @@ -1015,7 +1443,6 @@ "os": [ "openbsd" ], - "peer": true, "engines": { "node": ">=18" } @@ -1033,7 +1460,6 @@ "os": [ "openbsd" ], - "peer": true, "engines": { "node": ">=18" } @@ -1051,7 +1477,6 @@ "os": [ "openharmony" ], - "peer": true, "engines": { "node": ">=18" } @@ -1069,7 +1494,6 @@ "os": [ "sunos" ], - "peer": true, "engines": { "node": ">=18" } @@ -1087,7 +1511,6 @@ "os": [ "win32" ], - "peer": true, "engines": { "node": ">=18" } @@ -1105,7 +1528,6 @@ "os": [ "win32" ], - "peer": true, "engines": { "node": ">=18" } @@ -1123,11 +1545,20 @@ "os": [ "win32" ], - "peer": true, "engines": { "node": ">=18" } }, + "node_modules/@hutson/parse-repository-url": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@hutson/parse-repository-url/-/parse-repository-url-3.0.2.tgz", + "integrity": "sha512-H9XAx3hc0BQHY6l+IFSWHDySypcXsvsuLhgYLUGywmJ5pswRVQJUHpOsobnLYp2ZUaUlKiKDrgWWhosOwAEM8Q==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@img/colour": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.1.0.tgz", @@ -2629,6 +3060,17 @@ "url": "https://github.com/sponsors/Boshen" } }, + "node_modules/@prettier/plugin-xml": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@prettier/plugin-xml/-/plugin-xml-2.2.0.tgz", + "integrity": "sha512-UWRmygBsyj4bVXvDiqSccwT1kmsorcwQwaIy30yVh8T+Gspx4OlC0shX1y+ZuwXZvgnafmpRYKks0bAu9urJew==", + "dev": true, + "license": "MIT", + "dependencies": { + "@xml-tools/parser": "^1.0.11", + "prettier": ">=2.4.0" + } + }, "node_modules/@react-leaflet/core": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/@react-leaflet/core/-/core-2.1.0.tgz", @@ -3263,6 +3705,175 @@ "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==", "license": "MIT" }, + "node_modules/@trapezedev/gradle-parse": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/@trapezedev/gradle-parse/-/gradle-parse-7.1.3.tgz", + "integrity": "sha512-WQVF5pEJ5o/mUyvfGTG9nBKx9Te/ilKM3r2IT69GlbaooItT5ao7RyF1MUTBNjHLPk/xpGUY3c6PyVnjDlz0Vw==", + "dev": true, + "license": "SEE LICENSE" + }, + "node_modules/@trapezedev/project": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/@trapezedev/project/-/project-7.1.4.tgz", + "integrity": "sha512-b5rszBgT5XiRp/m4V2S2Ara2fdFXhPiduxhvCIVTSHq51PgLBjiTEStL6NbUz3V0K5bebF971O+SLRtyBxfCNA==", + "dev": true, + "license": "SEE LICENSE", + "dependencies": { + "@ionic/utils-fs": "^3.1.5", + "@ionic/utils-subprocess": "^2.1.8", + "@prettier/plugin-xml": "^2.2.0", + "@trapezedev/gradle-parse": "7.1.3", + "@xmldom/xmldom": "^0.9.9", + "conventional-changelog": "^3.1.4", + "cross-spawn": "^7.0.3", + "diff": "^5.1.0", + "env-paths": "^3.0.0", + "gradle-to-js": "^2.0.0", + "ini": "^2.0.0", + "kleur": "^4.1.5", + "lodash": "^4.17.21", + "plist": "^3.0.4", + "prettier": "^2.7.1", + "prompts": "^2.4.2", + "replace": "^1.1.0", + "tempy": "^3.1.0", + "tmp": "^0.2.1", + "ts-node": "^10.2.1", + "xcode": "^3.0.1", + "xml-js": "^1.6.11", + "xpath": "^0.0.32", + "yargs": "^17.2.1" + } + }, + "node_modules/@trapezedev/project/node_modules/@ionic/utils-process": { + "version": "2.1.11", + "resolved": "https://registry.npmjs.org/@ionic/utils-process/-/utils-process-2.1.11.tgz", + "integrity": "sha512-Uavxn+x8j3rDlZEk1X7YnaN6wCgbCwYQOeIjv/m94i1dzslqWhqIHEqxEyeE8HsT5Negboagg7GtQiABy+BLbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ionic/utils-object": "2.1.6", + "@ionic/utils-terminal": "2.3.4", + "debug": "^4.0.0", + "signal-exit": "^3.0.3", + "tree-kill": "^1.2.2", + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@trapezedev/project/node_modules/@ionic/utils-stream": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/@ionic/utils-stream/-/utils-stream-3.1.6.tgz", + "integrity": "sha512-4+Kitey1lTA1yGtnigeYNhV/0tggI3lWBMjC7tBs1K9GXa/q7q4CtOISppdh8QgtOhrhAXS2Igp8rbko/Cj+lA==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.0.0", + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@trapezedev/project/node_modules/@ionic/utils-subprocess": { + "version": "2.1.14", + "resolved": "https://registry.npmjs.org/@ionic/utils-subprocess/-/utils-subprocess-2.1.14.tgz", + "integrity": "sha512-nGYvyGVjU0kjPUcSRFr4ROTraT3w/7r502f5QJEsMRKTqa4eEzCshtwRk+/mpASm0kgBN5rrjYA5A/OZg8ahqg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ionic/utils-array": "2.1.6", + "@ionic/utils-fs": "3.1.7", + "@ionic/utils-process": "2.1.11", + "@ionic/utils-stream": "3.1.6", + "@ionic/utils-terminal": "2.3.4", + "cross-spawn": "^7.0.3", + "debug": "^4.0.0", + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@trapezedev/project/node_modules/@ionic/utils-terminal": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/@ionic/utils-terminal/-/utils-terminal-2.3.4.tgz", + "integrity": "sha512-cEiMFl3jklE0sW60r8JHH3ijFTwh/jkdEKWbylSyExQwZ8pPuwoXz7gpkWoJRLuoRHHSvg+wzNYyPJazIHfoJA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/slice-ansi": "^4.0.0", + "debug": "^4.0.0", + "signal-exit": "^3.0.3", + "slice-ansi": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "tslib": "^2.0.1", + "untildify": "^4.0.0", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@trapezedev/project/node_modules/diff": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.2.tgz", + "integrity": "sha512-vtcDfH3TOjP8UekytvnHH1o1P4FcUdt4eQ1Y+Abap1tk/OB2MWQvcwS2ClCd1zuIhc3JKOx6p3kod8Vfys3E+A==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/@trapezedev/project/node_modules/env-paths": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-3.0.0.tgz", + "integrity": "sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@trapezedev/project/node_modules/ini": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz", + "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/@trapezedev/project/node_modules/prettier": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/@trapezedev/project/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, "node_modules/@tsconfig/node10": { "version": "1.0.12", "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.12.tgz", @@ -3393,6 +4004,13 @@ "@types/geojson": "*" } }, + "node_modules/@types/minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/ms": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", @@ -3408,6 +4026,13 @@ "undici-types": ">=7.24.0 <7.24.7" } }, + "node_modules/@types/normalize-package-data": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", + "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/pako": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/@types/pako/-/pako-2.0.4.tgz", @@ -3679,6 +4304,16 @@ "@xtuc/long": "4.2.2" } }, + "node_modules/@xml-tools/parser": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@xml-tools/parser/-/parser-1.0.11.tgz", + "integrity": "sha512-aKqQ077XnR+oQtHJlrAflaZaL7qZsulWc/i/ZEooar5JiWj1eLt0+Wg28cpa+XLney107wXqneC+oG1IZvxkTA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "chevrotain": "7.1.1" + } + }, "node_modules/@xmldom/xmldom": { "version": "0.9.10", "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.9.10.tgz", @@ -3754,6 +4389,13 @@ "node": ">=0.4.0" } }, + "node_modules/add-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/add-stream/-/add-stream-1.0.0.tgz", + "integrity": "sha512-qQLMr+8o0WC4FZGQTcJiKBVC59JylcPSrTtk6usvmIDFUOCKegapy1VHQwRbFMOFyb/inzUVqHs+eMYKDM1YeQ==", + "dev": true, + "license": "MIT" + }, "node_modules/ajv": { "version": "8.18.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", @@ -3866,6 +4508,13 @@ "dev": true, "license": "Python-2.0" }, + "node_modules/array-ify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-ify/-/array-ify-1.0.0.tgz", + "integrity": "sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==", + "dev": true, + "license": "MIT" + }, "node_modules/array-timsort": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/array-timsort/-/array-timsort-1.0.3.tgz", @@ -3873,6 +4522,16 @@ "dev": true, "license": "MIT" }, + "node_modules/arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/astral-regex": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", @@ -3939,6 +4598,119 @@ "dev": true, "license": "MIT" }, + "node_modules/bare-events": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.9.1.tgz", + "integrity": "sha512-Z0oHEHAFDZkffN8Qc39zNZjQlMDkPJRyyyZieU1VH7u8c5S+qHZ2S8ixdKIAxEjfHO7FJxXmJWgteOghVanIsg==", + "dev": true, + "license": "Apache-2.0", + "peerDependencies": { + "bare-abort-controller": "*" + }, + "peerDependenciesMeta": { + "bare-abort-controller": { + "optional": true + } + } + }, + "node_modules/bare-fs": { + "version": "4.7.2", + "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.7.2.tgz", + "integrity": "sha512-aTvMFUWkBmjzKtEQMDGGDNF8bkfpD5N1b/FCwt7A3wrU4t1o/e/85Wzkluh6JlODCjqVESYCkQCdTXqZ9G7VFg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "bare-events": "^2.5.4", + "bare-path": "^3.0.0", + "bare-stream": "^2.6.4", + "bare-url": "^2.2.2", + "fast-fifo": "^1.3.2" + }, + "engines": { + "bare": ">=1.16.0" + }, + "peerDependencies": { + "bare-buffer": "*" + }, + "peerDependenciesMeta": { + "bare-buffer": { + "optional": true + } + } + }, + "node_modules/bare-os": { + "version": "3.9.1", + "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-3.9.1.tgz", + "integrity": "sha512-6M5XjcnsygQNPMCMPXSK379xrJFiZ/AEMNBmFEmQW8d/789VQATvriyi5r0HYTL9TkQ26rn3kgdTG3aisbrXkQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "bare": ">=1.14.0" + } + }, + "node_modules/bare-path": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-3.0.1.tgz", + "integrity": "sha512-ghj2DSK/2e99a1anTVPCV4m4YIYtrbXhfM7V3D7XZLOTsybnYyaJloymGqssQc8l/or0UoDyRtNQkmkEF/ysgQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "bare-os": "^3.0.1" + } + }, + "node_modules/bare-stream": { + "version": "2.13.3", + "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.13.3.tgz", + "integrity": "sha512-Kc+brLqvEqGkjyfiwJmImAOqLZL7OsoLKuavx+hJjgVV3nLTOjloJyPMFxjUPerGGHrNH0fLU06jjykMLWrERQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "b4a": "^1.8.1", + "streamx": "^2.25.0", + "teex": "^1.0.1" + }, + "peerDependencies": { + "bare-abort-controller": "*", + "bare-buffer": "*", + "bare-events": "*" + }, + "peerDependenciesMeta": { + "bare-abort-controller": { + "optional": true + }, + "bare-buffer": { + "optional": true + }, + "bare-events": { + "optional": true + } + } + }, + "node_modules/bare-stream/node_modules/b4a": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.8.1.tgz", + "integrity": "sha512-aiqre1Nr0B/6DgE2N5vwTc+2/oQZ4Wh1t4NznYY4E00y8LCt6NqdRv81so00oo27D8MVKTpUa/MwUUtBLXCoDw==", + "dev": true, + "license": "Apache-2.0", + "peerDependencies": { + "react-native-b4a": "*" + }, + "peerDependenciesMeta": { + "react-native-b4a": { + "optional": true + } + } + }, + "node_modules/bare-url": { + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/bare-url/-/bare-url-2.4.5.tgz", + "integrity": "sha512-K+y9xF1tN+CdPu4qWwr0QiK1Al07eFPGYK5M2pDXcmHdMdgC/tT/bpmMe1hrmRHaidKLkXrC+cRNYf3XVDUhSQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "bare-path": "^3.0.0" + } + }, "node_modules/base64-arraybuffer": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz", @@ -4063,6 +4835,23 @@ "url": "https://opencollective.com/express" } }, + "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/bplist-creator": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/bplist-creator/-/bplist-creator-0.1.0.tgz", + "integrity": "sha512-sXaHZicyEEmY86WyueLTQesbeoH/mquvarJaQNbjuOQO+7gbFcDEWqKmcWA4cOTLzFlfgvkiVxolk1k5bBIpmg==", + "dev": true, + "license": "MIT", + "dependencies": { + "stream-buffers": "2.2.x" + } + }, "node_modules/bplist-parser": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.3.2.tgz", @@ -4311,6 +5100,34 @@ "node": ">=6" } }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase-keys": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", + "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "camelcase": "^5.3.1", + "map-obj": "^4.0.0", + "quick-lru": "^4.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/caniuse-lite": { "version": "1.0.30001799", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001799.tgz", @@ -4389,6 +5206,16 @@ "dev": true, "license": "MIT" }, + "node_modules/chevrotain": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/chevrotain/-/chevrotain-7.1.1.tgz", + "integrity": "sha512-wy3mC1x4ye+O+QkEinVJkPf5u2vsrDIYW9G7ZuwFl6v/Yu0LwUuT2POsb+NUWApebyxfkQq6+yDfRExbnI5rcw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "regexp-to-ast": "0.5.0" + } + }, "node_modules/chokidar": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", @@ -4510,6 +5337,20 @@ "node": ">=0.10.0" } }, + "node_modules/color": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", + "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1", + "color-string": "^1.9.0" + }, + "engines": { + "node": ">=12.5.0" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -4528,6 +5369,17 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "license": "MIT" }, + "node_modules/color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, "node_modules/commander": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", @@ -4552,6 +5404,17 @@ "node": ">= 6" } }, + "node_modules/compare-func": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz", + "integrity": "sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-ify": "^1.0.0", + "dot-prop": "^5.1.0" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -4624,6 +5487,273 @@ "node": ">= 0.6" } }, + "node_modules/conventional-changelog": { + "version": "3.1.25", + "resolved": "https://registry.npmjs.org/conventional-changelog/-/conventional-changelog-3.1.25.tgz", + "integrity": "sha512-ryhi3fd1mKf3fSjbLXOfK2D06YwKNic1nC9mWqybBHdObPd8KJ2vjaXZfYj1U23t+V8T8n0d7gwnc9XbIdFbyQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "conventional-changelog-angular": "^5.0.12", + "conventional-changelog-atom": "^2.0.8", + "conventional-changelog-codemirror": "^2.0.8", + "conventional-changelog-conventionalcommits": "^4.5.0", + "conventional-changelog-core": "^4.2.1", + "conventional-changelog-ember": "^2.0.9", + "conventional-changelog-eslint": "^3.0.9", + "conventional-changelog-express": "^2.0.6", + "conventional-changelog-jquery": "^3.0.11", + "conventional-changelog-jshint": "^2.0.9", + "conventional-changelog-preset-loader": "^2.3.4" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/conventional-changelog-angular": { + "version": "5.0.13", + "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-5.0.13.tgz", + "integrity": "sha512-i/gipMxs7s8L/QeuavPF2hLnJgH6pEZAttySB6aiQLWcX3puWDL3ACVmvBhJGxnAy52Qc15ua26BufY6KpmrVA==", + "dev": true, + "license": "ISC", + "dependencies": { + "compare-func": "^2.0.0", + "q": "^1.5.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/conventional-changelog-atom": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/conventional-changelog-atom/-/conventional-changelog-atom-2.0.8.tgz", + "integrity": "sha512-xo6v46icsFTK3bb7dY/8m2qvc8sZemRgdqLb/bjpBsH2UyOS8rKNTgcb5025Hri6IpANPApbXMg15QLb1LJpBw==", + "deprecated": "This preset is deprecated. Please use conventional-changelog-conventionalcommits or conventional-changelog-angular instead.", + "dev": true, + "license": "ISC", + "dependencies": { + "q": "^1.5.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/conventional-changelog-codemirror": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/conventional-changelog-codemirror/-/conventional-changelog-codemirror-2.0.8.tgz", + "integrity": "sha512-z5DAsn3uj1Vfp7po3gpt2Boc+Bdwmw2++ZHa5Ak9k0UKsYAO5mH1UBTN0qSCuJZREIhX6WU4E1p3IW2oRCNzQw==", + "deprecated": "This preset is deprecated. Please use conventional-changelog-conventionalcommits or conventional-changelog-angular instead.", + "dev": true, + "license": "ISC", + "dependencies": { + "q": "^1.5.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/conventional-changelog-conventionalcommits": { + "version": "4.6.3", + "resolved": "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-4.6.3.tgz", + "integrity": "sha512-LTTQV4fwOM4oLPad317V/QNQ1FY4Hju5qeBIM1uTHbrnCE+Eg4CdRZ3gO2pUeR+tzWdp80M2j3qFFEDWVqOV4g==", + "dev": true, + "license": "ISC", + "dependencies": { + "compare-func": "^2.0.0", + "lodash": "^4.17.15", + "q": "^1.5.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/conventional-changelog-core": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/conventional-changelog-core/-/conventional-changelog-core-4.2.4.tgz", + "integrity": "sha512-gDVS+zVJHE2v4SLc6B0sLsPiloR0ygU7HaDW14aNJE1v4SlqJPILPl/aJC7YdtRE4CybBf8gDwObBvKha8Xlyg==", + "deprecated": "Deprecated and no longer maintained. Please use conventional-changelog instead.", + "dev": true, + "license": "MIT", + "dependencies": { + "add-stream": "^1.0.0", + "conventional-changelog-writer": "^5.0.0", + "conventional-commits-parser": "^3.2.0", + "dateformat": "^3.0.0", + "get-pkg-repo": "^4.0.0", + "git-raw-commits": "^2.0.8", + "git-remote-origin-url": "^2.0.0", + "git-semver-tags": "^4.1.1", + "lodash": "^4.17.15", + "normalize-package-data": "^3.0.0", + "q": "^1.5.1", + "read-pkg": "^3.0.0", + "read-pkg-up": "^3.0.0", + "through2": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/conventional-changelog-ember": { + "version": "2.0.9", + "resolved": "https://registry.npmjs.org/conventional-changelog-ember/-/conventional-changelog-ember-2.0.9.tgz", + "integrity": "sha512-ulzIReoZEvZCBDhcNYfDIsLTHzYHc7awh+eI44ZtV5cx6LVxLlVtEmcO+2/kGIHGtw+qVabJYjdI5cJOQgXh1A==", + "deprecated": "This preset is deprecated. Please use conventional-changelog-conventionalcommits or conventional-changelog-angular instead.", + "dev": true, + "license": "ISC", + "dependencies": { + "q": "^1.5.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/conventional-changelog-eslint": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/conventional-changelog-eslint/-/conventional-changelog-eslint-3.0.9.tgz", + "integrity": "sha512-6NpUCMgU8qmWmyAMSZO5NrRd7rTgErjrm4VASam2u5jrZS0n38V7Y9CzTtLT2qwz5xEChDR4BduoWIr8TfwvXA==", + "deprecated": "This preset is deprecated. Please use conventional-changelog-conventionalcommits or conventional-changelog-angular instead.", + "dev": true, + "license": "ISC", + "dependencies": { + "q": "^1.5.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/conventional-changelog-express": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/conventional-changelog-express/-/conventional-changelog-express-2.0.6.tgz", + "integrity": "sha512-SDez2f3iVJw6V563O3pRtNwXtQaSmEfTCaTBPCqn0oG0mfkq0rX4hHBq5P7De2MncoRixrALj3u3oQsNK+Q0pQ==", + "deprecated": "This preset is deprecated. Please use conventional-changelog-conventionalcommits or conventional-changelog-angular instead.", + "dev": true, + "license": "ISC", + "dependencies": { + "q": "^1.5.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/conventional-changelog-jquery": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/conventional-changelog-jquery/-/conventional-changelog-jquery-3.0.11.tgz", + "integrity": "sha512-x8AWz5/Td55F7+o/9LQ6cQIPwrCjfJQ5Zmfqi8thwUEKHstEn4kTIofXub7plf1xvFA2TqhZlq7fy5OmV6BOMw==", + "deprecated": "This preset is deprecated. Please use conventional-changelog-conventionalcommits or conventional-changelog-angular instead.", + "dev": true, + "license": "ISC", + "dependencies": { + "q": "^1.5.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/conventional-changelog-jshint": { + "version": "2.0.9", + "resolved": "https://registry.npmjs.org/conventional-changelog-jshint/-/conventional-changelog-jshint-2.0.9.tgz", + "integrity": "sha512-wMLdaIzq6TNnMHMy31hql02OEQ8nCQfExw1SE0hYL5KvU+JCTuPaDO+7JiogGT2gJAxiUGATdtYYfh+nT+6riA==", + "deprecated": "This preset is deprecated. Please use conventional-changelog-conventionalcommits or conventional-changelog-angular instead.", + "dev": true, + "license": "ISC", + "dependencies": { + "compare-func": "^2.0.0", + "q": "^1.5.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/conventional-changelog-preset-loader": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/conventional-changelog-preset-loader/-/conventional-changelog-preset-loader-2.3.4.tgz", + "integrity": "sha512-GEKRWkrSAZeTq5+YjUZOYxdHq+ci4dNwHvpaBC3+ENalzFWuCWa9EZXSuZBpkr72sMdKB+1fyDV4takK1Lf58g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/conventional-changelog-writer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-5.0.1.tgz", + "integrity": "sha512-5WsuKUfxW7suLblAbFnxAcrvf6r+0b7GvNaWUwUIk0bXMnENP/PEieGKVUQrjPqwPT4o3EPAASBXiY6iHooLOQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "conventional-commits-filter": "^2.0.7", + "dateformat": "^3.0.0", + "handlebars": "^4.7.7", + "json-stringify-safe": "^5.0.1", + "lodash": "^4.17.15", + "meow": "^8.0.0", + "semver": "^6.0.0", + "split": "^1.0.0", + "through2": "^4.0.0" + }, + "bin": { + "conventional-changelog-writer": "cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/conventional-changelog-writer/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/conventional-commits-filter": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/conventional-commits-filter/-/conventional-commits-filter-2.0.7.tgz", + "integrity": "sha512-ASS9SamOP4TbCClsRHxIHXRfcGCnIoQqkvAzCSbZzTFLfcTqJVugB0agRgsEELsqaeWgsXv513eS116wnlSSPA==", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash.ismatch": "^4.4.0", + "modify-values": "^1.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/conventional-commits-parser": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-3.2.4.tgz", + "integrity": "sha512-nK7sAtfi+QXbxHCYfhpZsfRtaitZLIA6889kFIouLvz6repszQDgxBu7wf2WbU+Dco7sAnNCJYERCwt54WPC2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-text-path": "^1.0.1", + "JSONStream": "^1.0.4", + "lodash": "^4.17.15", + "meow": "^8.0.0", + "split2": "^3.0.0", + "through2": "^4.0.0" + }, + "bin": { + "conventional-commits-parser": "cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/conventional-commits-parser/node_modules/split2": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz", + "integrity": "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==", + "dev": true, + "license": "ISC", + "dependencies": { + "readable-stream": "^3.0.0" + } + }, "node_modules/cookie": { "version": "0.7.2", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", @@ -4654,6 +5784,13 @@ "url": "https://opencollective.com/core-js" } }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true, + "license": "MIT" + }, "node_modules/cors": { "version": "2.8.6", "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz", @@ -4719,6 +5856,35 @@ "node": ">= 8" } }, + "node_modules/crypto-random-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-4.0.0.tgz", + "integrity": "sha512-x8dy3RnvYdlUcPOjkEHqozhiwzKNSq7GcPuXFbnyMOCHxX8V3OgIg/pYuabl2sbUPfIJaeAQB7PMOK8DFIdoRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^1.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/crypto-random-string/node_modules/type-fest": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz", + "integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/css-line-break": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/css-line-break/-/css-line-break-2.1.0.tgz", @@ -4729,6 +5895,36 @@ "utrie": "^1.0.2" } }, + "node_modules/css-select": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", + "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.0.1", + "domhandler": "^4.3.1", + "domutils": "^2.8.0", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-what": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz", + "integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, "node_modules/csstype": { "version": "3.2.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", @@ -4736,6 +5932,16 @@ "devOptional": true, "license": "MIT" }, + "node_modules/dargs": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/dargs/-/dargs-7.0.0.tgz", + "integrity": "sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/date-fns": { "version": "2.30.0", "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", @@ -4753,6 +5959,16 @@ "url": "https://opencollective.com/date-fns" } }, + "node_modules/dateformat": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz", + "integrity": "sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, "node_modules/debug": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", @@ -4770,6 +5986,59 @@ } } }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decamelize-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.1.tgz", + "integrity": "sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==", + "dev": true, + "license": "MIT", + "dependencies": { + "decamelize": "^1.1.0", + "map-obj": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decamelize-keys/node_modules/map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/deep-equal": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.2.tgz", @@ -4790,6 +6059,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, "node_modules/deepmerge": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", @@ -4884,6 +6163,50 @@ "node": ">=0.3.1" } }, + "node_modules/dom-serializer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "dev": true, + "license": "MIT", + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "BSD-2-Clause" + }, + "node_modules/domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.2.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, "node_modules/dompurify": { "version": "3.4.11", "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.11.tgz", @@ -4894,6 +6217,34 @@ "@types/trusted-types": "^2.0.7" } }, + "node_modules/domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/dot-prop": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz", + "integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-obj": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/dotenv": { "version": "17.4.2", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.4.2.tgz", @@ -5008,10 +6359,20 @@ "node": ">= 0.8" } }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, "node_modules/engine.io": { - "version": "6.6.8", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.8.tgz", - "integrity": "sha512-2agL3ueZhqxoVrfmntO8yuVj+uNSlIOnhykYHk3Cq0ShYPdUjjUiSJrQvXjq01I9jAuI0Zl2YO8Evv5Mqytm5g==", + "version": "6.6.9", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.9.tgz", + "integrity": "sha512-clKkw4C7nJ22mGgoVcCg6V/W/TxdNyIOTr89k2ONZu81qqkddPFDF0LXcbAwhzPD8DjkiRCjzuiO6Y+fkpD4vg==", "license": "MIT", "dependencies": { "@types/cors": "^2.8.12", @@ -5023,7 +6384,7 @@ "cors": "~2.8.5", "debug": "~4.4.1", "engine.io-parser": "~5.2.1", - "ws": "~8.20.1" + "ws": "~8.21.0" }, "engines": { "node": ">=10.2.0" @@ -5042,27 +6403,6 @@ "xmlhttprequest-ssl": "~2.1.1" } }, - "node_modules/engine.io-client/node_modules/ws": { - "version": "8.21.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.0.tgz", - "integrity": "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==", - "license": "MIT", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, "node_modules/engine.io-parser": { "version": "5.2.3", "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz", @@ -5086,6 +6426,16 @@ "node": ">=10.13.0" } }, + "node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "dev": true, + "license": "BSD-2-Clause", + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/env-paths": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", @@ -5200,6 +6550,16 @@ "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", "license": "MIT" }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/eslint-scope": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", @@ -5286,12 +6646,32 @@ "node": ">=0.8.x" } }, + "node_modules/events-universal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/events-universal/-/events-universal-1.0.1.tgz", + "integrity": "sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "bare-events": "^2.7.0" + } + }, "node_modules/exifr": { "version": "7.1.3", "resolved": "https://registry.npmjs.org/exifr/-/exifr-7.1.3.tgz", "integrity": "sha512-g/aje2noHivrRSLbAUtBPWFbxKdKhgj/xr1vATDdUXPOFYJlQ62Ft0oy+72V6XLIpDJfHs6gXLbBLAolqOXYRw==", "license": "MIT" }, + "node_modules/expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "dev": true, + "license": "(MIT OR WTFPL)", + "engines": { + "node": ">=6" + } + }, "node_modules/express": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", @@ -5392,6 +6772,13 @@ "integrity": "sha512-KaJUt+M9t1qaIteSvjc6P3RbMdXsNhK61GRftR6SNxqmhthcd9MGIi4T+o0jD8LUSpSnSKXE20nLtJ3fOHxQig==", "license": "Apache-2.0" }, + "node_modules/fast-fifo": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", + "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", + "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", @@ -5505,6 +6892,19 @@ "url": "https://opencollective.com/express" } }, + "node_modules/find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/fork-ts-checker-webpack-plugin": { "version": "9.1.0", "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-9.1.0.tgz", @@ -5569,6 +6969,13 @@ "resolved": "frontend", "link": true }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "dev": true, + "license": "MIT" + }, "node_modules/fs-extra": { "version": "10.1.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", @@ -5584,6 +6991,32 @@ "node": ">=12" } }, + "node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/fs-minipass/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/fs-monkey": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.1.0.tgz", @@ -5591,11 +7024,17 @@ "dev": true, "license": "Unlicense" }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" + }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, "hasInstallScript": true, "license": "MIT", "optional": true, @@ -5667,6 +7106,110 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-pkg-repo": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/get-pkg-repo/-/get-pkg-repo-4.2.1.tgz", + "integrity": "sha512-2+QbHjFRfGB74v/pYWjd5OhU3TDIC2Gv/YKUTk/tCvAz0pkn/Mz6P3uByuBimLOcPvN2jYdScl3xGFSrx0jEcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@hutson/parse-repository-url": "^3.0.0", + "hosted-git-info": "^4.0.0", + "through2": "^2.0.0", + "yargs": "^16.2.0" + }, + "bin": { + "get-pkg-repo": "src/cli.js" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-pkg-repo/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/get-pkg-repo/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/get-pkg-repo/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true, + "license": "MIT" + }, + "node_modules/get-pkg-repo/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/get-pkg-repo/node_modules/through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "node_modules/get-pkg-repo/node_modules/yargs": { + "version": "16.2.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.2.tgz", + "integrity": "sha512-Nt9ZJjXTv5R8MHbqby/wXQ6Gi0Bb3TcYZkR1bzuL4yB2OxWPkXknz513gEF0GoA6tn00UpbPvERW8rzCuWCA6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/get-pkg-repo/node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, "node_modules/get-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", @@ -5680,6 +7223,103 @@ "node": ">= 0.4" } }, + "node_modules/git-raw-commits": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-2.0.11.tgz", + "integrity": "sha512-VnctFhw+xfj8Va1xtfEqCUD2XDrbAPSJx+hSrE5K7fGdjZruW7XV+QOrN7LF/RJyvspRiD2I0asWsxFp0ya26A==", + "deprecated": "Deprecated and no longer maintained. Use @conventional-changelog/git-client instead.", + "dev": true, + "license": "MIT", + "dependencies": { + "dargs": "^7.0.0", + "lodash": "^4.17.15", + "meow": "^8.0.0", + "split2": "^3.0.0", + "through2": "^4.0.0" + }, + "bin": { + "git-raw-commits": "cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/git-raw-commits/node_modules/split2": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz", + "integrity": "sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==", + "dev": true, + "license": "ISC", + "dependencies": { + "readable-stream": "^3.0.0" + } + }, + "node_modules/git-remote-origin-url": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/git-remote-origin-url/-/git-remote-origin-url-2.0.0.tgz", + "integrity": "sha512-eU+GGrZgccNJcsDH5LkXR3PB9M958hxc7sbA8DFJjrv9j4L2P/eZfKhM+QD6wyzpiv+b1BpK0XrYCxkovtjSLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "gitconfiglocal": "^1.0.0", + "pify": "^2.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/git-semver-tags": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/git-semver-tags/-/git-semver-tags-4.1.1.tgz", + "integrity": "sha512-OWyMt5zBe7xFs8vglMmhM9lRQzCWL3WjHtxNNfJTMngGym7pC1kh8sP6jevfydJ6LP3ZvGxfb6ABYgPUM0mtsA==", + "deprecated": "Deprecated and no longer maintained. Use @conventional-changelog/git-client instead.", + "dev": true, + "license": "MIT", + "dependencies": { + "meow": "^8.0.0", + "semver": "^6.0.0" + }, + "bin": { + "git-semver-tags": "cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/git-semver-tags/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/gitconfiglocal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/gitconfiglocal/-/gitconfiglocal-1.0.0.tgz", + "integrity": "sha512-spLUXeTAVHxDtKsJc8FkFVgFtMdEN9qPGpL23VfSHx4fP4+Ds097IXLvymbnDH8FnmxX5Nr9bPw3A+AQ6mWEaQ==", + "dev": true, + "license": "BSD", + "dependencies": { + "ini": "^1.3.2" + } + }, + "node_modules/gitconfiglocal/node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true, + "license": "ISC" + }, + "node_modules/github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", + "dev": true, + "license": "MIT" + }, "node_modules/glob": { "version": "13.0.6", "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", @@ -5758,6 +7398,61 @@ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "license": "ISC" }, + "node_modules/gradle-to-js": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/gradle-to-js/-/gradle-to-js-2.0.1.tgz", + "integrity": "sha512-is3hDn9zb8XXnjbEeAEIqxTpLHUiGBqjegLmXPuyMBfKAggpadWFku4/AP8iYAGBX6qR9/5UIUIp47V0XI3aMw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "lodash.merge": "^4.6.2" + }, + "bin": { + "gradle-to-js": "cli.js" + } + }, + "node_modules/handlebars": { + "version": "4.7.9", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.9.tgz", + "integrity": "sha512-4E71E0rpOaQuJR2A3xDZ+GM1HyWYv1clR58tC8emQNeQe3RH7MAzSbat+V0wG78LQBo6m6bzSG/L4pBuCsgnUQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.2", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, + "node_modules/handlebars/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/hard-rejection": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz", + "integrity": "sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -5831,6 +7526,16 @@ "node": ">= 0.4" } }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "license": "MIT", + "bin": { + "he": "bin/he" + } + }, "node_modules/heic-convert": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/heic-convert/-/heic-convert-2.1.0.tgz", @@ -5863,6 +7568,32 @@ "integrity": "sha512-MvG/clsADq1GPM2KGo2nyfaWVyn9naPiXrqIe4jYjXNZQt238kWyOGrsyc/DmRAQ+Re6yeo6yX/yoNCG5KAEVg==", "license": "MIT" }, + "node_modules/hosted-git-info": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", + "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/hosted-git-info/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/html2canvas": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/html2canvas/-/html2canvas-1.4.1.tgz", @@ -5950,6 +7681,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", @@ -6003,6 +7744,22 @@ "dev": true, "license": "MIT" }, + "node_modules/is-core-module": { + "version": "2.16.2", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.2.tgz", + "integrity": "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-date-object": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", @@ -6053,6 +7810,26 @@ "node": ">=8" } }, + "node_modules/is-obj": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", + "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-promise": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", @@ -6077,6 +7854,32 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-text-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-text-path/-/is-text-path-1.0.1.tgz", + "integrity": "sha512-xFuJpne9oFz5qDaodwmmG08e3CawH/2ZV8Qqza1Ko7Sk8POWbkRdwIoAWVhqvq0XeUzANEhKo2n0IXUGBm7A/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "text-extensions": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/is-unicode-supported": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", @@ -6102,6 +7905,13 @@ "node": ">=8" } }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true, + "license": "MIT" + }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -6177,6 +7987,13 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true, + "license": "MIT" + }, "node_modules/json-parse-even-better-errors": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", @@ -6191,6 +8008,13 @@ "dev": true, "license": "MIT" }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", + "dev": true, + "license": "ISC" + }, "node_modules/json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", @@ -6223,6 +8047,33 @@ "graceful-fs": "^4.1.6" } }, + "node_modules/jsonparse": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==", + "dev": true, + "engines": [ + "node >= 0.2.0" + ], + "license": "MIT" + }, + "node_modules/JSONStream": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", + "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", + "dev": true, + "license": "(MIT OR Apache-2.0)", + "dependencies": { + "jsonparse": "^1.2.0", + "through": ">=2.2.7 <3" + }, + "bin": { + "JSONStream": "bin.js" + }, + "engines": { + "node": "*" + } + }, "node_modules/jsonwebtoken": { "version": "9.0.3", "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.3.tgz", @@ -6301,6 +8152,16 @@ "@keyv/serialize": "^1.1.1" } }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/kleur": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", @@ -6621,6 +8482,46 @@ "node": ">=13.2.0" } }, + "node_modules/load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/load-json-file/node_modules/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", + "dev": true, + "license": "MIT", + "dependencies": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/load-json-file/node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/loader-runner": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.2.tgz", @@ -6635,6 +8536,20 @@ "url": "https://opencollective.com/webpack" } }, + "node_modules/locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/lodash": { "version": "4.18.1", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", @@ -6678,6 +8593,13 @@ "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", "license": "MIT" }, + "node_modules/lodash.ismatch": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.ismatch/-/lodash.ismatch-4.4.0.tgz", + "integrity": "sha512-fPMfXjGQEV9Xsq/8MTSgUf255gawYRbjwMyDbcvDhXgV7enSZA0hynz6vMPnpAb5iONEzBHBPsT+0zes5Z301g==", + "dev": true, + "license": "MIT" + }, "node_modules/lodash.isnumber": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", @@ -6696,6 +8618,13 @@ "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", "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/lodash.once": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", @@ -6766,6 +8695,19 @@ "dev": true, "license": "ISC" }, + "node_modules/map-obj": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz", + "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -6797,6 +8739,202 @@ "node": ">= 4.0.0" } }, + "node_modules/meow": { + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/meow/-/meow-8.1.2.tgz", + "integrity": "sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/minimist": "^1.2.0", + "camelcase-keys": "^6.2.2", + "decamelize-keys": "^1.1.0", + "hard-rejection": "^2.1.0", + "minimist-options": "4.1.0", + "normalize-package-data": "^3.0.0", + "read-pkg-up": "^7.0.1", + "redent": "^3.0.0", + "trim-newlines": "^3.0.0", + "type-fest": "^0.18.0", + "yargs-parser": "^20.2.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/meow/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/meow/node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true, + "license": "ISC" + }, + "node_modules/meow/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/meow/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/meow/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/meow/node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/meow/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/meow/node_modules/read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/meow/node_modules/read-pkg-up": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/meow/node_modules/read-pkg-up/node_modules/type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=8" + } + }, + "node_modules/meow/node_modules/read-pkg/node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/meow/node_modules/read-pkg/node_modules/type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=8" + } + }, + "node_modules/meow/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/meow/node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, "node_modules/merge-descriptors": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", @@ -6856,6 +8994,29 @@ "node": ">=6" } }, + "node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/minimatch": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", @@ -6879,6 +9040,21 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/minimist-options": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", + "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==", + "dev": true, + "license": "MIT", + "dependencies": { + "arrify": "^1.0.1", + "is-plain-obj": "^1.1.0", + "kind-of": "^6.0.3" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/minipass": { "version": "7.1.3", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", @@ -6900,6 +9076,36 @@ "node": ">= 18" } }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, + "license": "MIT", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "dev": true, + "license": "MIT" + }, + "node_modules/modify-values": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/modify-values/-/modify-values-1.0.1.tgz", + "integrity": "sha512-xV2bxeN6F7oYjZWTe/YPAy6MN2M+sL4u/Rlm2AHCIVGfo2p1yGmBHQ6vHehl4bRTZBdHu3TSkWdYgkwpYzAGSw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -6976,6 +9182,13 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/napi-build-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz", + "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==", + "dev": true, + "license": "MIT" + }, "node_modules/native-run": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/native-run/-/native-run-2.0.3.tgz", @@ -7017,6 +9230,19 @@ "dev": true, "license": "MIT" }, + "node_modules/node-abi": { + "version": "3.92.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.92.0.tgz", + "integrity": "sha512-KdHvFWZjEKDf0cakgFjebl371GPsISX2oZHcuyKqM7DtogIsHrqKeLTo8wBHxaXRAQlY2PsPlZmfo+9ZCxEREQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/node-abort-controller": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz", @@ -7043,6 +9269,27 @@ "lodash": "^4.17.21" } }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, "node_modules/node-gyp-build": { "version": "4.8.4", "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz", @@ -7054,6 +9301,17 @@ "node-gyp-build-test": "build-test.js" } }, + "node_modules/node-html-parser": { + "version": "5.4.2", + "resolved": "https://registry.npmjs.org/node-html-parser/-/node-html-parser-5.4.2.tgz", + "integrity": "sha512-RaBPP3+51hPne/OolXxcz89iYvQvKOydaqoePpOgXcrOKZhjVIzmpKZz+Hd/RBO2/zN2q6CNJhQzucVz+u3Jyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "css-select": "^4.2.1", + "he": "1.2.0" + } + }, "node_modules/node-releases": { "version": "2.0.47", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.47.tgz", @@ -7073,6 +9331,35 @@ "node": ">=6.0.0" } }, + "node_modules/normalize-package-data": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", + "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^4.0.1", + "is-core-module": "^2.5.0", + "semver": "^7.3.4", + "validate-npm-package-license": "^3.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "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/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -7206,6 +9493,42 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "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", @@ -7301,6 +9624,16 @@ "node": ">= 0.4.0" } }, + "node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", @@ -7310,6 +9643,13 @@ "node": ">=8" } }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, "node_modules/path-scurry": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", @@ -7482,6 +9822,16 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/plist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/plist/-/plist-3.1.1.tgz", @@ -7581,6 +9931,94 @@ "node": ">=0.10.0" } }, + "node_modules/prebuild-install": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", + "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==", + "deprecated": "No longer maintained. Please contact the author of the relevant native addon; alternatives are available.", + "dev": true, + "license": "MIT", + "dependencies": { + "detect-libc": "^2.0.0", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^2.0.0", + "node-abi": "^3.3.0", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^4.0.0", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + }, + "bin": { + "prebuild-install": "bin.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/prebuild-install/node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "dev": true, + "license": "ISC" + }, + "node_modules/prebuild-install/node_modules/tar-fs": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.4.tgz", + "integrity": "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "node_modules/prebuild-install/node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/prettier": { + "version": "3.8.5", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.5.tgz", + "integrity": "sha512-zxcTTCedNGJM4R8sj/Cq/F0W/c4iE0afWBcBwMTRtw4WHYP9TWkYjdiH3npPRUYsXQCPR0hTU9yjovOu+E6EQA==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true, + "license": "MIT" + }, "node_modules/promise-coalesce": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/promise-coalesce/-/promise-coalesce-1.5.0.tgz", @@ -7625,6 +10063,17 @@ "node": ">= 0.10" } }, + "node_modules/pump": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz", + "integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -7635,6 +10084,18 @@ "node": ">=6" } }, + "node_modules/q": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", + "integrity": "sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==", + "deprecated": "You or someone you depend on is using Q, the JavaScript Promise library that gave JavaScript developers strong feelings about promises. They can almost certainly migrate to the native JavaScript promise now. Thank you literally everyone for joining me in this bet against the odds. Be excellent to each other.\n\n(For a CapTP with native promises, see @endo/eventual-send and @endo/captp)", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6.0", + "teleport": ">=0.2.0" + } + }, "node_modules/qs": { "version": "6.15.2", "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.2.tgz", @@ -7650,6 +10111,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/quick-lru": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", + "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/quill": { "version": "1.3.7", "resolved": "https://registry.npmjs.org/quill/-/quill-1.3.7.tgz", @@ -7727,6 +10198,29 @@ "node": ">= 0.10" } }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dev": true, + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/rc/node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true, + "license": "ISC" + }, "node_modules/react": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", @@ -7796,6 +10290,88 @@ "react-dom": "^16 || ^17 || ^18" } }, + "node_modules/read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-3.0.0.tgz", + "integrity": "sha512-YFzFrVvpC6frF1sz8psoHDBGF7fLPc+llq/8NB43oagqWkx8ar5zYtsTORtOjw9W2RHLpWP+zTWwBvf1bCmcSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^2.0.0", + "read-pkg": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg/node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true, + "license": "ISC" + }, + "node_modules/read-pkg/node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/read-pkg/node_modules/path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, + "license": "MIT", + "dependencies": { + "pify": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg/node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/read-pkg/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, "node_modules/readable-stream": { "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", @@ -7824,6 +10400,20 @@ "url": "https://paulmillr.com/funding/" } }, + "node_modules/redent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/redis": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/redis/-/redis-6.0.0.tgz", @@ -7912,6 +10502,12 @@ "@redis/client": "^6.0.0" } }, + "node_modules/reflect-metadata": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.2.tgz", + "integrity": "sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==", + "license": "Apache-2.0" + }, "node_modules/regenerator-runtime": { "version": "0.13.11", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", @@ -7919,6 +10515,13 @@ "license": "MIT", "optional": true }, + "node_modules/regexp-to-ast": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/regexp-to-ast/-/regexp-to-ast-0.5.0.tgz", + "integrity": "sha512-tlbJqcMHnPKI9zSrystikWKwHkBqu2a/Sgw01h3zFjvYrMxEDYHzzoMZnUrbIfpTFEsoRnnviOXNCzFiSc54Qw==", + "dev": true, + "license": "MIT" + }, "node_modules/regexp.prototype.flags": { "version": "1.5.4", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", @@ -7939,6 +10542,289 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/replace": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/replace/-/replace-1.2.2.tgz", + "integrity": "sha512-C4EDifm22XZM2b2JOYe6Mhn+lBsLBAvLbK8drfUQLTfD1KYl/n3VaW/CDju0Ny4w3xTtegBpg8YNSpFJPUDSjA==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "2.4.2", + "minimatch": "3.0.5", + "yargs": "^15.3.1" + }, + "bin": { + "replace": "bin/replace.js", + "search": "bin/search.js" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/replace/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/replace/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/replace/node_modules/cliui": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", + "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^6.2.0" + } + }, + "node_modules/replace/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/replace/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true, + "license": "MIT" + }, + "node_modules/replace/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/replace/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/replace/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/replace/node_modules/minimatch": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.5.tgz", + "integrity": "sha512-tUpxzX0VAzJHjLu0xUfFv1gwVp9ba3IOuRAVH2EGuRW8a5emA2FlACLqiT/lDVtS1W+TGNwqz3sWaNyLgDJWuw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/replace/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/replace/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/replace/node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/replace/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/replace/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/replace/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/replace/node_modules/wrap-ansi/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/replace/node_modules/wrap-ansi/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/replace/node_modules/wrap-ansi/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/replace/node_modules/y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/replace/node_modules/yargs": { + "version": "15.4.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz", + "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^6.0.0", + "decamelize": "^1.2.0", + "find-up": "^4.1.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^4.2.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^18.1.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/replace/node_modules/yargs-parser": { + "version": "18.1.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", + "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -7959,6 +10845,35 @@ "node": ">=0.10.0" } }, + "node_modules/require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true, + "license": "ISC" + }, + "node_modules/resolve": { + "version": "1.22.12", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz", + "integrity": "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -8245,6 +11160,13 @@ "url": "https://opencollective.com/express" } }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "dev": true, + "license": "ISC" + }, "node_modules/set-function-length": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", @@ -8446,6 +11368,95 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/simple-get": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "node_modules/simple-plist": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/simple-plist/-/simple-plist-1.3.1.tgz", + "integrity": "sha512-iMSw5i0XseMnrhtIzRb7XpQEXepa9xhWxGUojHBL43SIpQuDQkh3Wpy67ZbDzZVr6EKxvwVChnVpdl8hEVLDiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "bplist-creator": "0.1.0", + "bplist-parser": "0.3.1", + "plist": "^3.0.5" + } + }, + "node_modules/simple-plist/node_modules/bplist-parser": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.3.1.tgz", + "integrity": "sha512-PyJxiNtA5T2PlLIeBot4lbp7rj4OadzjnMZD/G5zuBNt8ei/yCU7+wW0h2bag9vr8c+/WuRWmSxbqAl9hL1rBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "big-integer": "1.6.x" + }, + "engines": { + "node": ">= 5.10.0" + } + }, + "node_modules/simple-swizzle": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.4.tgz", + "integrity": "sha512-nAu1WFPQSMNr2Zn9PGSZK9AGn4t/y97lEm+MXTtUDwfP0ksAIX4nO+6ruD9Jwut4C49SB1Ws+fbXsm/yScWOHw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, + "node_modules/simple-swizzle/node_modules/is-arrayish": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.4.tgz", + "integrity": "sha512-m6UrgzFVUYawGBh1dUsWR5M2Clqic9RVXC/9f8ceNlv2IcO9j9J/z8UoCLPqtsPBFNzEpfR3xftohbfqDx8EQA==", + "dev": true, + "license": "MIT" + }, "node_modules/sisteransi": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", @@ -8488,13 +11499,13 @@ } }, "node_modules/socket.io-adapter": { - "version": "2.5.7", - "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.7.tgz", - "integrity": "sha512-e0LyK91f3cUxTmv95/KzoLg47+zF+s/sbxRGDNsyG4dmIP8ZSX8ax6byOxfJXeNNtS/8AZlfD+uP7gBeR7DLlg==", + "version": "2.5.8", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.8.tgz", + "integrity": "sha512-6Oy52pbg+kvdCVvjcN+FnY7BvxZ7cIHNScbvztT/It5d0vbwoJoVZmF2gjJmnV0/4WlXRfG15zc45ySk9Ah8bw==", "license": "MIT", "dependencies": { "debug": "~4.4.1", - "ws": "~8.20.1" + "ws": "~8.21.0" } }, "node_modules/socket.io-client": { @@ -8572,6 +11583,55 @@ "integrity": "sha512-zC8zGoGkmc8J9ndvml8Xksr1Amk9qBujgbF0JAIWO7kXr43w0h/0GJNM/Vustixu+YE8N/MTrQ7N31FvHUACxQ==", "dev": true }, + "node_modules/spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", + "dev": true, + "license": "CC-BY-3.0" + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.23", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.23.tgz", + "integrity": "sha512-CWLcCCH7VLu13TgOH+r8p1O/Znwhqv/dbb6lqWy67G+pT1kHmeD/+V36AVb/vq8QMIQwVShJ6Ssl5FPh0fuSdw==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/split": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz", + "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==", + "dev": true, + "license": "MIT", + "dependencies": { + "through": "2" + }, + "engines": { + "node": "*" + } + }, "node_modules/split2": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", @@ -8600,6 +11660,16 @@ "node": ">= 0.8" } }, + "node_modules/stream-buffers": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/stream-buffers/-/stream-buffers-2.2.0.tgz", + "integrity": "sha512-uyQK/mx5QjHun80FLJTfaWE7JtwfRMKBLkMne6udYOmvH0CawotVa7TfgYHzAnpphn4+TweIx1QKMnRIbipmUg==", + "dev": true, + "license": "Unlicense", + "engines": { + "node": ">= 0.10.0" + } + }, "node_modules/streamsearch": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", @@ -8608,6 +11678,18 @@ "node": ">=10.0.0" } }, + "node_modules/streamx": { + "version": "2.28.0", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.28.0.tgz", + "integrity": "sha512-1Yowhzjf0ivGMrTIkY9hav5TxobO9qIVqUE41fiCGMGgc3CLlf4MY+9AHmZqBWgDTue0fY9zWjYFVyf6Diuobw==", + "dev": true, + "license": "MIT", + "dependencies": { + "events-universal": "^1.0.0", + "fast-fifo": "^1.3.2", + "text-decoder": "^1.1.0" + } + }, "node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -8653,6 +11735,29 @@ "node": ">=4" } }, + "node_modules/strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "min-indent": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/strtok3": { "version": "10.3.5", "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-10.3.5.tgz", @@ -8685,6 +11790,19 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/svg-pathdata": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/svg-pathdata/-/svg-pathdata-6.0.3.tgz", @@ -8742,6 +11860,49 @@ "node": ">=18" } }, + "node_modules/tar-fs": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.1.2.tgz", + "integrity": "sha512-QGxxTxxyleAdyM3kpFs14ymbYmNFrfY+pHj7Z8FgtbZ7w2//VAgLMac7sT6nRpIHjppXO2AwwEOg0bPFVRcmXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "pump": "^3.0.0", + "tar-stream": "^3.1.5" + }, + "optionalDependencies": { + "bare-fs": "^4.0.1", + "bare-path": "^3.0.0" + } + }, + "node_modules/tar-stream": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.2.0.tgz", + "integrity": "sha512-ojzvCvVaNp6aOTFmG7jaRD0meowIAuPc3cMMhSgKiVWws1GyHbGd/xvnyuRKcKlMpt3qvxx6r0hreCNITP9hIg==", + "dev": true, + "license": "MIT", + "dependencies": { + "b4a": "^1.6.4", + "bare-fs": "^4.5.5", + "fast-fifo": "^1.2.0", + "streamx": "^2.15.0" + } + }, + "node_modules/tar-stream/node_modules/b4a": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.8.1.tgz", + "integrity": "sha512-aiqre1Nr0B/6DgE2N5vwTc+2/oQZ4Wh1t4NznYY4E00y8LCt6NqdRv81so00oo27D8MVKTpUa/MwUUtBLXCoDw==", + "dev": true, + "license": "Apache-2.0", + "peerDependencies": { + "react-native-b4a": "*" + }, + "peerDependenciesMeta": { + "react-native-b4a": { + "optional": true + } + } + }, "node_modules/tar/node_modules/yallist": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", @@ -8751,6 +11912,58 @@ "node": ">=18" } }, + "node_modules/teex": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/teex/-/teex-1.0.1.tgz", + "integrity": "sha512-eYE6iEI62Ni1H8oIa7KlDU6uQBtqr4Eajni3wX7rpfXD8ysFx8z0+dri+KWEPWpBsxXfxu58x/0jvTVT1ekOSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "streamx": "^2.12.5" + } + }, + "node_modules/temp-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-3.0.0.tgz", + "integrity": "sha512-nHc6S/bwIilKHNRgK/3jlhDoIHcp45YgyiwcAk46Tr0LfEqGBVpmiAyuiuxeVE44m3mXnEeVhaipLOEWmH+Njw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.16" + } + }, + "node_modules/tempy": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/tempy/-/tempy-3.2.0.tgz", + "integrity": "sha512-d79HhZya5Djd7am0q+W4RTsSU+D/aJzM+4Y4AGJGuGlgM2L6sx5ZvOYTmZjqPhrDrV6xJTtRSm1JCLj6V6LHLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-stream": "^3.0.0", + "temp-dir": "^3.0.0", + "type-fest": "^2.12.2", + "unique-string": "^3.0.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/tempy/node_modules/type-fest": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/terser": { "version": "5.48.0", "resolved": "https://registry.npmjs.org/terser/-/terser-5.48.0.tgz", @@ -8876,6 +12089,41 @@ "dev": true, "license": "MIT" }, + "node_modules/text-decoder": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.7.tgz", + "integrity": "sha512-vlLytXkeP4xvEq2otHeJfSQIRyWxo/oZGEbXrtEEF9Hnmrdly59sUbzZ/QgyWuLYHctCHxFF4tRQZNQ9k60ExQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "b4a": "^1.6.4" + } + }, + "node_modules/text-decoder/node_modules/b4a": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.8.1.tgz", + "integrity": "sha512-aiqre1Nr0B/6DgE2N5vwTc+2/oQZ4Wh1t4NznYY4E00y8LCt6NqdRv81so00oo27D8MVKTpUa/MwUUtBLXCoDw==", + "dev": true, + "license": "Apache-2.0", + "peerDependencies": { + "react-native-b4a": "*" + }, + "peerDependenciesMeta": { + "react-native-b4a": { + "optional": true + } + } + }, + "node_modules/text-extensions": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-1.9.0.tgz", + "integrity": "sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10" + } + }, "node_modules/text-segmentation": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/text-segmentation/-/text-segmentation-1.0.3.tgz", @@ -8886,6 +12134,13 @@ "utrie": "^1.0.2" } }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", + "dev": true, + "license": "MIT" + }, "node_modules/through2": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz", @@ -8912,6 +12167,16 @@ "url": "https://github.com/sponsors/SuperchupuDev" } }, + "node_modules/tmp": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.7.tgz", + "integrity": "sha512-e0votIpp4Uo2AJYSzVHV6xCcawuiez3DzqDAbrTc3YxBkplN6e+dM13ZeIcZnDg/QpSuU2zfZ3rzwY8ukEnaXw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.14" + } + }, "node_modules/toidentifier": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", @@ -8939,6 +12204,13 @@ "url": "https://github.com/sponsors/Borewit" } }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "dev": true, + "license": "MIT" + }, "node_modules/tree-kill": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", @@ -8948,6 +12220,16 @@ "tree-kill": "cli.js" } }, + "node_modules/trim-newlines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", + "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/ts-node": { "version": "10.9.2", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", @@ -9048,6 +12330,32 @@ "fsevents": "~2.3.3" } }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/type-fest": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", + "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/type-is": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.1.0.tgz", @@ -9115,6 +12423,20 @@ "node": ">=14.17" } }, + "node_modules/uglify-js": { + "version": "3.19.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", + "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", + "dev": true, + "license": "BSD-2-Clause", + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, "node_modules/uid": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/uid/-/uid-2.0.2.tgz", @@ -9145,6 +12467,22 @@ "integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==", "license": "MIT" }, + "node_modules/unique-string": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-3.0.0.tgz", + "integrity": "sha512-VGXBUVwxKMBUznyffQweQABPRRW1vHZAbadFZud4pLFAqRGvv/96vafgjWFqzourzr8YonlQiPgH0YCJfawoGQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "crypto-random-string": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/universalify": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", @@ -9238,6 +12576,17 @@ "base64-arraybuffer": "^1.0.2" } }, + "node_modules/uuid": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-7.0.3.tgz", + "integrity": "sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg==", + "deprecated": "uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028).", + "dev": true, + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/v8-compile-cache-lib": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", @@ -9245,6 +12594,17 @@ "dev": true, "license": "MIT" }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", @@ -9355,6 +12715,13 @@ "defaults": "^1.0.3" } }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "dev": true, + "license": "BSD-2-Clause" + }, "node_modules/webpack": { "version": "5.106.2", "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.106.2.tgz", @@ -9461,6 +12828,17 @@ "url": "https://opencollective.com/webpack" } }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -9476,6 +12854,20 @@ "node": ">= 8" } }, + "node_modules/which-module": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", + "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", + "dev": true, + "license": "MIT" + }, "node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", @@ -9500,9 +12892,9 @@ "license": "ISC" }, "node_modules/ws": { - "version": "8.20.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.20.1.tgz", - "integrity": "sha512-It4dO0K5v//JtTXuPkfEOaI3uUN87iYPnqo/ZzqCoG3g8uhA66QUMs/SrM0YK7/NAu+r4LMh/9dq2A7k+rHs+w==", + "version": "8.21.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.0.tgz", + "integrity": "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==", "license": "MIT", "engines": { "node": ">=10.0.0" @@ -9520,6 +12912,43 @@ } } }, + "node_modules/xcode": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/xcode/-/xcode-3.0.1.tgz", + "integrity": "sha512-kCz5k7J7XbJtjABOvkc5lJmkiDh8VhjVCGNiqdKCscmVpdVUpEAyXv1xmCLkQJ5dsHqx3IPO4XW+NTDhU/fatA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "simple-plist": "^1.1.0", + "uuid": "^7.0.3" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/xml-js": { + "version": "1.6.11", + "resolved": "https://registry.npmjs.org/xml-js/-/xml-js-1.6.11.tgz", + "integrity": "sha512-7rVi2KMfwfWFl+GpPg6m80IVMWXLRjO+PxTq7V2CDhoGak0wzYzFgUY2m4XJ47OGdXd8eLE8EmwfAmdjw7lC1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "sax": "^1.2.4" + }, + "bin": { + "xml-js": "bin/cli.js" + } + }, + "node_modules/xml-js/node_modules/sax": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.0.tgz", + "integrity": "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=11.0.0" + } + }, "node_modules/xml2js": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.6.2.tgz", @@ -9559,6 +12988,16 @@ "node": ">=0.4.0" } }, + "node_modules/xpath": { + "version": "0.0.32", + "resolved": "https://registry.npmjs.org/xpath/-/xpath-0.0.32.tgz", + "integrity": "sha512-rxMJhSIoiO8vXcWvSifKqhvV96GjiD5wYb8/QHdoRyQvraTpp4IEv944nhGausZZ3u7dhQXteZuZbaqfpB7uYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6.0" + } + }, "node_modules/xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", diff --git a/task.md b/task.md new file mode 100644 index 0000000..e820c74 --- /dev/null +++ b/task.md @@ -0,0 +1,46 @@ +# Nhiệm vụ: Sửa giao diện Bong bóng Tour & Kết nối Thành viên + +- [x] [Database] Thêm model `UserConnection` và `DirectMessage` vào schema.prisma và chạy db:migrate +- [x] [Backend] Tạo các API quản lý kết nối (`connections`) và tin nhắn (`messages`) cùng WebSocket Gateway +- [x] [Frontend] Sửa luồng redirect sau khi đăng nhập/đăng ký bằng Google trong App.tsx +- [x] [Frontend] Sửa vị trí tâm neo bong bóng Tour (iconSize & iconAnchor) trong ExploreMap.tsx +- [x] [Frontend] Thêm màu viền (xanh, đỏ, trắng) và tooltip phân loại Tour trên bản đồ khám phá +- [x] [Frontend] Chặn click trái đối với tour viền xanh; cài đặt touchstart/contextmenu để xin join bằng nhấn giữ/click phải +- [x] [Frontend] Xây dựng trang Dashboard thành viên (`MemberDashboard.tsx`) tích hợp chat box, quản lý kết nối bạn bè/gia đình, quản lý tour và ảnh cá nhân + +## Giai đoạn 2: Sửa đổi giao diện Dashboard Thành viên (Desktop & Mobile) +- [x] [Frontend] Cập nhật giao diện MyPhotosPage.tsx thành Dark Slate theme để đồng bộ với dashboard +- [x] [Frontend] Tích hợp trực tiếp MyPhotosPage vào tab Thư viện ảnh trong MemberDashboard.tsx +- [x] [Frontend] Sửa giao diện Desktop của MemberDashboard.tsx: căn giữa h-[90vh], chỉnh màu thẻ tour, và sửa đè viền Mạng lưới kết nối +- [x] [Frontend] Sửa giao diện Mobile của MemberDashboard.tsx: thiết lập hiển thị fullscreen menu các nút, và quay lại menu từ các trang chi tiết/chat +- [x] Xác minh toàn bộ tính năng và lỗi hiển thị trên cả desktop và điện thoại di động + +## Giai đoạn 3: Realtime Notification qua WebSockets +- [x] [Backend] Thêm notifyConnectionAccepted và notifyJoinRequestAccepted vào CommentGateway trong main.ts +- [x] [Backend] Inject CommentGateway và trigger notifyConnectionAccepted trong ConnectionController +- [x] [Backend] Inject CommentGateway và trigger notifyJoinRequestAccepted trong TourController +- [x] [Frontend] Thêm event listeners (connectionAccepted, joinRequestAccepted) vào socket hook trong MemberDashboard.tsx +- [x] Xác minh các sự kiện thông báo thời gian thực trên giao diện người dùng + +## Giai đoạn 4: Notification Badges & System Push Notifications (Mobile/Desktop) +- [x] [Frontend] Khởi tạo yêu cầu quyền Notification hệ thống khi mount và tạo helper showSystemNotification +- [x] [Frontend] Khai báo các state unreadChatSenders và unreadTourSenders cùng helper isMemberOfMyTours +- [x] [Frontend] Cập nhật event listener messageReceived để kích hoạt push notification và set các state chưa đọc +- [x] [Frontend] Hiển thị Badge thông báo trên các tab/menu button và danh sách chat liên hệ +- [x] [Frontend] Xử lý xóa trạng thái chưa đọc khi chọn chat user hoặc click tab Hành trình của tôi +- [x] Xác minh hiển thị Badge và thông báo đẩy hệ thống thành công trên cả desktop/mobile + +## Giai đoạn 5: Trò chuyện Nhóm Hành trình & Đính kèm (Ảnh nén 2K + Vị trí GPS) +- [x] [Database] Cập nhật schema.prisma (thêm TourMessage và các trường DirectMessage), chạy db migration +- [x] [Backend] Thêm UploadController, cập nhật DirectMessageController và tạo TourMessageController trong main.ts +- [x] [Frontend] Viết component TourChat.tsx (nén canvas 2K, tải ảnh, lấy vị trí GPS) +- [x] [Frontend] Tích hợp tab Trò chuyện và sự kiện socket vào TourDetailPage.tsx +- [x] [Frontend] Nâng cấp giao diện chat trong MemberDashboard.tsx (nén canvas 2K, tải ảnh, GPS, và badge tour chat) +- [x] Xác minh biên dịch TypeScript và chạy các kịch bản kiểm thử trò chuyện trực tiếp/nhóm thành công + +## Giai đoạn 6: Nhắc tên (@Mention) trong Chat & Tắt thông báo +- [x] [Frontend] Sửa lỗi hiển thị bong bóng tour trên bản đồ bằng cách quét tất cả chặng (ExploreMap.tsx) +- [x] [Backend] Cập nhật API gửi tin nhắn nhóm tour để nhận taggedUserIds và gửi socket notification riêng (main.ts) +- [x] [Frontend] Thêm chức năng tag @ thành viên trong component TourChat.tsx +- [x] [Frontend] Thêm nút Bật/Tắt thông báo đẩy & logic Mute trong MemberDashboard.tsx +- [x] Xác minh biên dịch TypeScript và chạy các kịch bản kiểm thử tag & mute thành công diff --git a/ui_implementation_plan.md b/ui_implementation_plan.md new file mode 100644 index 0000000..5a17822 --- /dev/null +++ b/ui_implementation_plan.md @@ -0,0 +1,185 @@ +# Kế hoạch: Tái thiết kế giao diện xem ảnh và bình luận trên Mobile (PublicPhotoModal) + +Thiết kế lại giao diện mobile của modal xem ảnh công khai để trải nghiệm cuộn trang tự nhiên hơn, tương tự như Instagram/Twitter — ảnh chiếm toàn bộ chiều rộng màn hình từ trên xuống, thông tin người đăng và các nút được bố cục rõ ràng, sau đó cuộn xuống xem bình luận một cách liền mạch. + +--- + +## Phân tích hiện trạng + +Cấu trúc hiện tại của modal trên mobile (`flex flex-col`): +1. **Container (90vh, overflow-hidden)** — toàn bộ nội dung bị "kẹt" trong 90vh +2. Phần ảnh (`h-[45vh]`) — cố định, không thể co giãn +3. Phần "Mobile Uploader" strip — cố định (`shrink-0`) +4. Phần comment panel (`flex-1 min-h-0`) — chỉ phần danh sách comment cuộn bên trong + +**Vấn đề**: Ảnh bị giới hạn ở `h-[45vh]`, có padding `p-4` từ container ngoài nên không chạm mép màn hình. Nút like nằm ở góc trên trái (gần nút X), người đăng nằm ở vùng riêng giữa ảnh và comment, không cuộn cùng content. + +--- + +## Thay đổi đề xuất + +### [MODIFY] [PublicPhotoModal.tsx](file:///home/locpham/travelplanning/frontend/src/components/PublicPhotoModal.tsx) + +#### 1. Outer wrapper — Bỏ padding và căn giữa + +**Hiện tại** (line 358): +``` +
+``` + +**Thay thành** — trên mobile modal full screen, không padding, không căn giữa. Desktop giữ nguyên layout cũ: +``` +
+``` + +#### 2. Container modal — Mobile: full screen cuộn tự nhiên; Desktop: giữ nguyên + +**Hiện tại** (line 366): +``` +
+``` + +**Thay thành**: +- Mobile: `fixed inset-0 overflow-y-auto flex flex-col bg-slate-900` — toàn màn hình, cuộn tự nhiên theo trang +- Desktop: `relative w-full max-w-5xl h-[85vh] overflow-hidden flex flex-row rounded-[32px]` + +``` +
+``` + +#### 3. Nút X — Fixed ở góc trên phải (hiện tại đúng, cần giữ nguyên) + +Nút X hiện đang dùng `fixed` — **giữ nguyên**, chỉ điều chỉnh `top` để tính safe-area: +``` +className="fixed top-[calc(1rem+env(safe-area-inset-top,0px))] right-4 z-50 ..." +``` + +#### 4. Phần ảnh (Left Side Photo) — Mobile: full width, không giới hạn chiều cao, aspect ratio tự nhiên + +**Hiện tại** (line 377): +``` +
+``` + +**Thay thành** — Trên mobile: ảnh full width, chiều cao theo tỷ lệ ảnh (aspect-ratio), không giới hạn, chạm mép trên màn hình (không có padding trên): +``` +
+``` + +Với ảnh bên trong: +``` + +``` + +#### 5. Nút Like — Di chuyển từ góc trên trái sang góc dưới phải của vùng ảnh + +**Hiện tại**: `absolute top-[...] left-4` — góc trên bên trái. + +**Thay thành**: `absolute bottom-4 right-4` — góc dưới bên phải của ảnh, không bị che bởi thanh địa chỉ và không xung đột với nút X: +``` +className="absolute bottom-4 right-4 z-40 ..." +``` + +#### 6. Thông tin người đăng — Di chuyển thành overlay góc trên trái trên ảnh + +Hiện tại "Mobile Uploader strip" là một block riêng biệt giữa ảnh và comment (`block md:hidden p-6 ...`). + +**Thay thành**: Overlay trực tiếp trên ảnh ở góc **trên bên trái**, nằm trong vùng ảnh, có safe-area padding: +```html + +
+
+ +
+ {photo.uploader?.name || 'Ẩn danh'} +
+``` + +#### 7. "Mobile Uploader strip" block — Xóa block cũ + +Block `{/* Mobile Only: Uploader details & divider */}` ở lines 597–623 sẽ được **xóa** vì thông tin người đăng đã được hiển thị trực tiếp trên ảnh ở bước 6. + +Nếu `isAuthorized && photo.originalUrl` (nút Tải ảnh gốc): sẽ được giữ lại nhưng chuyển vào trong phần metadata/overlay của ảnh hoặc đặt trước phần comments. + +#### 8. Phần Comments — Cuộn cùng với nội dung (không bị kẹt trong flex) + +Trên mobile, toàn bộ trang cuộn — phần comment không cần `overflow-y-auto` riêng nữa (vì parent đã cuộn): + +**Hiện tại** (line 626): +``` +
+``` + +**Thay thành**: +``` +
+``` + +Danh sách bình luận (line 640): +``` +
+``` +→ Trên mobile không giới hạn `overflow-y-auto`: +``` +
+``` + +#### 9. Comment Input — Sticky ở cuối trang trên mobile + +Thay vì nằm cố định cuối flex column, ô nhập bình luận sẽ sticky ở bottom: +``` +
+``` + +--- + +## Tóm tắt layout cuối cùng trên mobile + +``` +┌─────────────────────────────────────────────┐ ← màn hình (fixed inset-0) +│ [X nút đóng] (góc trên phải, fixed/z-50) +│─────────────────────────────────────────────│ +│ │ +│ [Người đăng] (overlay TL) │ +│ │ ← Vùng ảnh (full width, +│ ẢNH │ height theo tỷ lệ) +│ │ +│ [❤ Like + số] │ +│─────────────────────────────────────────────│ +│ 📷 Timeline ảnh (nếu có nhiều ảnh) │ ← Cuộn cùng trang +│─────────────────────────────────────────────│ +│ Tiêu đề, mô tả, ngày chụp, địa điểm │ ← Info overlay panel +│─────────────────────────────────────────────│ +│ [Tải ảnh gốc] (nếu là chủ sở hữu) │ +│─────────────────────────────────────────────│ +│ 💬 Comment 1 │ ← Cuộn cùng trang +│ 💬 Comment 2 │ +│ 💬 Comment 3 │ +│ ... │ +│─────────────────────────────────────────────│ +│ [Viết bình luận...] [▶ Gửi] (sticky bottom)│ +└─────────────────────────────────────────────┘ +``` + +--- + +## Verification Plan + +### Automated Tests +- `npm run build -w frontend` — không có lỗi compilation. + +### Manual Verification +1. Mở app trên Chrome mobile (Android) → modal mở full screen, ảnh chạm top, không có padding. +2. Nút X hiển thị góc trên phải, luôn visible. +3. Tên người đăng hiển thị overlay góc trên trái, không nhầm với người bình luận. +4. Nút like hiển thị góc dưới phải của ảnh. +5. Vuốt lên → ảnh và bình luận cuộn cùng nhau một cách liền mạch. +6. Ô bình luận sticky ở dưới cùng. +7. Trên desktop (≥768px) layout 2 cột giữ nguyên hoàn toàn. +8. Safe area insets hoạt động đúng trên iPhone (notch/Dynamic Island) dùng Safari. diff --git a/yotrip_system_fix_plan.md b/yotrip_system_fix_plan.md new file mode 100644 index 0000000..50ce739 --- /dev/null +++ b/yotrip_system_fix_plan.md @@ -0,0 +1,171 @@ +# Kế hoạch triển khai: Tính năng nâng cấp & Sửa lỗi hệ thống YoTrip + +Bản kế hoạch này phác thảo chi tiết phương án kỹ thuật để thực hiện các tính năng mới và khắc phục các lỗi tồn tại trong dự án theo yêu cầu. + +--- + +## User Review Required + +> [!IMPORTANT] +> - **Cập nhật database schema (Prisma Migrations)**: Chúng ta cần thêm bảng mới `TourNote` và `RecommendedLocation`, đồng thời bổ sung các trường `isDeleted` và `deletedAt` cho bảng `Tour` và `Photo` để phục vụ tính năng Thùng rác (Trash Bin). Việc này yêu cầu chạy prisma migrations (`npx prisma migrate dev`). +> - **Thay đổi Nginx config**: Cấu hình Nginx trong Docker production cần bổ sung `client_max_body_size 50M;` để hỗ trợ tải ảnh lớn lên proxy trước khi nén trên client, tránh lỗi HTTP 413. +> - **Cơ chế Hybrid Lưu trữ Ghi chú (Notes)**: Ghi chú sẽ được lưu trữ song song (hybrid) cả ở `localStorage` (để hiển thị tức thời và hoạt động offline) lẫn đồng bộ lên server (để lưu trữ lâu dài và cho phép Admin kiểm duyệt/moderation). Khi mở trang ghi chú, ứng dụng sẽ tải dữ liệu từ cache `localStorage` trước, sau đó fetch dữ liệu mới nhất từ server để đồng bộ và cập nhật lại cache. + +--- + +## Proposed Changes + +### 1. Cơ sở dữ liệu (Database Schema) + +#### [MODIFY] [schema.prisma](file:///home/locpham/travelplanning/backend/prisma/schema.prisma) +- **Bảng `Photo` & `Tour`**: Bổ sung hai trường `isDeleted Boolean @default(false)` và `deletedAt DateTime?` để hỗ trợ cơ chế soft-delete (chuyển vào thùng rác). +- **Bảng `ModerationSetting`**: Thêm trường `trashRetentionDays Int @default(30)` để lưu trữ số ngày lưu trữ trong thùng rác do admin quy định. +- **Thêm bảng `TourNote`**: Lưu trữ ghi chú của Tour liên kết với `Tour` và `User` sở hữu: + ```prisma + model TourNote { + id String @id @default(uuid()) + tourId String + tour Tour @relation(fields: [tourId], references: [id], onDelete: Cascade) + userId String + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + title String + content String @db.Text + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + isDeleted Boolean @default(false) + deletedAt DateTime? + } + ``` +- **Thêm bảng `RecommendedLocation`**: Lưu trữ các địa điểm đề xuất: + ```prisma + model RecommendedLocation { + id String @id @default(uuid()) + type String // e.g. "RESTAURANT", "HOTEL", "HOMESTAY" + name String + phone String? + email String? + address String? + latitude Float? + longitude Float? + description String @db.Text + stars Int @default(5) // Số sao đánh giá từ 1 đến 5 + isApproved Boolean @default(false) + createdAt DateTime @default(now()) + } + ``` + +--- + +### 2. Backend API (`backend/src/main.ts`) + +- **Soft-Delete cho Tours, Photos, và Notes**: + - Cập nhật hàm `deleteTour`, `deletePhoto` để chỉ set `isDeleted: true` và `deletedAt: new Date()` thay vì xóa vĩnh viễn. + - Cập nhật tất cả các truy vấn tour, ảnh (public & private) để loại trừ các bản ghi có `isDeleted: true`. +- **TourNote API**: + - `GET /api/v1/tours/:tourId/notes`: Lấy danh sách ghi chú của một tour (phải là thành viên tour hoặc admin). + - `POST /api/v1/tours/:tourId/notes`: Tạo ghi chú cho tour. + - `PUT /api/v1/tours/:tourId/notes/:noteId`: Sửa ghi chú. + - `DELETE /api/v1/tours/:tourId/notes/:noteId`: Soft-delete ghi chú của tour. +- **RecommendedLocation API**: + - `GET /api/v1/recommendations`: Lấy danh sách địa điểm đề xuất đã duyệt (`isApproved: true`). + - `POST /api/v1/recommendations`: Người dùng gửi đề xuất địa điểm mới. + - `GET /api/v1/admin/recommendations`: Admin xem toàn bộ danh sách đề xuất. + - `PATCH /api/v1/admin/recommendations/:id/approve`: Admin phê duyệt địa điểm đề xuất. + - `DELETE /api/v1/admin/recommendations/:id`: Admin xóa đề xuất. +- **Admin Trash Bin API**: + - `GET /api/v1/admin/trash`: Lấy danh sách các đối tượng trong thùng rác (phân loại Tours, Photos, Notes) kèm thông tin ngày xóa. + - `POST /api/v1/admin/trash/restore`: Phục hồi các đối tượng được chọn (`isDeleted: false`). + - `POST /api/v1/admin/trash/delete-permanent`: Xóa vĩnh viễn các đối tượng được chọn khỏi ổ đĩa và DB. + - `PATCH /api/v1/admin/trash/retention-days`: Cập nhật cấu hình `trashRetentionDays`. +- **Cronjob dọn dẹp tự động (Background Service)**: + - Khởi tạo một task chạy định kỳ (24 giờ một lần) quét qua các bảng `Tour`, `Photo`, `TourNote` có `isDeleted: true` và `deletedAt` đã quá hạn so với cấu hình `trashRetentionDays` để tự động xóa vĩnh viễn bản ghi và tệp vật lý. + +--- + +### 3. Frontend & UI + +#### [MODIFY] [nginx.conf](file:///home/locpham/travelplanning/frontend/nginx.conf) +- Thêm dòng `client_max_body_size 50M;` vào cấu hình block `server` của Nginx để khắc phục triệt để lỗi `413 (Request Entity Too Large)`. + +#### [NEW] [image.ts](file:///home/locpham/travelplanning/frontend/src/utils/image.ts) +- Viết hàm tiện ích nén ảnh trên Client sử dụng Canvas API, giảm kích thước ảnh về chiều dài nhất tối đa **2048px (2K)**, chuyển định dạng sang `image/jpeg` chất lượng `0.85` trước khi tải lên. +- Áp dụng hàm nén này cho: + - Chụp ảnh/Tải ảnh ẩn danh ở [LandingPage.tsx](file:///home/locpham/travelplanning/frontend/src/pages/LandingPage.tsx) + - Tải ảnh lên trong Tour ở [AddPhotoModal.tsx](file:///home/locpham/travelplanning/frontend/src/components/AddPhotoModal.tsx) + - Các vị trí upload ảnh khác. + +#### [MODIFY] [LoginModal.tsx](file:///home/locpham/travelplanning/frontend/src/components/LoginModal.tsx) +- Thêm class CSS `max-h-[90vh] overflow-y-auto` vào container chính của modal để tránh bị che mất nút đăng nhập trên các trình duyệt di động có chiều cao màn hình nhỏ hoặc khi bàn phím ảo hiển thị. + +#### [MODIFY] [TourDetailPage.tsx](file:///home/locpham/travelplanning/frontend/src/pages/TourDetailPage.tsx) +- **Khắc phục lỗi xuất PDF (`oklab` error)**: + - Thay vì clone trực tiếp DOM timeline chứa màu oklab phức tạp của Tailwind v4, chúng ta sẽ xây dựng trực tiếp một phần tử HTML `div` đơn giản, dùng cấu trúc bảng hoặc danh sách căn lề cơ bản, sử dụng màu HEX tiêu chuẩn (`#ffffff`, `#3b82f6`, v.v.). + - Trước khi gọi `html2pdf().from(element).save()`, tạm thời đặt `.disabled = true` cho tất cả các thẻ `