commit 0875890dc70aa7629437fadf4a4cf146e122ff5d Author: Pierre Date: Sun Aug 23 09:04:47 2026 +0000 Initial commit: Club DynaSQY diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5f2b225 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +node_modules/ +.env +uploads/ +gallery/ +*.log +.DS_Store diff --git a/README.md b/README.md new file mode 100644 index 0000000..014b3f2 --- /dev/null +++ b/README.md @@ -0,0 +1,97 @@ +# Club DynaSQY - Application de Gestion + +Application web de gestion du club **Dyna Biz Coignières** : membres, invités, événements, honneurs/recommandations, absences, galerie photos, invitations, notifications push, éditeur de présentations/slideshow, exports et logs d'activité. + +## Stack technique + +| Composant | Technologie | +|-----------|------------| +| Backend | Node.js 20 + Express | +| Frontend | React 18 + Vite 5 + Tailwind 4 | +| Base de données | MariaDB 10.11 (Docker) | +| Authentification | JWT (jsonwebtoken) | +| Notifications push | web-push (VAPID) | +| Emails | Nodemailer (SMTP Infomaniak) | +| PWA | vite-plugin-pwa (service worker injectManifest) | + +## Déploiement + +```bash +cd /opt/dyna +docker compose up -d + +# Initialiser les mots de passe des utilisateurs +docker compose exec backend node seed.js +``` + +L'application est accessible sur le port **3001** (frontend nginx) qui proxy vers le backend sur le port **3000**. + +## Structure + +``` +/opt/dyna/ +├── backend/ +│ ├── server.js # Point d'entrée Express +│ ├── config/db.js # Connexion MariaDB +│ ├── middleware/auth.js # JWT middleware +│ ├── controllers/ # Logique métier +│ ├── routes/ # Routes API +│ └── seed.js # Initialisation des comptes +├── frontend/ +│ └── src/ # Composants React + PWA +├── database/ # Scripts SQL +├── uploads/ # Fichiers uploadés (bind mount) +├── gallery/ # Photos galerie (bind mount) +├── docker-compose.yml # Stack complète +└── nginx.conf # Config reverse proxy +``` + +## Variables d'environnement (backend) + +| Variable | Description | Valeur par défaut | +|----------|-------------|-------------------| +| `DB_HOST` | Host MariaDB | `db` (service Docker) | +| `DB_PORT` | Port MariaDB | `3306` | +| `DB_USER` | Utilisateur BDD | `dynausr` | +| `DB_PASSWORD` | Mot de passe BDD | `L0relei69` | +| `DB_NAME` | Nom de la BDD | `dynasqy` | +| `JWT_SECRET` | Secret JWT | `L0relei69SuperSecret2026` | +| `PORT` | Port backend | `3000` | +| `SMTP_HOST` | Serveur SMTP | `mail.infomaniak.com` | +| `SMTP_PORT` | Port SMTP | `465` | +| `SMTP_USER` | Email SMTP | `dyna@secourit.fr` | +| `SMTP_PASS` | Mot de passe SMTP | `cAq0cF9t41BE458sdAuzea45215` | +| `EMAIL_FROM` | Email expéditeur | `dyna@secourit.fr` | +| `PUBLIC_URL` | URL publique | `https://dyna.secourit.fr` | +| `VAPID_PUBLIC_KEY` | Clé publique VAPID | configuré dans docker-compose | +| `VAPID_PRIVATE_KEY` | Clé privée VAPID | configuré dans docker-compose | + +## Développement + +```bash +# Backend +cd backend +npm install +node seed.js # Générer les mots de passe +node server.js # Démarre sur :3000 + +# Frontend +cd frontend +npm install +npm run dev # Vite sur :5173 avec proxy /api -> :3000 +``` + +## Domaine de production + +- **URL** : https://dyna.secourit.fr +- **LXC** : 103 (IP 10.10.10.20) +- **SSL** : Let's Encrypt via Nginx Proxy Manager +- **Preprod** : https://dyna-preprod.secourit.fr (LXC 110, port 3101) + +## Comptes de test + +| Rôle | Email | Mot de passe | +|------|-------|--------------| +| Admin | admin@dynasqy.fr | admin123 | +| Membre | jean.dupont@email.fr | member123 | +| Membre | marie.durand@email.fr | member123 | diff --git a/backend/.env.example b/backend/.env.example new file mode 100644 index 0000000..c37dd02 --- /dev/null +++ b/backend/.env.example @@ -0,0 +1,15 @@ +DB_HOST=localhost +DB_PORT=3306 +DB_USER=club_dynasqy +DB_PASSWORD=change_this_password +DB_NAME=club_dynasqy +JWT_SECRET=change_this_jwt_secret_key_64chars_min +PORT=3000 + +# SMTP (Infomaniak) +SMTP_HOST=mail.infomaniak.com +SMTP_PORT=465 +SMTP_USER=invitation@votredomaine.com +SMTP_PASS=your_smtp_password +EMAIL_FROM=invitation@votredomaine.com +PUBLIC_URL=https://dyna.secourit.fr diff --git a/backend/.gitignore b/backend/.gitignore new file mode 100644 index 0000000..713d500 --- /dev/null +++ b/backend/.gitignore @@ -0,0 +1,2 @@ +node_modules/ +.env diff --git a/backend/Dockerfile b/backend/Dockerfile new file mode 100644 index 0000000..50e9107 --- /dev/null +++ b/backend/Dockerfile @@ -0,0 +1,14 @@ +FROM node:20-alpine + +RUN apk add --no-cache libreoffice + +WORKDIR /app + +COPY package.json package-lock.json* ./ +RUN npm install --production + +COPY . . + +EXPOSE 3000 + +CMD ["node", "server.js"] diff --git a/backend/config/db.js b/backend/config/db.js new file mode 100644 index 0000000..6f2918d --- /dev/null +++ b/backend/config/db.js @@ -0,0 +1,16 @@ +const mysql = require('mysql2/promise'); +require('dotenv').config(); + +const pool = mysql.createPool({ + host: process.env.DB_HOST || 'localhost', + port: parseInt(process.env.DB_PORT, 10) || 3306, + user: process.env.DB_USER || 'club_dynasqy', + password: process.env.DB_PASSWORD || '', + database: process.env.DB_NAME || 'club_dynasqy', + charset: 'utf8mb4', + waitForConnections: true, + connectionLimit: 10, + queueLimit: 0, +}); + +module.exports = pool; diff --git a/backend/config/email.js b/backend/config/email.js new file mode 100644 index 0000000..8e39e83 --- /dev/null +++ b/backend/config/email.js @@ -0,0 +1,73 @@ +const nodemailer = require('nodemailer'); + +const transporter = nodemailer.createTransport({ + host: process.env.SMTP_HOST || 'mail.infomaniak.com', + port: parseInt(process.env.SMTP_PORT, 10) || 465, + secure: parseInt(process.env.SMTP_PORT, 10) === 465 || true, + auth: { + user: process.env.SMTP_USER, + pass: process.env.SMTP_PASS, + }, +}); + +async function sendInvitationEmail(guest, inviter, token, baseUrl) { + const link = `${baseUrl}/invitation/${token}`; + const dateStr = guest.date_club + ? new Date(guest.date_club).toLocaleDateString('fr-FR', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' }) + : 'à venir'; + + const html = ` + + + + + + +
+ + + +
+

Dyna Biz Coignieres

+
+

Bonjour ${guest.prenom} ${guest.nom},

+

+ ${inviter.prenom} ${inviter.nom} de Dyna Biz Coignieres vous invite à participer à notre prochain événement réseau. +

+ ${guest.date_club ? `

Date : ${dateStr}

` : ''} +

Horaires : 10h45 - 13h00

+

Lieu : 7 Av. Roger Hennequin, 78190 Trappes

+

Merci de nous confirmer votre présence en cliquant sur l'un des boutons ci-dessous :

+ + + + + +
+ J'accepte + + Je refuse +
+

+ Ou rendez-vous sur :
${link} +

+
+
+ +`; + + try { + await transporter.sendMail({ + from: `"Dyna Biz Coignieres" <${process.env.EMAIL_FROM}>`, + to: guest.email, + subject: `Invitation - Dyna Biz Coignieres ${guest.date_club ? new Date(guest.date_club).toLocaleDateString('fr-FR') : ''}`, + html, + }); + return true; + } catch (err) { + console.error('Email send error:', err); + throw err; + } +} + +module.exports = { transporter, sendInvitationEmail }; diff --git a/backend/controllers/absenceController.js b/backend/controllers/absenceController.js new file mode 100644 index 0000000..586f450 --- /dev/null +++ b/backend/controllers/absenceController.js @@ -0,0 +1,70 @@ +const pool = require('../config/db'); +const { logActivity } = require('../services/activityLog'); + +exports.listByMember = async (req, res) => { + try { + const [rows] = await pool.query(` + SELECT a.*, + u.prenom AS created_prenom, + r.prenom AS remplace_prenom, r.nom AS remplace_nom, + rg.prenom AS remplace_guest_prenom, rg.nom AS remplace_guest_nom + FROM absences a + LEFT JOIN users u ON u.id = a.created_by + LEFT JOIN users r ON r.id = a.remplace_par_id AND (a.remplace_par_type = 'member' OR a.remplace_par_type IS NULL) + LEFT JOIN invites rg ON rg.id = a.remplace_par_guest_id AND a.remplace_par_type = 'guest' + WHERE a.membre_id = ? + ORDER BY a.date_absence DESC + `, [req.params.id]); + res.json(rows); + } catch (err) { + console.error(err); + res.status(500).json({ error: 'Erreur serveur' }); + } +}; + +exports.create = async (req, res) => { + try { + const { membre_id, date_absence, motif, prevenu, remplace_par_type, remplace_par_guest_id, remplace_par_id } = req.body; + const rType = remplace_par_type === 'guest' && remplace_par_guest_id ? 'guest' : 'member' + const rMemberId = rType === 'member' ? (remplace_par_id || null) : null + const rGuestId = rType === 'guest' ? (remplace_par_guest_id || null) : null + await pool.query( + 'INSERT INTO absences (membre_id, date_absence, motif, prevenu, remplace_par_id, remplace_par_type, remplace_par_guest_id, created_by) VALUES (?, ?, ?, ?, ?, ?, ?, ?)', + [membre_id, date_absence, motif || null, prevenu ? 1 : 0, rMemberId, rType, rGuestId, req.user.id] + ); + logActivity(req, 'absence_ajout', { membre_id, date_absence }); + res.status(201).json({ message: 'Absence enregistrée' }); + } catch (err) { + console.error(err); + res.status(500).json({ error: 'Erreur serveur' }); + } +}; + +exports.remove = async (req, res) => { + try { + await pool.query('DELETE FROM absences WHERE id = ?', [req.params.id]); + logActivity(req, 'absence_suppression', { absenceId: req.params.id }); + res.json({ message: 'Absence supprimée' }); + } catch (err) { + console.error(err); + res.status(500).json({ error: 'Erreur serveur' }); + } +}; + +exports.stats = async (req, res) => { + try { + const [rows] = await pool.query(` + SELECT u.id, u.prenom, u.nom, COUNT(a.id) AS total_absences + FROM users u + LEFT JOIN absences a ON a.membre_id = u.id + WHERE u.actif = 1 + GROUP BY u.id + ORDER BY total_absences DESC + LIMIT 10 + `); + res.json(rows); + } catch (err) { + console.error(err); + res.status(500).json({ error: 'Erreur serveur' }); + } +}; \ No newline at end of file diff --git a/backend/controllers/activityLogController.js b/backend/controllers/activityLogController.js new file mode 100644 index 0000000..cc8c065 --- /dev/null +++ b/backend/controllers/activityLogController.js @@ -0,0 +1,32 @@ +const pool = require('../config/db'); + +exports.getLogs = async (req, res) => { + try { + const page = Math.max(1, parseInt(req.query.page) || 1); + const limit = Math.min(100, Math.max(10, parseInt(req.query.limit) || 50)); + const offset = (page - 1) * limit; + const actionFilter = req.query.action || ''; + + let where = ''; + const params = []; + if (actionFilter) { + where = 'WHERE al.action LIKE ?'; + params.push(`%${actionFilter}%`); + } + + const [countRows] = await pool.query( + `SELECT COUNT(*) AS total FROM activity_logs al ${where}`, params + ); + const total = countRows[0].total; + + const [rows] = await pool.query( + `SELECT al.* FROM activity_logs al ${where} ORDER BY al.created_at DESC LIMIT ? OFFSET ?`, + [...params, limit, offset] + ); + + res.json({ logs: rows, total, page, limit, pages: Math.ceil(total / limit) }); + } catch (err) { + console.error(err); + res.status(500).json({ error: 'Erreur' }); + } +}; diff --git a/backend/controllers/adminController.js b/backend/controllers/adminController.js new file mode 100644 index 0000000..f99de31 --- /dev/null +++ b/backend/controllers/adminController.js @@ -0,0 +1,32 @@ +const pool = require('../config/db'); +const { logActivity } = require('../services/activityLog'); + +exports.listUsers = async (req, res) => { + try { + const [rows] = await pool.query( + 'SELECT id, prenom, nom, societe, email, telephone, role, actif, date_adhesion, date_echeance FROM users ORDER BY prenom ASC' + ); + res.json(rows); + } catch (err) { + console.error(err); + res.status(500).json({ error: 'Server error' }); + } +}; + +exports.updateUserRole = async (req, res) => { + try { + const { role } = req.body; + if (!['admin', 'member'].includes(role)) { + return res.status(400).json({ error: 'Role must be admin or member' }); + } + const userId = req.params.id; + if (Number(userId) === req.user.id) { + return res.status(400).json({ error: 'Vous ne pouvez pas modifier votre propre rôle' }); + } + await pool.query('UPDATE users SET role = ? WHERE id = ?', [role, userId]); + logActivity(req, 'role_changement', { targetUserId: userId, newRole: role }); + } catch (err) { + console.error(err); + res.status(500).json({ error: 'Server error' }); + } +}; diff --git a/backend/controllers/authController.js b/backend/controllers/authController.js new file mode 100644 index 0000000..27a6bb8 --- /dev/null +++ b/backend/controllers/authController.js @@ -0,0 +1,73 @@ +const bcrypt = require('bcrypt'); +const jwt = require('jsonwebtoken'); +const pool = require('../config/db'); +const { logActivity } = require('../services/activityLog'); + +exports.login = async (req, res) => { + try { + const { identifier, password } = req.body; + if (!identifier || !password) { + return res.status(400).json({ error: 'Identifiant et mot de passe requis' }); + } + const [rows] = await pool.query( + 'SELECT * FROM users WHERE (prenom = ? OR email = ?) AND actif = 1', + [identifier, identifier] + ); + if (rows.length === 0) { + return res.status(401).json({ error: 'Identifiants invalides' }); + } + const user = rows[0]; + const match = await bcrypt.compare(password, user.password_hash); + if (!match) { + return res.status(401).json({ error: 'Identifiants invalides' }); + } + const token = jwt.sign( + { id: user.id, prenom: user.prenom, nom: user.nom, email: user.email, role: user.role }, + process.env.JWT_SECRET, + { expiresIn: '7d' } + ); + logActivity(user, 'connexion'); + res.json({ + token, + user: { id: user.id, prenom: user.prenom, nom: user.nom, societe: user.societe, email: user.email, role: user.role }, + }); + } catch (err) { + console.error(err); + res.status(500).json({ error: 'Erreur serveur' }); + } +}; + +exports.me = async (req, res) => { + try { + const [rows] = await pool.query( + 'SELECT id, prenom, nom, societe, activite, email, telephone, role, date_adhesion, date_echeance FROM users WHERE id = ?', + [req.user.id] + ); + if (rows.length === 0) return res.status(404).json({ error: 'Utilisateur introuvable' }); + res.json(rows[0]); + } catch (err) { + console.error(err); + res.status(500).json({ error: 'Erreur serveur' }); + } +}; +exports.changePassword = async (req, res) => { + try { + const { currentPassword, newPassword } = req.body; + if (!currentPassword || !newPassword) { + return res.status(400).json({ error: 'Mot de passe actuel et nouveau requis' }); + } + if (newPassword.length < 6) { + return res.status(400).json({ error: 'Le nouveau mot de passe doit faire au moins 6 caractères' }); + } + const [rows] = await pool.query('SELECT password_hash FROM users WHERE id = ?', [req.user.id]); + const match = await bcrypt.compare(currentPassword, rows[0].password_hash); + if (!match) return res.status(401).json({ error: 'Mot de passe actuel incorrect' }); + const hash = await bcrypt.hash(newPassword, 10); + await pool.query('UPDATE users SET password_hash = ? WHERE id = ?', [hash, req.user.id]); + logActivity(req, 'changement_mot_de_passe'); + res.json({ message: 'Mot de passe modifié' }); + } catch (err) { + console.error(err); + res.status(500).json({ error: 'Erreur serveur' }); + } +}; diff --git a/backend/controllers/eventController.js b/backend/controllers/eventController.js new file mode 100644 index 0000000..b225580 --- /dev/null +++ b/backend/controllers/eventController.js @@ -0,0 +1,133 @@ +const pool = require('../config/db'); +const cheerio = require('cheerio'); +const { logActivity } = require('../services/activityLog'); + +const MONTHS_FR = { + 'janv': '01', 'janvier': '01', 'févr': '02', 'février': '02', 'fevrier': '02', + 'mars': '03', 'avr': '04', 'avril': '04', 'mai': '05', 'juin': '06', + 'juil': '07', 'juillet': '07', 'août': '08', 'aout': '08', + 'sept': '09', 'septembre': '09', 'oct': '10', 'octobre': '10', + 'nov': '11', 'novembre': '11', 'déc': '12', 'décembre': '12', 'decembre': '12' +}; + +function parseFrenchDate(str) { + const m = str.trim().match(/^(\d+)\s+(\S+)\s+(\d{4})$/); + if (!m) return null; + const month = MONTHS_FR[m[2].toLowerCase().replace(/\.$/, '')]; + if (!month) return null; + return `${m[3]}-${month}-${m[1].padStart(2, '0')}`; +} + +exports.list = async (req, res) => { + try { + const [rows] = await pool.query('SELECT * FROM evenements ORDER BY date ASC'); + res.json(rows); + } catch (err) { + console.error(err); + res.status(500).json({ error: 'Server error' }); + } +}; + +exports.create = async (req, res) => { + try { + const { date, nom_evenement, lieu, horaire } = req.body; + if (!date || !nom_evenement) { + return res.status(400).json({ error: 'Date and name required' }); + } + const [result] = await pool.query( + 'INSERT INTO evenements (date, nom_evenement, lieu, horaire) VALUES (?, ?, ?, ?)', + [date, nom_evenement, lieu || null, horaire || null] + ); + res.status(201).json({ id: result.insertId, message: 'Event created' }); + logActivity(req, 'evenement_ajout', { date, nom_evenement }); + } catch (err) { + console.error(err); + res.status(500).json({ error: 'Server error' }); + } +}; + +exports.update = async (req, res) => { + try { + const { date, nom_evenement, lieu, horaire } = req.body; + await pool.query( + 'UPDATE evenements SET date = ?, nom_evenement = ?, lieu = ?, horaire = ? WHERE id = ?', + [date, nom_evenement, lieu || null, horaire || null, req.params.id] + ); + res.json({ message: 'Updated' }); + logActivity(req, 'evenement_modification', { id: req.params.id, date, nom_evenement }); + } catch (err) { + console.error(err); + res.status(500).json({ error: 'Server error' }); + } +}; + +exports.remove = async (req, res) => { + try { + await pool.query('DELETE FROM evenements WHERE id = ?', [req.params.id]); + res.json({ message: 'Deleted' }); + logActivity(req, 'evenement_suppression', { id: req.params.id }); + } catch (err) { + console.error(err); + res.status(500).json({ error: 'Server error' }); + } +}; + +exports.deletePast = async (req, res) => { + try { + const [result] = await pool.query('DELETE FROM evenements WHERE date < CURDATE()'); + res.json({ message: `${result.affectedRows} événement(s) supprimé(s)` }); + logActivity(req, 'evenements_passes_supprimes', { count: result.affectedRows }); + } catch (err) { + console.error(err); + res.status(500).json({ error: 'Server error' }); + } +}; + +exports.scrape = async (req, res) => { + try { + const url = 'https://www.rencontres-dirigeants.com/nos-rencontres?region=11&startAt=&endAt=&dateRange=&format=&agency%5B%5D=289&department='; + const resp = await fetch(url); + const html = await resp.text(); + const $ = cheerio.load(html); + + const candidates = []; + + $('article, [class*="card"], [class*="event"], [class*="rencontre"]').each((i, el) => { + const text = $(el).text(); + const lines = text.split('\n').map(l => l.trim()).filter(Boolean); + + let location = '', dateStr = '', timeStr = ''; + for (const line of lines) { + if (!location && line.length > 3 && line[0] === line[0].toUpperCase() && !line.includes('€') && !line.includes('Participation') && !line.includes('places')) { + location = line; + } + const dateMatch = line.match(/(\d{1,2}\s+\S+\s+\d{4})/); + if (dateMatch && !dateStr) dateStr = dateMatch[1]; + const timeMatch = line.match(/(\d{2}h\d{2})\s*[àa]\s*(\d{2}h\d{2})/); + if (timeMatch && !timeStr) timeStr = `${timeMatch[1]} - ${timeMatch[2]}`; + } + + if (!dateStr) return; + const isoDate = parseFrenchDate(dateStr); + if (!isoDate) return; + candidates.push({ isoDate, location, timeStr }); + }); + + const imported = []; + for (const c of candidates) { + const [existing] = await pool.query('SELECT id FROM evenements WHERE date = ? AND nom_evenement LIKE ?', [c.isoDate, `%Rencontre%`]); + if (existing.length > 0) continue; + const title = c.location ? `Rencontre dirigeants - ${c.location}` : 'Rencontre dirigeants Dynabuy'; + await pool.query( + 'INSERT INTO evenements (date, nom_evenement, lieu, horaire) VALUES (?, ?, ?, ?)', + [c.isoDate, title, c.location || null, c.timeStr || null] + ); + imported.push({ date: c.isoDate, title, location: c.location, time: c.timeStr }); + } + + res.json({ message: `${imported.length} événement(s) importé(s)`, events: imported }); + } catch (err) { + console.error(err); + res.status(500).json({ error: 'Erreur lors de l\'import: ' + err.message }); + } +}; diff --git a/backend/controllers/exportController.js b/backend/controllers/exportController.js new file mode 100644 index 0000000..bf85193 --- /dev/null +++ b/backend/controllers/exportController.js @@ -0,0 +1,65 @@ +const pool = require('../config/db'); + +function escapeCsv(val) { + if (val == null) return ''; + const s = String(val); + return s.includes(',') || s.includes('"') || s.includes('\n') || s.includes('\r') + ? `"${s.replace(/"/g, '""')}"` + : s; +} + +function rowsToCsv(headers, rows) { + const lines = [headers.map(escapeCsv).join(',')]; + for (const row of rows) { + lines.push(headers.map((h) => escapeCsv(row[h])).join(',')); + } + return '\uFEFF' + lines.join('\r\n'); +} + +exports.exportCsv = async (req, res) => { + try { + const [members] = await pool.query( + 'SELECT id, prenom, nom, email, telephone, societe, fonction, activite, site_web, role, actif, date_echeance, created_at FROM users ORDER BY nom, prenom' + ); + const [guests] = await pool.query( + `SELECT i.id, i.prenom, i.nom, i.email, i.telephone, i.societe, i.activite, i.site_web, i.statut, i.visit_number, i.invite_par_id, u.prenom AS invite_par_prenom, u.nom AS invite_par_nom, i.date_club, i.commentaires, i.created_at + FROM invites i + LEFT JOIN users u ON u.id = i.invite_par_id + ORDER BY i.date_club DESC, i.nom, i.prenom` + ); + const [recs] = await pool.query( + `SELECT r.id, r.date, r.nom_prospect, r.type, r.transformee, r.ca_realise, + m.prenom AS membre_prenom, m.nom AS membre_nom, m.societe AS membre_societe, + d.prenom AS dest_prenom, d.nom AS dest_nom, d.societe AS dest_societe + FROM recommandations r + LEFT JOIN users m ON m.id = r.membre_id + LEFT JOIN users d ON d.id = r.destinataire_id + ORDER BY r.date DESC` + ); + + const membresCsv = rowsToCsv( + ['id', 'prenom', 'nom', 'email', 'telephone', 'societe', 'fonction', 'activite', 'site_web', 'role', 'actif', 'date_echeance', 'date_inscription'], + members + ); + const invitesCsv = rowsToCsv( + ['id', 'prenom', 'nom', 'email', 'telephone', 'societe', 'activite', 'site_web', 'statut', 'visite_numero', 'invite_par_id', 'invite_par_prenom', 'invite_par_nom', 'date_club', 'commentaires', 'date_creation'], + guests + ); + const recosCsv = rowsToCsv( + ['id', 'date', 'nom_prospect', 'type', 'transformee', 'ca_realise', 'membre_prenom', 'membre_nom', 'membre_societe', 'destinataire_prenom', 'destinataire_nom', 'destinataire_societe'], + recs + ); + + const date = new Date().toISOString().slice(0, 10); + res.setHeader('Content-Type', 'text/csv; charset=utf-8'); + res.setHeader('Content-Disposition', `attachment; filename="dyna-biz-export-${date}.csv"`); + + res.write('=== MEMBRES ===\r\n' + membresCsv + '\r\n\r\n'); + res.write('=== INVITES ===\r\n' + invitesCsv + '\r\n\r\n'); + res.write('=== RECOMMANDATIONS ===\r\n' + recosCsv); + res.end(); + } catch (err) { + console.error(err); + res.status(500).json({ error: 'Erreur export' }); + } +}; diff --git a/backend/controllers/galleryController.js b/backend/controllers/galleryController.js new file mode 100644 index 0000000..bdda684 --- /dev/null +++ b/backend/controllers/galleryController.js @@ -0,0 +1,77 @@ +const fs = require('fs'); +const path = require('path'); +const pool = require('../config/db'); + +const galleryDir = path.join(__dirname, '..', 'gallery'); + +exports.list = async (req, res) => { + try { + if (!fs.existsSync(galleryDir)) return res.json([]); + const dates = fs.readdirSync(galleryDir).filter((d) => fs.statSync(path.join(galleryDir, d)).isDirectory()).sort().reverse(); + const result = []; + for (const date of dates) { + const dirPath = path.join(galleryDir, date); + const files = fs.readdirSync(dirPath) + .filter((f) => /\.(jpg|jpeg|png|gif|webp|heic)$/i.test(f) && f !== '.thumbnails') + .map((f) => ({ filename: f, url: `/gallery/${date}/${encodeURIComponent(f)}` })) + .sort(); + if (files.length > 0) result.push({ date, files }); + } + res.json(result); + } catch (err) { + console.error(err); + res.status(500).json({ error: 'Erreur serveur' }); + } +}; + +exports.upload = async (req, res) => { + try { + if (!req.files || req.files.length === 0) { + return res.status(400).json({ error: 'Aucun fichier sélectionné' }); + } + const { date_club } = req.body; + if (!date_club) return res.status(400).json({ error: 'Date du rendez-vous requise' }); + const dateFolder = date_club.replace(/-/g, ''); + const targetDir = path.join(galleryDir, dateFolder); + if (!fs.existsSync(targetDir)) fs.mkdirSync(targetDir, { recursive: true }); + const saved = []; + for (const file of req.files) { + const ext = path.extname(file.originalname); + const baseName = path.basename(file.originalname, ext); + let destPath = path.join(targetDir, file.originalname); + let counter = 1; + while (fs.existsSync(destPath)) { + destPath = path.join(targetDir, `${baseName}_${counter}${ext}`); + counter++; + } + fs.renameSync(file.path, destPath); + saved.push({ filename: path.basename(destPath), url: `/gallery/${dateFolder}/${path.basename(destPath)}` }); + } + res.json({ message: `${saved.length} photo(s) ajoutée(s)`, files: saved }); + } catch (err) { + console.error(err); + res.status(500).json({ error: 'Erreur upload' }); + } +}; + +exports.deletePhoto = async (req, res) => { + try { + if (req.user.role !== 'admin') return res.status(403).json({ error: 'Admin seulement' }); + const { date, filename } = req.params; + const filePath = path.join(galleryDir, date, filename); + if (!fs.existsSync(filePath)) return res.status(404).json({ error: 'Fichier introuvable' }); + fs.unlinkSync(filePath); + const thumbPath = path.join(galleryDir, date, '.thumbnails', filename); + if (fs.existsSync(thumbPath)) fs.unlinkSync(thumbPath); + const dirPath = path.join(galleryDir, date); + if (fs.existsSync(dirPath) && fs.readdirSync(dirPath).filter((f) => f !== '.thumbnails').length === 0) { + const thumbDir = path.join(galleryDir, date, '.thumbnails'); + if (fs.existsSync(thumbDir)) fs.rmdirSync(thumbDir); + fs.rmdirSync(dirPath); + } + res.json({ message: 'Photo supprimée' }); + } catch (err) { + console.error(err); + res.status(500).json({ error: 'Erreur serveur' }); + } +}; diff --git a/backend/controllers/guestController.js b/backend/controllers/guestController.js new file mode 100644 index 0000000..d5ec611 --- /dev/null +++ b/backend/controllers/guestController.js @@ -0,0 +1,231 @@ +const pool = require('../config/db'); +const crypto = require('crypto'); +const { sendInvitationEmail } = require('../config/email'); +const { logActivity } = require('../services/activityLog'); + +exports.list = async (req, res) => { + try { + const [rows] = await pool.query(` + SELECT i.*, u.prenom AS invite_par_prenom, u.nom AS invite_par_nom + FROM invites i + LEFT JOIN users u ON u.id = i.invite_par_id + WHERE i.statut_candidature IS NULL OR i.statut_candidature != 'accepte' + ORDER BY i.date_club DESC, i.created_at DESC + `); + res.json(rows); + } catch (err) { + console.error(err); + res.status(500).json({ error: 'Server error' }); + } +}; + +exports.getById = async (req, res) => { + try { + const [rows] = await pool.query('SELECT * FROM invites WHERE id = ?', [req.params.id]); + if (rows.length === 0) return res.status(404).json({ error: 'Not found' }); + res.json(rows[0]); + } catch (err) { + console.error(err); + res.status(500).json({ error: 'Server error' }); + } +}; + +exports.search = async (req, res) => { + try { + const { q } = req.query; + if (!q || q.length < 2) return res.json([]); + const [rows] = await pool.query(` + SELECT prenom, nom, societe, activite, telephone, email, site_web, + MAX(visit_number) AS visit_number, COUNT(*) AS total_visits + FROM invites + WHERE CONCAT(prenom, ' ', nom) LIKE ? + GROUP BY prenom, nom, societe, activite, telephone, email, site_web + ORDER BY total_visits DESC LIMIT 20 + `, [`%${q}%`]); + res.json(rows); + } catch (err) { + console.error(err); + res.status(500).json({ error: 'Server error' }); + } +}; + +const BLOCKED_DATES = ['2026-10-20', '2026-12-15']; + +exports.create = async (req, res) => { + try { + const { prenom, nom, societe, activite, telephone, email, site_web, commentaires, date_club } = req.body; + if (!prenom || !nom || !telephone || !email || !date_club) { + return res.status(400).json({ error: 'Tous les champs sauf commentaires sont requis' }); + } + if (BLOCKED_DATES.includes(date_club)) { + return res.status(403).json({ error: "Cette date n'accepte pas de nouveaux invités" }); + } + const invite_par_id = req.user.id; + + // Determine visit_number + const [existing] = await pool.query( + 'SELECT MAX(visit_number) AS max_vn FROM invites WHERE prenom = ? AND nom = ?', + [prenom, nom] + ); + const visit_number = (existing[0].max_vn || 0) + 1; + + // Only admin can add 3rd+ visit + if (visit_number >= 3 && req.user.role !== 'admin') { + return res.status(403).json({ error: 'Seul un administrateur peut ajouter une 3e visite ou plus' }); + } + + const [result] = await pool.query( + `INSERT INTO invites (prenom, nom, societe, activite, telephone, email, site_web, commentaires, date_club, invite_par_id, visit_number) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, + [prenom, nom, societe || null, activite || null, telephone, email, site_web || null, commentaires || null, date_club, invite_par_id, visit_number] + ); + logActivity(req, 'invite_ajout', { prenom, nom }); + res.status(201).json({ id: result.insertId, message: 'Guest added', visit_number }); + } catch (err) { + console.error(err); + res.status(500).json({ error: 'Server error' }); + } +}; + +exports.update = async (req, res) => { + try { + const guestId = req.params.id; + const [existing] = await pool.query('SELECT invite_par_id FROM invites WHERE id = ?', [guestId]); + if (existing.length === 0) return res.status(404).json({ error: 'Not found' }); + if (existing[0].invite_par_id !== req.user.id && req.user.role !== 'admin') { + return res.status(403).json({ error: 'You can only edit your own guests' }); + } + const { prenom, nom, societe, activite, telephone, email, site_web, commentaires, date_club } = req.body; + if (BLOCKED_DATES.includes(date_club)) { + return res.status(403).json({ error: "Cette date n'accepte pas de nouveaux invités" }); + } + await pool.query( + `UPDATE invites SET prenom=?, nom=?, societe=?, activite=?, telephone=?, email=?, site_web=?, commentaires=?, date_club=? WHERE id=?`, + [prenom, nom, societe || null, activite || null, telephone, email, site_web || null, commentaires || null, date_club, guestId] + ); + logActivity(req, 'invite_modification', { guestId, prenom, nom }); + } catch (err) { + console.error(err); + res.status(500).json({ error: 'Server error' }); + } +}; + +exports.remove = async (req, res) => { + try { + const guestId = req.params.id; + const [existing] = await pool.query('SELECT invite_par_id FROM invites WHERE id = ?', [guestId]); + if (existing.length === 0) return res.status(404).json({ error: 'Not found' }); + if (existing[0].invite_par_id !== req.user.id && req.user.role !== 'admin') { + return res.status(403).json({ error: 'Vous ne pouvez supprimer que vos propres invités' }); + } + await pool.query('DELETE FROM invites WHERE id = ?', [guestId]); + logActivity(req, 'invite_suppression', { guestId }); + } catch (err) { + console.error(err); + res.status(500).json({ error: 'Server error' }); + } +}; + +exports.sendInvitation = async (req, res) => { + try { + const guestId = req.params.id; + const [guests] = await pool.query( + `SELECT i.*, u.prenom AS invite_par_prenom, u.nom AS invite_par_nom + FROM invites i + LEFT JOIN users u ON u.id = i.invite_par_id + WHERE i.id = ?`, + [guestId] + ); + if (guests.length === 0) return res.status(404).json({ error: 'Invité non trouvé' }); + const guest = guests[0]; + if (guest.invite_par_id !== req.user.id && req.user.role !== 'admin') { + return res.status(403).json({ error: 'Vous ne pouvez envoyer que vos propres invitations' }); + } + if (!guest.email) { + return res.status(400).json({ error: "Cet invité n'a pas d'adresse email" }); + } + const token = crypto.randomBytes(32).toString('hex'); + const baseUrl = process.env.PUBLIC_URL || 'https://dyna.secourit.fr'; + const [inviter] = await pool.query('SELECT prenom, nom FROM users WHERE id = ?', [guest.invite_par_id]); + await sendInvitationEmail(guest, inviter[0], token, baseUrl); + await pool.query( + 'UPDATE invites SET token=?, mail_envoye=1, invitation_sent_at=NOW(), statut="en_attente" WHERE id=?', + [token, guestId] + ); + logActivity(req, 'invite_email_envoye', { guestId, email: guest.email }); + } catch (err) { + console.error(err); + res.status(500).json({ error: "Erreur lors de l'envoi de l'email" }); + } +}; + +exports.candidature = async (req, res) => { + try { + if (req.user.role !== 'admin') return res.status(403).json({ error: 'Admin seulement' }); + const guestId = req.params.id; + const { action } = req.body; + if (!['start', 'accept', 'reject'].includes(action)) { + return res.status(400).json({ error: 'Action invalide (start/accept/reject)' }); + } + const [guests] = await pool.query('SELECT * FROM invites WHERE id = ?', [guestId]); + if (guests.length === 0) return res.status(404).json({ error: 'Invité non trouvé' }); + const guest = guests[0]; + + if (action === 'start') { + await pool.query( + 'UPDATE invites SET statut_candidature = ?, candidature_date = NOW() WHERE id = ?', + ['candidature', guestId] + ); + logActivity(req, 'candidature_demarrage', { guestId, prenom: guest.prenom, nom: guest.nom }); + return res.json({ message: 'Candidature en cours', statut_candidature: 'candidature' }); + } + + if (action === 'reject') { + await pool.query( + 'UPDATE invites SET statut_candidature = ? WHERE id = ?', + ['refuse', guestId] + ); + logActivity(req, 'candidature_refus', { guestId, prenom: guest.prenom, nom: guest.nom }); + return res.json({ message: 'Candidature refusée', statut_candidature: 'refuse' }); + } + + if (action === 'accept') { + const bcrypt = require('bcrypt'); + const hash = await bcrypt.hash('Bienvenue2026', 10); + const [result] = await pool.query( + `INSERT INTO users (prenom, nom, societe, activite, telephone, email, site_web, role, password_hash, date_adhesion, invite_par_id, actif) + VALUES (?, ?, ?, ?, ?, ?, ?, 'member', ?, CURDATE(), ?, 1)`, + [guest.prenom, guest.nom, guest.societe || null, guest.activite || null, + guest.telephone || null, guest.email || null, guest.site_web || null, + hash, guest.invite_par_id] + ); + await pool.query('DELETE FROM invites WHERE id = ?', [guestId]); + logActivity(req, 'candidature_acceptee', { guestId, prenom: guest.prenom, nom: guest.nom }); + return res.json({ message: 'Membre créé avec succès', user_id: result.insertId, defaultPassword: 'Bienvenue2026' }); + } + } catch (err) { + if (err.code === 'ER_DUP_ENTRY') { + return res.status(400).json({ error: 'Cet email est déjà utilisé par un membre existant' }); + } + console.error(err); + res.status(500).json({ error: 'Erreur serveur' }); + } +}; + +exports.nextEventGuestCount = async (req, res) => { + try { + const [events] = await pool.query( + 'SELECT date FROM evenements WHERE date >= CURDATE() ORDER BY date ASC LIMIT 1' + ); + if (events.length === 0) return res.json({ count: 0, next_event_date: null }); + const nextDate = events[0].date; + const [rows] = await pool.query( + 'SELECT COUNT(*) AS count FROM invites WHERE date_club = ?', + [nextDate] + ); + res.json({ count: rows[0].count, next_event_date: nextDate }); + } catch (err) { + console.error(err); + res.status(500).json({ error: 'Server error' }); + } +}; diff --git a/backend/controllers/honorController.js b/backend/controllers/honorController.js new file mode 100644 index 0000000..4eacc2c --- /dev/null +++ b/backend/controllers/honorController.js @@ -0,0 +1,61 @@ +const pool = require('../config/db'); +const { logActivity } = require('../services/activityLog'); + +exports.list = async (req, res) => { + try { + const [rows] = await pool.query(` + SELECT mh.*, u.prenom, u.nom, u.societe AS membre_societe + FROM mises_en_honneur mh + LEFT JOIN users u ON u.id = mh.membre_id + ORDER BY mh.date ASC + `); + res.json(rows); + } catch (err) { + console.error(err); + res.status(500).json({ error: 'Server error' }); + } +}; + +exports.create = async (req, res) => { + try { + const { date, membre_id, societe, theme_sujet } = req.body; + if (!date || !theme_sujet) { + return res.status(400).json({ error: 'Date and theme required' }); + } + const [result] = await pool.query( + 'INSERT INTO mises_en_honneur (date, membre_id, societe, theme_sujet) VALUES (?, ?, ?, ?)', + [date, membre_id || null, societe || null, theme_sujet] + ); + res.status(201).json({ id: result.insertId, message: 'Honor added' }); + logActivity(req, 'honneur_ajout', { date, membre_id, theme_sujet }); + } catch (err) { + console.error(err); + res.status(500).json({ error: 'Server error' }); + } +}; + +exports.update = async (req, res) => { + try { + const { date, membre_id, societe, theme_sujet } = req.body; + await pool.query( + 'UPDATE mises_en_honneur SET date = ?, membre_id = ?, societe = ?, theme_sujet = ? WHERE id = ?', + [date, membre_id || null, societe || null, theme_sujet, req.params.id] + ); + res.json({ message: 'Updated' }); + logActivity(req, 'honneur_modification', { id: req.params.id, date, membre_id, theme_sujet }); + } catch (err) { + console.error(err); + res.status(500).json({ error: 'Server error' }); + } +}; + +exports.remove = async (req, res) => { + try { + await pool.query('DELETE FROM mises_en_honneur WHERE id = ?', [req.params.id]); + res.json({ message: 'Deleted' }); + logActivity(req, 'honneur_suppression', { id: req.params.id }); + } catch (err) { + console.error(err); + res.status(500).json({ error: 'Server error' }); + } +}; diff --git a/backend/controllers/invitationController.js b/backend/controllers/invitationController.js new file mode 100644 index 0000000..26a7b72 --- /dev/null +++ b/backend/controllers/invitationController.js @@ -0,0 +1,185 @@ +const pool = require('../config/db'); +const path = require('path'); +const fs = require('fs'); + +exports.showPage = async (req, res) => { + try { + const { token } = req.params; + const reponse = req.query.reponse || null; + const [rows] = await pool.query( + `SELECT i.*, u.prenom AS invite_par_prenom, u.nom AS invite_par_nom, u.societe AS invite_par_societe + FROM invites i + LEFT JOIN users u ON u.id = i.invite_par_id + WHERE i.token = ?`, + [token] + ); + if (rows.length === 0) { + return sendErrorPage(res, 'Lien invalide ou expiré.'); + } + const guest = rows[0]; + + if (reponse && ['acceptee', 'refusee'].includes(reponse)) { + await pool.query('UPDATE invites SET statut=? WHERE id=?', [reponse, guest.id]); + guest.statut = reponse; + } + + const dateStr = guest.date_club + ? new Date(guest.date_club).toLocaleDateString('fr-FR', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' }) + : ''; + + const statutLabels = { en_attente: 'En attente', acceptee: 'Acceptée', refusee: 'Refusée' }; + const statutColors = { en_attente: '#f59e0b', acceptee: '#16a34a', refusee: '#dc2626' }; + + const html = ` + + + + +Invitation Dyna Biz + + + +
+
+

Dyna Biz Coignieres

+
+
+

Bonjour ${guest.prenom} ${guest.nom} !

+

${guest.invite_par_prenom} ${guest.invite_par_nom}${guest.invite_par_societe ? ' (' + guest.invite_par_societe + ')' : ''} vous invite au club Dyna Biz Coignieres.

+ ${guest.date_club ? '

Date : ' + dateStr + '

' : ''} +

Statut actuel :
${statutLabels[guest.statut]}

+ + ${guest.statut === 'en_attente' ? ` + + ` : ` +
+ ${guest.statut === 'acceptee' ? 'Vous avez accepté l\'invitation. Merci !' : 'Vous avez refusé l\'invitation.'} +
+ `} + + ${guest.statut === 'acceptee' ? ` +
+

Ajouter une photo

+

Vous pouvez ajouter une photo pour que les membres puissent vous reconnaître.

+ Votre photo +
+ + +

+
+ + ` : ''} +
+
+ +`; + res.send(html); + } catch (err) { + console.error(err); + sendErrorPage(res, 'Une erreur est survenue.'); + } +}; + +exports.respond = async (req, res) => { + try { + const { token } = req.params; + const { reponse } = req.body; + if (!['acceptee', 'refusee'].includes(reponse)) { + return res.status(400).json({ error: 'Réponse invalide' }); + } + const [rows] = await pool.query('SELECT id FROM invites WHERE token = ?', [token]); + if (rows.length === 0) return res.status(404).json({ error: 'Lien invalide' }); + await pool.query('UPDATE invites SET statut=? WHERE id=?', [reponse, rows[0].id]); + res.json({ message: 'Réponse enregistrée' }); + } catch (err) { + console.error(err); + res.status(500).json({ error: 'Server error' }); + } +}; + +exports.uploadPhoto = async (req, res) => { + try { + const { token } = req.params; + const [rows] = await pool.query('SELECT id, photo_url FROM invites WHERE token = ?', [token]); + if (rows.length === 0) return res.status(404).json({ error: 'Lien invalide' }); + if (!req.file) return res.status(400).json({ error: 'Fichier requis' }); + + if (rows[0].photo_url) { + const oldPath = path.join(__dirname, '..', rows[0].photo_url); + if (fs.existsSync(oldPath)) fs.unlinkSync(oldPath); + } + + const photoUrl = '/uploads/' + req.file.filename; + await pool.query('UPDATE invites SET photo_url=? WHERE id=?', [photoUrl, rows[0].id]); + res.json({ message: 'Photo enregistrée', photo_url: photoUrl }); + } catch (err) { + console.error(err); + res.status(500).json({ error: 'Server error' }); + } +}; + +function sendErrorPage(res, msg) { + res.status(404).send(` + +Invitation Dyna Biz + +

Oups !

${msg}

`); +} diff --git a/backend/controllers/memberController.js b/backend/controllers/memberController.js new file mode 100644 index 0000000..60754fb --- /dev/null +++ b/backend/controllers/memberController.js @@ -0,0 +1,171 @@ +const pool = require('../config/db'); +const { logActivity } = require('../services/activityLog'); +const sharp = require('sharp'); +const path = require('path'); +const fs = require('fs'); + +exports.list = async (req, res) => { + try { + const [rows] = await pool.query(` + SELECT u.id, u.prenom, u.nom, u.societe, u.activite, u.email, u.telephone, + u.photo_url, u.merged_display_name, u.site_web, u.role, u.date_adhesion, u.date_echeance, + u.invite_par_id, ip.prenom AS invite_par_prenom, ip.nom AS invite_par_nom, + u.date_postulation, u.reception_connu, u.reception_inconnu, + rc.prenom AS reception_connu_prenom, rc.nom AS reception_connu_nom, + ri.prenom AS reception_inconnu_prenom, ri.nom AS reception_inconnu_nom, + u.date_entretien_postulation, u.date_demande_whatsapp, + u.fonction, u.fonction_emoji, u.kdrive_link + FROM users u + LEFT JOIN users ip ON ip.id = u.invite_par_id + LEFT JOIN users rc ON rc.id = u.reception_connu + LEFT JOIN users ri ON ri.id = u.reception_inconnu + WHERE u.actif = 1 + ORDER BY u.prenom ASC + `); + res.json(rows); + } catch (err) { + console.error(err); + res.status(500).json({ error: 'Erreur serveur' }); + } +}; + +exports.getById = async (req, res) => { + try { + const [rows] = await pool.query(` + SELECT u.*, + ip.prenom AS invite_par_prenom, ip.nom AS invite_par_nom, + rc.prenom AS reception_connu_prenom, rc.nom AS reception_connu_nom, + ri.prenom AS reception_inconnu_prenom, ri.nom AS reception_inconnu_nom + FROM users u + LEFT JOIN users ip ON ip.id = u.invite_par_id + LEFT JOIN users rc ON rc.id = u.reception_connu + LEFT JOIN users ri ON ri.id = u.reception_inconnu + WHERE u.id = ? + `, [req.params.id]); + if (rows.length === 0) return res.status(404).json({ error: 'Not found' }); + const { password_hash, ...member } = rows[0]; + res.json(member); + } catch (err) { + console.error(err); + res.status(500).json({ error: 'Server error' }); + } +}; + +exports.update = async (req, res) => { + try { + const memberId = req.params.id; + if (Number(memberId) !== req.user.id && req.user.role !== 'admin') { + return res.status(403).json({ error: 'Unauthorized' }); + } + const { prenom, nom, societe, activite, email, telephone, site_web, + date_adhesion, date_echeance, + invite_par_id, date_postulation, reception_connu, reception_inconnu, + date_entretien_postulation, date_demande_whatsapp, + fonction, fonction_emoji, kdrive_link, merged_display_name } = req.body; + if (!prenom || !nom) return res.status(400).json({ error: 'Prénom et nom requis' }); + const isAdmin = req.user.role === 'admin'; + const adminFields = isAdmin ? { + date_adhesion: date_adhesion || null, + date_echeance: date_echeance || null, + invite_par_id: invite_par_id || null, + date_postulation: date_postulation || null, + reception_connu: reception_connu || null, + reception_inconnu: reception_inconnu || null, + date_entretien_postulation: date_entretien_postulation || null, + date_demande_whatsapp: date_demande_whatsapp || null, + fonction: fonction || null, + fonction_emoji: fonction_emoji || null, + kdrive_link: kdrive_link || null, + merged_display_name: merged_display_name || null, + } : {}; + await pool.query( + `UPDATE users SET prenom=?, nom=?, societe=?, activite=?, email=?, telephone=?, site_web=?, + date_adhesion=?, date_echeance=?, invite_par_id=?, date_postulation=?, + reception_connu=?, reception_inconnu=?, date_entretien_postulation=?, + date_demande_whatsapp=?, fonction=?, fonction_emoji=?, kdrive_link=?, merged_display_name=? WHERE id=?`, + [prenom, nom, societe || null, activite || null, email || null, telephone || null, site_web || null, + adminFields.date_adhesion, adminFields.date_echeance, + adminFields.invite_par_id, adminFields.date_postulation, + adminFields.reception_connu, adminFields.reception_inconnu, + adminFields.date_entretien_postulation, adminFields.date_demande_whatsapp, + adminFields.fonction, adminFields.fonction_emoji, adminFields.kdrive_link, + adminFields.merged_display_name, + memberId] + ); + logActivity(req, 'membre_modification', { memberId }); + res.json({ message: 'Mis à jour' }); + } catch (err) { + console.error(err); + res.status(500).json({ error: 'Erreur serveur' }); + } +}; + +exports.uploadPhoto = async (req, res) => { + try { + if (!req.file) return res.status(400).json({ error: 'No file uploaded' }); + const filePath = req.file.path; + const ext = path.extname(filePath); + const jpgPath = ext === '.jpg' ? filePath : filePath.replace(ext, '.jpg'); + try { + await sharp(filePath).resize(400, 400, { fit: 'cover', position: 'centre' }).jpeg({ quality: 85 }).toFile(jpgPath); + if (jpgPath !== filePath) fs.unlinkSync(filePath); + } catch (sharpErr) { + console.error('Sharp error, fallback:', sharpErr); + if (jpgPath !== filePath) fs.renameSync(filePath, jpgPath); + } + const finalUrl = `/uploads/${path.basename(jpgPath)}`; + const [existing] = await pool.query('SELECT photo_url FROM users WHERE id = ?', [req.user.id]); + if (existing[0]?.photo_url) { + const oldRelativePath = existing[0].photo_url.replace(/^\//, ''); + const oldPath = path.join(__dirname, '..', oldRelativePath); + if (fs.existsSync(oldPath)) fs.unlinkSync(oldPath); + } + await pool.query('UPDATE users SET photo_url = ? WHERE id = ?', [finalUrl, req.user.id]); + logActivity(req, 'photo_changement'); + res.json({ photo_url: finalUrl }); + } catch (err) { + console.error(err); + res.status(500).json({ error: 'Server error' }); + } +}; + +exports.create = async (req, res) => { + try { + const { prenom, nom, societe, activite, email, telephone, role } = req.body; + if (!prenom || !nom || !email) { + return res.status(400).json({ error: 'Prénom, nom et email requis' }); + } + const defaultPassword = 'Bienvenue2026'; + const bcrypt = require('bcrypt'); + const hash = await bcrypt.hash(defaultPassword, 10); + const [result] = await pool.query( + 'INSERT INTO users (prenom, nom, societe, activite, email, telephone, role, password_hash, date_adhesion, actif) VALUES (?, ?, ?, ?, ?, ?, ?, ?, CURDATE(), 1)', + [prenom, nom, societe || null, activite || null, email, telephone || null, role || 'member', hash] + ); + res.status(201).json({ + id: result.insertId, + message: 'Membre créé', + defaultPassword: 'Bienvenue2026', + }); + logActivity(req, 'membre_ajout', { prenom, nom }); + } catch (err) { + if (err.code === 'ER_DUP_ENTRY') { + return res.status(400).json({ error: 'Cet email est déjà utilisé' }); + } + console.error(err); + res.status(500).json({ error: 'Server error' }); + } +}; + +exports.remove = async (req, res) => { + try { + if (req.user.role !== 'admin') return res.status(403).json({ error: 'Admin seulement' }); + const memberId = req.params.id; + if (Number(memberId) === req.user.id) return res.status(400).json({ error: 'Vous ne pouvez pas vous supprimer vous-même' }); + await pool.query('UPDATE users SET actif = 0 WHERE id = ?', [memberId]); + logActivity(req, 'membre_desactivation', { memberId }); + } catch (err) { + console.error(err); + res.status(500).json({ error: 'Server error' }); + } +}; diff --git a/backend/controllers/recommendationController.js b/backend/controllers/recommendationController.js new file mode 100644 index 0000000..d6e6f32 --- /dev/null +++ b/backend/controllers/recommendationController.js @@ -0,0 +1,112 @@ +const pool = require('../config/db'); +const { logActivity } = require('../services/activityLog'); + +exports.list = async (req, res) => { + try { + const [rows] = await pool.query(` + SELECT r.*, + m.prenom AS membre_prenom, m.nom AS membre_nom, + d.prenom AS dest_prenom, d.nom AS dest_nom + FROM recommandations r + LEFT JOIN users m ON m.id = r.membre_id + LEFT JOIN users d ON d.id = r.destinataire_id + ORDER BY r.date DESC + `); + res.json(rows); + } catch (err) { + console.error(err); + res.status(500).json({ error: 'Server error' }); + } +}; + +exports.getById = async (req, res) => { + try { + const [rows] = await pool.query('SELECT * FROM recommandations WHERE id = ?', [req.params.id]); + if (rows.length === 0) return res.status(404).json({ error: 'Not found' }); + res.json(rows[0]); + } catch (err) { + console.error(err); + res.status(500).json({ error: 'Server error' }); + } +}; + +exports.create = async (req, res) => { + try { + const { date, destinataire_id, nom_prospect, type, transformee, sans_suite, ca_realise } = req.body; + if (!date || !nom_prospect || !type) { + return res.status(400).json({ error: 'Date, prospect name and type required' }); + } + const membre_id = req.user.id; + const [result] = await pool.query( + `INSERT INTO recommandations (date, membre_id, destinataire_id, nom_prospect, type, transformee, sans_suite, ca_realise) + VALUES (?, ?, ?, ?, ?, ?, ?, ?)`, + [date, membre_id, destinataire_id || null, nom_prospect, type, transformee ? 1 : 0, sans_suite ? 1 : 0, ca_realise || null] + ); + logActivity(req, 'recommandation_ajout', { nom_prospect, type }); + res.status(201).json({ id: result.insertId, message: 'Recommendation added' }); + } catch (err) { + console.error(err); + res.status(500).json({ error: 'Server error' }); + } +}; + +exports.update = async (req, res) => { + try { + const recId = req.params.id; + const [existing] = await pool.query('SELECT membre_id FROM recommandations WHERE id = ?', [recId]); + if (existing.length === 0) return res.status(404).json({ error: 'Not found' }); + if (existing[0].membre_id !== req.user.id && req.user.role !== 'admin') { + return res.status(403).json({ error: 'You can only edit your own recommendations' }); + } + const { date, destinataire_id, nom_prospect, type, transformee, sans_suite, ca_realise } = req.body; + await pool.query( + `UPDATE recommandations SET date=?, destinataire_id=?, nom_prospect=?, type=?, transformee=?, sans_suite=?, ca_realise=? WHERE id=?`, + [date, destinataire_id || null, nom_prospect, type, transformee ? 1 : 0, sans_suite ? 1 : 0, ca_realise || null, recId] + ); + logActivity(req, 'recommandation_modification', { recId, nom_prospect }); + res.json({ message: 'Updated' }); + } catch (err) { + console.error(err); + res.status(500).json({ error: 'Server error' }); + } +}; + +exports.remove = async (req, res) => { + try { + const recId = req.params.id; + const [existing] = await pool.query('SELECT membre_id FROM recommandations WHERE id = ?', [recId]); + if (existing.length === 0) return res.status(404).json({ error: 'Not found' }); + if (existing[0].membre_id !== req.user.id && req.user.role !== 'admin') { + return res.status(403).json({ error: 'Vous ne pouvez supprimer que vos propres recommandations' }); + } + await pool.query('DELETE FROM recommandations WHERE id = ?', [recId]); + logActivity(req, 'recommandation_suppression', { recId }); + } catch (err) { + console.error(err); + res.status(500).json({ error: 'Server error' }); + } +}; + +exports.setStatus = async (req, res) => { + try { + const recId = req.params.id; + const [existing] = await pool.query('SELECT membre_id FROM recommandations WHERE id = ?', [recId]); + if (existing.length === 0) return res.status(404).json({ error: 'Not found' }); + if (existing[0].membre_id !== req.user.id && req.user.role !== 'admin') { + return res.status(403).json({ error: 'Vous ne pouvez modifier que vos propres recommandations' }); + } + const { status, ca_realise } = req.body; + if (status === 'transformee') { + await pool.query('UPDATE recommandations SET transformee=1, sans_suite=0, ca_realise=? WHERE id=?', [ca_realise || null, recId]); + } else if (status === 'sans_suite') { + await pool.query('UPDATE recommandations SET transformee=0, sans_suite=1, ca_realise=NULL WHERE id=?', [recId]); + } else { + await pool.query('UPDATE recommandations SET transformee=0, sans_suite=0 WHERE id=?', [recId]); + } + logActivity(req, 'recommandation_statut', { recId, status }); + res.json({ message: 'Status updated' }); + } catch (err) { + console.error(err); + res.status(500).json({ error: 'Server error' }); + } +}; diff --git a/backend/controllers/statsController.js b/backend/controllers/statsController.js new file mode 100644 index 0000000..3879692 --- /dev/null +++ b/backend/controllers/statsController.js @@ -0,0 +1,203 @@ +const pool = require('../config/db'); + +exports.dashboard = async (req, res) => { + try { + const { filter } = req.query; + let dateCondition; + const now = new Date(); + const year = now.getFullYear(); + const month = String(now.getMonth() + 1).padStart(2, '0'); + + if (filter === 'year') { + dateCondition = `r.date >= '${year}-01-01'`; + } else if (filter === 'month') { + dateCondition = `r.date >= '${year}-${month}-01'`; + } else { + dateCondition = `r.date >= DATE_SUB(CURDATE(), INTERVAL 12 MONTH)`; + } + + const nextEventPromise = pool.query(` + SELECT e.date, e.nom_evenement, + (SELECT COUNT(*) FROM invites WHERE date_club = e.date) AS invites, + (SELECT SUM(statut = 'acceptee') FROM invites WHERE date_club = e.date) AS acceptees, + (SELECT SUM(statut = 'refusee') FROM invites WHERE date_club = e.date) AS refusees, + (SELECT SUM(visit_number >= 2) FROM invites WHERE date_club = e.date) AS secondes_visites + FROM evenements e + WHERE e.nom_evenement = 'Club d''Affaires' AND e.date >= CURDATE() + ORDER BY e.date ASC LIMIT 1 + `); + + const honorsPromise = pool.query( + "SELECT mh.*, u.prenom, u.nom, u.societe AS membre_societe FROM mises_en_honneur mh LEFT JOIN users u ON u.id = mh.membre_id WHERE mh.date >= CURDATE() ORDER BY mh.date ASC LIMIT 5" + ); + + const eventsPromise = pool.query( + 'SELECT * FROM evenements WHERE date >= CURDATE() ORDER BY date ASC LIMIT 3' + ); + + const recsPromise = pool.query(` + SELECT r.id, r.date, r.nom_prospect, r.type, r.transformee, r.ca_realise, + m.prenom AS membre_prenom, m.nom AS membre_nom, + d.prenom AS dest_prenom, d.nom AS dest_nom + FROM recommandations r + LEFT JOIN users m ON m.id = r.membre_id + LEFT JOIN users d ON d.id = r.destinataire_id + WHERE ${dateCondition} + ORDER BY r.date DESC + `); + + const countPromise = pool.query(` + SELECT + COUNT(*) AS total, + SUM(CASE WHEN r.type='donnee' THEN 1 ELSE 0 END) AS donnees, + SUM(CASE WHEN r.type='recue' THEN 1 ELSE 0 END) AS recues, + SUM(r.transformee) AS transformees, + SUM(COALESCE(r.ca_realise,0)) AS ca_total + FROM recommandations r + WHERE ${dateCondition} + `); + + const memberRecsPromise = pool.query(` + SELECT + u.id, u.prenom, u.nom, u.societe, + COUNT(r.id) AS total_recs, + SUM(r.transformee) AS total_transformees, + SUM(COALESCE(r.ca_realise,0)) AS total_ca + FROM users u + LEFT JOIN recommandations r ON r.membre_id = u.id AND ${dateCondition} + WHERE u.actif = 1 + GROUP BY u.id + ORDER BY total_recs DESC + `); + + const guestCountPromise = pool.query(` + SELECT i.invite_par_id, u.prenom, u.nom, COUNT(i.id) AS total_invites + FROM invites i + JOIN users u ON u.id = i.invite_par_id + WHERE i.date_club >= DATE_SUB(CURDATE(), INTERVAL 12 MONTH) + GROUP BY i.invite_par_id, u.prenom, u.nom + ORDER BY total_invites DESC + `); + + const echeancePromise = pool.query( + "SELECT id, prenom, nom, societe, date_echeance FROM users WHERE actif = 1 AND date_echeance IS NOT NULL AND date_echeance BETWEEN CURDATE() AND DATE_ADD(CURDATE(), INTERVAL 30 DAY) ORDER BY date_echeance ASC" + ); + + const [nextEvent] = await nextEventPromise; + const [honors] = await honorsPromise; + const [events] = await eventsPromise; + const [recs] = await recsPromise; + const [counts] = await countPromise; + const [memberRecs] = await memberRecsPromise; + const [guestCounts] = await guestCountPromise; + const guestMetricsPromise = pool.query(` + SELECT + COUNT(*) AS total_invites, + SUM(CASE WHEN statut = 'acceptee' THEN 1 ELSE 0 END) AS acceptees, + SUM(CASE WHEN statut = 'refusee' THEN 1 ELSE 0 END) AS refusees, + SUM(CASE WHEN visit_number >= 2 THEN 1 ELSE 0 END) AS secondes_visites + FROM invites + `); + + const [echeances] = await echeancePromise; + const [guestMetricsRows] = await guestMetricsPromise; + + let top_absences = []; + if (req.user.role === 'admin') { + const [absRows] = await pool.query(` + SELECT u.id, u.prenom, u.nom, COUNT(a.id) AS total_absences + FROM users u + LEFT JOIN absences a ON a.membre_id = u.id + WHERE u.actif = 1 + GROUP BY u.id + HAVING total_absences > 0 + ORDER BY total_absences DESC + LIMIT 5 + `); + top_absences = absRows; + } + + let top_conversion = []; + const [convRows] = await pool.query(` + SELECT u.id, u.prenom, u.nom, COUNT(m.id) AS total_convertis + FROM users u + JOIN users m ON m.invite_par_id = u.id + WHERE u.actif = 1 + GROUP BY u.id + ORDER BY total_convertis DESC + LIMIT 15 + `); + top_conversion = convRows; + const [candRows] = await pool.query( + 'SELECT id, prenom, nom, societe, created_at FROM invites WHERE statut_candidature = ? ORDER BY created_at DESC', + ['candidature'] + ); + const candidatures = candRows; + + const total = Number(counts[0].total) || 0; + const transformees = Number(counts[0].transformees) || 0; + const tauxTransformation = total > 0 ? ((transformees / total) * 100).toFixed(1) : 0; + + const topDonnees = [...memberRecs].filter(m => m.total_recs > 0).sort((a, b) => b.total_recs - a.total_recs).slice(0, 15); + const topInvites = [...guestCounts].sort((a, b) => b.total_invites - a.total_invites).slice(0, 15); + const flopDonnees = [...memberRecs].sort((a, b) => a.total_recs - b.total_recs).slice(0, 5); + + res.json({ + prochaine_seance: nextEvent.length > 0 ? { + date: nextEvent[0].date, + nom: nextEvent[0].nom_evenement, + invites: Number(nextEvent[0].invites) || 0, + acceptees: Number(nextEvent[0].acceptees) || 0, + refusees: Number(nextEvent[0].refusees) || 0, + secondes_visites: Number(nextEvent[0].secondes_visites) || 0, + } : null, + counts: { + total, + donnees: Number(counts[0].donnees) || 0, + recues: Number(counts[0].recues) || 0, + transformees, + ca_total: Number(counts[0].ca_total) || 0, + taux_transformation: tauxTransformation, + }, + top_donnees: topDonnees, + top_invites: topInvites, + flop_donnees: flopDonnees, + prochaines_honors: honors, + prochains_evenements: events, + alertes_echeance: echeances, + guest_metrics: { + total_invites: Number(guestMetricsRows[0].total_invites) || 0, + acceptees: Number(guestMetricsRows[0].acceptees) || 0, + refusees: Number(guestMetricsRows[0].refusees) || 0, + secondes_visites: Number(guestMetricsRows[0].secondes_visites) || 0, + }, + top_absences, + top_conversion, + candidatures, + }); + } catch (err) { + console.error(err); + res.status(500).json({ error: 'Server error' }); + } +}; + +exports.memberStats = async (req, res) => { + try { + const userId = req.params.id || req.user.id; + const [recs] = await pool.query(` + SELECT r.*, d.prenom AS dest_prenom, d.nom AS dest_nom + FROM recommandations r + LEFT JOIN users d ON d.id = r.destinataire_id + WHERE r.membre_id = ? + ORDER BY r.date DESC + `, [userId]); + const [guests] = await pool.query( + 'SELECT * FROM invites WHERE invite_par_id = ? ORDER BY created_at DESC', + [userId] + ); + res.json({ recommandations: recs, invites: guests }); + } catch (err) { + console.error(err); + res.status(500).json({ error: 'Server error' }); + } +}; diff --git a/backend/gen-vapid.js b/backend/gen-vapid.js new file mode 100644 index 0000000..5e54077 --- /dev/null +++ b/backend/gen-vapid.js @@ -0,0 +1,4 @@ +const webPush = require('web-push'); +const keys = webPush.generateVAPIDKeys(); +console.log(keys.publicKey); +console.log(keys.privateKey); diff --git a/backend/member_slides/10_00000000_1781596912230.jpg b/backend/member_slides/10_00000000_1781596912230.jpg new file mode 100644 index 0000000..f4d308e Binary files /dev/null and b/backend/member_slides/10_00000000_1781596912230.jpg differ diff --git a/backend/member_slides/11_00000000_1782743368845.jpg b/backend/member_slides/11_00000000_1782743368845.jpg new file mode 100644 index 0000000..27a58b0 Binary files /dev/null and b/backend/member_slides/11_00000000_1782743368845.jpg differ diff --git a/backend/member_slides/11_00000000_1782743370169.jpg b/backend/member_slides/11_00000000_1782743370169.jpg new file mode 100644 index 0000000..fd97d20 Binary files /dev/null and b/backend/member_slides/11_00000000_1782743370169.jpg differ diff --git a/backend/member_slides/11_00000000_1782743371806.jpg b/backend/member_slides/11_00000000_1782743371806.jpg new file mode 100644 index 0000000..8e27b36 Binary files /dev/null and b/backend/member_slides/11_00000000_1782743371806.jpg differ diff --git a/backend/member_slides/11_00000000_1782743373598.jpg b/backend/member_slides/11_00000000_1782743373598.jpg new file mode 100644 index 0000000..8a024e8 Binary files /dev/null and b/backend/member_slides/11_00000000_1782743373598.jpg differ diff --git a/backend/member_slides/15_00000000_1781271836310.jpg b/backend/member_slides/15_00000000_1781271836310.jpg new file mode 100644 index 0000000..ab7b9a8 Binary files /dev/null and b/backend/member_slides/15_00000000_1781271836310.jpg differ diff --git a/backend/member_slides/1_00000000_1782744249579.png b/backend/member_slides/1_00000000_1782744249579.png new file mode 100644 index 0000000..a101c7d Binary files /dev/null and b/backend/member_slides/1_00000000_1782744249579.png differ diff --git a/backend/member_slides/2_00000000_1781271801508.jpg b/backend/member_slides/2_00000000_1781271801508.jpg new file mode 100644 index 0000000..343e190 Binary files /dev/null and b/backend/member_slides/2_00000000_1781271801508.jpg differ diff --git a/backend/member_slides/2_00000000_1782280813679.png b/backend/member_slides/2_00000000_1782280813679.png new file mode 100644 index 0000000..1ce5fd6 Binary files /dev/null and b/backend/member_slides/2_00000000_1782280813679.png differ diff --git a/backend/member_slides/3_00000000_1781549976194.jpeg b/backend/member_slides/3_00000000_1781549976194.jpeg new file mode 100644 index 0000000..946df32 Binary files /dev/null and b/backend/member_slides/3_00000000_1781549976194.jpeg differ diff --git a/backend/member_slides/3_00000000_1782803054991.jpeg b/backend/member_slides/3_00000000_1782803054991.jpeg new file mode 100644 index 0000000..d47397b Binary files /dev/null and b/backend/member_slides/3_00000000_1782803054991.jpeg differ diff --git a/backend/member_slides/4_00000000_1782746536531.jpg b/backend/member_slides/4_00000000_1782746536531.jpg new file mode 100644 index 0000000..df88131 Binary files /dev/null and b/backend/member_slides/4_00000000_1782746536531.jpg differ diff --git a/backend/member_slides/9_00000000_1785227227446.png b/backend/member_slides/9_00000000_1785227227446.png new file mode 100644 index 0000000..b7ab0d8 Binary files /dev/null and b/backend/member_slides/9_00000000_1785227227446.png differ diff --git a/backend/member_slides/9_00000000_1785227227648.png b/backend/member_slides/9_00000000_1785227227648.png new file mode 100644 index 0000000..f117c9c Binary files /dev/null and b/backend/member_slides/9_00000000_1785227227648.png differ diff --git a/backend/middleware/auth.js b/backend/middleware/auth.js new file mode 100644 index 0000000..8ce5bd5 --- /dev/null +++ b/backend/middleware/auth.js @@ -0,0 +1,25 @@ +const jwt = require('jsonwebtoken'); + +function authenticate(req, res, next) { + const header = req.headers.authorization; + if (!header || !header.startsWith('Bearer ')) { + return res.status(401).json({ error: 'Token required' }); + } + const token = header.split(' ')[1]; + try { + const decoded = jwt.verify(token, process.env.JWT_SECRET); + req.user = decoded; + next(); + } catch { + return res.status(401).json({ error: 'Invalid or expired token' }); + } +} + +function adminOnly(req, res, next) { + if (req.user.role !== 'admin') { + return res.status(403).json({ error: 'Admin access required' }); + } + next(); +} + +module.exports = { authenticate, adminOnly }; diff --git a/backend/package.json b/backend/package.json new file mode 100644 index 0000000..33d6d01 --- /dev/null +++ b/backend/package.json @@ -0,0 +1,23 @@ +{ + "name": "club-dynasqy-backend", + "version": "1.0.0", + "private": true, + "scripts": { + "start": "node server.js", + "dev": "node --watch server.js" + }, + "dependencies": { + "bcrypt": "^5.1.1", + "cheerio": "^1.0.0", + "cors": "^2.8.5", + "dotenv": "^16.4.5", + "express": "^4.21.0", + "jsonwebtoken": "^9.0.2", + "mysql2": "^3.12.0", + "multer": "^1.4.5-lts.1", + "node-cron": "^3.0.3", + "nodemailer": "^6.9.14", + "sharp": "^0.33.5", + "web-push": "^3.6.7" + } +} diff --git a/backend/routes/absences.js b/backend/routes/absences.js new file mode 100644 index 0000000..71c5ad1 --- /dev/null +++ b/backend/routes/absences.js @@ -0,0 +1,12 @@ +const { Router } = require('express'); +const { listByMember, create, remove, stats } = require('../controllers/absenceController'); +const { authenticate, adminOnly } = require('../middleware/auth'); + +const router = Router(); + +router.get('/stats', authenticate, adminOnly, stats); +router.get('/member/:id', authenticate, adminOnly, listByMember); +router.post('/', authenticate, adminOnly, create); +router.delete('/:id', authenticate, adminOnly, remove); + +module.exports = router; \ No newline at end of file diff --git a/backend/routes/activityLog.js b/backend/routes/activityLog.js new file mode 100644 index 0000000..953be2c --- /dev/null +++ b/backend/routes/activityLog.js @@ -0,0 +1,8 @@ +const { Router } = require('express'); +const { getLogs } = require('../controllers/activityLogController'); +const { authenticate, adminOnly } = require('../middleware/auth'); + +const router = Router(); +router.get('/', authenticate, adminOnly, getLogs); + +module.exports = router; diff --git a/backend/routes/admin.js b/backend/routes/admin.js new file mode 100644 index 0000000..5e24921 --- /dev/null +++ b/backend/routes/admin.js @@ -0,0 +1,9 @@ +const { Router } = require('express'); +const { listUsers, updateUserRole } = require('../controllers/adminController'); +const { authenticate, adminOnly } = require('../middleware/auth'); + +const router = Router(); +router.get('/users', authenticate, adminOnly, listUsers); +router.put('/users/:id/role', authenticate, adminOnly, updateUserRole); + +module.exports = router; diff --git a/backend/routes/auth.js b/backend/routes/auth.js new file mode 100644 index 0000000..ca84c45 --- /dev/null +++ b/backend/routes/auth.js @@ -0,0 +1,11 @@ +const { Router } = require('express'); +const { login, me, changePassword } = require('../controllers/authController'); +const { authenticate } = require('../middleware/auth'); + +const router = Router(); +router.post('/login', login); +router.get('/me', authenticate, me); + +router.put('/password', authenticate, changePassword); + +module.exports = router; diff --git a/backend/routes/events.js b/backend/routes/events.js new file mode 100644 index 0000000..a1a6e42 --- /dev/null +++ b/backend/routes/events.js @@ -0,0 +1,13 @@ +const { Router } = require('express'); +const { list, create, update, remove, scrape, deletePast } = require('../controllers/eventController'); +const { authenticate, adminOnly } = require('../middleware/auth'); + +const router = Router(); +router.get('/', authenticate, list); +router.post('/', authenticate, adminOnly, create); +router.post('/scrape', authenticate, adminOnly, scrape); +router.delete('/past', authenticate, adminOnly, deletePast); +router.put('/:id', authenticate, adminOnly, update); +router.delete('/:id', authenticate, adminOnly, remove); + +module.exports = router; diff --git a/backend/routes/export.js b/backend/routes/export.js new file mode 100644 index 0000000..482fab7 --- /dev/null +++ b/backend/routes/export.js @@ -0,0 +1,8 @@ +const { Router } = require('express'); +const { exportCsv } = require('../controllers/exportController'); +const { authenticate, adminOnly } = require('../middleware/auth'); + +const router = Router(); +router.get('/csv', authenticate, adminOnly, exportCsv); + +module.exports = router; diff --git a/backend/routes/gallery.js b/backend/routes/gallery.js new file mode 100644 index 0000000..c293521 --- /dev/null +++ b/backend/routes/gallery.js @@ -0,0 +1,52 @@ +const { Router } = require('express'); +const multer = require('multer'); +const path = require('path'); +const fs = require('fs'); +const sharp = require('sharp'); +const { list, upload, deletePhoto } = require('../controllers/galleryController'); +const { authenticate } = require('../middleware/auth'); + +const galleryDir = path.join(__dirname, '..', 'gallery'); +if (!fs.existsSync(galleryDir)) fs.mkdirSync(galleryDir, { recursive: true }); + +const storage = multer.diskStorage({ + destination: (req, file, cb) => cb(null, galleryDir), + filename: (req, file, cb) => cb(null, Date.now() + '-' + file.originalname), +}); +const uploadMulter = multer({ storage, limits: { fileSize: 20 * 1024 * 1024 } }); + +const router = Router(); +router.get('/', authenticate, list); +router.post('/upload', authenticate, uploadMulter.array('photos', 20), upload); +router.get('/thumbnail/:date/:filename', async (req, res) => { + try { + const { date, filename } = req.params; + const filePath = path.join(galleryDir, date, filename); + if (!fs.existsSync(filePath)) return res.status(404).json({ error: 'Fichier introuvable' }); + + const ext = path.extname(filename).toLowerCase(); + if (!['.jpg', '.jpeg', '.png', '.webp'].includes(ext)) { + return res.sendFile(filePath); + } + + const thumbDir = path.join(galleryDir, date, '.thumbnails'); + if (!fs.existsSync(thumbDir)) fs.mkdirSync(thumbDir, { recursive: true }); + const thumbPath = path.join(thumbDir, filename); + + if (!fs.existsSync(thumbPath)) { + await sharp(filePath) + .resize(300, 200, { fit: 'cover', position: 'centre' }) + .jpeg({ quality: 70 }) + .toFile(thumbPath); + } + + res.sendFile(thumbPath); + } catch (err) { + console.error(err); + res.status(500).json({ error: 'Erreur' }); + } +}); + +router.delete('/:date/:filename', authenticate, deletePhoto); + +module.exports = router; diff --git a/backend/routes/guests.js b/backend/routes/guests.js new file mode 100644 index 0000000..fe88a29 --- /dev/null +++ b/backend/routes/guests.js @@ -0,0 +1,16 @@ +const { Router } = require('express'); +const { list, getById, create, update, remove, search, sendInvitation, candidature, nextEventGuestCount } = require('../controllers/guestController'); +const { authenticate } = require('../middleware/auth'); + +const router = Router(); +router.get('/', authenticate, list); +router.get('/search', authenticate, search); +router.get('/:id', authenticate, getById); +router.post('/', authenticate, create); +router.put('/:id', authenticate, update); +router.delete('/:id', authenticate, remove); +router.post('/:id/send-invitation', authenticate, sendInvitation); +router.post('/:id/candidature', authenticate, candidature); +router.get('/next-count', authenticate, nextEventGuestCount); + +module.exports = router; diff --git a/backend/routes/honors.js b/backend/routes/honors.js new file mode 100644 index 0000000..12df52f --- /dev/null +++ b/backend/routes/honors.js @@ -0,0 +1,11 @@ +const { Router } = require('express'); +const { list, create, update, remove } = require('../controllers/honorController'); +const { authenticate, adminOnly } = require('../middleware/auth'); + +const router = Router(); +router.get('/', authenticate, list); +router.post('/', authenticate, adminOnly, create); +router.put('/:id', authenticate, adminOnly, update); +router.delete('/:id', authenticate, adminOnly, remove); + +module.exports = router; diff --git a/backend/routes/invitation.js b/backend/routes/invitation.js new file mode 100644 index 0000000..33078a7 --- /dev/null +++ b/backend/routes/invitation.js @@ -0,0 +1,21 @@ +const { Router } = require('express'); +const multer = require('multer'); +const path = require('path'); +const { showPage, respond, uploadPhoto } = require('../controllers/invitationController'); + +const router = Router(); + +const storage = multer.diskStorage({ + destination: (req, file, cb) => cb(null, path.join(__dirname, '..', 'uploads')), + filename: (req, file, cb) => { + const { token } = req.params; + cb(null, `invite_${token}_${Date.now()}${path.extname(file.originalname)}`); + }, +}); +const upload = multer({ storage, limits: { fileSize: 5 * 1024 * 1024 } }); + +router.get('/:token', showPage); +router.post('/:token/respond', respond); +router.post('/:token/photo', upload.single('photo'), uploadPhoto); + +module.exports = router; diff --git a/backend/routes/memberSlides.js b/backend/routes/memberSlides.js new file mode 100644 index 0000000..0d7bc25 --- /dev/null +++ b/backend/routes/memberSlides.js @@ -0,0 +1,100 @@ +const { Router } = require('express'); +const multer = require('multer'); +const path = require('path'); +const fs = require('fs'); +const pool = require('../config/db'); +const { authenticate } = require('../middleware/auth'); +const { logActivity } = require('../services/activityLog'); + +const slidesDir = path.join(__dirname, '..', 'member_slides'); +if (!fs.existsSync(slidesDir)) fs.mkdirSync(slidesDir, { recursive: true }); + +const storage = multer.diskStorage({ + destination: (req, file, cb) => cb(null, slidesDir), + filename: (req, file, cb) => { + const memberId = req.body.member_id && req.user.role === 'admin' ? req.body.member_id : req.user.id; + const dateKey = req.body.date_key || '00000000'; + const ext = path.extname(file.originalname); + cb(null, `${memberId}_${dateKey}_${Date.now()}${ext}`); + }, +}); +const uploadMulter = multer({ storage, limits: { fileSize: 300 * 1024 * 1024 } }); + +const router = Router(); + +router.post('/upload', authenticate, uploadMulter.single('file'), async (req, res) => { + try { + if (!req.file) return res.status(400).json({ error: 'Aucun fichier' }); + const { date_key } = req.body; + if (!date_key) return res.status(400).json({ error: 'date_key requis' }); + const memberId = req.body.member_id && req.user.role === 'admin' ? req.body.member_id : req.user.id; + + let filename = req.file.filename; + const ext = path.extname(filename).toLowerCase(); + + const imageExts = ['.jpg', '.jpeg', '.png', '.gif', '.webp', '.svg']; + const videoExts = ['.mp4', '.webm', '.ogg', '.mov', '.avi', '.mkv']; + const allowedExts = [...imageExts, ...videoExts]; + if (!allowedExts.includes(ext)) { + fs.unlinkSync(req.file.path); + return res.status(400).json({ error: 'Seules les images et vidéos sont acceptées' }); + } + + const [result] = await pool.query( + 'INSERT INTO member_slides (member_id, filename, original_name, date_key) VALUES (?, ?, ?, ?)', + [memberId, filename, req.file.originalname, date_key] + ); + res.json({ success: true, id: result.insertId, filename }); + logActivity(req, 'slide_upload', { memberId, date_key, original_name: req.file.originalname }); + } catch (err) { + console.error(err); + res.status(500).json({ error: 'Erreur upload' }); + } +}); + +router.get('/:memberId', authenticate, async (req, res) => { + try { + const { memberId } = req.params; + if (Number(memberId) !== req.user.id && req.user.role !== 'admin') + return res.status(403).json({ error: 'Interdit' }); + const [rows] = await pool.query( + 'SELECT id, filename, original_name, date_key, created_at FROM member_slides WHERE member_id = ? ORDER BY created_at DESC', + [memberId] + ); + res.json(rows); + } catch (err) { + console.error(err); + res.status(500).json({ error: 'Erreur' }); + } +}); + +router.delete('/:id', authenticate, async (req, res) => { + try { + const [rows] = await pool.query('SELECT * FROM member_slides WHERE id = ?', [req.params.id]); + if (rows.length === 0) return res.status(404).json({ error: 'Introuvable' }); + const slide = rows[0]; + if (slide.member_id !== req.user.id && req.user.role !== 'admin') + return res.status(403).json({ error: 'Interdit' }); + const filePath = path.join(slidesDir, slide.filename); + if (fs.existsSync(filePath)) fs.unlinkSync(filePath); + await pool.query('DELETE FROM member_slides WHERE id = ?', [req.params.id]); + res.json({ success: true }); + logActivity(req, 'slide_suppression', { slideId: req.params.id, filename: slide.filename }); + } catch (err) { + console.error(err); + res.status(500).json({ error: 'Erreur' }); + } +}); + +router.use((err, req, res, next) => { + if (err instanceof multer.MulterError) { + if (err.code === 'LIMIT_FILE_SIZE') { + return res.status(413).json({ error: 'Le fichier dépasse la limite autorisée (300 Mo maximum)' }); + } + return res.status(400).json({ error: err.message }); + } + if (err) return res.status(500).json({ error: 'Erreur serveur' }); + next(); +}); + +module.exports = router; diff --git a/backend/routes/members.js b/backend/routes/members.js new file mode 100644 index 0000000..7fc0f1c --- /dev/null +++ b/backend/routes/members.js @@ -0,0 +1,22 @@ +const { Router } = require('express'); +const multer = require('multer'); +const path = require('path'); +const { list, getById, update, uploadPhoto, create, remove } = require('../controllers/memberController'); +const { authenticate, adminOnly } = require('../middleware/auth'); + +const router = Router(); + +const storage = multer.diskStorage({ + destination: (req, file, cb) => cb(null, 'uploads/'), + filename: (req, file, cb) => cb(null, `user_${req.user.id}_${Date.now()}${path.extname(file.originalname)}`) +}); +const upload = multer({ storage }); + +router.get('/', authenticate, list); +router.get('/:id', authenticate, getById); +router.put('/:id', authenticate, update); +router.post('/upload-photo', authenticate, upload.single('photo'), uploadPhoto); +router.post('/', authenticate, adminOnly, create); +router.delete('/:id', authenticate, adminOnly, remove); + +module.exports = router; diff --git a/backend/routes/notifications.js b/backend/routes/notifications.js new file mode 100644 index 0000000..f1e8ea9 --- /dev/null +++ b/backend/routes/notifications.js @@ -0,0 +1,97 @@ +const { Router } = require('express'); +const pool = require('../config/db'); +const { authenticate } = require('../middleware/auth'); + +const router = Router(); + +router.get('/', authenticate, async (req, res) => { + try { + const [rows] = await pool.query( + `SELECT n.id, n.title, n.body, n.created_at, + u.prenom AS sender_prenom, u.nom AS sender_nom, + nr.read_at, 'admin' AS source, + NULL AS invite_id, + NULL AS guest_name + FROM notifications n + JOIN notification_recipients nr ON nr.notification_id = n.id + JOIN users u ON u.id = n.sender_id + WHERE nr.user_id = ? + UNION ALL + SELECT -(i.id) AS id, + CONCAT('Invitation en attente - ', i.prenom, ' ', i.nom) AS title, + CONCAT('Vous n''avez pas encore envoy?? l''email d''invitation ?? ', i.prenom, ' ', i.nom) AS body, + i.created_at, + NULL AS sender_prenom, NULL AS sender_nom, + NULL AS read_at, + 'pending_invite' AS source, + i.id AS invite_id, + CONCAT(i.prenom, ' ', i.nom) AS guest_name + FROM invites i + WHERE i.invite_par_id = ? AND (i.mail_envoye IS NULL OR i.mail_envoye = 0) + ORDER BY created_at DESC`, + [req.user.id, req.user.id] + ); + res.json(rows); + } catch (err) { + console.error(err); + res.status(500).json({ error: 'Erreur serveur' }); + } +}); + +router.get('/unread-count', authenticate, async (req, res) => { + try { + const [rows] = await pool.query( + 'SELECT COUNT(*) AS count FROM notification_recipients WHERE user_id = ? AND read_at IS NULL', + [req.user.id] + ); + const [inviteRows] = await pool.query( + 'SELECT COUNT(*) AS count FROM invites WHERE invite_par_id = ? AND (mail_envoye IS NULL OR mail_envoye = 0)', + [req.user.id] + ); + res.json({ count: Number(rows[0].count) + Number(inviteRows[0].count) }); + } catch (err) { + console.error(err); + res.status(500).json({ error: 'Erreur serveur' }); + } +}); + +router.post('/:id/read', authenticate, async (req, res) => { + try { + await pool.query( + 'UPDATE notification_recipients SET read_at = NOW() WHERE notification_id = ? AND user_id = ?', + [req.params.id, req.user.id] + ); + res.json({ message: 'Marqu?? comme lu' }); + } catch (err) { + console.error(err); + res.status(500).json({ error: 'Erreur serveur' }); + } +}); + +router.post('/read-all', authenticate, async (req, res) => { + try { + await pool.query( + 'UPDATE notification_recipients SET read_at = NOW() WHERE user_id = ? AND read_at IS NULL', + [req.user.id] + ); + res.json({ message: 'Tout marqu?? comme lu' }); + } catch (err) { + console.error(err); + res.status(500).json({ error: 'Erreur serveur' }); + } +}); + +router.delete('/:id/recipient', authenticate, async (req, res) => { + try { + await pool.query( + 'DELETE FROM notification_recipients WHERE notification_id = ? AND user_id = ?', + [req.params.id, req.user.id] + ); + res.json({ message: 'Supprim??' }); + } catch (err) { + console.error(err); + res.status(500).json({ error: 'Erreur serveur' }); + } +}); + +module.exports = router; diff --git a/backend/routes/presentation.js b/backend/routes/presentation.js new file mode 100644 index 0000000..82b89fa --- /dev/null +++ b/backend/routes/presentation.js @@ -0,0 +1,136 @@ +const { Router } = require('express'); +const pool = require('../config/db'); +const { authenticate } = require('../middleware/auth'); + +const router = Router(); + +router.get('/:date', authenticate, async (req, res) => { + if (req.user.role !== 'admin') return res.status(403).json({ error: 'Admin seulement' }); + try { + const { date } = req.params; + if (!/^\d{4}-\d{2}-\d{2}$/.test(date)) { + return res.status(400).json({ error: 'Format de date invalide (AAAA-MM-JJ)' }); + } + + const [absences] = await pool.query(` + SELECT a.membre_id, a.prevenu, a.motif, + a.remplace_par_type, a.remplace_par_id, a.remplace_par_guest_id, + r.prenom AS remplace_prenom, r.nom AS remplace_nom, + rg.prenom AS remplace_guest_prenom, rg.nom AS remplace_guest_nom + FROM absences a + LEFT JOIN users r ON r.id = a.remplace_par_id AND (a.remplace_par_type = 'member' OR a.remplace_par_type IS NULL) + LEFT JOIN invites rg ON rg.id = a.remplace_par_guest_id AND a.remplace_par_type = 'guest' + WHERE a.date_absence = ? + `, [date]); + + const [allMembers] = await pool.query( + `SELECT id, prenom, nom, societe, activite, photo_url, merged_display_name, fonction, fonction_emoji, + telephone, site_web, email + FROM users WHERE actif = 1 ORDER BY fonction IS NULL, fonction, prenom` + ); + + const absentWithoutReplacement = new Set( + absences.filter(a => !a.remplace_par_id && !a.remplace_par_guest_id).map(a => a.membre_id) + ); + const members = allMembers.filter(m => !absentWithoutReplacement.has(m.id)); + + const [guests] = await pool.query( + `SELECT i.id, i.prenom, i.nom, i.societe, i.activite, i.photo_url, + i.telephone, i.site_web, i.email, i.statut, + u.prenom AS invite_par_prenom, u.nom AS invite_par_nom + FROM invites i + LEFT JOIN users u ON u.id = i.invite_par_id + WHERE i.date_club = ? AND (i.statut != 'refusee' OR i.statut IS NULL) + ORDER BY i.prenom`, + [date] + ); + + const [currentHonorRow] = await pool.query( + `SELECT CONCAT(u.prenom, ' ', u.nom) AS currentHonor + FROM mises_en_honneur mh + JOIN users u ON u.id = mh.membre_id + WHERE mh.date = ? + LIMIT 1`, + [date] + ); + const currentHonor = currentHonorRow[0]?.currentHonor || null; + + const [nextHonorRow] = await pool.query( + `SELECT CONCAT(u.prenom, ' ', u.nom) AS nextHonor + FROM mises_en_honneur mh + JOIN users u ON u.id = mh.membre_id + WHERE mh.date > ? + ORDER BY mh.date ASC LIMIT 1`, + [date] + ); + const nextHonor = nextHonorRow[0]?.nextHonor || null; + + const [events] = await pool.query( + `SELECT nom_evenement AS titre, date AS date_event, horaire AS heure_debut, lieu + FROM evenements WHERE date >= ? ORDER BY date ASC LIMIT 5`, + [date] + ); + + const [y, m] = date.split('-') + const monthStart = `${y}-${m}-01` + const lastDay = new Date(y, m, 0).getDate() + const monthEnd = `${y}-${m}-${lastDay}` + const [recommendations] = await pool.query( + `SELECT r.id, r.type, r.nom_prospect, r.ca_realise, r.created_at, + u.prenom, u.nom, u.societe + FROM recommandations r + JOIN users u ON u.id = r.membre_id + WHERE r.created_at >= ? AND r.created_at <= ? + ORDER BY r.created_at DESC LIMIT 10`, + [monthStart, monthEnd] + ); + + const [yr, mo, dy] = date.split('-') + const dateKey = `${dy}${mo}${yr}` + const [memberSlides] = await pool.query( + 'SELECT id, member_id, filename FROM member_slides WHERE date_key = ?', + [dateKey] + ); + + const [topInviters] = await pool.query( + `SELECT u.id, u.prenom, u.nom, u.photo_url, COUNT(i.id) AS total + FROM users u + LEFT JOIN invites i ON i.invite_par_id = u.id AND i.created_at >= DATE_SUB(NOW(), INTERVAL 12 MONTH) + WHERE u.actif = 1 + GROUP BY u.id, u.prenom, u.nom, u.photo_url + ORDER BY total DESC LIMIT 3` + ); + + const [topDonnees] = await pool.query( + `SELECT u.id, u.prenom, u.nom, u.photo_url, COUNT(r.id) AS total + FROM users u + LEFT JOIN recommandations r ON r.membre_id = u.id + WHERE u.actif = 1 + GROUP BY u.id, u.prenom, u.nom, u.photo_url + ORDER BY total DESC LIMIT 3` + ); + + const [topConversion] = await pool.query( + `SELECT u.id, u.prenom, u.nom, u.photo_url, COUNT(m2.id) AS total + FROM users u + LEFT JOIN invites i ON i.invite_par_id = u.id AND i.statut = 'acceptee' + LEFT JOIN users m2 ON m2.email = i.email + WHERE u.actif = 1 + GROUP BY u.id, u.prenom, u.nom, u.photo_url + HAVING total > 0 + ORDER BY total DESC LIMIT 3` + ); + + const [overrides] = await pool.query( + 'SELECT slide_key, content, hidden, sort_order FROM presentation_overrides WHERE date_key = ?', + [dateKey] + ); + + res.json({ members, guests, absences, currentHonor: currentHonor || null, nextHonor: nextHonor || null, events, recommendations, memberSlides, topInviters, topDonnees, topConversion, overrides }); + } catch (err) { + console.error(err); + res.status(500).json({ error: 'Erreur serveur' }); + } +}); + +module.exports = router; diff --git a/backend/routes/presentationEditor.js b/backend/routes/presentationEditor.js new file mode 100644 index 0000000..fc8d9a7 --- /dev/null +++ b/backend/routes/presentationEditor.js @@ -0,0 +1,55 @@ +const { Router } = require('express'); +const pool = require('../config/db'); +const { authenticate } = require('../middleware/auth'); + +const router = Router(); + +router.get('/:date', authenticate, async (req, res) => { + if (req.user.role !== 'admin') return res.status(403).json({ error: 'Admin seulement' }); + try { + const { date } = req.params; + const [yr, mo, dy] = date.split('-'); + const dateKey = `${dy}${mo}${yr}`; + const [rows] = await pool.query( + 'SELECT id, slide_key, content, hidden, sort_order FROM presentation_overrides WHERE date_key = ? ORDER BY sort_order ASC', + [dateKey] + ); + res.json(rows); + } catch (err) { + console.error(err); + res.status(500).json({ error: 'Erreur serveur' }); + } +}); + +router.put('/:date', authenticate, async (req, res) => { + if (req.user.role !== 'admin') return res.status(403).json({ error: 'Admin seulement' }); + try { + const { date } = req.params; + const { overrides } = req.body; + const [yr, mo, dy] = date.split('-'); + const dateKey = `${dy}${mo}${yr}`; + if (!Array.isArray(overrides)) return res.status(400).json({ error: 'overrides doit être un tableau' }); + + for (const ov of overrides) { + const { slide_key, content, hidden, sort_order } = ov; + if (!slide_key) continue; + await pool.query( + `INSERT INTO presentation_overrides (date_key, slide_key, content, hidden, sort_order) + VALUES (?, ?, ?, ?, ?) + ON DUPLICATE KEY UPDATE content = VALUES(content), hidden = VALUES(hidden), sort_order = VALUES(sort_order)`, + [dateKey, slide_key, content ? JSON.stringify(content) : null, hidden ? 1 : 0, sort_order ?? 0] + ); + } + + const [rows] = await pool.query( + 'SELECT slide_key, content, hidden, sort_order FROM presentation_overrides WHERE date_key = ? ORDER BY sort_order ASC', + [dateKey] + ); + res.json({ overrides: rows }); + } catch (err) { + console.error(err); + res.status(500).json({ error: 'Erreur serveur' }); + } +}); + +module.exports = router; diff --git a/backend/routes/push.js b/backend/routes/push.js new file mode 100644 index 0000000..ddb4ca2 --- /dev/null +++ b/backend/routes/push.js @@ -0,0 +1,175 @@ +const { Router } = require('express'); +const pool = require('../config/db'); +const { authenticate } = require('../middleware/auth'); +const webPush = require('web-push'); + +const router = Router(); + +const publicVapidKey = process.env.VAPID_PUBLIC_KEY; +const privateVapidKey = process.env.VAPID_PRIVATE_KEY; + +if (publicVapidKey && privateVapidKey) { + webPush.setVapidDetails('mailto:pierre@secourit.fr', publicVapidKey, privateVapidKey); +} + +router.post('/subscribe', authenticate, async (req, res) => { + try { + const { endpoint, keys, reminderDays } = req.body; + if (!endpoint || !keys?.p256dh || !keys?.auth) { + return res.status(400).json({ error: 'endpoint, p256dh, and auth required' }); + } + await pool.query( + `INSERT INTO push_subscriptions (user_id, endpoint, p256dh, auth, reminder_days) + VALUES (?, ?, ?, ?, ?) + ON DUPLICATE KEY UPDATE p256dh = VALUES(p256dh), auth = VALUES(auth), reminder_days = VALUES(reminder_days)`, + [req.user.id, endpoint, keys.p256dh, keys.auth, reminderDays || 3] + ); + res.json({ message: 'Abonnement aux notifications enregistré' }); + } catch (err) { + console.error(err); + res.status(500).json({ error: 'Erreur serveur' }); + } +}); + +router.delete('/unsubscribe', authenticate, async (req, res) => { + try { + const { endpoint } = req.body; + if (!endpoint) return res.status(400).json({ error: 'endpoint required' }); + await pool.query('DELETE FROM push_subscriptions WHERE user_id = ? AND endpoint = ?', [req.user.id, endpoint]); + res.json({ message: 'Désabonné' }); + } catch (err) { + console.error(err); + res.status(500).json({ error: 'Erreur serveur' }); + } +}); + +router.get('/subscriptions', authenticate, async (req, res) => { + try { + const [rows] = await pool.query( + 'SELECT id, endpoint, reminder_days, last_sent_at, created_at FROM push_subscriptions WHERE user_id = ?', + [req.user.id] + ); + res.json(rows); + } catch (err) { + console.error(err); + res.status(500).json({ error: 'Erreur serveur' }); + } +}); + +router.get('/vapid-public-key', (req, res) => { + res.json({ publicKey: publicVapidKey }); +}); + +router.get('/members-with-push', authenticate, async (req, res) => { + if (req.user.role !== 'admin') return res.status(403).json({ error: 'Admin seulement' }); + try { + const [rows] = await pool.query( + `SELECT u.id, u.prenom, u.nom, u.societe, + (SELECT COUNT(*) FROM push_subscriptions ps WHERE ps.user_id = u.id) AS has_push + FROM users u WHERE u.actif = 1 ORDER BY u.prenom` + ); + res.json(rows); + } catch (err) { + console.error(err); + res.status(500).json({ error: 'Erreur serveur' }); + } +}); + +router.post('/broadcast', authenticate, async (req, res) => { + if (req.user.role !== 'admin') return res.status(403).json({ error: 'Admin seulement' }); + try { + const { title, body, url, userIds } = req.body; + if (!title || !body) return res.status(400).json({ error: 'Titre et message requis' }); + + let subs; + if (userIds && Array.isArray(userIds) && userIds.length > 0) { + const placeholders = userIds.map(() => '?').join(','); + [subs] = await pool.query( + `SELECT endpoint, p256dh, auth, user_id FROM push_subscriptions WHERE user_id IN (${placeholders})`, + userIds + ); + } else { + [subs] = await pool.query('SELECT endpoint, p256dh, auth, user_id FROM push_subscriptions'); + } + + if (subs.length === 0) return res.status(400).json({ error: 'Aucun abonné' }); + + const [notifResult] = await pool.query( + 'INSERT INTO notifications (sender_id, title, body) VALUES (?, ?, ?)', + [req.user.id, title.slice(0, 100), body.slice(0, 255)] + ); + const notifId = notifResult.insertId; + + const recipientIds = [...new Set(subs.map((s) => s.user_id))]; + for (const uid of recipientIds) { + await pool.query( + 'INSERT IGNORE INTO notification_recipients (notification_id, user_id) VALUES (?, ?)', + [notifId, uid] + ); + } + + const payload = JSON.stringify({ + title: title.slice(0, 100), + body: body.slice(0, 255), + icon: '/logo.svg', + badge: '/favicon.svg', + data: { url: '/notifications' }, + }); + + let sent = 0; + for (const sub of subs) { + try { + await webPush.sendNotification( + { endpoint: sub.endpoint, keys: { p256dh: sub.p256dh, auth: sub.auth } }, + payload + ); + sent++; + } catch (err) { + if (err.statusCode === 410 || err.statusCode === 404) { + await pool.query('DELETE FROM push_subscriptions WHERE endpoint = ?', [sub.endpoint]); + } + } + } + res.json({ message: `Notification envoyée à ${sent} abonné(s) sur ${recipientIds.length} destinataire(s)` }); + } catch (err) { + console.error(err); + res.status(500).json({ error: 'Erreur serveur' }); + } +}); + +router.post('/test', authenticate, async (req, res) => { + try { + const [subs] = await pool.query( + 'SELECT endpoint, p256dh, auth FROM push_subscriptions WHERE user_id = ?', + [req.user.id] + ); + if (subs.length === 0) return res.status(400).json({ error: 'Aucun abonnement trouvé' }); + + const payload = JSON.stringify({ + title: 'Dyna Biz Coignières', + body: 'Notification de test ✓', + icon: '/logo.svg', + badge: '/favicon.svg', + data: { url: '/' }, + }); + + for (const sub of subs) { + try { + await webPush.sendNotification( + { endpoint: sub.endpoint, keys: { p256dh: sub.p256dh, auth: sub.auth } }, + payload + ); + } catch (err) { + if (err.statusCode === 410 || err.statusCode === 404) { + await pool.query('DELETE FROM push_subscriptions WHERE endpoint = ?', [sub.endpoint]); + } + } + } + res.json({ message: 'Notification de test envoyée' }); + } catch (err) { + console.error(err); + res.status(500).json({ error: 'Erreur serveur' }); + } +}); + +module.exports = router; diff --git a/backend/routes/recommendations.js b/backend/routes/recommendations.js new file mode 100644 index 0000000..ea65737 --- /dev/null +++ b/backend/routes/recommendations.js @@ -0,0 +1,13 @@ +const { Router } = require('express'); +const { list, getById, create, update, remove, setStatus } = require('../controllers/recommendationController'); +const { authenticate } = require('../middleware/auth'); + +const router = Router(); +router.get('/', authenticate, list); +router.get('/:id', authenticate, getById); +router.post('/', authenticate, create); +router.put('/:id', authenticate, update); +router.patch('/:id/status', authenticate, setStatus); +router.delete('/:id', authenticate, remove); + +module.exports = router; diff --git a/backend/routes/stats.js b/backend/routes/stats.js new file mode 100644 index 0000000..9778784 --- /dev/null +++ b/backend/routes/stats.js @@ -0,0 +1,9 @@ +const { Router } = require('express'); +const { dashboard, memberStats } = require('../controllers/statsController'); +const { authenticate } = require('../middleware/auth'); + +const router = Router(); +router.get('/dashboard', authenticate, dashboard); +router.get('/member/:id?', authenticate, memberStats); + +module.exports = router; diff --git a/backend/seed.js b/backend/seed.js new file mode 100644 index 0000000..14616df --- /dev/null +++ b/backend/seed.js @@ -0,0 +1,39 @@ +const bcrypt = require('bcrypt'); +const mysql = require('mysql2/promise'); +require('dotenv').config(); + +const members = [ + { email: 'pierre@dynabiz.fr', password: 'Pierre78@' }, + { email: 'estelle@dynabiz.fr', password: 'Estelle78@' }, + { email: 'patricia@dynabiz.fr', password: 'Patricia78@' }, + { email: 'jennifer@dynabiz.fr', password: 'Jennifer78@' }, + { email: 'amandine@dynabiz.fr', password: 'Amandine78@' }, + { email: 'anais@dynabiz.fr', password: 'Anais78@' }, + { email: 'bastien@dynabiz.fr', password: 'Bastien78@' }, + { email: 'henrique@dynabiz.fr', password: 'Henrique78@' }, + { email: 'laetitia@dynabiz.fr', password: 'Laetitia78@' }, + { email: 'marie@dynabiz.fr', password: 'Marie78@' }, + { email: 'miguel@dynabiz.fr', password: 'Miguel78@' }, + { email: 'seb@dynabiz.fr', password: 'Seb78@' }, + { email: 'christelle@dynabiz.fr', password: 'Christelle78@' }, + { email: 'mathieu@dynabiz.fr', password: 'Mathieu78@' }, + { email: 'morgane@dynabiz.fr', password: 'Morgane78@' }, +]; + +async function seed() { + const connection = await mysql.createConnection({ + host: process.env.DB_HOST || 'localhost', + port: parseInt(process.env.DB_PORT, 10) || 3306, + user: process.env.DB_USER || 'dynausr', + password: process.env.DB_PASSWORD || '', + database: process.env.DB_NAME || 'dynasqy', + }); + for (const m of members) { + const hash = await bcrypt.hash(m.password, 10); + await connection.execute('UPDATE users SET password_hash = ? WHERE email = ?', [hash, m.email]); + } + console.log('Mots de passe individuels crees !'); + for (const m of members) console.log(' ' + m.email.split('@')[0] + ' -> ' + m.password); + await connection.end(); +} +seed().catch(console.error); diff --git a/backend/server.js b/backend/server.js new file mode 100644 index 0000000..39c84de --- /dev/null +++ b/backend/server.js @@ -0,0 +1,68 @@ +const express = require('express'); +const cors = require('cors'); +const path = require('path'); +const fs = require('fs'); +require('dotenv').config(); + +const db = require('./config/db'); + +const authRoutes = require('./routes/auth'); +const memberRoutes = require('./routes/members'); +const guestRoutes = require('./routes/guests'); +const recommendationRoutes = require('./routes/recommendations'); +const honorRoutes = require('./routes/honors'); +const eventRoutes = require('./routes/events'); +const statsRoutes = require('./routes/stats'); +const adminRoutes = require('./routes/admin'); +const invitationRoutes = require('./routes/invitation'); +const absenceRoutes = require('./routes/absences'); +const galleryRoutes = require('./routes/gallery'); +const pushRoutes = require('./routes/push'); +const notificationRoutes = require('./routes/notifications'); +const presentationRoutes = require('./routes/presentation'); +const memberSlidesRoutes = require('./routes/memberSlides'); +const exportRoutes = require('./routes/export'); +const activityLogRoutes = require('./routes/activityLog'); +const presentationEditorRoutes = require('./routes/presentationEditor'); +const { startPushCron } = require('./services/pushNotifications'); + +const app = express(); + +const uploadsDir = path.join(__dirname, 'uploads'); +if (!fs.existsSync(uploadsDir)) fs.mkdirSync(uploadsDir, { recursive: true }); + +const galleryDir = path.join(__dirname, 'gallery'); +if (!fs.existsSync(galleryDir)) fs.mkdirSync(galleryDir, { recursive: true }); + +app.use(cors()); +app.use(express.json({ limit: '100mb' })); +app.use('/uploads', express.static(uploadsDir)); +app.use('/gallery', express.static(galleryDir)); + +app.use('/api/auth', authRoutes); +app.use('/api/members', memberRoutes); +app.use('/api/guests', guestRoutes); +app.use('/api/recommendations', recommendationRoutes); +app.use('/api/honors', honorRoutes); +app.use('/api/events', eventRoutes); +app.use('/api/stats', statsRoutes); +app.use('/api/admin', adminRoutes); +app.use('/api/admin/presentation-editor', presentationEditorRoutes); +app.use('/api/public/invitation', invitationRoutes); +app.use('/invitation', invitationRoutes); +app.use('/api/absences', absenceRoutes); +app.use('/api/gallery', galleryRoutes); +app.use('/api/push', pushRoutes); +app.use('/api/notifications', notificationRoutes); +app.use('/api/presentation', presentationRoutes); +app.use('/api/member-slides', memberSlidesRoutes); +app.use('/api/export', exportRoutes); +app.use('/api/activity-logs', activityLogRoutes); +app.use('/member-slides', express.static(path.join(__dirname, 'member_slides'))); + +app.get('/api/health', (req, res) => res.json({ status: 'ok' })); + +startPushCron(); + +const PORT = process.env.PORT || 3000; +app.listen(PORT, () => console.log(`Backend running on port ${PORT}`)); diff --git a/backend/services/activityLog.js b/backend/services/activityLog.js new file mode 100644 index 0000000..e24e7b0 --- /dev/null +++ b/backend/services/activityLog.js @@ -0,0 +1,28 @@ +const pool = require('../config/db'); + +async function logActivity(reqOrUser, action, details = null) { + let userId, userPrenom, userNom, ip; + if (reqOrUser && reqOrUser.user) { + userId = reqOrUser.user.id; + userPrenom = reqOrUser.user.prenom; + userNom = reqOrUser.user.nom; + ip = reqOrUser.ip || reqOrUser.connection?.remoteAddress || null; + } else if (reqOrUser && reqOrUser.id) { + userId = reqOrUser.id; + userPrenom = reqOrUser.prenom || ''; + userNom = reqOrUser.nom || ''; + ip = null; + } else { + return; + } + try { + await pool.query( + 'INSERT INTO activity_logs (user_id, user_prenom, user_nom, action, details, ip_address) VALUES (?, ?, ?, ?, ?, ?)', + [userId, userPrenom, userNom, action, details ? JSON.stringify(details) : null, ip] + ); + } catch (err) { + console.error('Failed to log activity:', err.message); + } +} + +module.exports = { logActivity }; diff --git a/backend/services/pushNotifications.js b/backend/services/pushNotifications.js new file mode 100644 index 0000000..458951d --- /dev/null +++ b/backend/services/pushNotifications.js @@ -0,0 +1,86 @@ +const cron = require('node-cron'); +const webPush = require('web-push'); +const pool = require('../config/db'); + +const publicVapidKey = process.env.VAPID_PUBLIC_KEY; +const privateVapidKey = process.env.VAPID_PRIVATE_KEY; + +if (publicVapidKey && privateVapidKey) { + webPush.setVapidDetails('mailto:pierre@secourit.fr', publicVapidKey, privateVapidKey); +} + +const REMINDER_MESSAGES = [ + { + title: 'Dyna Biz Coignières', + body: 'Pensez à mettre à jour vos invités et recommandations !', + icon: '/logo.svg', + badge: '/favicon.svg', + }, +]; + +function getNextReminderDate(days) { + const d = new Date(); + d.setDate(d.getDate() + days); + d.setHours(9, 0, 0, 0); + return d; +} + +async function sendReminders() { + console.log('[Push Cron] Checking reminders...'); + try { + const [subscriptions] = await pool.query( + `SELECT ps.*, u.prenom, u.nom + FROM push_subscriptions ps + JOIN users u ON u.id = ps.user_id + WHERE ps.last_sent_at IS NULL + OR DATE_ADD(ps.last_sent_at, INTERVAL ps.reminder_days DAY) <= NOW()` + ); + + console.log(`[Push Cron] Found ${subscriptions.length} subscription(s) to notify`); + + for (const sub of subscriptions) { + try { + const msg = REMINDER_MESSAGES[0]; + const payload = JSON.stringify({ + title: msg.title, + body: msg.body, + icon: msg.icon, + badge: msg.badge, + data: { url: '/' }, + }); + + await webPush.sendNotification( + { endpoint: sub.endpoint, keys: { p256dh: sub.p256dh, auth: sub.auth } }, + payload + ); + + await pool.query('UPDATE push_subscriptions SET last_sent_at = NOW() WHERE id = ?', [sub.id]); + console.log(`[Push Cron] Sent to ${sub.prenom} ${sub.nom}`); + } catch (err) { + if (err.statusCode === 410 || err.statusCode === 404) { + await pool.query('DELETE FROM push_subscriptions WHERE id = ?', [sub.id]); + console.log(`[Push Cron] Removed expired subscription for ${sub.prenom} ${sub.nom}`); + } else { + console.error(`[Push Cron] Error sending to ${sub.prenom} ${sub.nom}:`, err.message); + } + } + } + } catch (err) { + console.error('[Push Cron] Error:', err); + } +} + +function startPushCron() { + if (!publicVapidKey || !privateVapidKey) { + console.log('[Push Cron] VAPID keys not configured — skipping cron start'); + return; + } + + cron.schedule('0 9 * * *', () => { + sendReminders(); + }); + + console.log('[Push Cron] Scheduled daily at 09:00'); +} + +module.exports = { startPushCron, sendReminders }; diff --git a/database/fix_reception.sql b/database/fix_reception.sql new file mode 100644 index 0000000..85bdac7 --- /dev/null +++ b/database/fix_reception.sql @@ -0,0 +1,2 @@ +ALTER TABLE users MODIFY COLUMN reception_connu INT UNSIGNED DEFAULT NULL; +ALTER TABLE users MODIFY COLUMN reception_inconnu INT UNSIGNED DEFAULT NULL; diff --git a/database/fix_reception2.sql b/database/fix_reception2.sql new file mode 100644 index 0000000..daf9462 --- /dev/null +++ b/database/fix_reception2.sql @@ -0,0 +1,4 @@ +UPDATE users SET reception_connu = NULL WHERE reception_connu = 0 OR reception_connu NOT IN (SELECT id FROM users); +UPDATE users SET reception_inconnu = NULL WHERE reception_inconnu = 0 OR reception_inconnu NOT IN (SELECT id FROM users); +ALTER TABLE users ADD FOREIGN KEY (reception_connu) REFERENCES users(id) ON DELETE SET NULL; +ALTER TABLE users ADD FOREIGN KEY (reception_inconnu) REFERENCES users(id) ON DELETE SET NULL; diff --git a/database/migrate_vn.sql b/database/migrate_vn.sql new file mode 100644 index 0000000..9a27d2e --- /dev/null +++ b/database/migrate_vn.sql @@ -0,0 +1,3 @@ +SHOW COLUMNS FROM invites like 'visit_number'; +ALTER TABLE invites ADD COLUMN visit_number INT UNSIGNED NOT NULL DEFAULT 1 AFTER photo_url; +SHOW COLUMNS FROM invites like 'visit_number'; diff --git a/database/migration_absences.sql b/database/migration_absences.sql new file mode 100644 index 0000000..2e47a51 --- /dev/null +++ b/database/migration_absences.sql @@ -0,0 +1,10 @@ +CREATE TABLE IF NOT EXISTS absences ( + id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, + membre_id INT UNSIGNED NOT NULL, + date_absence DATE NOT NULL, + motif VARCHAR(255) DEFAULT NULL, + created_by INT UNSIGNED NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (membre_id) REFERENCES users(id) ON DELETE CASCADE, + FOREIGN KEY (created_by) REFERENCES users(id) +) ENGINE=InnoDB; diff --git a/database/migration_absences_v2.sql b/database/migration_absences_v2.sql new file mode 100644 index 0000000..55550ca --- /dev/null +++ b/database/migration_absences_v2.sql @@ -0,0 +1,4 @@ +ALTER TABLE absences + ADD COLUMN prevenu TINYINT(1) NOT NULL DEFAULT 0 AFTER motif, + ADD COLUMN remplace_par_id INT UNSIGNED DEFAULT NULL AFTER prevenu, + ADD FOREIGN KEY (remplace_par_id) REFERENCES users(id) ON DELETE SET NULL; diff --git a/database/migration_absences_v3.sql b/database/migration_absences_v3.sql new file mode 100644 index 0000000..d9721de --- /dev/null +++ b/database/migration_absences_v3.sql @@ -0,0 +1,4 @@ +ALTER TABLE absences + ADD COLUMN remplace_par_type ENUM('member','guest') DEFAULT 'member' AFTER remplace_par_id, + ADD COLUMN remplace_par_guest_id INT UNSIGNED DEFAULT NULL AFTER remplace_par_type, + ADD FOREIGN KEY (remplace_par_guest_id) REFERENCES invites(id) ON DELETE SET NULL; diff --git a/database/migration_activity_logs.sql b/database/migration_activity_logs.sql new file mode 100644 index 0000000..a89e7ac --- /dev/null +++ b/database/migration_activity_logs.sql @@ -0,0 +1,14 @@ +CREATE TABLE IF NOT EXISTS activity_logs ( + id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, + user_id INT UNSIGNED NOT NULL, + user_prenom VARCHAR(100) NOT NULL, + user_nom VARCHAR(100) NOT NULL, + action VARCHAR(100) NOT NULL, + details TEXT DEFAULT NULL, + ip_address VARCHAR(45) DEFAULT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE, + INDEX idx_created_at (created_at), + INDEX idx_user_id (user_id), + INDEX idx_action (action) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; diff --git a/database/migration_candidature.sql b/database/migration_candidature.sql new file mode 100644 index 0000000..5cfdcdf --- /dev/null +++ b/database/migration_candidature.sql @@ -0,0 +1,2 @@ +ALTER TABLE invites ADD COLUMN statut_candidature VARCHAR(20) DEFAULT NULL; +ALTER TABLE invites ADD COLUMN candidature_date TIMESTAMP NULL DEFAULT NULL; diff --git a/database/migration_fonction_kdrive.sql b/database/migration_fonction_kdrive.sql new file mode 100644 index 0000000..265acba --- /dev/null +++ b/database/migration_fonction_kdrive.sql @@ -0,0 +1,3 @@ +ALTER TABLE users ADD COLUMN fonction VARCHAR(255) DEFAULT NULL; +ALTER TABLE users ADD COLUMN fonction_emoji VARCHAR(10) DEFAULT NULL; +ALTER TABLE users ADD COLUMN kdrive_link TEXT DEFAULT NULL; diff --git a/database/migration_invites.sql b/database/migration_invites.sql new file mode 100644 index 0000000..90187ab --- /dev/null +++ b/database/migration_invites.sql @@ -0,0 +1,16 @@ +-- Migration: add email invitation columns to invites table +-- Run this on the existing database before deploying the new code + +ALTER TABLE invites + ADD COLUMN statut ENUM('en_attente','acceptee','refusee') NOT NULL DEFAULT 'en_attente' + AFTER mail_envoye, + ADD COLUMN token VARCHAR(64) DEFAULT NULL + AFTER statut, + ADD COLUMN invitation_sent_at TIMESTAMP NULL DEFAULT NULL + AFTER token, + ADD COLUMN photo_url VARCHAR(255) DEFAULT NULL + AFTER invitation_sent_at, + ADD COLUMN site_web VARCHAR(255) DEFAULT NULL + AFTER photo_url, + ADD COLUMN visit_number INT UNSIGNED NOT NULL DEFAULT 1 + AFTER photo_url; diff --git a/database/migration_membre_fields.sql b/database/migration_membre_fields.sql new file mode 100644 index 0000000..fca48f5 --- /dev/null +++ b/database/migration_membre_fields.sql @@ -0,0 +1,8 @@ +ALTER TABLE users + ADD COLUMN invite_par_id INT UNSIGNED DEFAULT NULL AFTER site_web, + ADD COLUMN date_postulation DATE DEFAULT NULL AFTER invite_par_id, + ADD COLUMN reception_connu TINYINT(1) NOT NULL DEFAULT 0 AFTER date_postulation, + ADD COLUMN reception_inconnu TINYINT(1) NOT NULL DEFAULT 0 AFTER reception_connu, + ADD COLUMN date_entretien_postulation DATE DEFAULT NULL AFTER reception_inconnu, + ADD COLUMN date_demande_whatsapp DATE DEFAULT NULL AFTER date_entretien_postulation, + ADD FOREIGN KEY (invite_par_id) REFERENCES users(id) ON DELETE SET NULL; diff --git a/database/migration_merged_accounts.sql b/database/migration_merged_accounts.sql new file mode 100644 index 0000000..d5a9300 --- /dev/null +++ b/database/migration_merged_accounts.sql @@ -0,0 +1 @@ +ALTER TABLE users ADD COLUMN merged_display_name VARCHAR(255) DEFAULT NULL AFTER photo_url; diff --git a/database/migration_notifications.sql b/database/migration_notifications.sql new file mode 100644 index 0000000..5dc78c1 --- /dev/null +++ b/database/migration_notifications.sql @@ -0,0 +1,17 @@ +CREATE TABLE IF NOT EXISTS notifications ( + id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, + sender_id INT UNSIGNED NOT NULL, + title VARCHAR(255) NOT NULL, + body TEXT NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (sender_id) REFERENCES users(id) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +CREATE TABLE IF NOT EXISTS notification_recipients ( + notification_id INT UNSIGNED NOT NULL, + user_id INT UNSIGNED NOT NULL, + read_at DATETIME DEFAULT NULL, + PRIMARY KEY (notification_id, user_id), + FOREIGN KEY (notification_id) REFERENCES notifications(id) ON DELETE CASCADE, + FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; diff --git a/database/migration_presentation_overrides.sql b/database/migration_presentation_overrides.sql new file mode 100644 index 0000000..caadc80 --- /dev/null +++ b/database/migration_presentation_overrides.sql @@ -0,0 +1,10 @@ +CREATE TABLE IF NOT EXISTS presentation_overrides ( + id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, + date_key VARCHAR(8) NOT NULL, + slide_key VARCHAR(100) NOT NULL, + content JSON DEFAULT NULL, + hidden TINYINT(1) NOT NULL DEFAULT 0, + sort_order INT NOT NULL DEFAULT 0, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + UNIQUE KEY uq_date_slide (date_key, slide_key) +) ENGINE=InnoDB; diff --git a/database/migration_push_notifications.sql b/database/migration_push_notifications.sql new file mode 100644 index 0000000..55bf5ae --- /dev/null +++ b/database/migration_push_notifications.sql @@ -0,0 +1,13 @@ +CREATE TABLE IF NOT EXISTS push_subscriptions ( + id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, + user_id INT UNSIGNED NOT NULL, + endpoint TEXT NOT NULL, + p256dh VARCHAR(255) NOT NULL, + auth VARCHAR(255) NOT NULL, + reminder_days INT UNSIGNED NOT NULL DEFAULT 3, + last_sent_at DATETIME DEFAULT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE, + UNIQUE KEY unique_user_sub (user_id, endpoint(255)) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; diff --git a/database/migration_reception_fix.sql b/database/migration_reception_fix.sql new file mode 100644 index 0000000..be738e9 --- /dev/null +++ b/database/migration_reception_fix.sql @@ -0,0 +1,5 @@ +ALTER TABLE users + MODIFY COLUMN reception_connu INT UNSIGNED DEFAULT NULL, + MODIFY COLUMN reception_inconnu INT UNSIGNED DEFAULT NULL, + ADD FOREIGN KEY (reception_connu) REFERENCES users(id) ON DELETE SET NULL, + ADD FOREIGN KEY (reception_inconnu) REFERENCES users(id) ON DELETE SET NULL; diff --git a/database/schema.sql b/database/schema.sql new file mode 100644 index 0000000..2e20208 --- /dev/null +++ b/database/schema.sql @@ -0,0 +1,168 @@ +CREATE DATABASE IF NOT EXISTS dynasqy + CHARACTER SET utf8mb4 + COLLATE utf8mb4_unicode_ci; + +USE dynasqy; + +CREATE TABLE users ( + id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, + prenom VARCHAR(100) NOT NULL, + nom VARCHAR(100) NOT NULL, + societe VARCHAR(255) DEFAULT NULL, + activite VARCHAR(255) DEFAULT NULL, + email VARCHAR(255) NOT NULL UNIQUE, + telephone VARCHAR(30) DEFAULT NULL, + password_hash VARCHAR(255) NOT NULL, + role ENUM('admin','member') NOT NULL DEFAULT 'member', + date_adhesion DATE DEFAULT NULL, + date_echeance DATE DEFAULT NULL, + actif TINYINT(1) NOT NULL DEFAULT 1, + invite_par_id INT UNSIGNED DEFAULT NULL, + date_postulation DATE DEFAULT NULL, + reception_connu INT UNSIGNED DEFAULT NULL, + reception_inconnu INT UNSIGNED DEFAULT NULL, + date_entretien_postulation DATE DEFAULT NULL, + date_demande_whatsapp DATE DEFAULT NULL, + fonction VARCHAR(255) DEFAULT NULL, + fonction_emoji VARCHAR(10) DEFAULT NULL, + kdrive_link TEXT DEFAULT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, + FOREIGN KEY (invite_par_id) REFERENCES users(id) ON DELETE SET NULL, + FOREIGN KEY (reception_connu) REFERENCES users(id) ON DELETE SET NULL, + FOREIGN KEY (reception_inconnu) REFERENCES users(id) ON DELETE SET NULL +) ENGINE=InnoDB; + +CREATE TABLE invites ( + id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, + prenom VARCHAR(100) NOT NULL, + nom VARCHAR(100) NOT NULL, + societe VARCHAR(255) DEFAULT NULL, + activite VARCHAR(255) DEFAULT NULL, + telephone VARCHAR(30) DEFAULT NULL, + email VARCHAR(255) DEFAULT NULL, + site_web VARCHAR(255) DEFAULT NULL, + commentaires TEXT DEFAULT NULL, + mail_envoye TINYINT(1) NOT NULL DEFAULT 0, + statut ENUM('en_attente','acceptee','refusee') NOT NULL DEFAULT 'en_attente', + token VARCHAR(64) DEFAULT NULL, + invitation_sent_at TIMESTAMP NULL DEFAULT NULL, + photo_url VARCHAR(255) DEFAULT NULL, + visit_number INT UNSIGNED NOT NULL DEFAULT 1, + date_club DATE DEFAULT NULL, + statut_candidature VARCHAR(20) DEFAULT NULL, + candidature_date TIMESTAMP NULL DEFAULT NULL, + invite_par_id INT UNSIGNED NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (invite_par_id) REFERENCES users(id) ON DELETE CASCADE +) ENGINE=InnoDB; + +CREATE TABLE recommandations ( + id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, + date DATE NOT NULL, + membre_id INT UNSIGNED NOT NULL, + destinataire_id INT UNSIGNED DEFAULT NULL, + nom_prospect VARCHAR(255) NOT NULL, + type ENUM('donnee','recue') NOT NULL DEFAULT 'donnee', + transformee TINYINT(1) NOT NULL DEFAULT 0, + ca_realise DECIMAL(12,2) DEFAULT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (membre_id) REFERENCES users(id) ON DELETE CASCADE, + FOREIGN KEY (destinataire_id) REFERENCES users(id) ON DELETE SET NULL +) ENGINE=InnoDB; + +CREATE TABLE mises_en_honneur ( + id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, + date DATE NOT NULL, + membre_id INT UNSIGNED DEFAULT NULL, + societe VARCHAR(255) DEFAULT NULL, + theme_sujet VARCHAR(500) NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (membre_id) REFERENCES users(id) ON DELETE SET NULL +) ENGINE=InnoDB; + +CREATE TABLE evenements ( + id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, + date DATE NOT NULL, + nom_evenement VARCHAR(255) NOT NULL, + lieu VARCHAR(255) DEFAULT NULL, + horaire VARCHAR(50) DEFAULT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP +) ENGINE=InnoDB; + +CREATE TABLE IF NOT EXISTS absences ( + id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, + membre_id INT UNSIGNED NOT NULL, + date_absence DATE NOT NULL, + motif VARCHAR(255) DEFAULT NULL, + prevenu TINYINT(1) NOT NULL DEFAULT 0, + remplace_par_id INT UNSIGNED DEFAULT NULL, + remplace_par_type ENUM('member','guest') DEFAULT 'member', + remplace_par_guest_id INT UNSIGNED DEFAULT NULL, + created_by INT UNSIGNED NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (membre_id) REFERENCES users(id) ON DELETE CASCADE, + FOREIGN KEY (remplace_par_id) REFERENCES users(id) ON DELETE SET NULL, + FOREIGN KEY (remplace_par_guest_id) REFERENCES invites(id) ON DELETE SET NULL, + FOREIGN KEY (created_by) REFERENCES users(id) +) ENGINE=InnoDB; + +CREATE TABLE presences ( + id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, + membre_id INT UNSIGNED NOT NULL, + date_reunion DATE NOT NULL, + present TINYINT(1) NOT NULL DEFAULT 1, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (membre_id) REFERENCES users(id) ON DELETE CASCADE, + UNIQUE KEY uk_membre_reunion (membre_id, date_reunion) +) ENGINE=InnoDB; + +CREATE TABLE member_slides ( + id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY, + member_id INT UNSIGNED NOT NULL, + filename VARCHAR(255) NOT NULL, + original_name VARCHAR(255) NOT NULL, + date_key VARCHAR(8) NOT NULL, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (member_id) REFERENCES users(id) ON DELETE CASCADE +) ENGINE=InnoDB; + +INSERT INTO users (prenom, nom, societe, activite, email, telephone, password_hash, role, date_adhesion, date_echeance) VALUES +('Pierre', 'Admin', 'Dyna Biz', 'Administration', 'pierre@dynabiz.fr', '0600000001', '$2b$10$seedplaceholder','admin', '2024-01-01','2026-12-31'), +('Estelle', 'Admin', 'Dyna Biz', 'Administration', 'estelle@dynabiz.fr', '0600000002', '$2b$10$seedplaceholder','admin', '2024-01-01','2026-12-31'), +('Patricia', 'Membre', '', '', 'patricia@dynabiz.fr', '0600000003', '$2b$10$seedplaceholder','member', '2024-01-01','2026-12-31'), +('Jennifer', 'Membre', '', '', 'jennifer@dynabiz.fr', '0600000004', '$2b$10$seedplaceholder','member', '2024-01-01','2026-12-31'), +('Amandine', 'Membre', '', '', 'amandine@dynabiz.fr', '0600000005', '$2b$10$seedplaceholder','member', '2024-01-01','2026-12-31'), +('Anais', 'Membre', '', '', 'anais@dynabiz.fr', '0600000006', '$2b$10$seedplaceholder','member', '2024-01-01','2026-12-31'), +('Bastien', 'Membre', '', '', 'bastien@dynabiz.fr', '0600000007', '$2b$10$seedplaceholder','member', '2024-01-01','2026-12-31'), +('Henrique', 'Membre', '', '', 'henrique@dynabiz.fr', '0600000008', '$2b$10$seedplaceholder','member', '2024-01-01','2026-12-31'), +('Laetitia', 'Membre', '', '', 'laetitia@dynabiz.fr', '0600000009', '$2b$10$seedplaceholder','member', '2024-01-01','2026-12-31'), +('Marie', 'Membre', '', '', 'marie@dynabiz.fr', '0600000010', '$2b$10$seedplaceholder','member', '2024-01-01','2026-12-31'), +('Miguel', 'Membre', '', '', 'miguel@dynabiz.fr', '0600000011', '$2b$10$seedplaceholder','member', '2024-01-01','2026-12-31'), +('Seb', 'Membre', '', '', 'seb@dynabiz.fr', '0600000012', '$2b$10$seedplaceholder','member', '2024-01-01','2026-12-31'), +('Christelle','Membre', '', '', 'christelle@dynabiz.fr', '0600000013', '$2b$10$seedplaceholder','member', '2024-01-01','2026-12-31'), +('Mathieu', 'Membre', '', '', 'mathieu@dynabiz.fr', '0600000014', '$2b$10$seedplaceholder','member', '2024-01-01','2026-12-31'), +('Morgane', 'Membre', '', '', 'morgane@dynabiz.fr', '0600000015', '$2b$10$seedplaceholder','member', '2024-01-01','2026-12-31'); + +INSERT INTO evenements (date, nom_evenement, lieu, horaire) VALUES +('2025-03-14','Petit-déjeuner réseau','Hotel IBIS SQY','08h30'), +('2025-05-09','Petit-déjeuner réseau','Hotel IBIS SQY','08h30'), +('2025-06-13','Petit-déjeuner réseau','Hotel IBIS SQY','08h30'), +('2025-09-12','Petit-déjeuner réseau','Hotel IBIS SQY','08h30'), +('2025-11-14','Petit-déjeuner réseau','Hotel IBIS SQY','08h30'), +('2025-12-12','Petit-déjeuner réseau','Hotel IBIS SQY','08h30'), +('2026-01-13','Petit-déjeuner réseau','Hotel IBIS SQY','08h30'), +('2026-01-27','Petit-déjeuner réseau','Hotel IBIS SQY','08h30'), +('2026-02-10','Petit-déjeuner réseau','Hotel IBIS SQY','08h30'), +('2026-02-24','Petit-déjeuner réseau','Hotel IBIS SQY','08h30'), +('2026-03-10','Petit-déjeuner réseau','Hotel IBIS SQY','08h30'), +('2026-03-24','Petit-déjeuner réseau','Hotel IBIS SQY','08h30'), +('2026-04-07','Petit-déjeuner réseau','Hotel IBIS SQY','08h30'), +('2026-04-21','Petit-déjeuner réseau','Hotel IBIS SQY','08h30'), +('2026-05-05','Petit-déjeuner réseau','Hotel IBIS SQY','08h30'), +('2026-06-02','Petit-déjeuner réseau','Hotel IBIS SQY','08h30'), +('2026-06-12','Rendez-vous Club d''affaires','Hotel IBIS SQY','08h30'), +('2026-06-16','Petit-déjeuner réseau','Hotel IBIS SQY','08h30'), +('2026-07-10','After-work Club','Le Bistrot SQY','18h30'), +('2026-07-28','Petit-déjeuner réseau','Hotel IBIS SQY','08h30'), +('2026-09-11','Réunion plénière','CCI SQY','09h00'); diff --git a/database/seed_csv_invites.sql b/database/seed_csv_invites.sql new file mode 100644 index 0000000..04cfad0 --- /dev/null +++ b/database/seed_csv_invites.sql @@ -0,0 +1,201 @@ +DELETE FROM invites; +-- Import from Dyna Biz CSV (UTF-8 encoded) + +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, date_club, invite_par_id, visit_number) VALUES ( + 'Guillaume', 'BALDECK', 'Mercure', 'Hôtellerie Restauration', '06 50 00 87 52', 'Guillaume.BALDECK@accor.com', '2026-01-13', (SELECT id FROM users WHERE prenom='Estelle'), 1); +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, date_club, invite_par_id, visit_number) VALUES ( + 'Morgane', 'MONTEL', 'Generali', 'Assurance pro', '06 63 70 28 55', 'morgane.montel@generali.com', '2026-01-13', (SELECT id FROM users WHERE prenom='Estelle'), 1); +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, date_club, invite_par_id, visit_number) VALUES ( + 'Morgane', 'MONTEL', 'Generali', 'Assurance pro', '06 63 70 28 55', 'morgane.montel@generali.com', '2026-01-27', (SELECT id FROM users WHERE prenom='Estelle'), 2); +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, date_club, invite_par_id, visit_number) VALUES ( + 'Lou', 'MENANT', 'Ninphi', 'Conseil aux organisations, transition, management, qualité, QVCT', '06 11 17 53 08', 'lmenant@ninphi.fr', '2026-01-27', (SELECT id FROM users WHERE prenom='Estelle'), 1); +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, date_club, invite_par_id, visit_number) VALUES ( + 'Damien', 'RICCIO', 'DARE Consulting', 'Commerce et import-export', '07 87 81 60 71', 'driccio@dare-consulting.eu', '2026-01-27', (SELECT id FROM users WHERE prenom='Estelle'), 1); +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, date_club, invite_par_id, visit_number) VALUES ( + 'Damien', 'RICCIO', 'DARE Consulting', 'Commerce et import-export', '07 87 81 60 71', 'driccio@dare-consulting.eu', '2026-02-24', (SELECT id FROM users WHERE prenom='Estelle'), 2); +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, date_club, invite_par_id, visit_number) VALUES ( + 'Amandine', 'CASSEL', 'Banque populaire', 'Banque', '06 74 26 00 64', 'amandine.cassel@bpvf.fr', '2026-02-10', (SELECT id FROM users WHERE prenom='Estelle'), 1); +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, date_club, invite_par_id, visit_number) VALUES ( + 'Amandine', 'CASSEL', 'Banque populaire', 'Banque', '06 74 26 00 64', 'amandine.cassel@bpvf.fr', '2026-02-24', (SELECT id FROM users WHERE prenom='Estelle'), 2); +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, date_club, invite_par_id, visit_number) VALUES ( + 'Marc-André', 'CHALMANDRIER', 'SIA', 'Sécurité incendie', '06 87 72 29 44', 'mchalmandrier@sia-service.fr', '2026-02-10', (SELECT id FROM users WHERE prenom='Estelle'), 1); +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, date_club, invite_par_id, visit_number) VALUES ( + 'Patrick', 'DIAMANT-BERGER', 'QUALRISK', 'Organisme de formation', '06 74 91 54 97', 'patrick.diamant-berger@qualrisk.fr', '2026-02-10', (SELECT id FROM users WHERE prenom='Estelle'), 1); +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, date_club, invite_par_id, visit_number) VALUES ( + 'Yann', 'RAOUL', 'Phare away', 'conseil communication - évènementiel - sponsoring - brand content - relations publiques', '06 12 66 38 47', 'yann.raoul@phareaway.com', '2026-02-10', (SELECT id FROM users WHERE prenom='Estelle'), 1); +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, date_club, invite_par_id, visit_number) VALUES ( + 'Yann', 'RAOUL', 'Phare away', 'conseil communication - évènementiel - sponsoring - brand content - relations publiques', '06 12 66 38 47', 'yann.raoul@phareaway.com', '2026-02-24', (SELECT id FROM users WHERE prenom='Estelle'), 2); +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, date_club, invite_par_id, visit_number) VALUES ( + 'Tony', 'ROY', 'Banque Populaire', 'banque', '06 10 72 65 93', 'tony.roy@bpvf.fr', '2026-02-10', (SELECT id FROM users WHERE prenom='Estelle'), 1); +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, date_club, invite_par_id, visit_number) VALUES ( + 'Anaïs', 'WOESTELANDT', 'RAS Intérim', 'Agence Intérim', '06 20 55 11 71', 'Anais.woestelandt@ras-interim.fr', '2026-02-10', (SELECT id FROM users WHERE prenom='Estelle'), 1); +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, date_club, invite_par_id, visit_number) VALUES ( + 'Gaëlle', 'CARLIER', 'CBIS Architectes', 'Architecte', '06 16 35 36 87', 'g.carlier@cbis-architectes.com', '2026-02-24', (SELECT id FROM users WHERE prenom='Estelle'), 1); +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, date_club, invite_par_id, visit_number) VALUES ( + 'Amélie', 'DURIZOT', 'RAS Intérim', 'Agence Intérim', '06 67 96 11 52', 'amelie.durizot@ras-interim.fr', '2026-02-24', (SELECT id FROM users WHERE prenom='Estelle'), 1); +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, date_club, invite_par_id, visit_number) VALUES ( + 'Amélie', 'DURIZOT', 'RAS Intérim', 'Agence Intérim', '06 67 96 11 52', 'amelie.durizot@ras-interim.fr', '2026-03-10', (SELECT id FROM users WHERE prenom='Estelle'), 2); +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, date_club, invite_par_id, visit_number) VALUES ( + 'Marc', 'LANOY', 'Dunamis solutions', 'QHSE et management', '06 80 38 87 76', 'marc.lanoy@dunamissolutions.fr', '2026-02-24', (SELECT id FROM users WHERE prenom='Estelle'), 1); +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, date_club, invite_par_id, visit_number) VALUES ( + 'Yun-Tyng', 'SUN', 'Sunshine Conseil', 'Gestionnaire de patrimoine', '06 99 76 21 64', 'sunshine.conseil@outlook.fr', '2026-02-24', (SELECT id FROM users WHERE prenom='Estelle'), 1); +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, date_club, invite_par_id, visit_number) VALUES ( + 'Yun-Tyng', 'SUN', 'Sunshine Conseil', 'Gestionnaire de patrimoine', '06 99 76 21 64', 'sunshine.conseil@outlook.fr', '2026-03-24', (SELECT id FROM users WHERE prenom='Estelle'), 2); +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, date_club, invite_par_id, visit_number) VALUES ( + 'Alexandre', 'DUBOST', 'Just Be Flex', 'Application Web', '06 14 34 30 12', 'alexandre.dubost@justbeflex.com', '2026-03-10', (SELECT id FROM users WHERE prenom='Estelle'), 1); +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, date_club, invite_par_id, visit_number) VALUES ( + 'Christelle', 'GEBELIN', 'CGE Conseil Patrimoine', 'Conseiller en gestion de patrimoine', '06 63 72 19 77', 'christelle.gebelin1@gmail.com', '2026-03-10', (SELECT id FROM users WHERE prenom='Estelle'), 1); +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, date_club, invite_par_id, visit_number) VALUES ( + 'Christelle', 'GEBELIN', 'CGE Conseil Patrimoine', 'Conseiller en gestion de patrimoine', '06 63 72 19 77', 'christelle.gebelin1@gmail.com', '2026-04-07', (SELECT id FROM users WHERE prenom='Estelle'), 2); +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, date_club, invite_par_id, visit_number) VALUES ( + 'Clémence', 'MORET', 'Cool Tech', 'Bâtiment', '07 64 14 03 64', 'contact@cooltech-idf.fr', '2026-03-10', (SELECT id FROM users WHERE prenom='Estelle'), 1); +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, date_club, invite_par_id, visit_number) VALUES ( + 'Richard', 'WAWRZYNIAK', 'FACET', 'Gestion et communication de crise', '06.89.36.86.27.', 'rwawrzyniak.facet@gmail.com', '2026-03-10', (SELECT id FROM users WHERE prenom='Estelle'), 1); +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, date_club, invite_par_id, visit_number) VALUES ( + 'Alexandre', 'BOULINE', 'Marie Blachère', 'Restauration', '06 34 11 98 00', 'cmabouline@gmail.com', '2026-03-24', (SELECT id FROM users WHERE prenom='Estelle'), 1); +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, date_club, invite_par_id, visit_number) VALUES ( + 'Alexandre', 'BOULINE', 'Marie Blachère', 'Restauration', '06 34 11 98 00', 'cmabouline@gmail.com', '2026-06-16', (SELECT id FROM users WHERE prenom='Estelle'), 2); +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, date_club, invite_par_id, visit_number) VALUES ( + 'Cindy', 'COATANEA', 'Pretpro.fr', 'Expert en financement professionnel', '06 89 69 49 29', 'cindy.coatanea@pretpro.expert', '2026-03-24', (SELECT id FROM users WHERE prenom='Estelle'), 1); +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, date_club, invite_par_id, visit_number) VALUES ( + 'Sandra', 'COCHEME', 'Expert Gestion GCL', 'Gestion d''entreprise', '06 88 90 54 62', 's.cocheme@expertgcl.fr', '2026-03-24', (SELECT id FROM users WHERE prenom='Estelle'), 1); +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, date_club, invite_par_id, visit_number) VALUES ( + 'Nadir', 'HAMMOUCHE', '4pointsur', 'Centre de formation', '07 44 96 43 30', 'nadir.hammouche@4pointsur.fr', '2026-03-24', (SELECT id FROM users WHERE prenom='Estelle'), 1); +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, date_club, invite_par_id, visit_number) VALUES ( + 'Rémy', 'MAKADJI', 'Perfzone', '', '06 21 23 57 17', 'remy.makadji@p-cub3.fr', '2026-03-24', (SELECT id FROM users WHERE prenom='Estelle'), 1); +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, date_club, invite_par_id, visit_number) VALUES ( + 'Aurélie', 'STRUZYNSKI', 'AS-UMAN', 'Coach professionnelle et formatrice PCM', '06 19 24 59 82', 'aureliestruzynski@as-uman.fr', '2026-03-24', (SELECT id FROM users WHERE prenom='Estelle'), 1); +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, date_club, invite_par_id, visit_number) VALUES ( + 'Anne-Sophie', 'VALET', 'Essentionnelle', 'coaching de dirigeants', '06 58 07 53 19', 'essensionnel@gmail.com', '2026-03-24', (SELECT id FROM users WHERE prenom='Estelle'), 1); +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, date_club, invite_par_id, visit_number) VALUES ( + 'AMAR Manal', 'BEN', 'LMC Partners', 'Avocat', '06 09 60 32 04', 'pole-social1@lmcpartenaires.fr', '2026-03-24', (SELECT id FROM users WHERE prenom='Estelle'), 1); +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, date_club, invite_par_id, visit_number) VALUES ( + 'Anthony', 'SOUSTELLE', 'Xefi', 'Informatique', '06 08 64 91 92', 'a.soustelle@xefi.fr', '2026-04-07', (SELECT id FROM users WHERE prenom='Estelle'), 1); +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, date_club, invite_par_id, visit_number) VALUES ( + 'Anne', 'JOUANNE', 'AJC', 'RAF externalisé', '06 62 21 60 61', 'anne.ajconseil@gmail.com', '2026-04-07', (SELECT id FROM users WHERE prenom='Estelle'), 1); +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, date_club, invite_par_id, visit_number) VALUES ( + 'Victoire', 'AUBERTIN', 'Formya', 'Formation', '06 71 37 01 39', 'victoire.aubertin@formya.fr', '2026-04-07', (SELECT id FROM users WHERE prenom='Estelle'), 1); +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, date_club, invite_par_id, visit_number) VALUES ( + 'FONTE Julio', 'DA', 'JDF RENOV', 'Plombier', '06 75 50 73 05', 'jdfonte@jdfrenov.fr', '2026-04-07', (SELECT id FROM users WHERE prenom='Estelle'), 1); +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, date_club, invite_par_id, visit_number) VALUES ( + 'Axelle', 'JAQUET', 'Infor Créances', 'Recouvrement', '06 14 30 16 37', 'axelle.jaquet@infor-creances.com', '2026-04-07', (SELECT id FROM users WHERE prenom='Estelle'), 1); +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, date_club, invite_par_id, visit_number) VALUES ( + 'Séverine', 'GIRAUDO', 'Girotec', 'Visserie', '06 72 25 84 56', 'severine@girotec.com', '2026-04-07', (SELECT id FROM users WHERE prenom='Estelle'), 1); +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, date_club, invite_par_id, visit_number) VALUES ( + 'Cyndie', 'GOMBAULD', 'Dycine consulting', 'Appels d''offres', '06 24 24 47 72', 'contact@dycineconsulting.com', '2026-04-07', (SELECT id FROM users WHERE prenom='Estelle'), 1); +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, date_club, invite_par_id, visit_number) VALUES ( + 'Cyndie', 'GOMBAULD', 'Dycine consulting', 'Appels d''offres', '06 24 24 47 72', 'contact@dycineconsulting.com', '2026-06-02', (SELECT id FROM users WHERE prenom='Estelle'), 2); +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, date_club, invite_par_id, visit_number) VALUES ( + 'Cyril', 'CAUDYSER', 'AMS', 'Aménagement d''utilitaire', '06 83 55 71 10', 'cyril@automulltiservices.com', '2026-04-07', (SELECT id FROM users WHERE prenom='Estelle'), 1); +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, date_club, invite_par_id, visit_number) VALUES ( + 'Cyril', 'CAUDYSER', 'AMS', 'Aménagement d''utilitaire', '06 83 55 71 10', 'cyril@automulltiservices.com', '2026-06-02', (SELECT id FROM users WHERE prenom='Estelle'), 2); +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, date_club, invite_par_id, visit_number) VALUES ( + 'Maxime', 'GUILLIET', 'FORD', 'Concessionnaire auto', '07 85 96 49 10', 'maxime.guilliet@autosphere.fr', '2026-06-02', (SELECT id FROM users WHERE prenom='Estelle'), 1); +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, date_club, invite_par_id, visit_number) VALUES ( + 'Maxime', 'GUILLIET', 'FORD', 'Concessionnaire auto', '07 85 96 49 10', 'maxime.guilliet@autosphere.fr', '2026-06-16', (SELECT id FROM users WHERE prenom='Estelle'), 2); +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, date_club, invite_par_id, visit_number) VALUES ( + 'Mélanie', 'LOUCHART', 'Emulsion MCL', 'Conseil en stratégie d''entreprise', '06 10 90 32 46', 'mel.louchart@gmail.com', '2026-05-05', (SELECT id FROM users WHERE prenom='Estelle'), 1); +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, date_club, invite_par_id, visit_number) VALUES ( + 'David', 'HERNANDEZ', 'SAVAC', 'Transport de voyageurs', '06 89 52 12 01', 'davidhperso@gmail.com', '2026-04-21', (SELECT id FROM users WHERE prenom='Estelle'), 1); +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, date_club, invite_par_id, visit_number) VALUES ( + 'Valérie', 'PASCUAL', 'Kintsugi', 'Médiateure', '06 67 11 26 07', 'valerie.pascual@kintsugimediation.fr', '2026-06-02', (SELECT id FROM users WHERE prenom='Estelle'), 1); +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, date_club, invite_par_id, visit_number) VALUES ( + 'BEAUMONT Maryline', 'CHALANDON', 'JH2C', 'Notaire', '06 60 67 16 38', 'maryline.chalandon-beaumont@jh2c.notaires.fr', '2026-04-21', (SELECT id FROM users WHERE prenom='Estelle'), 1); +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, date_club, invite_par_id, visit_number) VALUES ( + 'Wilfried', 'MOISANT', 'Atout Concept', 'Maîtrise d''oeuvre', '07 63 62 17 96', 'wmoisant@moe-atoutconcept.com', '2026-04-21', (SELECT id FROM users WHERE prenom='Estelle'), 1); +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, date_club, invite_par_id, visit_number) VALUES ( + 'François', 'JORDA', 'Domaines créatifs', 'Espaces verts', '', '', '2026-04-21', (SELECT id FROM users WHERE prenom='Estelle'), 1); +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, date_club, invite_par_id, visit_number) VALUES ( + 'Claire', 'GERMAIN', 'Clairver', 'Accompagnement d''entreprise', '', '', '2026-04-21', (SELECT id FROM users WHERE prenom='Estelle'), 1); +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, date_club, invite_par_id, visit_number) VALUES ( + 'Olivier', 'LEGRAVEREND', 'DC Pilot', 'Direction commerciale externalisée', '06 25 54 28 78', 'olivier.legraverend@dc-pilot.com', '2026-05-05', (SELECT id FROM users WHERE prenom='Estelle'), 1); +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, date_club, invite_par_id, visit_number) VALUES ( + 'Jérémy', 'MALINOSKY', 'MT Cuisines', 'Cuisiniste', '06 12 86 40 51', 'jmalinosky@mtcuisines.fr', '2026-04-21', (SELECT id FROM users WHERE prenom='Estelle'), 1); +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, date_club, invite_par_id, visit_number) VALUES ( + 'Ludovic', 'DESIRE', 'Massy Carrelage', 'Négoce du bâtiment', '07 48 88 47 51', 'massycarrelage@gmail.com', '2026-07-28', (SELECT id FROM users WHERE prenom='Estelle'), 1); +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, date_club, invite_par_id, visit_number) VALUES ( + 'Francine', 'FOURRE', 'Aurizéo', 'Mobilité inclusive', '06 83 02 88 88', 'francine.fourre@aurizeo.com', '2026-06-16', (SELECT id FROM users WHERE prenom='Estelle'), 1); +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, date_club, invite_par_id, visit_number) VALUES ( + 'Margaux', 'PERRIN', 'Only Brunch', 'Restauration', '07 82 49 01 25', 'margaux.perrin@onlybrunch.fr', '2026-06-16', (SELECT id FROM users WHERE prenom='Estelle'), 1); +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, date_club, invite_par_id, visit_number) VALUES ( + 'Marie', 'DESARMENIEN', 'Only Brunch', 'Restauration', '', '', '2026-06-16', (SELECT id FROM users WHERE prenom='Estelle'), 1); +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, date_club, invite_par_id, visit_number) VALUES ( + 'Mathieu', 'DUBOIS', 'Uni Pare-brise', 'Remplacement vitrerie de véhicule', '07 59 70 62 48', 'mjrmdirection.upb@gmail.com', '2026-06-16', (SELECT id FROM users WHERE prenom='Estelle'), 1); +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, date_club, invite_par_id, visit_number) VALUES ( + 'Williams', 'LAVALEE', 'Green patrimoine', 'Menuiserie', '06 16 16 52 36', 'williams.lavalee@green-patrimoine.fr', '2026-06-02', (SELECT id FROM users WHERE prenom='Estelle'), 1); +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, date_club, invite_par_id, visit_number) VALUES ( + 'Christophe', 'MEDICI', 'HQR', 'Formation', '06 88 35 32 11', 'Christophe.hqr@gmail.com', '2026-06-16', (SELECT id FROM users WHERE prenom='Estelle'), 1); +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, date_club, invite_par_id, visit_number) VALUES ( + 'Fabien', 'HEMERY', 'BSOP', 'Matériel informatique', '06 75 44 53 02', 'fabien.hemery@bsop78.fr', '2026-06-16', (SELECT id FROM users WHERE prenom='Estelle'), 1); +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, date_club, invite_par_id, visit_number) VALUES ( + 'Pascal', 'RYBAK', 'Sitadel', 'Solution GMAO pour services généraux', '06 69 74 70 00', 'pascal.rybak@sitadel.fr', '2026-07-28', (SELECT id FROM users WHERE prenom='Estelle'), 1); +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, date_club, invite_par_id, visit_number) VALUES ( + 'Charles', 'GOBESSI', 'Agent Keller Williams Imobilier', 'Agent immobilier', '06 88 52 24 05', 'charles.gobessi@kwfrance.com', '2026-06-16', (SELECT id FROM users WHERE prenom='Estelle'), 1); +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, date_club, invite_par_id, visit_number) VALUES ( + 'Damien', 'Dufour', 'GroupeActive', 'DSI en temps partagé', '06 86 07 17 25', '', '2026-04-07', (SELECT id FROM users WHERE prenom='Pierre'), 1); +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, date_club, invite_par_id, visit_number) VALUES ( + 'Stéphanie', 'Mrowczynski', 'Web Me Baby', 'Référencement SEO', '07 56 94 39 45', 'stephanie@webmebaby.com', '2026-06-02', (SELECT id FROM users WHERE prenom='Pierre'), 1); +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, date_club, invite_par_id, visit_number) VALUES ( + 'Roxane', 'Robin', 'Roxanerobin', 'Graphiste Webdesigner', '06 23 68 78 15', 'robinroxane78@gmail.com', '2026-06-02', (SELECT id FROM users WHERE prenom='Pierre'), 1); +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, date_club, invite_par_id, visit_number) VALUES ( + 'Clara', 'Trouchet', 'CDC Projet', 'Consultante RSE', '06 65 37 77 49', 'clara@cdc-projet.fr', '2026-06-02', (SELECT id FROM users WHERE prenom='Pierre'), 1); +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, date_club, invite_par_id, visit_number) VALUES ( + 'Laure', 'Préaubert', 'h2o', 'Conseillère h2o', '06 03 56 76 80', 'laure.preaubert@orange.fr', '2026-06-02', (SELECT id FROM users WHERE prenom='Pierre'), 1); +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, date_club, invite_par_id, visit_number) VALUES ( + 'Rémy', 'Makadji', 'ActionCOACH 78', 'Accompagnement entreprises', '06 21 23 57 17', 'remy.makadji@p-cub3.fr', '2026-04-21', (SELECT id FROM users WHERE prenom='Jennifer'), 1); +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, date_club, invite_par_id, visit_number) VALUES ( + 'Bertrand', 'CLAIRIS', 'ESSPAIE RH', 'Gestion PAIE', '06 07 02 50 05', 'b.clairis@esspaierh.fr', '2026-05-05', (SELECT id FROM users WHERE prenom='Jennifer'), 1); +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, date_club, invite_par_id, visit_number) VALUES ( + 'Magali', 'ROUSSEL', 'QB / SETIN', 'Caincaillerie et consommables', '07 87 03 25 60', 'magalie.roussel@setin.fr', '2026-06-02', (SELECT id FROM users WHERE prenom='Jennifer'), 1); +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, date_club, invite_par_id, visit_number) VALUES ( + 'Eric', 'BIBAL', 'V&B', 'commerce de vin et bieres', '06 13 08 06 17', 'maurepas@vandb.fr', '2026-06-02', (SELECT id FROM users WHERE prenom='Jennifer'), 1); +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, date_club, invite_par_id, visit_number) VALUES ( + 'Akouvi', 'BLEWOUSSI', 'NIRLYS PARIS GROUP', 'Cosmétiques', '06 44 89 59 66', 'nirlyscosmetik78@yahoo.com', '2026-04-07', (SELECT id FROM users WHERE prenom='Patricia'), 1); +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, date_club, invite_par_id, visit_number) VALUES ( + 'Audrey', 'LEMAIRE', 'Drevent Photobooth', 'Photobooth', '06 49 46 29 74', 'contact@drevent.fr', '2026-04-07', (SELECT id FROM users WHERE prenom='Patricia'), 1); +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, date_club, invite_par_id, visit_number) VALUES ( + 'Cindy', 'COATANEA', 'Pretpro', 'Experte financement professionnels', '06 89 69 49 29', 'cindy.coatanea@pretpro.expert', '2026-04-07', (SELECT id FROM users WHERE prenom='Patricia'), 1); +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, date_club, invite_par_id, visit_number) VALUES ( + 'BORGNE Cédric', 'LE', 'A.2.L.2 Diagnostics Immobiliers', 'Diagnostics', '06 38 62 70 93', 'a.2.l.2@hotmail.fr', '2026-05-05', (SELECT id FROM users WHERE prenom='Patricia'), 1); +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, date_club, invite_par_id, visit_number) VALUES ( + 'Guillaume', 'VALLOT', 'Vallotech', 'Systèmes de sécurité', '06 41 77 78 88', 'guillaume.vallot@vallotech.com', '2026-05-05', (SELECT id FROM users WHERE prenom='Patricia'), 1); +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, date_club, invite_par_id, visit_number) VALUES ( + 'Joëlle', 'BIGOTTEAU', 'A2J Courtage', 'Courtière', '06 36 46 95 95', 'joelle@a2jcourtage.fr', '2026-06-16', (SELECT id FROM users WHERE prenom='Patricia'), 1); +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, date_club, invite_par_id, visit_number) VALUES ( + 'Nathalie', 'ABREU', 'WiiSmile', 'Comité Entreprise Externe', '', 'n.abreu@wiismile.fr', '2026-06-16', (SELECT id FROM users WHERE prenom='Patricia'), 1); +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, date_club, invite_par_id, visit_number) VALUES ( + 'JACOPE', 'Stéphane', 'Axa', 'Assurance', '662611248', 'stephane.jacope.a2p@axa.fr', '2026-04-07', (SELECT id FROM users WHERE prenom='Morgane'), 1); +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, date_club, invite_par_id, visit_number) VALUES ( + 'Marchal', 'Nicola', 'Empruntis', 'Courtiage', '757007166', 'nicolas.marchal@empruntis,com', '2026-04-07', (SELECT id FROM users WHERE prenom='Morgane'), 1); +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, date_club, invite_par_id, visit_number) VALUES ( + 'Bruno', 'MASSON', 'Résilience RH', 'RH de crise', '06 75 04 02 90', 'contact@resiliencerh.fr', '2026-04-07', (SELECT id FROM users WHERE prenom='Miguel'), 1); +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, date_club, invite_par_id, visit_number) VALUES ( + 'Djibril', 'SAMBA', 'Golf Events et Consulting', 'Cours et organisation d''évenements golf', '06 43 10 60 40', 'djibril@golf-events-consulting.fr', '2026-05-05', (SELECT id FROM users WHERE prenom='Miguel'), 1); +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, date_club, invite_par_id, visit_number) VALUES ( + 'Broussard', 'Gontran', 'Serious Team 360', 'Communication', '', '', '2026-05-05', (SELECT id FROM users WHERE prenom='Marie'), 1); +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, date_club, invite_par_id, visit_number) VALUES ( + 'PERETTI Antoine', 'DE', 'Trujas', 'Concession automobile', '634446084', 'antoine.deperetti@trujas.carwest.fr', '2026-04-07', (SELECT id FROM users WHERE prenom='Seb'), 1); +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, date_club, invite_par_id, visit_number) VALUES ( + 'David', 'Willim', 'My Bank Privée', 'Financement/Assurance', '06 37 61 98 06', 'davidw@mybankprivee.com', '2026-03-24', (SELECT id FROM users WHERE prenom='Bastien'), 1); +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, date_club, invite_par_id, visit_number) VALUES ( + 'David', 'Chaumont', 'Forever', 'Bien être', '06 60 85 42 99', 'dchaumont@yahoo.fr', '2026-03-24', (SELECT id FROM users WHERE prenom='Bastien'), 1); +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, date_club, invite_par_id, visit_number) VALUES ( + 'Sophie', 'Marchand', 'DSM Conseil & Intégration Audiovisuelle', 'Audiovisuel', '06 87 51 44 65', 'sophie.marchand@dsm-conseil.com', '2026-03-24', (SELECT id FROM users WHERE prenom='Bastien'), 1); +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, date_club, invite_par_id, visit_number) VALUES ( + 'Wassim', 'Sayerh', 'Overto', 'Projections financières', '07 68 50 67 72', 'wassim.sayerh@overto.fr', '2026-03-24', (SELECT id FROM users WHERE prenom='Bastien'), 1); +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, date_club, invite_par_id, visit_number) VALUES ( + 'Clémence', 'Gastine', 'METRONOME Partners', 'Droit du travail', '06 29 55 28 24', 'clemence.gastine@metronome-partners.com', '2026-05-05', (SELECT id FROM users WHERE prenom='Bastien'), 1); +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, date_club, invite_par_id, visit_number) VALUES ( + 'Patrick', 'Lavergne', 'Digital & Vous', 'Solutions Web et Numériques', '06 03 88 56 67', 'patrick.lavergne@digital-et-vous.com', '2026-04-07', (SELECT id FROM users WHERE prenom='Bastien'), 1); +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, date_club, invite_par_id, visit_number) VALUES ( + 'Alex', 'Lasnier', 'Data Méchanic', 'Data Scientist', '06 32 46 36 51', 'alex.lasnier30@gmail.com', '2026-04-21', (SELECT id FROM users WHERE prenom='Bastien'), 1); +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, date_club, invite_par_id, visit_number) VALUES ( + 'Maryline', 'Jean', 'DYVERSITY', 'Formation en efficacité interpersonnelle', '06 08 80 17 21', 'maryline.jean@dyversity.fr', '2026-05-05', (SELECT id FROM users WHERE prenom='Bastien'), 1); +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, date_club, invite_par_id, visit_number) VALUES ( + 'Abouldhadid', 'Ouafa', 'FFC', 'Formation spécialisée First et Last Mile, Coaching, Leadership', '06 95 09 58 21', 'ouafa@firme-formationcoaching.fr', '2026-05-05', (SELECT id FROM users WHERE prenom='Bastien'), 1); +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, date_club, invite_par_id, visit_number) VALUES ( + 'Le Mercier', 'Marlène', 'SELARL THOMAZON AUDRANT BICHE ET ASSOCIES', 'Commisaire de Justice', '01 42 33 56 41', 'etude@tb-huissier.com', '2026-05-05', (SELECT id FROM users WHERE prenom='Bastien'), 1); +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, date_club, invite_par_id, visit_number) VALUES ( + 'Martin', 'Vincent', 'Experts Gestion GCL', 'Gestion, Conseil', '07 70 08 52 84', 'v.martin@expertgcl.fr', '2026-05-05', (SELECT id FROM users WHERE prenom='Bastien'), 1); + +-- Total unique guests: 88 diff --git a/database/seed_events_club.sql b/database/seed_events_club.sql new file mode 100644 index 0000000..f22178c --- /dev/null +++ b/database/seed_events_club.sql @@ -0,0 +1,13 @@ +INSERT INTO evenements (nom_evenement, date) VALUES +('Club d''Affaires', '2026-01-13'), +('Club d''Affaires', '2026-01-27'), +('Club d''Affaires', '2026-02-10'), +('Club d''Affaires', '2026-02-24'), +('Club d''Affaires', '2026-03-10'), +('Club d''Affaires', '2026-03-24'), +('Club d''Affaires', '2026-04-07'), +('Club d''Affaires', '2026-04-21'), +('Club d''Affaires', '2026-05-05'), +('Club d''Affaires', '2026-06-02'), +('Club d''Affaires', '2026-06-16'), +('Club d''Affaires', '2026-07-28'); diff --git a/database/seed_invites.sql b/database/seed_invites.sql new file mode 100644 index 0000000..5d0ebe1 --- /dev/null +++ b/database/seed_invites.sql @@ -0,0 +1,48 @@ +-- Seed guest data (invites) +-- Run AFTER schema.sql (or after migration_invites.sql on existing DB) +-- Uses subqueries to resolve user IDs by prenom + +-- 2025-03-14 (Petit-dejeuner reseau) +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, site_web, commentaires, date_club, invite_par_id, visit_number) VALUES +('Helene','Lasne','Distinxion','Distribution','0600000101','helene@distinxion.fr',NULL,NULL,'2025-03-14',(SELECT id FROM users WHERE prenom='Pierre'),1); + +-- 2025-05-09 +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, site_web, commentaires, date_club, invite_par_id, visit_number) VALUES +('Chakib','Aida','NP','Conseil','0600000102','chakib@np.fr',NULL,NULL,'2025-05-09',(SELECT id FROM users WHERE prenom='Pierre'),1), +('Alassane','Keita','Sericyne','Sante','0600000103','alassane@sericyne.fr',NULL,NULL,'2025-05-09',(SELECT id FROM users WHERE prenom='Pierre'),1), +('Steeven','Auffret','Digital Ynov','Formation','0600000104','steeven@ynov.fr',NULL,NULL,'2025-05-09',(SELECT id FROM users WHERE prenom='Pierre'),1); + +-- 2025-06-13 +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, site_web, commentaires, date_club, invite_par_id, visit_number) VALUES +('Aurore','Picard d''Estelan','Coaching Pro','Coaching','0600000105','aurore@coachingpro.fr',NULL,NULL,'2025-06-13',(SELECT id FROM users WHERE prenom='Pierre'),1), +('Virginie','Assouad','O2 Coignieres','Services','0600000106','virginie@o2coignieres.fr',NULL,NULL,'2025-06-13',(SELECT id FROM users WHERE prenom='Pierre'),1), +('Thierry','Thabouret','JT','Conseil','0600000107','thierry@jt.fr',NULL,NULL,'2025-06-13',(SELECT id FROM users WHERE prenom='Pierre'),1); + +-- 2025-09-12 (second visits) +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, site_web, commentaires, date_club, invite_par_id, visit_number) VALUES +('Angelique','Lebreton','Captive','Communication','0600000108','angelique@captive.fr',NULL,NULL,'2025-09-12',(SELECT id FROM users WHERE prenom='Pierre'),1), +('Stephane','Lebreton','Captive','Communication','0600000109','stephane@captive.fr',NULL,NULL,'2025-09-12',(SELECT id FROM users WHERE prenom='Pierre'),1), +('Alassane','Keita','Sericyne','Sante','0600000103','alassane@sericyne.fr',NULL,NULL,'2025-09-12',(SELECT id FROM users WHERE prenom='Pierre'),2), +('Aurore','Picard d''Estelan','Coaching Pro','Coaching','0600000105','aurore@coachingpro.fr',NULL,NULL,'2025-09-12',(SELECT id FROM users WHERE prenom='Pierre'),2); + +-- 2025-11-14 +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, site_web, commentaires, date_club, invite_par_id, visit_number) VALUES +('Rene','Gatou','L''immobilier du possible','Immobilier','0600000110','rene@immopossible.fr',NULL,NULL,'2025-11-14',(SELECT id FROM users WHERE prenom='Pierre'),1), +('Cyriaque','Djedji','ABC','Conseil','0600000111','cyriaque@abc.fr',NULL,NULL,'2025-11-14',(SELECT id FROM users WHERE prenom='Pierre'),1), +('Catherine','Lefevre','La Fourche','Alimentation','0600000112','catherine@lafourche.fr',NULL,NULL,'2025-11-14',(SELECT id FROM users WHERE prenom='Pierre'),1); + +-- 2025-12-12 (second visits) +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, site_web, commentaires, date_club, invite_par_id, visit_number) VALUES +('Cedrick','Coulibaly','Ekyp Management','Management','0600000113','cedrick@ekyp.fr',NULL,NULL,'2025-12-12',(SELECT id FROM users WHERE prenom='Pierre'),1), +('Thierry','Thabouret','JT','Conseil','0600000107','thierry@jt.fr',NULL,NULL,'2025-12-12',(SELECT id FROM users WHERE prenom='Pierre'),2), +('Virginie','Assouad','O2 Coignieres','Services','0600000106','virginie@o2coignieres.fr',NULL,NULL,'2025-12-12',(SELECT id FROM users WHERE prenom='Pierre'),2); + +-- 2026-06-12 (Rendez-vous Club d affaires - future) +INSERT INTO invites (prenom, nom, societe, activite, telephone, email, site_web, commentaires, date_club, invite_par_id, visit_number) VALUES +('Manon','Balesi','Conseil','Conseil','0600000114','manon@conseil.fr',NULL,NULL,'2026-06-12',(SELECT id FROM users WHERE prenom='Pierre'),1), +('Cedrick','Coulibaly','Ekyp Management','Management','0600000113','cedrick@ekyp.fr',NULL,NULL,'2026-06-12',(SELECT id FROM users WHERE prenom='Pierre'),2), +('Rene','Gatou','L''immobilier du possible','Immobilier','0600000110','rene@immopossible.fr',NULL,NULL,'2026-06-12',(SELECT id FROM users WHERE prenom='Pierre'),2), +('Angelique','Lebreton','Captive','Communication','0600000108','angelique@captive.fr',NULL,NULL,'2026-06-12',(SELECT id FROM users WHERE prenom='Pierre'),2), +('Stephane','Lebreton','Captive','Communication','0600000109','stephane@captive.fr',NULL,NULL,'2026-06-12',(SELECT id FROM users WHERE prenom='Pierre'),2), +('Cyriaque','Djedji','ABC','Conseil','0600000111','cyriaque@abc.fr',NULL,NULL,'2026-06-12',(SELECT id FROM users WHERE prenom='Pierre'),2), +('Catherine','Lefevre','La Fourche','Alimentation','0600000112','catherine@lafourche.fr',NULL,NULL,'2026-06-12',(SELECT id FROM users WHERE prenom='Pierre'),2); diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..7a6461a --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,52 @@ +services: + db: + image: mariadb:10.11 + restart: unless-stopped + environment: + MYSQL_ROOT_PASSWORD: L0reei69 + MYSQL_DATABASE: dynasqy + MYSQL_USER: dynausr + MYSQL_PASSWORD: L0relei69 + volumes: + - db_data:/var/lib/mysql + healthcheck: + test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"] + interval: 10s + timeout: 5s + retries: 5 + backend: + build: ./backend + restart: unless-stopped + depends_on: + db: + condition: service_healthy + environment: + DB_HOST: db + DB_PORT: 3306 + DB_USER: dynausr + DB_PASSWORD: L0relei69 + DB_NAME: dynasqy + JWT_SECRET: L0relei69SuperSecret2026 + PORT: 3000 + SMTP_HOST: mail.infomaniak.com + SMTP_PORT: 465 + SMTP_USER: dyna@secourit.fr + SMTP_PASS: cAq0cF9t41BE458sdAuzea45215 + EMAIL_FROM: dyna@secourit.fr + PUBLIC_URL: http://localhost:3001 + VAPID_PUBLIC_KEY: BA3f4_jkSxFKhldhTYbMR0AaMD1ROPE3PwFAW5bw_fVqmqQs6rdZ8kINs6aNWfyaXzaPbZxPDTD3tQbbFSFxmPA + VAPID_PRIVATE_KEY: xE1t4NCOZbGhjUgRs_P92sXvlgKeb3-64Vr7mwyiRTg + volumes: + - /opt/dyna/uploads/uploads:/app/uploads + - /opt/dyna/gallery:/app/gallery + ports: + - "3000:3000" + frontend: + build: ./frontend + restart: unless-stopped + depends_on: + - backend + ports: + - "3001:80" +volumes: + db_data: diff --git a/frontend/Dockerfile b/frontend/Dockerfile new file mode 100644 index 0000000..9fa778f --- /dev/null +++ b/frontend/Dockerfile @@ -0,0 +1,13 @@ +FROM node:20-alpine AS build + +WORKDIR /app +COPY package.json package-lock.json* ./ +RUN npm install +COPY . . +RUN npm run build + +FROM nginx:alpine +COPY --from=build /app/dist /usr/share/nginx/html +COPY nginx-frontend.conf /etc/nginx/conf.d/default.conf +EXPOSE 80 +CMD ["nginx", "-g", "daemon off;"] diff --git a/frontend/index.html b/frontend/index.html new file mode 100644 index 0000000..b86584e --- /dev/null +++ b/frontend/index.html @@ -0,0 +1,14 @@ + + + + + + + + Dyna Biz Coignières + + +
+ + + diff --git a/frontend/logo.png b/frontend/logo.png new file mode 100644 index 0000000..dce2fdc Binary files /dev/null and b/frontend/logo.png differ diff --git a/frontend/logo.svg b/frontend/logo.svg new file mode 100644 index 0000000..d8a8ef8 --- /dev/null +++ b/frontend/logo.svg @@ -0,0 +1,8 @@ + + + + + + + \ No newline at end of file diff --git a/frontend/nginx-frontend.conf b/frontend/nginx-frontend.conf new file mode 100644 index 0000000..be634f1 --- /dev/null +++ b/frontend/nginx-frontend.conf @@ -0,0 +1,66 @@ +server { + listen 80; + server_name _; + root /usr/share/nginx/html; + index index.html; + + location /invitation/ { + proxy_pass http://backend:3000; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + location /api/ { + proxy_pass http://backend:3000; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection 'upgrade'; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_cache_bypass $http_upgrade; + proxy_request_buffering off; + client_max_body_size 300M; + proxy_read_timeout 300s; + proxy_send_timeout 300s; + } + + location /uploads/ { + proxy_pass http://backend:3000; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + client_max_body_size 10M; + } + + location = /gallery { try_files $uri /index.html; } + location = /gallery/ { try_files $uri /index.html; } + + location /gallery/ { + proxy_pass http://backend:3000; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + client_max_body_size 300M; + } + + location /member-slides/ { + proxy_pass http://backend:3000; + proxy_http_version 1.1; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + proxy_request_buffering off; + proxy_buffering off; + client_max_body_size 300M; + proxy_read_timeout 300s; + proxy_send_timeout 300s; + } + + location / { + try_files $uri $uri/ /index.html; + } +} diff --git a/frontend/package-lock.json b/frontend/package-lock.json new file mode 100644 index 0000000..b3e9bb4 --- /dev/null +++ b/frontend/package-lock.json @@ -0,0 +1,6800 @@ +{ + "name": "club-dynasqy-frontend", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "club-dynasqy-frontend", + "version": "1.0.0", + "dependencies": { + "cropperjs": "^1.6.2", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "react-router-dom": "^6.26.0" + }, + "devDependencies": { + "@tailwindcss/vite": "^4.1.4", + "@vitejs/plugin-react": "^4.3.1", + "tailwindcss": "^4.1.4", + "vite": "^5.4.0", + "vite-plugin-pwa": "^0.20.1" + } + }, + "node_modules/@apideck/better-ajv-errors": { + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/@apideck/better-ajv-errors/-/better-ajv-errors-0.3.7.tgz", + "integrity": "sha512-TajUJwGWbDwkCx/CZi7tRE8PVB7simCvKJfHUsSdvps+aTM/PDPP4gkLmKnc+x3CE//y9i/nj74GqdL/hwk7Iw==", + "dev": true, + "license": "MIT", + "dependencies": { + "jsonpointer": "^5.0.1", + "leven": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "ajv": ">=8" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", + "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.29.7", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz", + "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz", + "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helpers": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.7.tgz", + "integrity": "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.29.7.tgz", + "integrity": "sha512-OoK6239jHPuSQOoS0kfTVKn0b/rVTk0seKq4Gd2UMLtmOVLjDC0ki3e+c90Trqv2gMfvJFqkiljrr568+qddiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz", + "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.29.7", + "@babel/helper-validator-option": "^7.29.7", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-create-class-features-plugin": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.29.7.tgz", + "integrity": "sha512-IY3ZD9Tmooqr3TUhc3DUWxiuo8xx1DWLhd5M7hQ+ZWJamqM2BbalrBJb2MisSLoYorOj75U03qULCxQTY9r3hg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.29.7", + "@babel/helper-member-expression-to-functions": "^7.29.7", + "@babel/helper-optimise-call-expression": "^7.29.7", + "@babel/helper-replace-supers": "^7.29.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7", + "@babel/traverse": "^7.29.7", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-regexp-features-plugin": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.29.7.tgz", + "integrity": "sha512-907Uymvqgg1dwUA+7IGwFAOSYzQOuzPXKNJ1yxzwPffzkYFg2q2eHi1fIOs6sXkG9NbIUMunnUlkYsfRFNvomg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.29.7", + "regexpu-core": "^6.3.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-define-polyfill-provider": { + "version": "0.6.8", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.8.tgz", + "integrity": "sha512-47UwBLPpQi1NoWzLuHNjRoHlYXMwIJoBf7MFou6viC/sIHWYygpvr0B6IAyh5sBdA2nr2LPIRww8lfaUVQINBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-plugin-utils": "^7.28.6", + "debug": "^4.4.3", + "lodash.debounce": "^4.0.8", + "resolve": "^1.22.11" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz", + "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.29.7.tgz", + "integrity": "sha512-j+7JYmk1JYDtACIGj0QJqqWZjoUpMoEikQGADMaHgCMCSDqd2+P32rfcibUNrGOMWrlzK1WJBdxrB3JJQZwWtg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz", + "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz", + "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7", + "@babel/traverse": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.29.7.tgz", + "integrity": "sha512-+kmGVjcT9RGYzoDwdwEqEvGgKe3BYq+O1iGzjFubaNgZHwYHP6lsF2Yghf4kEuv9BV7tYDZ913aBW9am6YKong==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz", + "integrity": "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-remap-async-to-generator": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.29.7.tgz", + "integrity": "sha512-16AMiW26DbXWBbr3B8wNozKM0ydMLB892vaOaJW/fPJdnT8vJk5sdkQcU/isqUxyCE0cEoa8wZOcbgDuC4b6Og==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.29.7", + "@babel/helper-wrap-function": "^7.29.7", + "@babel/traverse": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.29.7.tgz", + "integrity": "sha512-atfGXWSeCiF4DnKZIfmJfQRkSw9b9gNNXR1kqKjbhG4pGYCOnkp8OcTB8E3NXjBu8NpheSnOeNKz8KT7UNFTmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-member-expression-to-functions": "^7.29.7", + "@babel/helper-optimise-call-expression": "^7.29.7", + "@babel/traverse": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.29.7.tgz", + "integrity": "sha512-brcMGQaVzIeUb+6/bs1Av0f8YuNNjKY2JyvfRCsFuFsdKccEQ5Ges2y74D74NZ1Rz8lKJ9ksJkfqwQFJ/iNEyQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz", + "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-wrap-function": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.29.7.tgz", + "integrity": "sha512-iES0Skag9ERIF68aXadpO6dbXa03mNWK3sEqJaMnLNs/eC3l0lkImdfoy6Y09/SfkpawdAB4RjQ7PVA7TcVGdw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.29.7", + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz", + "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz", + "integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.7" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.29.7.tgz", + "integrity": "sha512-j8SrR0zLZrRsC09DlszEx8FpMiwukKffYXMK0d5LmOglO7vGG6sz/BR/20yHqWH+Lnn31JTt2PE3hIWNgM2J6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/traverse": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-safari-class-field-initializer-scope": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.29.7.tgz", + "integrity": "sha512-r8j8escF+U2FUHo0KOhPUdMzUO+jp9fInva6+ACVAF3Y97Ev+5iNZwiqTghmzNeWwDkOPlYuTcfb1vDaoZKmAQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.29.7.tgz", + "integrity": "sha512-GE1TFSiuFeGsCxmYXZl8HwoPrVlwe4rHPFE8weieGKZqnDORK+Ar3vgWMgW+AOxQ6/2TgLSKx9p6W7O4rC6qgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-safari-rest-destructuring-rhs-array": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-rest-destructuring-rhs-array/-/plugin-bugfix-safari-rest-destructuring-rhs-array-7.29.7.tgz", + "integrity": "sha512-oBNVCvnO5tND+xSopWvV8WNGfpTfgP4Zr/YXXSj8zfmcPktp5Ku/aZlsIowgSD4fjmgHn6sGmB9APVsU5zOdhA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.29.7.tgz", + "integrity": "sha512-QQt9qKHZ2sg/kivaLr7lnQr8HVrQDdBNSfCsTjiDxRuX/K5ORyKq+Bu8Xr0cDE3Dfkv0cw28Ve0EKyKMvulkOw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7", + "@babel/plugin-transform-optional-chaining": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.13.0" + } + }, + "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.29.7.tgz", + "integrity": "sha512-pn6QacGLgvCcwc+syUhKE/qSjV2D1IHDB84RNxWYSt1mW3K/SCtjinZ2p0cETJxAWBjPy3K/1lHwG5BjjPxNlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/traverse": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-proposal-private-property-in-object": { + "version": "7.21.0-placeholder-for-preset-env.2", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz", + "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-assertions": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.29.7.tgz", + "integrity": "sha512-/An1OCBN93thpBAGyfsK2pcf0jvju1SAtKkL2Ny++B5Sy6sqgzXDQH1cZxWbF96Wuk+bn41MDA9bLd4VVAw6rw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.29.7.tgz", + "integrity": "sha512-zGYcYfq/WmZ4V+kBIXQon9dSSc8ircGZqw9ZaNhhGj9nZkeBu1jHLBDQqYYi5WA9uawvA2sIMbry2nCFhf5Djg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-unicode-sets-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz", + "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-arrow-functions": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.29.7.tgz", + "integrity": "sha512-N7zArUXWzAMzm+/N0uPBeVB3Fam5lMxtUwMmDK5f/IBBS7a7p1qeUoxd/6CckXoxUdgsntq1Dh8xNW06maZbDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-async-generator-functions": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.29.7.tgz", + "integrity": "sha512-d98gXZkgswvkyohMBABkhm3GeXhYj8psWfwQ2C7gtfrKGTykQa/iOIi+JJhwMjPlZ6Vm2XN+DCf3Es1EoG4ZLA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-remap-async-to-generator": "^7.29.7", + "@babel/traverse": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-async-to-generator": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.29.7.tgz", + "integrity": "sha512-pcUb2SS+RMo9TWVBwKGI5ShtoG7R+zBsFmCKDa6fe8c+hPr3XJlZgoE5j6i8W7gDjhyvy+85vmYexanvXh3d1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-remap-async-to-generator": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoped-functions": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.29.7.tgz", + "integrity": "sha512-cUSmjh72N+rN4PrkFlN1dJwNCwjVp5d38/CQrEsFggkD10UiFlBFgdH3tv5dNsLuHY+3S8db2xCHjhZcv5WgvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoping": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.29.7.tgz", + "integrity": "sha512-ONyr4+AZhKh8yKWInVxU9AXA9EbsyeLcL6V0dJy6M2/62vuvpGm29zzuymbTpdc451GEpDIdAyPLP3r+P61yKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-class-properties": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.29.7.tgz", + "integrity": "sha512-GtcpjFvanPfzNQi3eTitsCqtRRmmqzpy/A+yhTR1HaZo1Ly3EA8ZXxlPyHdR8/IuRMYc3E4wdGBewB2QKQjAaA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-class-static-block": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.29.7.tgz", + "integrity": "sha512-kibJgmEdX2iMwsHY2tSZNDgj8PwIlCQz7FK9KuGKO8zsuoUwSEhoNnNVp/emKWrbY4HeO6kkXfdMqRKKKXBm2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.12.0" + } + }, + "node_modules/@babel/plugin-transform-classes": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.29.7.tgz", + "integrity": "sha512-qV0OGGBVacduzQHE649JyCneOFI/maT+YKsO+K4Yi3xv2wTPNjM/W2o2gdzMwEAZz7fXNTHAe0NcSg30bIN69g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.29.7", + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-globals": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-replace-supers": "^7.29.7", + "@babel/traverse": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-computed-properties": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.29.7.tgz", + "integrity": "sha512-RK7/IyU5phpuCdBAuig5VkzG/EnbDaui5SQGdU9BFrHdV+mV4cUjLMQ9lJDjLNtWHsqtiefpGZUXQP2BiTYMsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/template": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-destructuring": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.29.7.tgz", + "integrity": "sha512-iPX8aD6H9zV5s7ZsqTdNocPN/MGQ5sSMnElKrktxjJRMnB2jN/1p2+R7GkfD6CAYoVFqy5A4XnSIUeGgJzIWpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/traverse": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-dotall-regex": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.29.7.tgz", + "integrity": "sha512-3qc18hsD2RdZiyJNDNc7HQpv6xbncwh8FYtxNFFzclSyh/trPD9KkVR9BDECUjDLvb7yJVF15GfYUuC+LMkkiQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-duplicate-keys": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.29.7.tgz", + "integrity": "sha512-6IvRRriEMqnBwD6chtxdLpMYCHWEzN+oL5cyQtjykya19UgzbmKhxmhZgKC/LHxS2nYr9Q/qYPZ5Lr6jOL9+yQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-duplicate-named-capturing-groups-regex": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.29.7.tgz", + "integrity": "sha512-2wiIyo2BjtgU7HufSeDnL9L2O7zr8jmhFKuSr65VpRkUiRKRNpb0mdlk56+XPPKoIrfHqzbMuglDvZun0RISsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-dynamic-import": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.29.7.tgz", + "integrity": "sha512-giOlEm/EFjfjr+te9NsdjkUo2v4f8rS/SXPumRVHAtbNcyNlvtREkU1dZzaIDclNpnaVhlCqRdFKhJBjBikzLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-explicit-resource-management": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-explicit-resource-management/-/plugin-transform-explicit-resource-management-7.29.7.tgz", + "integrity": "sha512-Rstj7coNz8sE+7Ju7ihpHLI564lsK5pUpNNlvptCIC/16E/S5hbl6n3kESPKdNRmqEWlpn5xpS5Q2dvXBsySLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/plugin-transform-destructuring": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-exponentiation-operator": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.29.7.tgz", + "integrity": "sha512-zFpMOTLZBdW5LfObqcSbL6kefg4R4eLdmvS0wbN9M6D5Mym/sKm9toOoWyVOa+xDjvCnuWcHls2YonXwHvH3CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-export-namespace-from": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.29.7.tgz", + "integrity": "sha512-24B2nOy2TeJSMheqwPD4DDQOV/elLSIlKxjZt4i05H5AgdPdWR3n18HnNrcJ+j76WJd9gbwb9jPjNYUy6RautA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-for-of": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.29.7.tgz", + "integrity": "sha512-zeSIHh0+E1Um1WJRXCFlHQYu2ieJNdivLLjlBEp+dIBu3S51n+SZZmIXjxnItw6pz56Cn+KvK68BIBVsxq2JiQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-function-name": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.29.7.tgz", + "integrity": "sha512-otRWaHXE6fbAGkePvaj/kvs3HsqXfPhlnzwSOlnFgbqCPMd975dW+4wZ00WFBt+/YlBGcJwNrARQTOJOb4ZrIg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/traverse": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-json-strings": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.29.7.tgz", + "integrity": "sha512-RRnE2+eon1rJAq8MnoF1b5kTpY1vU88twHcvcKMrsqP/jxIRqDVs9iJB5fqPuqyeFAW0wJo4MlUIPpQCq/aRsg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-literals": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.29.7.tgz", + "integrity": "sha512-DZ/oLP21ZuWx1vKqnoNv6/tvEK48AQOBRai40CX9dTjGluvT/YZCyY3rryDtyUqCEoyNroy5KKPwX2iQCiRvyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-logical-assignment-operators": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.29.7.tgz", + "integrity": "sha512-A0H91hh6W8MFRkp5TqJmMr39jzGD1A1E1Ysiv2O06Sfbhkapm+XyIzxWCEh5kqwOZ1/8QZ0dY3SeQ7XBqfJd5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-member-expression-literals": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.29.7.tgz", + "integrity": "sha512-hl1kwFZCCiDyfH25Xmco9jTrkPgnS9pmOzSG7W5I4SaGbLeqKv417hcU2RKmaxoPEgsoJh7ZPOrnPGq99bHoUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-amd": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.29.7.tgz", + "integrity": "sha512-fxtQoH3m5ywUSIfaH0FGCzWu4McsYon5bD3K4XnskC7f+OyQMj7rsOMi4NvvmJ83WwBAg4UCe+ov4VZlqEvyew==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-commonjs": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.29.7.tgz", + "integrity": "sha512-j0vCldybPC5b5dwCQOJ21uKtHzt7hxLygJTg9eF1ScfaikEDNfzn94XoW5Fi+seBR0nCyL23xaBFFkq7dTM8XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-systemjs": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.29.7.tgz", + "integrity": "sha512-TM2ZcQLoG2/y4HODiStCo10DibYhWhGWAwVv+EQKmG/7GFl0N+AAmUiXOMKM+aiJ9XBJ9AHVZBvTzMnJ2sM3cQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7", + "@babel/traverse": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-umd": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.29.7.tgz", + "integrity": "sha512-B4UkaTK3QpgCwJnrxKfMPKdo92CN7OKXAlpAAnM3UPu0Q0lCCk57ylA9AJbRy2v8dDKOPAAWcoR6CMyeoHwRCA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.29.7.tgz", + "integrity": "sha512-vuFoLwr4qnv2xbZ16SQd6uPcH5FNrLHhk/Jzo++0XJFcaDsr4gjJVg6j398oMHiC+83k/GiBzviwF5KBJkPUtQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-new-target": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.29.7.tgz", + "integrity": "sha512-fEo41GmsOUhOBlw8ioo6zvjX5Xc2Lqkzlyfqbpsk3eB6TReV18uhxZ0esfEokVbY2+PVJAQHNKxER6lGrzNd3A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.29.7.tgz", + "integrity": "sha512-idmp1dFaekP9GbcMvG24Kvw2BfhFZjHnNJCkV4WuIY4PskJzwI3f1N5OdgYke38T7rftO6ERulFRn2cFeZwRkg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-numeric-separator": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.29.7.tgz", + "integrity": "sha512-zR7fv/z14OjgHl4AgRtkDBvBMhIzCxqV/qN/2BCRC7LjFwvuzjYe7gDWxC4Wl/SNsLM6SE1IWvRPYMgSJaUvNw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-object-rest-spread": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.29.7.tgz", + "integrity": "sha512-Ld98jn4c0smUywL57m7SgsHq3OpThOa6LqZJif3G6jYOovPleoFhVrBJ1WegRApSFB2wu4+RelAj9AC9G08Z4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/plugin-transform-destructuring": "^7.29.7", + "@babel/plugin-transform-parameters": "^7.29.7", + "@babel/traverse": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-object-super": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.29.7.tgz", + "integrity": "sha512-Ea/diGcw0twB5IlZPO5sgET6fJsLJqPABqTuFWIR+iMPGPZJkATEIWx0wa+aEQ5UY1CBQyP/gkAiLEqn1vBiQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-replace-supers": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-optional-catch-binding": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.29.7.tgz", + "integrity": "sha512-sLsyndxK2VwX6yNUOakMb7Sh553ZTe/vVM1XJ+9Z5aW1ytsc8xOIwmyk05NNjN60vkc5/KqoTH6hB4V41LJhng==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-optional-chaining": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.29.7.tgz", + "integrity": "sha512-6GM1dhvK3gNODkXcEcMCOLEDCLSoZ/sBbro2Ax8HURyasQ4NshagQixkRFdh5niI6E4gmA/jYI/4aT7rRos3ZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-parameters": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.29.7.tgz", + "integrity": "sha512-ZDOBqV/qLYJI0YElr8DcENEyARsFQeESqWXH6gZlghYXuPPjvweuDhP4VyEi4BlUBlLRFZVjxoZDMjxhLW766g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-private-methods": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.29.7.tgz", + "integrity": "sha512-/6Rz4DK1ETDEM/bWHsPHcaEe7ZaT1EqSXjtSP/L0DijOYuaUhiRiOKcwpZ8P7zR4xXEHc2ITdiCgBm9Tpyv9ug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-private-property-in-object": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.29.7.tgz", + "integrity": "sha512-+BNo06dnrzdNNqCm1X6YUaVv0DKk8Q+JYcoZfOkLhYWNCXzlwTSRq8zGWayT1csjcpNXV9CQTBRRbmTLZac5cA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.29.7", + "@babel/helper-create-class-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-property-literals": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.29.7.tgz", + "integrity": "sha512-bOMRLQuI0A5ZqHq3OWJ89/rXpJ/NJrbVhXiP4zwPGMs6kpcVsuTUNjwoE30K0Qm3mf48a/TnRYYD6vPNqcg6jA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.29.7.tgz", + "integrity": "sha512-TL0hMc9xzy86VD31nUiwzd5otRAcyEPcsegCxolO0PvcXuH1v0kECe/UIznYFihpkvU5wg/jk4v0TTEFfm53fw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.29.7.tgz", + "integrity": "sha512-06IyK09H3wi4cGbhDBwp5gUGo0IKtnYa8tyTiephirPCK6fbobVGiXMMI5zLQ4aKEYP3wZ3ArU44o+8KMrSG/Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-regenerator": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.29.7.tgz", + "integrity": "sha512-rNNFV0DBAJp988xW2DOntfDoYn1eR8GGF5AT5vYc+rjyfaQkM242c9tZUHHPe7KYaiJizXPWhQTzzdbXySyhBw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-regexp-modifiers": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.29.7.tgz", + "integrity": "sha512-mB5Fs0VWrJ42ZCmc8114v60qetdaUVNkj9PmSZRmanCZM3S9hm0CFRLjRmYIsuXav14l2jvZ+4T8iiCGnhj3nQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-reserved-words": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.29.7.tgz", + "integrity": "sha512-5+YhdpVgmfSmwZyLMftfaiffLRMHjzIRHFHHLdibcSyJm2pasMrKHrO3Ptrt2DRshjvpgjEJJ1zVW14WPq/6QA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-shorthand-properties": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.29.7.tgz", + "integrity": "sha512-I+WYbGBAiCn7nA6xBrlgPH+MB7HWb4u8pv5S0Pv7OtwNvIFvCCb24YlttKEeUFVurfBCEaOTnuhlqsb7f0Z5Dg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-spread": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.29.7.tgz", + "integrity": "sha512-/u5K1QWada7tbYNqTjMh96718g9NTwh9tfPJMsSmVsQwGT447FskV+KcfeXkXq2GWki4EM/MuTdmBec+hOuVTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-sticky-regex": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.29.7.tgz", + "integrity": "sha512-BCHzNYJGe9l7EpwwDBN/ztlL2NYFFq8hp9ddjtUEM9f2O7S7kKV/lL6Fwo7IF7NSkYhPK2vO+86nIGltA90MsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-template-literals": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.29.7.tgz", + "integrity": "sha512-NCSEJ4sLFU2gqAub45HYh4fus2yQ36rr6ei6vpU7NdoJqCpxvEG8E6eJpscGyXP3VHD2Ny+fSXr04k1hoUrFqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typeof-symbol": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.29.7.tgz", + "integrity": "sha512-223mNGoTkBiTEWFoK+Q6Go3tueMRclO8vxxxxquNCYuNI4jWOofFKJRRDu6SDrB8Sgo1UEGW9T4GAQ8ZyRso1A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-escapes": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.29.7.tgz", + "integrity": "sha512-jCfXxSjf94lf4E0hKE0AByxF6F3/pVFqRdUUNkDJhsY0m1ZKjnN6ZYyMeHNpzflxb/0q5b7t3p+BE+SLF1WOtA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-property-regex": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.29.7.tgz", + "integrity": "sha512-OgZ+zoAJgZLUCunsTRQ5LAjOywDv5zzZ2/hQ5aMw1pGXyY2rtE8/chXYUmu3AlVHKpm10KEdG9aMwbI/K76ZGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-regex": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.29.7.tgz", + "integrity": "sha512-7D/x/23/d/3VqZ0QA+LGbZMlGwZjztBygSWWWsfTPoQ1oQ6Q1P6Mr3d0kk42XabyUVw+fha3LqdRsFqeKqvCyA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-sets-regex": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.29.7.tgz", + "integrity": "sha512-BLOhLht9DOJwIxlmp91wHvkXv1lguuHS3/FwUO8HL1H0u8s4hR1gASVFyilu9iGtcTRYqjTZmlsFFeQletntEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/preset-env": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.29.7.tgz", + "integrity": "sha512-GYzX36n1nsciIb0uyH0GHwxwtNwPQIcpxSeiVLDtG/B7jB5xXgchnmL1f/jCX5o+pwnaDBtO60ONSJhEBJfxYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.29.7", + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-plugin-utils": "^7.29.7", + "@babel/helper-validator-option": "^7.29.7", + "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.29.7", + "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.29.7", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.29.7", + "@babel/plugin-bugfix-safari-rest-destructuring-rhs-array": "^7.29.7", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.29.7", + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.29.7", + "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", + "@babel/plugin-syntax-import-assertions": "^7.29.7", + "@babel/plugin-syntax-import-attributes": "^7.29.7", + "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", + "@babel/plugin-transform-arrow-functions": "^7.29.7", + "@babel/plugin-transform-async-generator-functions": "^7.29.7", + "@babel/plugin-transform-async-to-generator": "^7.29.7", + "@babel/plugin-transform-block-scoped-functions": "^7.29.7", + "@babel/plugin-transform-block-scoping": "^7.29.7", + "@babel/plugin-transform-class-properties": "^7.29.7", + "@babel/plugin-transform-class-static-block": "^7.29.7", + "@babel/plugin-transform-classes": "^7.29.7", + "@babel/plugin-transform-computed-properties": "^7.29.7", + "@babel/plugin-transform-destructuring": "^7.29.7", + "@babel/plugin-transform-dotall-regex": "^7.29.7", + "@babel/plugin-transform-duplicate-keys": "^7.29.7", + "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.29.7", + "@babel/plugin-transform-dynamic-import": "^7.29.7", + "@babel/plugin-transform-explicit-resource-management": "^7.29.7", + "@babel/plugin-transform-exponentiation-operator": "^7.29.7", + "@babel/plugin-transform-export-namespace-from": "^7.29.7", + "@babel/plugin-transform-for-of": "^7.29.7", + "@babel/plugin-transform-function-name": "^7.29.7", + "@babel/plugin-transform-json-strings": "^7.29.7", + "@babel/plugin-transform-literals": "^7.29.7", + "@babel/plugin-transform-logical-assignment-operators": "^7.29.7", + "@babel/plugin-transform-member-expression-literals": "^7.29.7", + "@babel/plugin-transform-modules-amd": "^7.29.7", + "@babel/plugin-transform-modules-commonjs": "^7.29.7", + "@babel/plugin-transform-modules-systemjs": "^7.29.7", + "@babel/plugin-transform-modules-umd": "^7.29.7", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.29.7", + "@babel/plugin-transform-new-target": "^7.29.7", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.29.7", + "@babel/plugin-transform-numeric-separator": "^7.29.7", + "@babel/plugin-transform-object-rest-spread": "^7.29.7", + "@babel/plugin-transform-object-super": "^7.29.7", + "@babel/plugin-transform-optional-catch-binding": "^7.29.7", + "@babel/plugin-transform-optional-chaining": "^7.29.7", + "@babel/plugin-transform-parameters": "^7.29.7", + "@babel/plugin-transform-private-methods": "^7.29.7", + "@babel/plugin-transform-private-property-in-object": "^7.29.7", + "@babel/plugin-transform-property-literals": "^7.29.7", + "@babel/plugin-transform-regenerator": "^7.29.7", + "@babel/plugin-transform-regexp-modifiers": "^7.29.7", + "@babel/plugin-transform-reserved-words": "^7.29.7", + "@babel/plugin-transform-shorthand-properties": "^7.29.7", + "@babel/plugin-transform-spread": "^7.29.7", + "@babel/plugin-transform-sticky-regex": "^7.29.7", + "@babel/plugin-transform-template-literals": "^7.29.7", + "@babel/plugin-transform-typeof-symbol": "^7.29.7", + "@babel/plugin-transform-unicode-escapes": "^7.29.7", + "@babel/plugin-transform-unicode-property-regex": "^7.29.7", + "@babel/plugin-transform-unicode-regex": "^7.29.7", + "@babel/plugin-transform-unicode-sets-regex": "^7.29.7", + "@babel/preset-modules": "0.1.6-no-external-plugins", + "babel-plugin-polyfill-corejs2": "^0.4.15", + "babel-plugin-polyfill-corejs3": "^0.14.0", + "babel-plugin-polyfill-regenerator": "^0.6.6", + "core-js-compat": "^3.48.0", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-modules": { + "version": "0.1.6-no-external-plugins", + "resolved": "https://registry.npmjs.org/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz", + "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@babel/types": "^7.4.4", + "esutils": "^2.0.2" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.7.tgz", + "integrity": "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz", + "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.7.tgz", + "integrity": "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-globals": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz", + "integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-9.0.0.tgz", + "integrity": "sha512-AokJm4tuBHillT+FpMtxQ60n8ObyXBatq7jD2/JA9dxbDDokKQm8KMht5ibGzLVU9IJDIKK4TPKgMHEYMn3lMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.11", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz", + "integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@remix-run/router": { + "version": "1.23.3", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.23.3.tgz", + "integrity": "sha512-4An71tdz9X8+3sI4Qqqd2LWd9vS39J7sqd9EU4Scw7TJE/qB10Flv/UuqbPVgfQV9XoK8Np6jNquZitnZq5i+Q==", + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-beta.27", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz", + "integrity": "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/plugin-babel": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-6.1.0.tgz", + "integrity": "sha512-dFZNuFD2YRcoomP4oYf+DvQNSUA9ih+A3vUqopQx5EdtPGo3WBnQcI/S8pwpz91UsGfL0HsMSOlaMld8HrbubA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.18.6", + "@rollup/pluginutils": "^5.0.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0", + "@types/babel__core": "^7.1.9", + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "@types/babel__core": { + "optional": true + }, + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-node-resolve": { + "version": "16.0.3", + "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-16.0.3.tgz", + "integrity": "sha512-lUYM3UBGuM93CnMPG1YocWu7X802BrNF3jW2zny5gQyLQgRFJhV1Sq0Zi74+dh/6NBx1DxFC4b4GXg9wUCG5Qg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^5.0.1", + "@types/resolve": "1.20.2", + "deepmerge": "^4.2.2", + "is-module": "^1.0.0", + "resolve": "^1.22.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^2.78.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-replace": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@rollup/plugin-replace/-/plugin-replace-6.0.3.tgz", + "integrity": "sha512-J4RZarRvQAm5IF0/LwUUg+obsm+xZhYnbMXmXROyoSE1ATJe3oXSb9L5MMppdxP2ylNSjv6zFBwKYjcKMucVfA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^5.0.1", + "magic-string": "^0.30.3" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/plugin-terser": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@rollup/plugin-terser/-/plugin-terser-1.0.0.tgz", + "integrity": "sha512-FnCxhTBx6bMOYQrar6C8h3scPt8/JwIzw3+AJ2K++6guogH5fYaIFia+zZuhqv0eo1RN7W1Pz630SyvLbDjhtQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "serialize-javascript": "^7.0.3", + "smob": "^1.0.0", + "terser": "^5.17.4" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "rollup": "^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/pluginutils": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.4.0.tgz", + "integrity": "sha512-MfPp06CjRLfXQ3wY0R8vJDYBy/MvVcc9OulEfR0B8Iv9ko+GCNaRZ+EpJYFl27LhKsZK0o420sYCRHCjfCgeUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.62.0.tgz", + "integrity": "sha512-IPIQ55ythEHkfEd9jMEi32OQ7SxURsGA43JI22lj01OLZNt2NUbJX8YUHxkVWyQ6daHPNn0truF5nSj3DQp6YQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.62.0.tgz", + "integrity": "sha512-M6s9cr10MibETyo8JsOkq+Lo1+lU6hcvb1MApnUql5qte/5hMEgzlN8/ReIKNfRV8rrqX50W1BX9zoUhC192RA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.62.0.tgz", + "integrity": "sha512-BqCoMoIbn0keKys+dEAdBa70EtOwV1bEsQCUgU9FdiZmmMge/Zk7LlkYGqbrdHR+Frnt0E1FOanly+rlwvvQzw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.62.0.tgz", + "integrity": "sha512-SIMzST3VFNXDAbeIWDWiFCNM5qncUBDWaEV7NfE7oZbDt2mgfW4MvbKdbYiGOLoM32gbTv608UMd0XktEYSD7w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.62.0.tgz", + "integrity": "sha512-ezjfSQMP7ArdUsbBwbQIfwAlhE84I2iVnzQNCFSveqV42q+BmKlzVpf7mxv5EchLcoWU4y6/heFzVg1F+hodUQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.62.0.tgz", + "integrity": "sha512-9+qTWGW9AZRhnUgwtTwzNwcPlL87ngkeN0LA+q1bADvmY9aNvWaF2TFW8BZgnQPYxpDI7+rMVLivcd4V737TAQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.62.0.tgz", + "integrity": "sha512-T1dMEQhXA/jkJ/jyMIw9IovK8bSUq7A8kLIlvZTb/6YIVsp2zLavr4F3oyllHWo7eIVJRyE5n3tUjQJEbE1IuQ==", + "cpu": [ + "arm" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.62.0.tgz", + "integrity": "sha512-2as0LgT7qQpyceQq6VUJYnumUMUrgGQCWIiDIN9DE0/tglsk6o66uCB4f3djRawAltvfCNLyZZrsqbPA6inCsA==", + "cpu": [ + "arm" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.62.0.tgz", + "integrity": "sha512-bVURMg+6eNN9C/yc0aVjooZcwTTtYF4YW3xta5pP0//r3o1V8gXEHXWCndj47w/HhwsFroZrFhR+6uQP5T0n0g==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.62.0.tgz", + "integrity": "sha512-Ful8pM/2yYI83PViWdFdpZhdI8HJ5qsXANe5atypbHDf+KIBBDsZsbyy8hbXnULVvW9NsTh5DHwbcBftyLTfiw==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.62.0.tgz", + "integrity": "sha512-9Gp/DgrkzfUBmNPVTyPTvay+4xEP7M/clXpj3efXBcm6uTIVIgDg4rqUpqKXvLEuFRVuEpSAOkhgNeecvaZ4Cg==", + "cpu": [ + "loong64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.62.0.tgz", + "integrity": "sha512-m9tsJz54LUXkSYM8+8PG81B9IKK5r+2T0clMq4QrS16xFosufU7firBDAZEsDheDs7wTlP7h3++S7lMsU955HA==", + "cpu": [ + "loong64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.62.0.tgz", + "integrity": "sha512-3UvJ5PNVU16aJf6M3tFI24pWzAl2/ynfbyRN3ICyQajK1lSkrnVYNnLz3v04J32qKa0FczJc22zeToc0lr2A3w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.62.0.tgz", + "integrity": "sha512-vRWUAbYLGHBZS6Q8Msb2sfnf1fvJf+47t8l/TwOerM2qArzy+IeNMTHrYLHXh95h8MoatPHI5hhSZNs+mGXKPg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.62.0.tgz", + "integrity": "sha512-c00T5SYENHAt86cfW47URaP3Us5vLC/4QO7GYud1G5VNRffCwwCuBspwqYrriuJB+5m0WFzClCn9wed0FBjKvg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.62.0.tgz", + "integrity": "sha512-krrCDilhXOwFkSkO3Wm9I/f9H0L92XHHwy2fwxjukxIbh0dem8gZqOW5Y8BsHrpJv5qwlRBV+Wl4ZFyRWhUpwg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.62.0.tgz", + "integrity": "sha512-7pfYFSTc4/rUC/FtAI0Qp6QthDBCIi6/AuP1xYqFk5vanI6KnL5dWKP60OM/05LOsbwTmIcvr6eXC4CJuJ75IA==", + "cpu": [ + "s390x" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.62.0.tgz", + "integrity": "sha512-7SDIalKeIpG0Ifogbbdn58HmSotYMlf23K3dCJEmiVd9Fg36Vmni82iPQec27N3wY4Bvbxftkxz6vSx9OcouTg==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.62.0.tgz", + "integrity": "sha512-eRZevouTH2i1HeAVLqJuLnt256krQkGY0TN6WsTmsIhuzbh457HuWDMakKwmi0Cjadux983CoSr8Lim2QhUIFw==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.62.0.tgz", + "integrity": "sha512-3oVS7FLGa4U1qcvao9ylGxrjXZyUQqR8UwxEcnUEyPX53O/C/mKDZegNXTdHCP+h3e6ta/f1EN38Yif1mmZHYg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.62.0.tgz", + "integrity": "sha512-yTB9TgfWj5wHe5QgktAgXTLLot1gvEjl1NiPPAUiCs4oPrIWFl5V4nC3GrkNdj9LaAU4s94nVrGbGOCqUpyWsg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.62.0.tgz", + "integrity": "sha512-5LOhoaesY3doG1c+ac/2JtgREpKoJr5bUHH8tKY0V8di7+uSV6BwLs2PlR0/yzefGOkR+wE7ZolZphHCsyG5Rw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.62.0.tgz", + "integrity": "sha512-yYkWHhmbhRTWTnWos5HC4GcPQfjlzzCNbM9e/+GXrLuaBXYA3qSDR9f0Vgufd5S8yX81U8jPKp7ZnAjZFMtRnw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.62.0.tgz", + "integrity": "sha512-SoTb6lPg25xZlA2ibwQ++ahCCnH+FP0qmEuafMJ4gznZKOlXioKEAeJLgCrqjM98ACziXM9V1amFjICVL4IFoA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.62.0.tgz", + "integrity": "sha512-5L+T1fMX4RIEBoZzT0+sQ0PhTS36NULFmMXtl1TZo44TMAROIMHbZufSOjVWt/Y622BtxgxtaNOokbTDvfsrZA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@tailwindcss/node": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.3.1.tgz", + "integrity": "sha512-6NDaqRoAMSXD1mr/RXu0HBvNE9a2n5tHPsxu9XHLws8o4Twes5rBM2205SUUiJ9goAtadrN6xTGX0UDEwp/N4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.5", + "enhanced-resolve": "5.21.6", + "jiti": "^2.7.0", + "lightningcss": "1.32.0", + "magic-string": "^0.30.21", + "source-map-js": "^1.2.1", + "tailwindcss": "4.3.1" + } + }, + "node_modules/@tailwindcss/oxide": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.3.1.tgz", + "integrity": "sha512-yVPyo8RNkabVr3O2EhHEE0Rewu7YKzc1DhIqfL46LKveFrmu9XbDazNOJY7/GRuvw1h6u3utWnR29H/p5JPlgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 20" + }, + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.3.1", + "@tailwindcss/oxide-darwin-arm64": "4.3.1", + "@tailwindcss/oxide-darwin-x64": "4.3.1", + "@tailwindcss/oxide-freebsd-x64": "4.3.1", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.3.1", + "@tailwindcss/oxide-linux-arm64-gnu": "4.3.1", + "@tailwindcss/oxide-linux-arm64-musl": "4.3.1", + "@tailwindcss/oxide-linux-x64-gnu": "4.3.1", + "@tailwindcss/oxide-linux-x64-musl": "4.3.1", + "@tailwindcss/oxide-wasm32-wasi": "4.3.1", + "@tailwindcss/oxide-win32-arm64-msvc": "4.3.1", + "@tailwindcss/oxide-win32-x64-msvc": "4.3.1" + } + }, + "node_modules/@tailwindcss/oxide-android-arm64": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.3.1.tgz", + "integrity": "sha512-SVlyf61g374l5cHyg8x9kf5xmLcOaxvOTsbsqDnSsDJaKOEFZ7GCvi84VAVGpxojYOs1+3K6M0UjXfqPU8vmOQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-arm64": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.3.1.tgz", + "integrity": "sha512-hVnWLwv+e/l7c4WKyVtHVrIPvYdqWHjRB3MDIqARynzFtnQg85kmQEFCbV9Ja0VVx4xXTIiDWY60Y7iz/iNoDA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-x64": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.3.1.tgz", + "integrity": "sha512-Cf7abu0WVgbhU7ANgPUnSAvm7nCvMweusHb8FnaHlLfv/Caq4GYaEZg7ZImzzmjx4lIAfuS8q+eLIS7A7IzxIg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-freebsd-x64": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.3.1.tgz", + "integrity": "sha512-ZZqzX2Y+GXtXXfqSfpJhDm60OoZfvLHLCgm+J7NVqgHHJjG/m9ugZI77RwTsVd4fnBJuCFP6Ae6kTJb71UdS8g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.3.1.tgz", + "integrity": "sha512-/Ah/xik0LaMYfv9DZ0S/t4pBlBNYOcqtRwusjgovHkvT8ixueWCLyJjsaF5kQIckjb4IT8Q6K6p/iPmZMixYgg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.3.1.tgz", + "integrity": "sha512-gqdFoVJlw444GvpnheZLHmvTzSxI/cOUUh2KSNejQjTcYkW062SVD+En0rUgD+QV91bz1XGIGtt1HJd48xUGbQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-musl": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.3.1.tgz", + "integrity": "sha512-Bwv9KwOvE0VKa86xPFif9b9c3Y1NxOV1P0gLti/IYaWEsQYZXDlxfGEtA8mdDZ7SG3wyNXAWYT5SIn3giL57oA==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.3.1.tgz", + "integrity": "sha512-Ymi8O8T15HYQdOUWUtTI6ldN0neHP85FC+Qz32xTcZ7iJXtem/x8ITev0o1e9e5rkqj4lONZfTRLvkmin1+tKg==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-musl": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.3.1.tgz", + "integrity": "sha512-M+P/91qJ6uILLw4k2G93GMDRAXj61SMvFQYt39AqvUqYgExXpLL5aepfns7sj4HiAQeolirQF9E0lzRvdf4zPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.3.1.tgz", + "integrity": "sha512-zsM8uOeqvVGHsAXsJxsT28ttosFahLJKCLOTUBqRAtKnVgGSRitds9T432QiT8b77Yga7JIBkulIRRlJPtYhRA==", + "bundleDependencies": [ + "@napi-rs/wasm-runtime", + "@emnapi/core", + "@emnapi/runtime", + "@tybys/wasm-util", + "@emnapi/wasi-threads", + "tslib" + ], + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.10.0", + "@emnapi/runtime": "^1.10.0", + "@emnapi/wasi-threads": "^1.2.1", + "@napi-rs/wasm-runtime": "^1.1.4", + "@tybys/wasm-util": "^0.10.2", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.3.1.tgz", + "integrity": "sha512-aiNvSq9BsVk8V513lDKlrCFAgf8qBMPZTpgEhInL+NwQqs97mYmupVMrPrgBBSL8Pv/0zXu9MrMF9rMun1ZeNg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.3.1.tgz", + "integrity": "sha512-xDEyu1rg290472FEGaKHnzyDyh5QH+AlWvsU5hMoMtPpzmKlRI0jaYKCgSHDYtaQWZOYbMaduSyCwFwY4n1HmA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/vite": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.3.1.tgz", + "integrity": "sha512-hItDHuIIlEV61R+faXu66s1K36aTurO/Qw0e45Vskz57gXl9pWOT6eg3zmcEui6CZXddbN7zd41bwmvag4JGwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tailwindcss/node": "4.3.1", + "@tailwindcss/oxide": "4.3.1", + "tailwindcss": "4.3.1" + }, + "peerDependencies": { + "vite": "^5.2.0 || ^6 || ^7 || ^8" + } + }, + "node_modules/@trickfilm400/rollup-plugin-off-main-thread": { + "version": "3.0.0-pre1", + "resolved": "https://registry.npmjs.org/@trickfilm400/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-3.0.0-pre1.tgz", + "integrity": "sha512-/67zpWDBLV+oYAEL682s1ktXL0HgqX76f6gaVGkGnVZlBbm1zd0v4Bz8MFF2GGhoX9rvfq3KSQHubFHwa6w6/Q==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "ejs": "^3.1.10", + "json5": "^2.2.3", + "magic-string": "^0.30.21", + "string.prototype.matchall": "^4.0.12" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/resolve": { + "version": "1.20.2", + "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.20.2.tgz", + "integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vitejs/plugin-react": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz", + "integrity": "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.28.0", + "@babel/plugin-transform-react-jsx-self": "^7.27.1", + "@babel/plugin-transform-react-jsx-source": "^7.27.1", + "@rolldown/pluginutils": "1.0.0-beta.27", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.17.0" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" + } + }, + "node_modules/acorn": { + "version": "8.17.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.17.0.tgz", + "integrity": "sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/ajv": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", + "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "is-array-buffer": "^3.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", + "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "is-array-buffer": "^3.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "dev": true, + "license": "MIT" + }, + "node_modules/async-function": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", + "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/babel-plugin-polyfill-corejs2": { + "version": "0.4.17", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.17.tgz", + "integrity": "sha512-aTyf30K/rqAsNwN76zYrdtx8obu0E4KoUME29B1xj+B3WxgvWkp943vYQ+z8Mv3lw9xHXMHpvSPOBxzAkIa94w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.28.6", + "@babel/helper-define-polyfill-provider": "^0.6.8", + "semver": "^6.3.1" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-plugin-polyfill-corejs3": { + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.14.2.tgz", + "integrity": "sha512-coWpDLJ410R781Npmn/SIBZEsAetR4xVi0SxLMXPaMO4lSf1MwnkGYMtkFxew0Dn8B3/CpbpYxN0JCgg8mn67g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.6.8", + "core-js-compat": "^3.48.0" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/babel-plugin-polyfill-regenerator": { + "version": "0.6.8", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.8.tgz", + "integrity": "sha512-M762rNHfSF1EV3SLtnCJXFoQbbIIz0OyRwnCmV0KPC7qosSfCO0QLTSuJX3ayAebubhE6oYBAYPrBA5ljowaZg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.6.8" + }, + "peerDependencies": { + "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" + } + }, + "node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/baseline-browser-mapping": { + "version": "2.10.37", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.37.tgz", + "integrity": "sha512-girxaJ7WZssDOFhzCGZTDKoTa1gk6A1TbflaYTpykLJ4UU9Fz9kx1aREM8JCuoVHbL8X8T/mJg7w2oYSq72Oig==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/brace-expansion": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", + "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/browserslist": { + "version": "4.28.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz", + "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.10.12", + "caniuse-lite": "^1.0.30001782", + "electron-to-chromium": "^1.5.328", + "node-releases": "^2.0.36", + "update-browserslist-db": "^1.2.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/call-bind": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.9.tgz", + "integrity": "sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "get-intrinsic": "^1.3.0", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001799", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001799.tgz", + "integrity": "sha512-hG1bReV+OUU+MOqK4t/ZWI0tZOyz3rqS9XuhOUz1cIcbwBKjOyJEJuw9ER5JuNyqxNk8u/JUVbGibBOL1yrjFw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/common-tags": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.2.tgz", + "integrity": "sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/core-js-compat": { + "version": "3.49.0", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.49.0.tgz", + "integrity": "sha512-VQXt1jr9cBz03b331DFDCCP90b3fanciLkgiOoy8SBHy06gNf+vQ1A3WFLqG7I8TipYIKeYK9wxd0tUrvHcOZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "browserslist": "^4.28.1" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/cropperjs": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/cropperjs/-/cropperjs-1.6.2.tgz", + "integrity": "sha512-nhymn9GdnV3CqiEHJVai54TULFAE3VshJTXSqSJKa8yXAKyBKDWdhHarnlIPrshJ0WMFTGuFvG02YjLXfPiuOA==", + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/crypto-random-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", + "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/data-view-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", + "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", + "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/inspect-js" + } + }, + "node_modules/data-view-byte-offset": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", + "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ejs": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", + "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "jake": "^10.8.5" + }, + "bin": { + "ejs": "bin/cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.375", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.375.tgz", + "integrity": "sha512-ZWP5eB4BVPW/ZYo9252hQZHZ5XavtsTgpbhcmMmRwymavC5AsLWQWBPaKMeNd2LW0KGby5HPXvj7+sr4ta5j/Q==", + "dev": true, + "license": "ISC" + }, + "node_modules/enhanced-resolve": { + "version": "5.21.6", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.21.6.tgz", + "integrity": "sha512-aNnGCvbJ/RIyWo1IuhNdVjnNF+EjH9wpzpNHt+ci/m9He9LJvUN8wrCcXjp9cWsGNAuvSpVFTx/vraAFQ8qGjQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.3.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/es-abstract": { + "version": "1.24.2", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.2.tgz", + "integrity": "sha512-2FpH9Q5i2RRwyEP1AylXe6nYLR5OhaJTZwmlcP0dL/+JCbgg7yyEo/sEK6HeGZRf3dFpWwThaRHVApXSkW3xeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.2", + "arraybuffer.prototype.slice": "^1.0.4", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "data-view-buffer": "^1.0.2", + "data-view-byte-length": "^1.0.2", + "data-view-byte-offset": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "es-set-tostringtag": "^2.1.0", + "es-to-primitive": "^1.3.0", + "function.prototype.name": "^1.1.8", + "get-intrinsic": "^1.3.0", + "get-proto": "^1.0.1", + "get-symbol-description": "^1.1.0", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "internal-slot": "^1.1.0", + "is-array-buffer": "^3.0.5", + "is-callable": "^1.2.7", + "is-data-view": "^1.0.2", + "is-negative-zero": "^2.0.3", + "is-regex": "^1.2.1", + "is-set": "^2.0.3", + "is-shared-array-buffer": "^1.0.4", + "is-string": "^1.1.1", + "is-typed-array": "^1.1.15", + "is-weakref": "^1.1.1", + "math-intrinsics": "^1.1.0", + "object-inspect": "^1.13.4", + "object-keys": "^1.1.1", + "object.assign": "^4.1.7", + "own-keys": "^1.0.1", + "regexp.prototype.flags": "^1.5.4", + "safe-array-concat": "^1.1.3", + "safe-push-apply": "^1.0.0", + "safe-regex-test": "^1.1.0", + "set-proto": "^1.0.0", + "stop-iteration-iterator": "^1.1.0", + "string.prototype.trim": "^1.2.10", + "string.prototype.trimend": "^1.0.9", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.3", + "typed-array-byte-length": "^1.0.3", + "typed-array-byte-offset": "^1.0.4", + "typed-array-length": "^1.0.7", + "unbox-primitive": "^1.1.0", + "which-typed-array": "^1.1.19" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-abstract-get": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-abstract-get/-/es-abstract-get-1.0.0.tgz", + "integrity": "sha512-6PMWXpdhshVvFp+FoWYs1EvG1Nj0tvk0dZM+XcK0xMEM1czRVcP6ohqPWHy6qPagSpC8j4+p89WXlT+xXJs/fg==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.2", + "is-callable": "^1.2.7", + "object-inspect": "^1.13.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", + "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-to-primitive": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.1.tgz", + "integrity": "sha512-CxN9N56HYfd2m/acc/NOFrZQsN9kU4eh+2kk6A707Kz1krH8tKmfrs5RnftB8WNX80T0NS7vSQsDOlg23diR2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-abstract-get": "^1.0.0", + "es-errors": "^1.3.0", + "is-callable": "^1.2.7", + "is-date-object": "^1.1.0", + "is-symbol": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eta": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/eta/-/eta-4.6.0.tgz", + "integrity": "sha512-lW6is4T1NFOYnmqGZIfvixqj7A7sSvScF+DN8EK6K58xI5MZ5UvYe0GjopxOXQtZvUn4eDdVuZ8XSoYWTMEKwA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/bgub/eta?sponsor=1" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.2.tgz", + "integrity": "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/filelist": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.6.tgz", + "integrity": "sha512-5giy2PkLYY1cP39p17Ech+2xlpTRL9HLspOfEgm0L6CwBXBTgsK5ou0JtzYuepxkaQ/tvhCFIJ5uXo0OrM2DxA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "minimatch": "^5.0.1" + } + }, + "node_modules/filelist/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/filelist/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/filelist/node_modules/minimatch": { + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz", + "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/for-each": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "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, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/function.prototype.name": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.2.0.tgz", + "integrity": "sha512-jObKIik1P2QjPHP5nz5BaOtUlfgS0fWo8IUByNXkM+o+02sJOi94em77GwJKQSJ3gfPHdgzLNrHc1uokV4P/ew==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2", + "hasown": "^2.0.4", + "is-callable": "^1.2.7", + "is-document.all": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/generator-function": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/generator-function/-/generator-function-2.0.1.tgz", + "integrity": "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-own-enumerable-property-symbols": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz", + "integrity": "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==", + "dev": true, + "license": "ISC" + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-symbol-description": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", + "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/glob": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.1.0.tgz", + "integrity": "sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==", + "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": "BlueOak-1.0.0", + "dependencies": { + "foreground-child": "^3.3.1", + "jackspeak": "^4.1.1", + "minimatch": "^10.1.1", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^2.0.0" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/has-bigints": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", + "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", + "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/idb": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/idb/-/idb-7.1.1.tgz", + "integrity": "sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/internal-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", + "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "hasown": "^2.0.2", + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-array-buffer": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", + "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-async-function": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", + "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "async-function": "^1.0.0", + "call-bound": "^1.0.3", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bigint": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", + "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-bigints": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-boolean-object": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", + "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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-data-view": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", + "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "is-typed-array": "^1.1.13" + }, + "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", + "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-document.all": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-document.all/-/is-document.all-1.0.0.tgz", + "integrity": "sha512-+XSoyS05OdBbhFuELhgTCpFNHkpBOJqtsZfUFFpe5QTw+9Sjbh8zitxhQkYAo6wV7e1Vb8cAPvpCk9jGam/82g==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-finalizationregistry": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", + "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-generator-function": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.2.tgz", + "integrity": "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.4", + "generator-function": "^2.0.0", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", + "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-negative-zero": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", + "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number-object": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", + "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", + "integrity": "sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-regexp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz", + "integrity": "sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-set": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", + "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-string": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", + "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", + "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-symbols": "^1.1.0", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakmap": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakref": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", + "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakset": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", + "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true, + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/jackspeak": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.2.3.tgz", + "integrity": "sha512-ykkVRwrYvFm1nb2AJfKKYPr0emF6IiXDYUaFx4Zn9ZuIH7MrzEZ3sD5RlqGXNRpHtvUHJyOnCEFxOlNDtGo7wg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^9.0.0" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/jake": { + "version": "10.9.4", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.4.tgz", + "integrity": "sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "async": "^3.2.6", + "filelist": "^1.0.4", + "picocolors": "^1.1.1" + }, + "bin": { + "jake": "bin/cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jiti": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.7.0.tgz", + "integrity": "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonfile": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", + "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsonpointer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-5.0.1.tgz", + "integrity": "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/lightningcss": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", + "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.32.0", + "lightningcss-darwin-arm64": "1.32.0", + "lightningcss-darwin-x64": "1.32.0", + "lightningcss-freebsd-x64": "1.32.0", + "lightningcss-linux-arm-gnueabihf": "1.32.0", + "lightningcss-linux-arm64-gnu": "1.32.0", + "lightningcss-linux-arm64-musl": "1.32.0", + "lightningcss-linux-x64-gnu": "1.32.0", + "lightningcss-linux-x64-musl": "1.32.0", + "lightningcss-win32-arm64-msvc": "1.32.0", + "lightningcss-win32-x64-msvc": "1.32.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", + "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", + "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", + "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", + "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", + "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", + "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", + "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", + "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", + "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", + "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", + "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.sortby": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", + "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==", + "dev": true, + "license": "MIT" + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/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/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.12", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", + "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/node-releases": { + "version": "2.0.47", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.47.tgz", + "integrity": "sha512-Uzmd6LXpouKo8EUK68IjH4+E01w/hXyV3R3g/geCJo+rXLNfh1xucB+LOzYEOQPSiUK3h/xZf0cQGcSsmyL2Og==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", + "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0", + "has-symbols": "^1.1.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/own-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", + "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.2.6", + "object-keys": "^1.1.1", + "safe-push-apply": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "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", + "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "11.5.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.1.tgz", + "integrity": "sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/possible-typed-array-names": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/postcss": { + "version": "8.5.15", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz", + "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.12", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/pretty-bytes": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-6.1.1.tgz", + "integrity": "sha512-mQUvGU6aUFQ+rNvTIAcZuWGRT9a6f6Yrg9bHs4ImKF+HZCEK+plBvnAZYSIQztknZF2qnzNtr6F8s0+IuptdlQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/react": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.2" + }, + "peerDependencies": { + "react": "^18.3.1" + } + }, + "node_modules/react-refresh": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz", + "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-router": { + "version": "6.30.4", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.30.4.tgz", + "integrity": "sha512-SVUsDe+DybHM/WmYKIVYhZh1o5Dcuf16yM6WjG02Q9XVFMZIJyHYhwrr6bFBXZkVP6z69kNkMyBCujt8FaFLJA==", + "license": "MIT", + "dependencies": { + "@remix-run/router": "1.23.3" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8" + } + }, + "node_modules/react-router-dom": { + "version": "6.30.4", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.30.4.tgz", + "integrity": "sha512-q4HvNl+mmDdkS0g+MqiBZNteQJCuimWoOyHMy4T/RQLAn9Z29+E91QXRaxOujeMl2HTzRSS0KFPd7lxX3PjV0Q==", + "license": "MIT", + "dependencies": { + "@remix-run/router": "1.23.3", + "react-router": "6.30.4" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8", + "react-dom": ">=16.8" + } + }, + "node_modules/reflect.getprototypeof": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", + "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.1", + "which-builtin-type": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regenerate": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", + "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", + "dev": true, + "license": "MIT" + }, + "node_modules/regenerate-unicode-properties": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.2.tgz", + "integrity": "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "regenerate": "^1.4.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", + "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regexpu-core": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.4.0.tgz", + "integrity": "sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA==", + "dev": true, + "license": "MIT", + "dependencies": { + "regenerate": "^1.4.2", + "regenerate-unicode-properties": "^10.2.2", + "regjsgen": "^0.8.0", + "regjsparser": "^0.13.0", + "unicode-match-property-ecmascript": "^2.0.0", + "unicode-match-property-value-ecmascript": "^2.2.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/regjsgen": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.8.0.tgz", + "integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/regjsparser": { + "version": "0.13.2", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.13.2.tgz", + "integrity": "sha512-NgRBy2Nx/bE+9F27nVHnqcN5HjyLmecqsqx2PJHu3/IEtADD4WuxuXIVExD5PoSDFVrl78dOonfcOe5O+5nbzQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "jsesc": "~3.1.0" + }, + "bin": { + "regjsparser": "bin/parser" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "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/rollup": { + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.62.0.tgz", + "integrity": "sha512-nc72Wgq62I7rtDV4izT5/aaS0zxy3kttkinf9586ApknY3jZO9NYsmtc24fUckA0X7Q2v+ML4a15pdUlV5V/jA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.9" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.62.0", + "@rollup/rollup-android-arm64": "4.62.0", + "@rollup/rollup-darwin-arm64": "4.62.0", + "@rollup/rollup-darwin-x64": "4.62.0", + "@rollup/rollup-freebsd-arm64": "4.62.0", + "@rollup/rollup-freebsd-x64": "4.62.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.62.0", + "@rollup/rollup-linux-arm-musleabihf": "4.62.0", + "@rollup/rollup-linux-arm64-gnu": "4.62.0", + "@rollup/rollup-linux-arm64-musl": "4.62.0", + "@rollup/rollup-linux-loong64-gnu": "4.62.0", + "@rollup/rollup-linux-loong64-musl": "4.62.0", + "@rollup/rollup-linux-ppc64-gnu": "4.62.0", + "@rollup/rollup-linux-ppc64-musl": "4.62.0", + "@rollup/rollup-linux-riscv64-gnu": "4.62.0", + "@rollup/rollup-linux-riscv64-musl": "4.62.0", + "@rollup/rollup-linux-s390x-gnu": "4.62.0", + "@rollup/rollup-linux-x64-gnu": "4.62.0", + "@rollup/rollup-linux-x64-musl": "4.62.0", + "@rollup/rollup-openbsd-x64": "4.62.0", + "@rollup/rollup-openharmony-arm64": "4.62.0", + "@rollup/rollup-win32-arm64-msvc": "4.62.0", + "@rollup/rollup-win32-ia32-msvc": "4.62.0", + "@rollup/rollup-win32-x64-gnu": "4.62.0", + "@rollup/rollup-win32-x64-msvc": "4.62.0", + "fsevents": "~2.3.2" + } + }, + "node_modules/safe-array-concat": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.4.tgz", + "integrity": "sha512-wtZlHyOje6OZTGqAoaDKxFkgRtkF9CnHAVnCHKfuj200wAgL+bSJhdsCD2l0Qx/2ekEXjPWcyKkfGb5CPboslg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", + "get-intrinsic": "^1.3.0", + "has-symbols": "^1.1.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-push-apply": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", + "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-regex-test": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-regex": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/scheduler": { + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + } + }, + "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/serialize-javascript": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-7.0.5.tgz", + "integrity": "sha512-F4LcB0UqUl1zErq+1nYEEzSHJnIwb3AF2XWB94b+afhrekOUijwooAYqFyRbjYkm2PAKBabx6oYv/xDxNi8IBw==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-proto": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", + "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz", + "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4", + "side-channel-list": "^1.0.1", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", + "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/smob": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/smob/-/smob-1.6.2.tgz", + "integrity": "sha512-RQsvleCbF8cVHEv+xuDGaA4pOizFqJ0GgjtMSRo6oP8pnN7WsigHgVGey6aILRBKv4W2YOMHLqbKdnB6hpB9fw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/source-map": { + "version": "0.8.0-beta.0", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.8.0-beta.0.tgz", + "integrity": "sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==", + "deprecated": "The work that was done in this beta branch won't be included in future versions", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "whatwg-url": "^7.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/source-map-support/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stop-iteration-iterator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", + "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "internal-slot": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/string.prototype.matchall": { + "version": "4.0.12", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz", + "integrity": "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.6", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.6", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "internal-slot": "^1.1.0", + "regexp.prototype.flags": "^1.5.3", + "set-function-name": "^2.0.2", + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trim": { + "version": "1.2.11", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.11.tgz", + "integrity": "sha512-PwvK7BU+CMTJGYQCTZb5RWXIML92lftJLhQz1tBzgKiqGxJaMlBAa48POXaNAC2s4y8jr3EFqrkF9+44neS46w==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", + "define-data-property": "^1.1.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.24.2", + "es-object-atoms": "^1.1.2", + "has-property-descriptors": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.10.tgz", + "integrity": "sha512-2+3aDAOmPTmuFwjDnmJG2ctEkQKVki7vOSqaxkv42Mowj1V6PnvuwFCRrR5lChUux1TBskPjfkeTOhqczDMxTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/stringify-object": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz", + "integrity": "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "get-own-enumerable-property-symbols": "^3.0.0", + "is-obj": "^1.0.1", + "is-regexp": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-comments/-/strip-comments-2.0.1.tgz", + "integrity": "sha512-ZprKx+bBLXv067WTCALv8SSz5l2+XhpYCsVtSqlMnkAXMWDq+/ekVbl1ghqP9rUHTzv6sm/DwCOiYutU/yp1fw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "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/tailwindcss": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.3.1.tgz", + "integrity": "sha512-hk+TB1m+K8CYNrP6rjQaq/Y+4Zylwpa87mLYBKCunwnnQ9p+fHb7kmSfGqyEJoxF/O6CDyABWVFEafNSYKll+Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/tapable": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz", + "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/temp-dir": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-2.0.0.tgz", + "integrity": "sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/tempy": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tempy/-/tempy-0.6.0.tgz", + "integrity": "sha512-G13vtMYPT/J8A4X2SjdtBTphZlrp1gKv6hZiOjw14RCWg6GbHuQBGtjlx75xLbYV/wEc0D7G5K4rxKP/cXk8Bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-stream": "^2.0.0", + "temp-dir": "^2.0.0", + "type-fest": "^0.16.0", + "unique-string": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "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", + "integrity": "sha512-J/9An6vs9Us6wKRriSFXBWdRZapREHqFzdNUKk0pmu804EMR6dr6winwo7e5JDxN4xahxQsuysyYFwlwj4XN/Q==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.15.0", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tr46": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", + "integrity": "sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==", + "dev": true, + "license": "MIT", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/type-fest": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.16.0.tgz", + "integrity": "sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typed-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", + "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", + "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.15", + "reflect.getprototypeof": "^1.0.9" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.8.tgz", + "integrity": "sha512-phPGCwqr2+Qo0fwniCE8e4pKnGu/yFb5nD5Y8bf0EEeiI5GklnACYA9GFy/DrAeRrKHXvHn+1SUsOWgJp6RO+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.9", + "for-each": "^0.3.5", + "gopd": "^1.2.0", + "is-typed-array": "^1.1.15", + "possible-typed-array-names": "^1.1.0", + "reflect.getprototypeof": "^1.0.10" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/unbox-primitive": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", + "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-bigints": "^1.0.2", + "has-symbols": "^1.1.0", + "which-boxed-primitive": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/unicode-canonical-property-names-ecmascript": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz", + "integrity": "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-ecmascript": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", + "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "unicode-canonical-property-names-ecmascript": "^2.0.0", + "unicode-property-aliases-ecmascript": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-match-property-value-ecmascript": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.1.tgz", + "integrity": "sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/unicode-property-aliases-ecmascript": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.2.0.tgz", + "integrity": "sha512-hpbDzxUY9BFwX+UeBnxv3Sh1q7HFxj48DTmXchNgRa46lO8uj3/1iEn3MiNUYTg1g9ctIqXCCERn8gYZhHC5lQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/unique-string": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", + "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "crypto-random-string": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/upath": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", + "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4", + "yarn": "*" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/vite": { + "version": "5.4.21", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", + "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vite-plugin-pwa": { + "version": "0.20.5", + "resolved": "https://registry.npmjs.org/vite-plugin-pwa/-/vite-plugin-pwa-0.20.5.tgz", + "integrity": "sha512-aweuI/6G6n4C5Inn0vwHumElU/UEpNuO+9iZzwPZGTCH87TeZ6YFMrEY6ZUBQdIHHlhTsbMDryFARcSuOdsz9Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.6", + "pretty-bytes": "^6.1.1", + "tinyglobby": "^0.2.0", + "workbox-build": "^7.1.0", + "workbox-window": "^7.1.0" + }, + "engines": { + "node": ">=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@vite-pwa/assets-generator": "^0.2.6", + "vite": "^3.1.0 || ^4.0.0 || ^5.0.0", + "workbox-build": "^7.1.0", + "workbox-window": "^7.1.0" + }, + "peerDependenciesMeta": { + "@vite-pwa/assets-generator": { + "optional": true + } + } + }, + "node_modules/webidl-conversions": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", + "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/whatwg-url": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.1.0.tgz", + "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash.sortby": "^4.7.0", + "tr46": "^1.0.1", + "webidl-conversions": "^4.0.2" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", + "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-bigint": "^1.1.0", + "is-boolean-object": "^1.2.1", + "is-number-object": "^1.1.1", + "is-string": "^1.1.1", + "is-symbol": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-builtin-type": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", + "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "function.prototype.name": "^1.1.6", + "has-tostringtag": "^1.0.2", + "is-async-function": "^2.0.0", + "is-date-object": "^1.1.0", + "is-finalizationregistry": "^1.1.0", + "is-generator-function": "^1.0.10", + "is-regex": "^1.2.1", + "is-weakref": "^1.0.2", + "isarray": "^2.0.5", + "which-boxed-primitive": "^1.1.0", + "which-collection": "^1.0.2", + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-collection": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-map": "^2.0.3", + "is-set": "^2.0.3", + "is-weakmap": "^2.0.2", + "is-weakset": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.22", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.22.tgz", + "integrity": "sha512-fvO4ExWMFsqyhG3AiPAObMuY1lxaqgYcxbc49CNdWDDECOJNgQyvsOWVwbZc+qf3rzRtxojBK+CMEv0Ld5CYpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/workbox-background-sync": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/workbox-background-sync/-/workbox-background-sync-7.4.1.tgz", + "integrity": "sha512-HhT7KE8tOWDm02wRNshXUnUPofMlhenF2DBdUnDPOubhizzPeItkYTmAB6td1Z2cjYPa98vzEiPLEuzn5hN66g==", + "dev": true, + "license": "MIT", + "dependencies": { + "idb": "^7.0.1", + "workbox-core": "7.4.1" + } + }, + "node_modules/workbox-broadcast-update": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/workbox-broadcast-update/-/workbox-broadcast-update-7.4.1.tgz", + "integrity": "sha512-uAlgslKLvbQY+suirIdnBCSYrcgBhjp81Nj4l1lj/Jmj0MJO2CJERnCJjT0GFVwmReV0N+zs78K6gqd5gr9/+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "workbox-core": "7.4.1" + } + }, + "node_modules/workbox-build": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/workbox-build/-/workbox-build-7.4.1.tgz", + "integrity": "sha512-SDhxIvEAde9Gy/5w4Yo1Jh/M49Z0qE3q0oteyE8zGq0DScxFqVBcCtIXFuLtmtxRQZCMbf0prco4VyEu3KBQuw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@apideck/better-ajv-errors": "^0.3.1", + "@babel/core": "^7.24.4", + "@babel/preset-env": "^7.11.0", + "@babel/runtime": "^7.11.2", + "@rollup/plugin-babel": "^6.1.0", + "@rollup/plugin-node-resolve": "^16.0.3", + "@rollup/plugin-replace": "^6.0.3", + "@rollup/plugin-terser": "^1.0.0", + "@trickfilm400/rollup-plugin-off-main-thread": "^3.0.0-pre1", + "ajv": "^8.6.0", + "common-tags": "^1.8.0", + "eta": "^4.5.1", + "fast-json-stable-stringify": "^2.1.0", + "fs-extra": "^9.0.1", + "glob": "^11.0.1", + "pretty-bytes": "^5.3.0", + "rollup": "^4.53.3", + "source-map": "^0.8.0-beta.0", + "stringify-object": "^3.3.0", + "strip-comments": "^2.0.1", + "tempy": "^0.6.0", + "upath": "^1.2.0", + "workbox-background-sync": "7.4.1", + "workbox-broadcast-update": "7.4.1", + "workbox-cacheable-response": "7.4.1", + "workbox-core": "7.4.1", + "workbox-expiration": "7.4.1", + "workbox-google-analytics": "7.4.1", + "workbox-navigation-preload": "7.4.1", + "workbox-precaching": "7.4.1", + "workbox-range-requests": "7.4.1", + "workbox-recipes": "7.4.1", + "workbox-routing": "7.4.1", + "workbox-strategies": "7.4.1", + "workbox-streams": "7.4.1", + "workbox-sw": "7.4.1", + "workbox-window": "7.4.1" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/workbox-build/node_modules/pretty-bytes": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz", + "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/workbox-cacheable-response": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/workbox-cacheable-response/-/workbox-cacheable-response-7.4.1.tgz", + "integrity": "sha512-8xaFoJdDc2OjrlbbL3gEeBO1WKcMwRqwLRupgqahYXu75yXajPLuwrbXMrIGZuWYXrQwk0xDjOxZ/ujCy/oJYw==", + "dev": true, + "license": "MIT", + "dependencies": { + "workbox-core": "7.4.1" + } + }, + "node_modules/workbox-core": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/workbox-core/-/workbox-core-7.4.1.tgz", + "integrity": "sha512-DT+vu46eh/2vRsSHTY4Xmc32Z1rr9PRlQUXr1Dx30ZuXRWwOsvZgGgcwxcasubQLQmbTNYZjv44LkBAQ4tT5tQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/workbox-expiration": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/workbox-expiration/-/workbox-expiration-7.4.1.tgz", + "integrity": "sha512-lRKUF7b+OGbeXkQk1s6MHXOa3d7Xxf7Of31W6c6hCfipfIyrtdWZ89stq21AHZMaoG7VNFoHply4Ox+rU31TWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "idb": "^7.0.1", + "workbox-core": "7.4.1" + } + }, + "node_modules/workbox-google-analytics": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/workbox-google-analytics/-/workbox-google-analytics-7.4.1.tgz", + "integrity": "sha512-Mks1JwLEt++ZAkF6sS1OpSh9RtAMIsiDgRpK+codiHGIPXeaUOgi4cPc3GFadUl8V5QPeypEk8Oxgl3HlwVzHw==", + "dev": true, + "license": "MIT", + "dependencies": { + "workbox-background-sync": "7.4.1", + "workbox-core": "7.4.1", + "workbox-routing": "7.4.1", + "workbox-strategies": "7.4.1" + } + }, + "node_modules/workbox-navigation-preload": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/workbox-navigation-preload/-/workbox-navigation-preload-7.4.1.tgz", + "integrity": "sha512-C4KVsjPcYKJOhr631AxR9XoG2rLF3QiTk5aMv36MXOjtWvm8axwNFAtKUPGsWUwLXXAMgYM1En7fsvndaXeXRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "workbox-core": "7.4.1" + } + }, + "node_modules/workbox-precaching": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/workbox-precaching/-/workbox-precaching-7.4.1.tgz", + "integrity": "sha512-cdr/9qByww7yzEp7zg/qI4ukUrrNjQLgN+ONQRpjy/VqGQXwkgHwr00KksGJK8v0VifwDXBb8a4cWNZH71jn3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "workbox-core": "7.4.1", + "workbox-routing": "7.4.1", + "workbox-strategies": "7.4.1" + } + }, + "node_modules/workbox-range-requests": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/workbox-range-requests/-/workbox-range-requests-7.4.1.tgz", + "integrity": "sha512-7i2oxAUE82gHdAJBCAQ04JzNOdRPqzuOzGfoUyJpFSmeqBNYGPrAH8GPoPjUQTfp+NycwrD2H68VtuF8qxv0vQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "workbox-core": "7.4.1" + } + }, + "node_modules/workbox-recipes": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/workbox-recipes/-/workbox-recipes-7.4.1.tgz", + "integrity": "sha512-gnbVfmV4/TtmQaM4x9AtuXhcdstJsep3XMVeztOrQVPT+R6+6DeBjGTCQ7fFCXm+4GEHUA5VEBTyi5+4gWGeog==", + "dev": true, + "license": "MIT", + "dependencies": { + "workbox-cacheable-response": "7.4.1", + "workbox-core": "7.4.1", + "workbox-expiration": "7.4.1", + "workbox-precaching": "7.4.1", + "workbox-routing": "7.4.1", + "workbox-strategies": "7.4.1" + } + }, + "node_modules/workbox-routing": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/workbox-routing/-/workbox-routing-7.4.1.tgz", + "integrity": "sha512-yubJGErZOusuidAenaL5ypfhQOa7urxP/f8E0ws7FPb4039RiWXUWBAyUkmUoOL/BcQGen3h0J8872d51IYxtA==", + "dev": true, + "license": "MIT", + "dependencies": { + "workbox-core": "7.4.1" + } + }, + "node_modules/workbox-strategies": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/workbox-strategies/-/workbox-strategies-7.4.1.tgz", + "integrity": "sha512-GZxpaw9NbmOelj7667uZ2kpk5BFpOGbO4X0qjwh5ls8XQ8C+Lha5LQchTiUzsTFSS+NlUpftYAyOVXvQUrcqOQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "workbox-core": "7.4.1" + } + }, + "node_modules/workbox-streams": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/workbox-streams/-/workbox-streams-7.4.1.tgz", + "integrity": "sha512-HWWtraKUbJknd9kgqGcpQ3G114HOPYvqs8HaJMDs2ebLNAimDkVDaWfAXE6Ybl+m8U6KsCE6pWyLYuigWmnAXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "workbox-core": "7.4.1", + "workbox-routing": "7.4.1" + } + }, + "node_modules/workbox-sw": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/workbox-sw/-/workbox-sw-7.4.1.tgz", + "integrity": "sha512-fez5f2DUlDJWTFYkCWQpY10N8gtztd849NswCbVFk0QlcSM4HT5A8x4g4ii650yem4I8tHY0R7JZahwp3ltIPw==", + "dev": true, + "license": "MIT" + }, + "node_modules/workbox-window": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/workbox-window/-/workbox-window-7.4.1.tgz", + "integrity": "sha512-notZDH2u8VXaqyuD7xaqIfEFi6SRM4SUSd7ewe9PDsVqADuepxX2ZMY3uvuZGxzY5ZOsGC/vD3A/3smFtJt4/A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/trusted-types": "^2.0.2", + "workbox-core": "7.4.1" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + } + } +} diff --git a/frontend/package.json b/frontend/package.json new file mode 100644 index 0000000..1d6e72e --- /dev/null +++ b/frontend/package.json @@ -0,0 +1,24 @@ +{ + "name": "club-dynasqy-frontend", + "private": true, + "version": "1.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview" + }, + "dependencies": { + "cropperjs": "^1.6.2", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "react-router-dom": "^6.26.0" + }, + "devDependencies": { + "@tailwindcss/vite": "^4.1.4", + "@vitejs/plugin-react": "^4.3.1", + "tailwindcss": "^4.1.4", + "vite": "^5.4.0", + "vite-plugin-pwa": "^0.20.1" + } +} diff --git a/frontend/presentation/banniere.png b/frontend/presentation/banniere.png new file mode 100644 index 0000000..663330e Binary files /dev/null and b/frontend/presentation/banniere.png differ diff --git a/frontend/presentation/padel_logo.png b/frontend/presentation/padel_logo.png new file mode 100644 index 0000000..bb153f0 Binary files /dev/null and b/frontend/presentation/padel_logo.png differ diff --git a/frontend/public/MF.mp3 b/frontend/public/MF.mp3 new file mode 100644 index 0000000..2920e72 Binary files /dev/null and b/frontend/public/MF.mp3 differ diff --git a/frontend/public/banniere.png b/frontend/public/banniere.png new file mode 100644 index 0000000..663330e Binary files /dev/null and b/frontend/public/banniere.png differ diff --git a/frontend/public/drum-roll.mp3 b/frontend/public/drum-roll.mp3 new file mode 100644 index 0000000..5b5eb7d Binary files /dev/null and b/frontend/public/drum-roll.mp3 differ diff --git a/frontend/public/favicon.svg b/frontend/public/favicon.svg new file mode 100644 index 0000000..d5b4410 --- /dev/null +++ b/frontend/public/favicon.svg @@ -0,0 +1,4 @@ + + + DS + diff --git a/frontend/public/icon-192.png b/frontend/public/icon-192.png new file mode 100644 index 0000000..ab2a678 Binary files /dev/null and b/frontend/public/icon-192.png differ diff --git a/frontend/public/icon-512.png b/frontend/public/icon-512.png new file mode 100644 index 0000000..aaaddd2 Binary files /dev/null and b/frontend/public/icon-512.png differ diff --git a/frontend/public/logo.png b/frontend/public/logo.png new file mode 100644 index 0000000..dce2fdc Binary files /dev/null and b/frontend/public/logo.png differ diff --git a/frontend/public/logo.svg b/frontend/public/logo.svg new file mode 100644 index 0000000..d8a8ef8 --- /dev/null +++ b/frontend/public/logo.svg @@ -0,0 +1,8 @@ + + + + + + + \ No newline at end of file diff --git a/frontend/public/padel_logo.png b/frontend/public/padel_logo.png new file mode 100644 index 0000000..bb153f0 Binary files /dev/null and b/frontend/public/padel_logo.png differ diff --git a/frontend/public/this-girl_30sec.mp3 b/frontend/public/this-girl_30sec.mp3 new file mode 100644 index 0000000..7c4675d Binary files /dev/null and b/frontend/public/this-girl_30sec.mp3 differ diff --git a/frontend/pump-it_30sec.mp3 b/frontend/pump-it_30sec.mp3 new file mode 100644 index 0000000..c7ace1b Binary files /dev/null and b/frontend/pump-it_30sec.mp3 differ diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx new file mode 100644 index 0000000..bad8352 --- /dev/null +++ b/frontend/src/App.jsx @@ -0,0 +1,75 @@ +import AddMember from './components/AddMember' +import { Routes, Route, Navigate } from 'react-router-dom' +import { useAuth } from './context/AuthContext' +import Login from './components/Login' +import Dashboard from './components/Dashboard' +import MembersList from './components/MembersList' +import MemberDetail from './components/MemberDetail' +import EditProfile from './components/EditProfile' +import GuestsList from './components/GuestsList' +import GuestDetail from './components/GuestDetail' +import AddGuestForm from './components/AddGuestForm' +import EditGuestForm from './components/EditGuestForm' +import RecommendationsList from './components/RecommendationsList' +import AddRecommendationForm from './components/AddRecommendationForm' +import EditRecommendationForm from './components/EditRecommendationForm' +import HonorsList from './components/HonorsList' +import Gallery from './components/Gallery' +import AdminHonors from './components/AdminHonors' +import AdminEvents from './components/AdminEvents' +import AdminUsers from './components/AdminUsers' +import AdminLogs from './components/AdminLogs' +import AdminPresentationEditor from './components/AdminPresentationEditor' +import Profile from './components/Profile' +import ChangePassword from './components/ChangePassword' +import Notifications from './components/Notifications' +import Presentation from './components/Presentation' +import Layout from './components/Layout' + +function PrivateRoute({ children }) { + const { user, loading } = useAuth() + if (loading) return

Chargement...

+ return user ? children : +} + +function AdminRoute({ children }) { + const { user, loading } = useAuth() + if (loading) return

Chargement...

+ if (!user) return + if (user.role !== 'admin') return + return children +} + +export default function App() { + return ( + + } /> + }> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + + } /> + } /> + + ) +} diff --git a/frontend/src/components/AddGuestForm.jsx b/frontend/src/components/AddGuestForm.jsx new file mode 100644 index 0000000..1a77212 --- /dev/null +++ b/frontend/src/components/AddGuestForm.jsx @@ -0,0 +1,206 @@ +import { useState, useEffect, useRef } from 'react' +import { useNavigate, Link } from 'react-router-dom' +import { useAuth } from '../context/AuthContext' + +export default function AddGuestForm() { + const { authFetch, user } = useAuth() + const navigate = useNavigate() + const [form, setForm] = useState({ + prenom: '', nom: '', societe: '', activite: '', + telephone: '', email: '', site_web: '', commentaires: '', + date_club: '', + }) + const [error, setError] = useState('') + const [busy, setBusy] = useState(false) + const [searchResults, setSearchResults] = useState([]) + const [showSearch, setShowSearch] = useState(false) + const [createdGuestId, setCreatedGuestId] = useState(null) + const [sending, setSending] = useState(false) + const [showBlockedModal, setShowBlockedModal] = useState(false) + const searchRef = useRef(null) + + useEffect(() => { + const handleClick = (e) => { + if (searchRef.current && !searchRef.current.contains(e.target)) setShowSearch(false) + } + document.addEventListener('mousedown', handleClick) + return () => document.removeEventListener('mousedown', handleClick) + }, []) + + const handleNameChange = async (field, value) => { + setForm((f) => ({ ...f, [field]: value })) + const nomVal = field === 'nom' ? value : form.nom + const prenomVal = field === 'prenom' ? value : form.prenom + const q = `${prenomVal} ${nomVal}`.trim() + if (q.length >= 2) { + const r = await authFetch(`/api/guests/search?q=${encodeURIComponent(q)}`) + if (r.ok) { const data = await r.json(); setSearchResults(data); setShowSearch(data.length > 0) } + } else { setShowSearch(false) } + } + + const selectExisting = (g) => { + setForm((f) => ({ + ...f, + prenom: g.prenom, + nom: g.nom, + societe: g.societe || '', + activite: g.activite || '', + telephone: g.telephone || '', + email: g.email || '', + site_web: g.site_web || '', + })) + setShowSearch(false) + } + + const handleChange = (e) => { + const { name, value } = e.target + if (name === 'prenom' || name === 'nom') { + handleNameChange(name, value) + } else { + setForm((f) => ({ ...f, [name]: value })) + } + } + + const BLOCKED_DATES = ['2026-10-20', '2026-12-15'] + + const handleSubmit = async (e) => { + e.preventDefault() + setError('') + if (BLOCKED_DATES.includes(form.date_club)) { + setShowBlockedModal(true) + return + } + setBusy(true) + try { + const r = await authFetch('/api/guests', { + method: 'POST', + body: JSON.stringify(form), + }) + if (!r.ok) { + const err = await r.json() + throw new Error(err.error || 'Erreur') + } + const data = await r.json() + setCreatedGuestId(data.id) + } catch (err) { + setError(err.message) + } finally { + setBusy(false) + } + } + + const handleSendInvitation = () => { + setSending(true) + if (createdGuestId && form.email) { + authFetch(`/api/guests/${createdGuestId}/send-invitation`, { method: 'POST' }) + } + navigate('/guests') + } + + const handleSkipInvitation = () => { + if (sending) return + navigate('/guests') + } + + return ( +
+ ← Retour +

Ajouter un(e) invite(e)

+ {error &&
{error}
} +
+
+ + +
+
+
+ + +
+
+ + +
+ {showSearch && ( +
+ {searchResults.map((g, i) => ( + + ))} +
+ )} +
+
+ + +
+
+ + +
+
+
+ + +
+
+ + +
+
+
+ + +
+
+ + +
+
+ +