diff --git a/.gitignore b/.gitignore index 6f2bd12..b39734e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +.idea *.sqlite3 *.sqlite3-shm *.sqlite3-wal @@ -27,4 +28,4 @@ dist/ build/ # Webpack output -public/assets/ \ No newline at end of file +public/assets/ diff --git a/backend/.prettierrc.json b/backend/.prettierrc.json new file mode 100644 index 0000000..45be71f --- /dev/null +++ b/backend/.prettierrc.json @@ -0,0 +1,6 @@ +{ + "tabWidth": 4, + "semi": true, + "singleQuote": true, + "trailingComma": "es5" +} diff --git a/backend/app.js b/backend/app.js index 87b75a6..b8d7b88 100644 --- a/backend/app.js +++ b/backend/app.js @@ -15,64 +15,91 @@ const app = express(); // Session store const sessionStore = new SequelizeStore({ - db: sequelize, + db: sequelize, }); // Middlewares -const sslEnabled = process.env.NODE_ENV === 'production' && process.env.TUDUDI_INTERNAL_SSL_ENABLED === 'true'; -app.use(helmet({ - hsts: sslEnabled, // Only enable HSTS when SSL is enabled - forceHTTPS: sslEnabled, // Only force HTTPS when SSL is enabled - contentSecurityPolicy: false // Disable CSP for now to avoid conflicts -})); +const sslEnabled = + process.env.NODE_ENV === 'production' && + process.env.TUDUDI_INTERNAL_SSL_ENABLED === 'true'; +app.use( + helmet({ + hsts: sslEnabled, // Only enable HSTS when SSL is enabled + forceHTTPS: sslEnabled, // Only force HTTPS when SSL is enabled + contentSecurityPolicy: false, // Disable CSP for now to avoid conflicts + }) +); app.use(compression()); app.use(morgan('combined')); // CORS configuration -const allowedOrigins = process.env.TUDUDI_ALLOWED_ORIGINS - ? process.env.TUDUDI_ALLOWED_ORIGINS.split(',').map(origin => origin.trim()) - : ['http://localhost:8080', 'http://localhost:9292', 'http://127.0.0.1:8080', 'http://127.0.0.1:9292']; +const allowedOrigins = process.env.TUDUDI_ALLOWED_ORIGINS + ? process.env.TUDUDI_ALLOWED_ORIGINS.split(',').map((origin) => + origin.trim() + ) + : [ + 'http://localhost:8080', + 'http://localhost:9292', + 'http://127.0.0.1:8080', + 'http://127.0.0.1:9292', + ]; -app.use(cors({ - origin: allowedOrigins, - credentials: true, - methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'], - allowedHeaders: ['Authorization', 'Content-Type', 'Accept', 'X-Requested-With'], - exposedHeaders: ['Content-Type'], - maxAge: 1728000 -})); +app.use( + cors({ + origin: allowedOrigins, + credentials: true, + methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'], + allowedHeaders: [ + 'Authorization', + 'Content-Type', + 'Accept', + 'X-Requested-With', + ], + exposedHeaders: ['Content-Type'], + maxAge: 1728000, + }) +); // Body parsing app.use(express.json({ limit: '10mb' })); app.use(express.urlencoded({ extended: true, limit: '10mb' })); // Session configuration -const secureFlag = process.env.NODE_ENV === 'production' && process.env.TUDUDI_INTERNAL_SSL_ENABLED === 'true'; -app.use(session({ - secret: process.env.TUDUDI_SESSION_SECRET || require('crypto').randomBytes(64).toString('hex'), - store: sessionStore, - resave: false, - saveUninitialized: false, - cookie: { - httpOnly: true, - secure: secureFlag, - maxAge: 2592000000, // 30 days - sameSite: secureFlag ? 'none' : 'lax' - } -})); +const secureFlag = + process.env.NODE_ENV === 'production' && + process.env.TUDUDI_INTERNAL_SSL_ENABLED === 'true'; +app.use( + session({ + secret: + process.env.TUDUDI_SESSION_SECRET || + require('crypto').randomBytes(64).toString('hex'), + store: sessionStore, + resave: false, + saveUninitialized: false, + cookie: { + httpOnly: true, + secure: secureFlag, + maxAge: 2592000000, // 30 days + sameSite: secureFlag ? 'none' : 'lax', + }, + }) +); // Static files if (process.env.NODE_ENV === 'production') { - app.use(express.static(path.join(__dirname, 'dist'))); + app.use(express.static(path.join(__dirname, 'dist'))); } else { - app.use(express.static('public')); + app.use(express.static('public')); } -// Serve locales +// Serve locales if (process.env.NODE_ENV === 'production') { - app.use('/locales', express.static(path.join(__dirname, 'dist/locales'))); + app.use('/locales', express.static(path.join(__dirname, 'dist/locales'))); } else { - app.use('/locales', express.static(path.join(__dirname, '../public/locales'))); + app.use( + '/locales', + express.static(path.join(__dirname, '../public/locales')) + ); } // Serve uploaded files @@ -83,12 +110,12 @@ const { requireAuth } = require('./middleware/auth'); // Health check (before auth middleware) - ensure it's completely bypassed app.get('/api/health', (req, res) => { - res.status(200).json({ - status: 'ok', - timestamp: new Date().toISOString(), - uptime: process.uptime(), - environment: process.env.NODE_ENV || 'development' - }); + res.status(200).json({ + status: 'ok', + timestamp: new Date().toISOString(), + uptime: process.uptime(), + environment: process.env.NODE_ENV || 'development', + }); }); // Routes @@ -108,74 +135,86 @@ app.use('/api/calendar', require('./routes/calendar')); // SPA fallback app.get('*', (req, res) => { - if (!req.path.startsWith('/api/') && !req.path.match(/\.(js|css|png|jpg|jpeg|gif|ico|svg)$/)) { - if (process.env.NODE_ENV === 'production') { - res.sendFile(path.join(__dirname, 'dist', 'index.html')); + if ( + !req.path.startsWith('/api/') && + !req.path.match(/\.(js|css|png|jpg|jpeg|gif|ico|svg)$/) + ) { + if (process.env.NODE_ENV === 'production') { + res.sendFile(path.join(__dirname, 'dist', 'index.html')); + } else { + res.sendFile(path.join(__dirname, '../public', 'index.html')); + } } else { - res.sendFile(path.join(__dirname, '../public', 'index.html')); + res.status(404).json({ + error: 'Not Found', + message: 'The requested resource could not be found.', + }); } - } else { - res.status(404).json({ error: 'Not Found', message: 'The requested resource could not be found.' }); - } }); // Error handling app.use((err, req, res, next) => { - console.error(err.stack); - res.status(500).json({ error: 'Internal Server Error', message: err.message }); + console.error(err.stack); + res.status(500).json({ + error: 'Internal Server Error', + message: err.message, + }); }); const PORT = process.env.PORT || 3002; // Initialize database and start server async function startServer() { - try { - // Create session store table - await sessionStore.sync(); - - // Sync database - await sequelize.sync(); - - // Auto-create user if not exists - if (process.env.TUDUDI_USER_EMAIL && process.env.TUDUDI_USER_PASSWORD) { - const { User } = require('./models'); - const bcrypt = require('bcrypt'); - - const [user, created] = await User.findOrCreate({ - where: { email: process.env.TUDUDI_USER_EMAIL }, - defaults: { - email: process.env.TUDUDI_USER_EMAIL, - password_digest: await bcrypt.hash(process.env.TUDUDI_USER_PASSWORD, 10) + try { + // Create session store table + await sessionStore.sync(); + + // Sync database + await sequelize.sync(); + + // Auto-create user if not exists + if (process.env.TUDUDI_USER_EMAIL && process.env.TUDUDI_USER_PASSWORD) { + const { User } = require('./models'); + const bcrypt = require('bcrypt'); + + const [user, created] = await User.findOrCreate({ + where: { email: process.env.TUDUDI_USER_EMAIL }, + defaults: { + email: process.env.TUDUDI_USER_EMAIL, + password_digest: await bcrypt.hash( + process.env.TUDUDI_USER_PASSWORD, + 10 + ), + }, + }); + + if (created) { + console.log('Default user created:', user.email); + } } - }); - - if (created) { - console.log('Default user created:', user.email); - } + + // Initialize Telegram polling after database is ready + await initializeTelegramPolling(); + + // Initialize task scheduler + await taskScheduler.initialize(); + + const server = app.listen(PORT, '0.0.0.0', () => { + console.log(`Server running on port ${PORT}`); + console.log(`Server listening on http://localhost:${PORT}`); + }); + + server.on('error', (err) => { + console.error('Server error:', err); + }); + } catch (error) { + console.error('Failed to start server:', error); + process.exit(1); } - - // Initialize Telegram polling after database is ready - await initializeTelegramPolling(); - - // Initialize task scheduler - await taskScheduler.initialize(); - - const server = app.listen(PORT, '0.0.0.0', () => { - console.log(`Server running on port ${PORT}`); - console.log(`Server listening on http://localhost:${PORT}`); - }); - - server.on('error', (err) => { - console.error('Server error:', err); - }); - } catch (error) { - console.error('Failed to start server:', error); - process.exit(1); - } } if (require.main === module) { - startServer(); + startServer(); } -module.exports = app; \ No newline at end of file +module.exports = app; diff --git a/backend/config/database.js b/backend/config/database.js index 05afcff..65f42c8 100644 --- a/backend/config/database.js +++ b/backend/config/database.js @@ -1,42 +1,42 @@ require('dotenv').config(); const path = require('path'); -const dbPath = process.env.DATABASE_URL - ? process.env.DATABASE_URL.replace('sqlite:///', '') - : path.join(__dirname, '..', 'db'); +const dbPath = process.env.DATABASE_URL + ? process.env.DATABASE_URL.replace('sqlite:///', '') + : path.join(__dirname, '..', 'db'); module.exports = { - development: { - dialect: 'sqlite', - storage: path.join(dbPath, 'development.sqlite3'), - logging: console.log, - define: { - timestamps: true, - underscored: true, - createdAt: 'created_at', - updatedAt: 'updated_at' - } - }, - test: { - dialect: 'sqlite', - storage: path.join(dbPath, 'test.sqlite3'), - logging: false, - define: { - timestamps: true, - underscored: true, - createdAt: 'created_at', - updatedAt: 'updated_at' - } - }, - production: { - dialect: 'sqlite', - storage: path.join(dbPath, 'production.sqlite3'), - logging: false, - define: { - timestamps: true, - underscored: true, - createdAt: 'created_at', - updatedAt: 'updated_at' - } - } -}; \ No newline at end of file + development: { + dialect: 'sqlite', + storage: path.join(dbPath, 'development.sqlite3'), + logging: console.log, + define: { + timestamps: true, + underscored: true, + createdAt: 'created_at', + updatedAt: 'updated_at', + }, + }, + test: { + dialect: 'sqlite', + storage: path.join(dbPath, 'test.sqlite3'), + logging: false, + define: { + timestamps: true, + underscored: true, + createdAt: 'created_at', + updatedAt: 'updated_at', + }, + }, + production: { + dialect: 'sqlite', + storage: path.join(dbPath, 'production.sqlite3'), + logging: false, + define: { + timestamps: true, + underscored: true, + createdAt: 'created_at', + updatedAt: 'updated_at', + }, + }, +}; diff --git a/backend/config/quotes.yml b/backend/config/quotes.yml index 74dced7..7cf2839 100644 --- a/backend/config/quotes.yml +++ b/backend/config/quotes.yml @@ -1,22 +1,21 @@ quotes: - - "Believe you can and you're halfway there." - - "The only way to do great work is to love what you do." - - "Success is not final, failure is not fatal: It is the courage to continue that counts." - - "It always seems impossible until it's done." - - "Your time is limited, don't waste it living someone else's life." - - "The future belongs to those who believe in the beauty of their dreams." - - "Don't watch the clock; do what it does. Keep going." - - "Quality is not an act, it is a habit." - - "The only limit to our realization of tomorrow is our doubts of today." - - "Act as if what you do makes a difference. It does." - - "The best way to predict the future is to create it." - - "Success is walking from failure to failure with no loss of enthusiasm." - - "You are never too old to set another goal or to dream a new dream." - - "The secret of getting ahead is getting started." - - "Don't let yesterday take up too much of today." - - "You don't have to be great to start, but you have to start to be great." - - "Focus on progress, not perfection." - - "One task at a time leads to great accomplishments." - - "Today's effort is tomorrow's success." - - "Small steps every day lead to big results." - + - "Believe you can and you're halfway there." + - 'The only way to do great work is to love what you do.' + - 'Success is not final, failure is not fatal: It is the courage to continue that counts.' + - "It always seems impossible until it's done." + - "Your time is limited, don't waste it living someone else's life." + - 'The future belongs to those who believe in the beauty of their dreams.' + - "Don't watch the clock; do what it does. Keep going." + - 'Quality is not an act, it is a habit.' + - 'The only limit to our realization of tomorrow is our doubts of today.' + - 'Act as if what you do makes a difference. It does.' + - 'The best way to predict the future is to create it.' + - 'Success is walking from failure to failure with no loss of enthusiasm.' + - 'You are never too old to set another goal or to dream a new dream.' + - 'The secret of getting ahead is getting started.' + - "Don't let yesterday take up too much of today." + - "You don't have to be great to start, but you have to start to be great." + - 'Focus on progress, not perfection.' + - 'One task at a time leads to great accomplishments.' + - "Today's effort is tomorrow's success." + - 'Small steps every day lead to big results.' diff --git a/backend/eslint.config.js b/backend/eslint.config.js new file mode 100644 index 0000000..8d31e44 --- /dev/null +++ b/backend/eslint.config.js @@ -0,0 +1,40 @@ +module.exports = [ + { + files: ['**/*.js'], + languageOptions: { + ecmaVersion: 2022, + sourceType: 'commonjs', + globals: { + require: 'readonly', + module: 'readonly', + exports: 'readonly', + __dirname: 'readonly', + __filename: 'readonly', + process: 'readonly', + console: 'readonly', + Buffer: 'readonly', + global: 'readonly', + setTimeout: 'readonly', + clearTimeout: 'readonly', + setInterval: 'readonly', + clearInterval: 'readonly', + }, + }, + plugins: { + prettier: require('eslint-plugin-prettier'), + jest: require('eslint-plugin-jest'), + }, + rules: { + ...require('eslint-plugin-prettier').configs.recommended.rules, + ...require('eslint-plugin-jest').configs.recommended.rules, + }, + }, + { + files: ['**/*.test.js', '**/*.spec.js', 'tests/**/*.js'], + languageOptions: { + globals: { + ...require('eslint-plugin-jest').environments.globals.globals, + }, + }, + }, +]; diff --git a/backend/jest.config.js b/backend/jest.config.js index 839bcc6..6078d38 100644 --- a/backend/jest.config.js +++ b/backend/jest.config.js @@ -1,25 +1,22 @@ module.exports = { - testEnvironment: 'node', - setupFilesAfterEnv: ['/tests/helpers/setup.js'], - testMatch: [ - '/tests/**/*.test.js', - '/tests/**/*.spec.js' - ], - maxWorkers: 1, - collectCoverageFrom: [ - 'routes/**/*.js', - 'models/**/*.js', - 'middleware/**/*.js', - 'services/**/*.js', - '!models/index.js', - '!**/*.test.js', - '!**/*.spec.js' - ], - coverageDirectory: 'coverage', - coverageReporters: ['text', 'lcov', 'html'], - verbose: true, - forceExit: true, - clearMocks: true, - resetMocks: true, - restoreMocks: true -}; \ No newline at end of file + testEnvironment: 'node', + setupFilesAfterEnv: ['/tests/helpers/setup.js'], + testMatch: ['/tests/**/*.test.js', '/tests/**/*.spec.js'], + maxWorkers: 1, + collectCoverageFrom: [ + 'routes/**/*.js', + 'models/**/*.js', + 'middleware/**/*.js', + 'services/**/*.js', + '!models/index.js', + '!**/*.test.js', + '!**/*.spec.js', + ], + coverageDirectory: 'coverage', + coverageReporters: ['text', 'lcov', 'html'], + verbose: true, + forceExit: true, + clearMocks: true, + resetMocks: true, + restoreMocks: true, +}; diff --git a/backend/middleware/auth.js b/backend/middleware/auth.js index 62ee1a1..58c5415 100644 --- a/backend/middleware/auth.js +++ b/backend/middleware/auth.js @@ -1,31 +1,31 @@ const { User } = require('../models'); const requireAuth = async (req, res, next) => { - try { - // Skip authentication for health check, login routes, and current_user - const skipPaths = ['/api/health', '/api/login', '/api/current_user']; - if (skipPaths.includes(req.path) || req.originalUrl === '/api/health') { - return next(); - } + try { + // Skip authentication for health check, login routes, and current_user + const skipPaths = ['/api/health', '/api/login', '/api/current_user']; + if (skipPaths.includes(req.path) || req.originalUrl === '/api/health') { + return next(); + } - if (!req.session || !req.session.userId) { - return res.status(401).json({ error: 'Authentication required' }); - } + if (!req.session || !req.session.userId) { + return res.status(401).json({ error: 'Authentication required' }); + } - const user = await User.findByPk(req.session.userId); - if (!user) { - req.session.destroy(); - return res.status(401).json({ error: 'User not found' }); - } + const user = await User.findByPk(req.session.userId); + if (!user) { + req.session.destroy(); + return res.status(401).json({ error: 'User not found' }); + } - req.currentUser = user; - next(); - } catch (error) { - console.error('Authentication error:', error); - res.status(500).json({ error: 'Authentication error' }); - } + req.currentUser = user; + next(); + } catch (error) { + console.error('Authentication error:', error); + res.status(500).json({ error: 'Authentication error' }); + } }; module.exports = { - requireAuth -}; \ No newline at end of file + requireAuth, +}; diff --git a/backend/migrations/20250615000001-create-users.js b/backend/migrations/20250615000001-create-users.js index 79cee6f..2b5e745 100644 --- a/backend/migrations/20250615000001-create-users.js +++ b/backend/migrations/20250615000001-create-users.js @@ -1,61 +1,61 @@ 'use strict'; module.exports = { - async up(queryInterface, Sequelize) { - await queryInterface.createTable('users', { - id: { - allowNull: false, - autoIncrement: true, - primaryKey: true, - type: Sequelize.INTEGER - }, - email: { - type: Sequelize.STRING, - allowNull: false, - unique: true - }, - password: { - type: Sequelize.STRING, - allowNull: false - }, - telegram_bot_token: { - type: Sequelize.STRING, - allowNull: true - }, - telegram_chat_id: { - type: Sequelize.STRING, - allowNull: true - }, - task_summary_enabled: { - type: Sequelize.BOOLEAN, - defaultValue: false - }, - task_summary_frequency: { - type: Sequelize.STRING, - defaultValue: 'daily' - }, - task_summary_last_run: { - type: Sequelize.DATE, - allowNull: true - }, - task_summary_next_run: { - type: Sequelize.DATE, - allowNull: true - }, - created_at: { - allowNull: false, - type: Sequelize.DATE, - defaultValue: Sequelize.literal('CURRENT_TIMESTAMP') - }, - updated_at: { - allowNull: false, - type: Sequelize.DATE, - defaultValue: Sequelize.literal('CURRENT_TIMESTAMP') - } - }); - }, + async up(queryInterface, Sequelize) { + await queryInterface.createTable('users', { + id: { + allowNull: false, + autoIncrement: true, + primaryKey: true, + type: Sequelize.INTEGER, + }, + email: { + type: Sequelize.STRING, + allowNull: false, + unique: true, + }, + password: { + type: Sequelize.STRING, + allowNull: false, + }, + telegram_bot_token: { + type: Sequelize.STRING, + allowNull: true, + }, + telegram_chat_id: { + type: Sequelize.STRING, + allowNull: true, + }, + task_summary_enabled: { + type: Sequelize.BOOLEAN, + defaultValue: false, + }, + task_summary_frequency: { + type: Sequelize.STRING, + defaultValue: 'daily', + }, + task_summary_last_run: { + type: Sequelize.DATE, + allowNull: true, + }, + task_summary_next_run: { + type: Sequelize.DATE, + allowNull: true, + }, + created_at: { + allowNull: false, + type: Sequelize.DATE, + defaultValue: Sequelize.literal('CURRENT_TIMESTAMP'), + }, + updated_at: { + allowNull: false, + type: Sequelize.DATE, + defaultValue: Sequelize.literal('CURRENT_TIMESTAMP'), + }, + }); + }, - async down(queryInterface, Sequelize) { - await queryInterface.dropTable('users'); - } -}; \ No newline at end of file + async down(queryInterface, Sequelize) { + await queryInterface.dropTable('users'); + }, +}; diff --git a/backend/migrations/20250618000001-enhance-recurring-tasks.js b/backend/migrations/20250618000001-enhance-recurring-tasks.js index ca84d85..35c0f91 100644 --- a/backend/migrations/20250618000001-enhance-recurring-tasks.js +++ b/backend/migrations/20250618000001-enhance-recurring-tasks.js @@ -1,47 +1,57 @@ 'use strict'; module.exports = { - async up(queryInterface, Sequelize) { - // Add new fields to support enhanced recurring task functionality - await queryInterface.addColumn('tasks', 'recurrence_weekday', { - type: Sequelize.INTEGER, - allowNull: true, - comment: 'Day of week (0=Sunday, 1=Monday, ..., 6=Saturday) for weekly recurrence' - }); + async up(queryInterface, Sequelize) { + // Add new fields to support enhanced recurring task functionality + await queryInterface.addColumn('tasks', 'recurrence_weekday', { + type: Sequelize.INTEGER, + allowNull: true, + comment: + 'Day of week (0=Sunday, 1=Monday, ..., 6=Saturday) for weekly recurrence', + }); - await queryInterface.addColumn('tasks', 'recurrence_month_day', { - type: Sequelize.INTEGER, - allowNull: true, - comment: 'Day of month (1-31) for monthly recurrence, -1 for last day' - }); + await queryInterface.addColumn('tasks', 'recurrence_month_day', { + type: Sequelize.INTEGER, + allowNull: true, + comment: + 'Day of month (1-31) for monthly recurrence, -1 for last day', + }); - await queryInterface.addColumn('tasks', 'recurrence_week_of_month', { - type: Sequelize.INTEGER, - allowNull: true, - comment: 'Week of month (1-5) for monthly weekday recurrence' - }); + await queryInterface.addColumn('tasks', 'recurrence_week_of_month', { + type: Sequelize.INTEGER, + allowNull: true, + comment: 'Week of month (1-5) for monthly weekday recurrence', + }); - await queryInterface.addColumn('tasks', 'completion_based', { - type: Sequelize.BOOLEAN, - allowNull: false, - defaultValue: false, - comment: 'Whether recurrence is based on completion date (true) or due date (false)' - }); + await queryInterface.addColumn('tasks', 'completion_based', { + type: Sequelize.BOOLEAN, + allowNull: false, + defaultValue: false, + comment: + 'Whether recurrence is based on completion date (true) or due date (false)', + }); - // Add index for efficient recurring task queries - await queryInterface.addIndex('tasks', ['recurrence_type', 'last_generated_date'], { - name: 'idx_tasks_recurrence_lookup' - }); - }, + // Add index for efficient recurring task queries + await queryInterface.addIndex( + 'tasks', + ['recurrence_type', 'last_generated_date'], + { + name: 'idx_tasks_recurrence_lookup', + } + ); + }, - async down(queryInterface, Sequelize) { - // Remove the added columns - await queryInterface.removeColumn('tasks', 'recurrence_weekday'); - await queryInterface.removeColumn('tasks', 'recurrence_month_day'); - await queryInterface.removeColumn('tasks', 'recurrence_week_of_month'); - await queryInterface.removeColumn('tasks', 'completion_based'); - - // Remove the index - await queryInterface.removeIndex('tasks', 'idx_tasks_recurrence_lookup'); - } -}; \ No newline at end of file + async down(queryInterface, Sequelize) { + // Remove the added columns + await queryInterface.removeColumn('tasks', 'recurrence_weekday'); + await queryInterface.removeColumn('tasks', 'recurrence_month_day'); + await queryInterface.removeColumn('tasks', 'recurrence_week_of_month'); + await queryInterface.removeColumn('tasks', 'completion_based'); + + // Remove the index + await queryInterface.removeIndex( + 'tasks', + 'idx_tasks_recurrence_lookup' + ); + }, +}; diff --git a/backend/migrations/20250619000001-add-recurring-parent-id.js b/backend/migrations/20250619000001-add-recurring-parent-id.js index bac2db7..a12290d 100644 --- a/backend/migrations/20250619000001-add-recurring-parent-id.js +++ b/backend/migrations/20250619000001-add-recurring-parent-id.js @@ -1,24 +1,24 @@ 'use strict'; module.exports = { - up: async (queryInterface, Sequelize) => { - await queryInterface.addColumn('tasks', 'recurring_parent_id', { - type: Sequelize.INTEGER, - allowNull: true, - references: { - model: 'tasks', - key: 'id' - }, - onUpdate: 'CASCADE', - onDelete: 'SET NULL' - }); + up: async (queryInterface, Sequelize) => { + await queryInterface.addColumn('tasks', 'recurring_parent_id', { + type: Sequelize.INTEGER, + allowNull: true, + references: { + model: 'tasks', + key: 'id', + }, + onUpdate: 'CASCADE', + onDelete: 'SET NULL', + }); - // Add index for performance - await queryInterface.addIndex('tasks', ['recurring_parent_id']); - }, + // Add index for performance + await queryInterface.addIndex('tasks', ['recurring_parent_id']); + }, - down: async (queryInterface, Sequelize) => { - await queryInterface.removeIndex('tasks', ['recurring_parent_id']); - await queryInterface.removeColumn('tasks', 'recurring_parent_id'); - } -}; \ No newline at end of file + down: async (queryInterface, Sequelize) => { + await queryInterface.removeIndex('tasks', ['recurring_parent_id']); + await queryInterface.removeColumn('tasks', 'recurring_parent_id'); + }, +}; diff --git a/backend/migrations/20250619000002-add-project-image-url.js b/backend/migrations/20250619000002-add-project-image-url.js index 3c6f8fe..9d59fe5 100644 --- a/backend/migrations/20250619000002-add-project-image-url.js +++ b/backend/migrations/20250619000002-add-project-image-url.js @@ -1,14 +1,14 @@ 'use strict'; module.exports = { - up: async (queryInterface, Sequelize) => { - await queryInterface.addColumn('projects', 'image_url', { - type: Sequelize.TEXT, - allowNull: true - }); - }, + up: async (queryInterface, Sequelize) => { + await queryInterface.addColumn('projects', 'image_url', { + type: Sequelize.TEXT, + allowNull: true, + }); + }, - down: async (queryInterface, Sequelize) => { - await queryInterface.removeColumn('projects', 'image_url'); - } -}; \ No newline at end of file + down: async (queryInterface, Sequelize) => { + await queryInterface.removeColumn('projects', 'image_url'); + }, +}; diff --git a/backend/migrations/20250620000001-add-task-intelligence-enabled.js b/backend/migrations/20250620000001-add-task-intelligence-enabled.js index 0652eb2..d867a94 100644 --- a/backend/migrations/20250620000001-add-task-intelligence-enabled.js +++ b/backend/migrations/20250620000001-add-task-intelligence-enabled.js @@ -1,15 +1,15 @@ 'use strict'; module.exports = { - up: async (queryInterface, Sequelize) => { - await queryInterface.addColumn('users', 'task_intelligence_enabled', { - type: Sequelize.BOOLEAN, - allowNull: false, - defaultValue: true - }); - }, + up: async (queryInterface, Sequelize) => { + await queryInterface.addColumn('users', 'task_intelligence_enabled', { + type: Sequelize.BOOLEAN, + allowNull: false, + defaultValue: true, + }); + }, - down: async (queryInterface, Sequelize) => { - await queryInterface.removeColumn('users', 'task_intelligence_enabled'); - } -}; \ No newline at end of file + down: async (queryInterface, Sequelize) => { + await queryInterface.removeColumn('users', 'task_intelligence_enabled'); + }, +}; diff --git a/backend/migrations/20250620000002-add-auto-suggest-next-actions-enabled.js b/backend/migrations/20250620000002-add-auto-suggest-next-actions-enabled.js index ac8bec9..67c3d25 100644 --- a/backend/migrations/20250620000002-add-auto-suggest-next-actions-enabled.js +++ b/backend/migrations/20250620000002-add-auto-suggest-next-actions-enabled.js @@ -1,15 +1,22 @@ 'use strict'; module.exports = { - up: async (queryInterface, Sequelize) => { - await queryInterface.addColumn('users', 'auto_suggest_next_actions_enabled', { - type: Sequelize.BOOLEAN, - allowNull: false, - defaultValue: false - }); - }, + up: async (queryInterface, Sequelize) => { + await queryInterface.addColumn( + 'users', + 'auto_suggest_next_actions_enabled', + { + type: Sequelize.BOOLEAN, + allowNull: false, + defaultValue: false, + } + ); + }, - down: async (queryInterface, Sequelize) => { - await queryInterface.removeColumn('users', 'auto_suggest_next_actions_enabled'); - } -}; \ No newline at end of file + down: async (queryInterface, Sequelize) => { + await queryInterface.removeColumn( + 'users', + 'auto_suggest_next_actions_enabled' + ); + }, +}; diff --git a/backend/migrations/20250621221841-add-completed-at-to-tasks.js b/backend/migrations/20250621221841-add-completed-at-to-tasks.js index 6ec3292..00e420f 100644 --- a/backend/migrations/20250621221841-add-completed-at-to-tasks.js +++ b/backend/migrations/20250621221841-add-completed-at-to-tasks.js @@ -1,22 +1,22 @@ 'use strict'; module.exports = { - async up(queryInterface, Sequelize) { - // Add completed_at column to tasks table - await queryInterface.addColumn('tasks', 'completed_at', { - type: Sequelize.DATE, - allowNull: true - }); - - // Add an index for better query performance - await queryInterface.addIndex('tasks', ['completed_at']); - }, + async up(queryInterface, Sequelize) { + // Add completed_at column to tasks table + await queryInterface.addColumn('tasks', 'completed_at', { + type: Sequelize.DATE, + allowNull: true, + }); - async down(queryInterface, Sequelize) { - // Remove the index first - await queryInterface.removeIndex('tasks', ['completed_at']); - - // Remove the completed_at column - await queryInterface.removeColumn('tasks', 'completed_at'); - } -}; \ No newline at end of file + // Add an index for better query performance + await queryInterface.addIndex('tasks', ['completed_at']); + }, + + async down(queryInterface, Sequelize) { + // Remove the index first + await queryInterface.removeIndex('tasks', ['completed_at']); + + // Remove the completed_at column + await queryInterface.removeColumn('tasks', 'completed_at'); + }, +}; diff --git a/backend/migrations/20250621223000-create-calendar-tokens.js b/backend/migrations/20250621223000-create-calendar-tokens.js index 0b841cc..691eae3 100644 --- a/backend/migrations/20250621223000-create-calendar-tokens.js +++ b/backend/migrations/20250621223000-create-calendar-tokens.js @@ -1,79 +1,79 @@ 'use strict'; module.exports = { - up: async (queryInterface, Sequelize) => { - await queryInterface.createTable('calendar_tokens', { - id: { - type: Sequelize.INTEGER, - primaryKey: true, - autoIncrement: true, - allowNull: false - }, - user_id: { - type: Sequelize.INTEGER, - allowNull: false, - references: { - model: 'users', - key: 'id' - }, - onDelete: 'CASCADE' - }, - provider: { - type: Sequelize.STRING, - allowNull: false, - defaultValue: 'google' - }, - access_token: { - type: Sequelize.TEXT, - allowNull: false - }, - refresh_token: { - type: Sequelize.TEXT, - allowNull: true - }, - token_type: { - type: Sequelize.STRING, - defaultValue: 'Bearer' - }, - expires_at: { - type: Sequelize.DATE, - allowNull: true - }, - scope: { - type: Sequelize.TEXT, - allowNull: true - }, - connected_email: { - type: Sequelize.STRING, - allowNull: true - }, - created_at: { - type: Sequelize.DATE, - allowNull: false, - defaultValue: Sequelize.literal('CURRENT_TIMESTAMP') - }, - updated_at: { - type: Sequelize.DATE, - allowNull: false, - defaultValue: Sequelize.literal('CURRENT_TIMESTAMP') - } - }); + up: async (queryInterface, Sequelize) => { + await queryInterface.createTable('calendar_tokens', { + id: { + type: Sequelize.INTEGER, + primaryKey: true, + autoIncrement: true, + allowNull: false, + }, + user_id: { + type: Sequelize.INTEGER, + allowNull: false, + references: { + model: 'users', + key: 'id', + }, + onDelete: 'CASCADE', + }, + provider: { + type: Sequelize.STRING, + allowNull: false, + defaultValue: 'google', + }, + access_token: { + type: Sequelize.TEXT, + allowNull: false, + }, + refresh_token: { + type: Sequelize.TEXT, + allowNull: true, + }, + token_type: { + type: Sequelize.STRING, + defaultValue: 'Bearer', + }, + expires_at: { + type: Sequelize.DATE, + allowNull: true, + }, + scope: { + type: Sequelize.TEXT, + allowNull: true, + }, + connected_email: { + type: Sequelize.STRING, + allowNull: true, + }, + created_at: { + type: Sequelize.DATE, + allowNull: false, + defaultValue: Sequelize.literal('CURRENT_TIMESTAMP'), + }, + updated_at: { + type: Sequelize.DATE, + allowNull: false, + defaultValue: Sequelize.literal('CURRENT_TIMESTAMP'), + }, + }); - // Add unique index for user_id + provider combination - await queryInterface.addIndex('calendar_tokens', { - fields: ['user_id', 'provider'], - unique: true, - name: 'calendar_tokens_user_provider_unique' - }); + // Add unique index for user_id + provider combination + await queryInterface.addIndex('calendar_tokens', { + fields: ['user_id', 'provider'], + unique: true, + name: 'calendar_tokens_user_provider_unique', + }); - // Add index for faster lookups by user_id - await queryInterface.addIndex('calendar_tokens', { - fields: ['user_id'], - name: 'calendar_tokens_user_id_index' - }); - }, + // Add index for faster lookups by user_id + await queryInterface.addIndex('calendar_tokens', { + fields: ['user_id'], + name: 'calendar_tokens_user_id_index', + }); + }, - down: async (queryInterface, Sequelize) => { - await queryInterface.dropTable('calendar_tokens'); - } -}; \ No newline at end of file + down: async (queryInterface, Sequelize) => { + await queryInterface.dropTable('calendar_tokens'); + }, +}; diff --git a/backend/migrations/20250622000001-create-task-events.js b/backend/migrations/20250622000001-create-task-events.js index 601879d..1d964be 100644 --- a/backend/migrations/20250622000001-create-task-events.js +++ b/backend/migrations/20250622000001-create-task-events.js @@ -1,88 +1,94 @@ 'use strict'; module.exports = { - async up(queryInterface, Sequelize) { - // Create task_events table - await queryInterface.createTable('task_events', { - id: { - type: Sequelize.INTEGER, - primaryKey: true, - autoIncrement: true, - allowNull: false - }, - task_id: { - type: Sequelize.INTEGER, - allowNull: false, - references: { - model: 'tasks', - key: 'id' - }, - onUpdate: 'CASCADE', - onDelete: 'CASCADE' - }, - user_id: { - type: Sequelize.INTEGER, - allowNull: false, - references: { - model: 'users', - key: 'id' - }, - onUpdate: 'CASCADE', - onDelete: 'CASCADE' - }, - event_type: { - type: Sequelize.STRING, - allowNull: false, - // Common event types: 'created', 'status_changed', 'priority_changed', - // 'due_date_changed', 'project_changed', 'name_changed', 'description_changed', - // 'completed', 'archived', 'deleted', 'restored' - }, - old_value: { - type: Sequelize.TEXT, - allowNull: true, - // JSON string of the old value(s) - for tracking what changed from - }, - new_value: { - type: Sequelize.TEXT, - allowNull: true, - // JSON string of the new value(s) - for tracking what changed to - }, - field_name: { - type: Sequelize.STRING, - allowNull: true, - // The name of the field that was changed (status, priority, due_date, etc.) - }, - metadata: { - type: Sequelize.TEXT, - allowNull: true, - // Additional context as JSON string (e.g., source of change: 'web', 'api', 'telegram') - }, - created_at: { - type: Sequelize.DATE, - allowNull: false, - defaultValue: Sequelize.literal('CURRENT_TIMESTAMP') - } - }); + async up(queryInterface, Sequelize) { + // Create task_events table + await queryInterface.createTable('task_events', { + id: { + type: Sequelize.INTEGER, + primaryKey: true, + autoIncrement: true, + allowNull: false, + }, + task_id: { + type: Sequelize.INTEGER, + allowNull: false, + references: { + model: 'tasks', + key: 'id', + }, + onUpdate: 'CASCADE', + onDelete: 'CASCADE', + }, + user_id: { + type: Sequelize.INTEGER, + allowNull: false, + references: { + model: 'users', + key: 'id', + }, + onUpdate: 'CASCADE', + onDelete: 'CASCADE', + }, + event_type: { + type: Sequelize.STRING, + allowNull: false, + // Common event types: 'created', 'status_changed', 'priority_changed', + // 'due_date_changed', 'project_changed', 'name_changed', 'description_changed', + // 'completed', 'archived', 'deleted', 'restored' + }, + old_value: { + type: Sequelize.TEXT, + allowNull: true, + // JSON string of the old value(s) - for tracking what changed from + }, + new_value: { + type: Sequelize.TEXT, + allowNull: true, + // JSON string of the new value(s) - for tracking what changed to + }, + field_name: { + type: Sequelize.STRING, + allowNull: true, + // The name of the field that was changed (status, priority, due_date, etc.) + }, + metadata: { + type: Sequelize.TEXT, + allowNull: true, + // Additional context as JSON string (e.g., source of change: 'web', 'api', 'telegram') + }, + created_at: { + type: Sequelize.DATE, + allowNull: false, + defaultValue: Sequelize.literal('CURRENT_TIMESTAMP'), + }, + }); - // Add indexes for better query performance - await queryInterface.addIndex('task_events', ['task_id']); - await queryInterface.addIndex('task_events', ['user_id']); - await queryInterface.addIndex('task_events', ['event_type']); - await queryInterface.addIndex('task_events', ['created_at']); - await queryInterface.addIndex('task_events', ['task_id', 'event_type']); - await queryInterface.addIndex('task_events', ['task_id', 'created_at']); - }, + // Add indexes for better query performance + await queryInterface.addIndex('task_events', ['task_id']); + await queryInterface.addIndex('task_events', ['user_id']); + await queryInterface.addIndex('task_events', ['event_type']); + await queryInterface.addIndex('task_events', ['created_at']); + await queryInterface.addIndex('task_events', ['task_id', 'event_type']); + await queryInterface.addIndex('task_events', ['task_id', 'created_at']); + }, - async down(queryInterface, Sequelize) { - // Remove indexes first - await queryInterface.removeIndex('task_events', ['task_id', 'created_at']); - await queryInterface.removeIndex('task_events', ['task_id', 'event_type']); - await queryInterface.removeIndex('task_events', ['created_at']); - await queryInterface.removeIndex('task_events', ['event_type']); - await queryInterface.removeIndex('task_events', ['user_id']); - await queryInterface.removeIndex('task_events', ['task_id']); - - // Drop the table - await queryInterface.dropTable('task_events'); - } -}; \ No newline at end of file + async down(queryInterface, Sequelize) { + // Remove indexes first + await queryInterface.removeIndex('task_events', [ + 'task_id', + 'created_at', + ]); + await queryInterface.removeIndex('task_events', [ + 'task_id', + 'event_type', + ]); + await queryInterface.removeIndex('task_events', ['created_at']); + await queryInterface.removeIndex('task_events', ['event_type']); + await queryInterface.removeIndex('task_events', ['user_id']); + await queryInterface.removeIndex('task_events', ['task_id']); + + // Drop the table + await queryInterface.dropTable('task_events'); + }, +}; diff --git a/backend/migrations/20250622053925-add-pomodoro-enabled-to-users.js b/backend/migrations/20250622053925-add-pomodoro-enabled-to-users.js index 40531f3..c3cb2a0 100644 --- a/backend/migrations/20250622053925-add-pomodoro-enabled-to-users.js +++ b/backend/migrations/20250622053925-add-pomodoro-enabled-to-users.js @@ -2,15 +2,15 @@ /** @type {import('sequelize-cli').Migration} */ module.exports = { - async up (queryInterface, Sequelize) { - await queryInterface.addColumn('users', 'pomodoro_enabled', { - type: Sequelize.BOOLEAN, - allowNull: false, - defaultValue: true - }); - }, + async up(queryInterface, Sequelize) { + await queryInterface.addColumn('users', 'pomodoro_enabled', { + type: Sequelize.BOOLEAN, + allowNull: false, + defaultValue: true, + }); + }, - async down (queryInterface, Sequelize) { - await queryInterface.removeColumn('users', 'pomodoro_enabled'); - } + async down(queryInterface, Sequelize) { + await queryInterface.removeColumn('users', 'pomodoro_enabled'); + }, }; diff --git a/backend/migrations/20250623000001-add-uuid-to-tasks.js b/backend/migrations/20250623000001-add-uuid-to-tasks.js index 377f098..2ab5bb2 100644 --- a/backend/migrations/20250623000001-add-uuid-to-tasks.js +++ b/backend/migrations/20250623000001-add-uuid-to-tasks.js @@ -3,39 +3,39 @@ const { v4: uuidv4 } = require('uuid'); module.exports = { - async up(queryInterface, Sequelize) { - // Add UUID column to tasks table (without unique constraint initially) - await queryInterface.addColumn('tasks', 'uuid', { - type: Sequelize.UUID, - allowNull: true - }); + async up(queryInterface, Sequelize) { + // Add UUID column to tasks table (without unique constraint initially) + await queryInterface.addColumn('tasks', 'uuid', { + type: Sequelize.UUID, + allowNull: true, + }); - // Backfill existing tasks with UUIDs - const tasks = await queryInterface.sequelize.query( - 'SELECT id FROM tasks WHERE uuid IS NULL', - { type: Sequelize.QueryTypes.SELECT } - ); + // Backfill existing tasks with UUIDs + const tasks = await queryInterface.sequelize.query( + 'SELECT id FROM tasks WHERE uuid IS NULL', + { type: Sequelize.QueryTypes.SELECT } + ); - for (const task of tasks) { - const uuid = uuidv4(); - await queryInterface.sequelize.query( - 'UPDATE tasks SET uuid = ? WHERE id = ?', - { replacements: [uuid, task.id] } - ); - } + for (const task of tasks) { + const uuid = uuidv4(); + await queryInterface.sequelize.query( + 'UPDATE tasks SET uuid = ? WHERE id = ?', + { replacements: [uuid, task.id] } + ); + } - // Add unique index for UUID - await queryInterface.addIndex('tasks', ['uuid'], { - unique: true, - name: 'tasks_uuid_unique' - }); - }, + // Add unique index for UUID + await queryInterface.addIndex('tasks', ['uuid'], { + unique: true, + name: 'tasks_uuid_unique', + }); + }, - async down(queryInterface, Sequelize) { - // Remove index first - await queryInterface.removeIndex('tasks', 'tasks_uuid_unique'); - - // Remove UUID column - await queryInterface.removeColumn('tasks', 'uuid'); - } -}; \ No newline at end of file + async down(queryInterface, Sequelize) { + // Remove index first + await queryInterface.removeIndex('tasks', 'tasks_uuid_unique'); + + // Remove UUID column + await queryInterface.removeColumn('tasks', 'uuid'); + }, +}; diff --git a/backend/migrations/20250623000003-create-notes-tags-table.js b/backend/migrations/20250623000003-create-notes-tags-table.js index 73e7491..b5716ca 100644 --- a/backend/migrations/20250623000003-create-notes-tags-table.js +++ b/backend/migrations/20250623000003-create-notes-tags-table.js @@ -1,48 +1,48 @@ 'use strict'; module.exports = { - async up(queryInterface, Sequelize) { - // Check if notes_tags table exists - const tables = await queryInterface.showAllTables(); - if (!tables.includes('notes_tags')) { - await queryInterface.createTable('notes_tags', { - note_id: { - type: Sequelize.INTEGER, - references: { - model: 'notes', - key: 'id' - }, - onDelete: 'CASCADE' - }, - tag_id: { - type: Sequelize.INTEGER, - references: { - model: 'tags', - key: 'id' - }, - onDelete: 'CASCADE' - }, - created_at: { - allowNull: false, - type: Sequelize.DATE, - defaultValue: Sequelize.literal('CURRENT_TIMESTAMP') - }, - updated_at: { - allowNull: false, - type: Sequelize.DATE, - defaultValue: Sequelize.literal('CURRENT_TIMESTAMP') + async up(queryInterface, Sequelize) { + // Check if notes_tags table exists + const tables = await queryInterface.showAllTables(); + if (!tables.includes('notes_tags')) { + await queryInterface.createTable('notes_tags', { + note_id: { + type: Sequelize.INTEGER, + references: { + model: 'notes', + key: 'id', + }, + onDelete: 'CASCADE', + }, + tag_id: { + type: Sequelize.INTEGER, + references: { + model: 'tags', + key: 'id', + }, + onDelete: 'CASCADE', + }, + created_at: { + allowNull: false, + type: Sequelize.DATE, + defaultValue: Sequelize.literal('CURRENT_TIMESTAMP'), + }, + updated_at: { + allowNull: false, + type: Sequelize.DATE, + defaultValue: Sequelize.literal('CURRENT_TIMESTAMP'), + }, + }); + + // Add unique index + await queryInterface.addIndex('notes_tags', ['note_id', 'tag_id'], { + unique: true, + name: 'notes_tags_unique_idx', + }); } - }); + }, - // Add unique index - await queryInterface.addIndex('notes_tags', ['note_id', 'tag_id'], { - unique: true, - name: 'notes_tags_unique_idx' - }); - } - }, - - async down(queryInterface, Sequelize) { - await queryInterface.dropTable('notes_tags'); - } -}; \ No newline at end of file + async down(queryInterface, Sequelize) { + await queryInterface.dropTable('notes_tags'); + }, +}; diff --git a/backend/migrations/20250623000004-add-timestamps-to-notes-tags.js b/backend/migrations/20250623000004-add-timestamps-to-notes-tags.js index a2ed701..86b507e 100644 --- a/backend/migrations/20250623000004-add-timestamps-to-notes-tags.js +++ b/backend/migrations/20250623000004-add-timestamps-to-notes-tags.js @@ -1,29 +1,29 @@ 'use strict'; module.exports = { - async up(queryInterface, Sequelize) { - // Add created_at and updated_at columns to notes_tags table - try { - await queryInterface.addColumn('notes_tags', 'created_at', { - type: Sequelize.DATE, - allowNull: false, - defaultValue: Sequelize.literal('CURRENT_TIMESTAMP') - }); - - await queryInterface.addColumn('notes_tags', 'updated_at', { - type: Sequelize.DATE, - allowNull: false, - defaultValue: Sequelize.literal('CURRENT_TIMESTAMP') - }); - - console.log('Successfully added timestamps to notes_tags table'); - } catch (error) { - console.error('Error adding timestamps to notes_tags:', error); - } - }, + async up(queryInterface, Sequelize) { + // Add created_at and updated_at columns to notes_tags table + try { + await queryInterface.addColumn('notes_tags', 'created_at', { + type: Sequelize.DATE, + allowNull: false, + defaultValue: Sequelize.literal('CURRENT_TIMESTAMP'), + }); - async down(queryInterface, Sequelize) { - await queryInterface.removeColumn('notes_tags', 'created_at'); - await queryInterface.removeColumn('notes_tags', 'updated_at'); - } -}; \ No newline at end of file + await queryInterface.addColumn('notes_tags', 'updated_at', { + type: Sequelize.DATE, + allowNull: false, + defaultValue: Sequelize.literal('CURRENT_TIMESTAMP'), + }); + + console.log('Successfully added timestamps to notes_tags table'); + } catch (error) { + console.error('Error adding timestamps to notes_tags:', error); + } + }, + + async down(queryInterface, Sequelize) { + await queryInterface.removeColumn('notes_tags', 'created_at'); + await queryInterface.removeColumn('notes_tags', 'updated_at'); + }, +}; diff --git a/backend/migrations/20250623000005-add-timestamps-to-projects-tags.js b/backend/migrations/20250623000005-add-timestamps-to-projects-tags.js index eed7d0e..f0bb039 100644 --- a/backend/migrations/20250623000005-add-timestamps-to-projects-tags.js +++ b/backend/migrations/20250623000005-add-timestamps-to-projects-tags.js @@ -1,82 +1,83 @@ 'use strict'; module.exports = { - async up(queryInterface, Sequelize) { - // Create the projects_tags table if it doesn't exist - const tableExists = await queryInterface.showAllTables() - .then(tables => tables.includes('projects_tags')); - - if (!tableExists) { - await queryInterface.createTable('projects_tags', { - project_id: { - type: Sequelize.INTEGER, - allowNull: false, - references: { - model: 'projects', - key: 'id' - }, - onUpdate: 'CASCADE', - onDelete: 'CASCADE' - }, - tag_id: { - type: Sequelize.INTEGER, - allowNull: false, - references: { - model: 'tags', - key: 'id' - }, - onUpdate: 'CASCADE', - onDelete: 'CASCADE' - }, - created_at: { - type: Sequelize.DATE, - allowNull: false, - defaultValue: Sequelize.literal('CURRENT_TIMESTAMP') - }, - updated_at: { - type: Sequelize.DATE, - allowNull: false, - defaultValue: Sequelize.literal('CURRENT_TIMESTAMP') - } - }); - - // Add composite primary key - await queryInterface.addConstraint('projects_tags', { - fields: ['project_id', 'tag_id'], - type: 'primary key', - name: 'projects_tags_pkey' - }); - } else { - // Add timestamps if table exists but doesn't have them - try { - await queryInterface.addColumn('projects_tags', 'created_at', { - type: Sequelize.DATE, - allowNull: false, - defaultValue: Sequelize.literal('CURRENT_TIMESTAMP') - }); - } catch (error) { - // Column might already exist - } - - try { - await queryInterface.addColumn('projects_tags', 'updated_at', { - type: Sequelize.DATE, - allowNull: false, - defaultValue: Sequelize.literal('CURRENT_TIMESTAMP') - }); - } catch (error) { - // Column might already exist - } - } - }, + async up(queryInterface, Sequelize) { + // Create the projects_tags table if it doesn't exist + const tableExists = await queryInterface + .showAllTables() + .then((tables) => tables.includes('projects_tags')); - async down(queryInterface, Sequelize) { - // Remove timestamps or drop table if needed - try { - await queryInterface.removeColumn('projects_tags', 'created_at'); - await queryInterface.removeColumn('projects_tags', 'updated_at'); - } catch (error) { - // Columns might not exist - } - } -}; \ No newline at end of file + if (!tableExists) { + await queryInterface.createTable('projects_tags', { + project_id: { + type: Sequelize.INTEGER, + allowNull: false, + references: { + model: 'projects', + key: 'id', + }, + onUpdate: 'CASCADE', + onDelete: 'CASCADE', + }, + tag_id: { + type: Sequelize.INTEGER, + allowNull: false, + references: { + model: 'tags', + key: 'id', + }, + onUpdate: 'CASCADE', + onDelete: 'CASCADE', + }, + created_at: { + type: Sequelize.DATE, + allowNull: false, + defaultValue: Sequelize.literal('CURRENT_TIMESTAMP'), + }, + updated_at: { + type: Sequelize.DATE, + allowNull: false, + defaultValue: Sequelize.literal('CURRENT_TIMESTAMP'), + }, + }); + + // Add composite primary key + await queryInterface.addConstraint('projects_tags', { + fields: ['project_id', 'tag_id'], + type: 'primary key', + name: 'projects_tags_pkey', + }); + } else { + // Add timestamps if table exists but doesn't have them + try { + await queryInterface.addColumn('projects_tags', 'created_at', { + type: Sequelize.DATE, + allowNull: false, + defaultValue: Sequelize.literal('CURRENT_TIMESTAMP'), + }); + } catch (error) { + // Column might already exist + } + + try { + await queryInterface.addColumn('projects_tags', 'updated_at', { + type: Sequelize.DATE, + allowNull: false, + defaultValue: Sequelize.literal('CURRENT_TIMESTAMP'), + }); + } catch (error) { + // Column might already exist + } + } + }, + + async down(queryInterface, Sequelize) { + // Remove timestamps or drop table if needed + try { + await queryInterface.removeColumn('projects_tags', 'created_at'); + await queryInterface.removeColumn('projects_tags', 'updated_at'); + } catch (error) { + // Columns might not exist + } + }, +}; diff --git a/backend/models/area.js b/backend/models/area.js index 61bf11c..786f8c7 100644 --- a/backend/models/area.js +++ b/backend/models/area.js @@ -1,36 +1,40 @@ const { DataTypes } = require('sequelize'); module.exports = (sequelize) => { - const Area = sequelize.define('Area', { - id: { - type: DataTypes.INTEGER, - primaryKey: true, - autoIncrement: true - }, - name: { - type: DataTypes.STRING, - allowNull: false - }, - description: { - type: DataTypes.STRING, - allowNull: true - }, - user_id: { - type: DataTypes.INTEGER, - allowNull: false, - references: { - model: 'users', - key: 'id' - } - } - }, { - tableName: 'areas', - indexes: [ - { - fields: ['user_id'] - } - ] - }); + const Area = sequelize.define( + 'Area', + { + id: { + type: DataTypes.INTEGER, + primaryKey: true, + autoIncrement: true, + }, + name: { + type: DataTypes.STRING, + allowNull: false, + }, + description: { + type: DataTypes.STRING, + allowNull: true, + }, + user_id: { + type: DataTypes.INTEGER, + allowNull: false, + references: { + model: 'users', + key: 'id', + }, + }, + }, + { + tableName: 'areas', + indexes: [ + { + fields: ['user_id'], + }, + ], + } + ); - return Area; -}; \ No newline at end of file + return Area; +}; diff --git a/backend/models/calendar_token.js b/backend/models/calendar_token.js index 5ec07d2..74a88c9 100644 --- a/backend/models/calendar_token.js +++ b/backend/models/calendar_token.js @@ -1,77 +1,81 @@ const { DataTypes } = require('sequelize'); const sequelize = require('../config/database'); -const CalendarToken = sequelize.define('CalendarToken', { - id: { - type: DataTypes.INTEGER, - primaryKey: true, - autoIncrement: true - }, - user_id: { - type: DataTypes.INTEGER, - allowNull: false, - references: { - model: 'Users', - key: 'id' - }, - onDelete: 'CASCADE' - }, - provider: { - type: DataTypes.STRING, - allowNull: false, - defaultValue: 'google' - }, - access_token: { - type: DataTypes.TEXT, - allowNull: false - }, - refresh_token: { - type: DataTypes.TEXT, - allowNull: true - }, - token_type: { - type: DataTypes.STRING, - defaultValue: 'Bearer' - }, - expires_at: { - type: DataTypes.DATE, - allowNull: true - }, - scope: { - type: DataTypes.TEXT, - allowNull: true - }, - connected_email: { - type: DataTypes.STRING, - allowNull: true - }, - created_at: { - type: DataTypes.DATE, - defaultValue: DataTypes.NOW - }, - updated_at: { - type: DataTypes.DATE, - defaultValue: DataTypes.NOW - } -}, { - tableName: 'calendar_tokens', - timestamps: true, - createdAt: 'created_at', - updatedAt: 'updated_at', - indexes: [ +const CalendarToken = sequelize.define( + 'CalendarToken', { - unique: true, - fields: ['user_id', 'provider'] + id: { + type: DataTypes.INTEGER, + primaryKey: true, + autoIncrement: true, + }, + user_id: { + type: DataTypes.INTEGER, + allowNull: false, + references: { + model: 'Users', + key: 'id', + }, + onDelete: 'CASCADE', + }, + provider: { + type: DataTypes.STRING, + allowNull: false, + defaultValue: 'google', + }, + access_token: { + type: DataTypes.TEXT, + allowNull: false, + }, + refresh_token: { + type: DataTypes.TEXT, + allowNull: true, + }, + token_type: { + type: DataTypes.STRING, + defaultValue: 'Bearer', + }, + expires_at: { + type: DataTypes.DATE, + allowNull: true, + }, + scope: { + type: DataTypes.TEXT, + allowNull: true, + }, + connected_email: { + type: DataTypes.STRING, + allowNull: true, + }, + created_at: { + type: DataTypes.DATE, + defaultValue: DataTypes.NOW, + }, + updated_at: { + type: DataTypes.DATE, + defaultValue: DataTypes.NOW, + }, + }, + { + tableName: 'calendar_tokens', + timestamps: true, + createdAt: 'created_at', + updatedAt: 'updated_at', + indexes: [ + { + unique: true, + fields: ['user_id', 'provider'], + }, + ], } - ] -}); +); // Associations -CalendarToken.associate = function(models) { - CalendarToken.belongsTo(models.User, { - foreignKey: 'user_id', - as: 'user' - }); +CalendarToken.associate = function (models) { + CalendarToken.belongsTo(models.User, { + foreignKey: 'user_id', + as: 'user', + }); }; -module.exports = CalendarToken; \ No newline at end of file +module.exports = CalendarToken; diff --git a/backend/models/inbox_item.js b/backend/models/inbox_item.js index 6bf854a..ebd587f 100644 --- a/backend/models/inbox_item.js +++ b/backend/models/inbox_item.js @@ -1,42 +1,46 @@ const { DataTypes } = require('sequelize'); module.exports = (sequelize) => { - const InboxItem = sequelize.define('InboxItem', { - id: { - type: DataTypes.INTEGER, - primaryKey: true, - autoIncrement: true - }, - content: { - type: DataTypes.STRING, - allowNull: false - }, - status: { - type: DataTypes.STRING, - allowNull: false, - defaultValue: 'added' - }, - source: { - type: DataTypes.STRING, - allowNull: false, - defaultValue: 'tududi' - }, - user_id: { - type: DataTypes.INTEGER, - allowNull: false, - references: { - model: 'users', - key: 'id' - } - } - }, { - tableName: 'inbox_items', - indexes: [ - { - fields: ['user_id'] - } - ] - }); + const InboxItem = sequelize.define( + 'InboxItem', + { + id: { + type: DataTypes.INTEGER, + primaryKey: true, + autoIncrement: true, + }, + content: { + type: DataTypes.STRING, + allowNull: false, + }, + status: { + type: DataTypes.STRING, + allowNull: false, + defaultValue: 'added', + }, + source: { + type: DataTypes.STRING, + allowNull: false, + defaultValue: 'tududi', + }, + user_id: { + type: DataTypes.INTEGER, + allowNull: false, + references: { + model: 'users', + key: 'id', + }, + }, + }, + { + tableName: 'inbox_items', + indexes: [ + { + fields: ['user_id'], + }, + ], + } + ); - return InboxItem; -}; \ No newline at end of file + return InboxItem; +}; diff --git a/backend/models/index.js b/backend/models/index.js index b736108..b61678a 100644 --- a/backend/models/index.js +++ b/backend/models/index.js @@ -5,35 +5,41 @@ const path = require('path'); let dbConfig; if (process.env.NODE_ENV === 'test') { - // Use temporary file database for tests to allow external script access - const testDbPath = path.join(__dirname, '../db', 'test.sqlite3'); - dbConfig = { - dialect: 'sqlite', - storage: testDbPath, - logging: false, - define: { - timestamps: true, - underscored: true, - createdAt: 'created_at', - updatedAt: 'updated_at' - } - }; + // Use temporary file database for tests to allow external script access + const testDbPath = path.join(__dirname, '../db', 'test.sqlite3'); + dbConfig = { + dialect: 'sqlite', + storage: testDbPath, + logging: false, + define: { + timestamps: true, + underscored: true, + createdAt: 'created_at', + updatedAt: 'updated_at', + }, + }; } else { - const dbPath = process.env.DATABASE_URL - ? process.env.DATABASE_URL.replace('sqlite:///', '') - : path.join(__dirname, '../db', process.env.NODE_ENV === 'production' ? 'production.sqlite3' : 'development.sqlite3'); + const dbPath = process.env.DATABASE_URL + ? process.env.DATABASE_URL.replace('sqlite:///', '') + : path.join( + __dirname, + '../db', + process.env.NODE_ENV === 'production' + ? 'production.sqlite3' + : 'development.sqlite3' + ); - dbConfig = { - dialect: 'sqlite', - storage: dbPath, - logging: process.env.NODE_ENV === 'development' ? console.log : false, - define: { - timestamps: true, - underscored: true, - createdAt: 'created_at', - updatedAt: 'updated_at' - } - }; + dbConfig = { + dialect: 'sqlite', + storage: dbPath, + logging: process.env.NODE_ENV === 'development' ? console.log : false, + define: { + timestamps: true, + underscored: true, + createdAt: 'created_at', + updatedAt: 'updated_at', + }, + }; } const sequelize = new Sequelize(dbConfig); @@ -80,23 +86,47 @@ Task.hasMany(TaskEvent, { foreignKey: 'task_id', as: 'TaskEvents' }); TaskEvent.belongsTo(Task, { foreignKey: 'task_id', as: 'Task' }); // Many-to-many associations -Task.belongsToMany(Tag, { through: 'tasks_tags', foreignKey: 'task_id', otherKey: 'tag_id' }); -Tag.belongsToMany(Task, { through: 'tasks_tags', foreignKey: 'tag_id', otherKey: 'task_id' }); +Task.belongsToMany(Tag, { + through: 'tasks_tags', + foreignKey: 'task_id', + otherKey: 'tag_id', +}); +Tag.belongsToMany(Task, { + through: 'tasks_tags', + foreignKey: 'tag_id', + otherKey: 'task_id', +}); -Note.belongsToMany(Tag, { through: 'notes_tags', foreignKey: 'note_id', otherKey: 'tag_id' }); -Tag.belongsToMany(Note, { through: 'notes_tags', foreignKey: 'tag_id', otherKey: 'note_id' }); +Note.belongsToMany(Tag, { + through: 'notes_tags', + foreignKey: 'note_id', + otherKey: 'tag_id', +}); +Tag.belongsToMany(Note, { + through: 'notes_tags', + foreignKey: 'tag_id', + otherKey: 'note_id', +}); -Project.belongsToMany(Tag, { through: 'projects_tags', foreignKey: 'project_id', otherKey: 'tag_id' }); -Tag.belongsToMany(Project, { through: 'projects_tags', foreignKey: 'tag_id', otherKey: 'project_id' }); +Project.belongsToMany(Tag, { + through: 'projects_tags', + foreignKey: 'project_id', + otherKey: 'tag_id', +}); +Tag.belongsToMany(Project, { + through: 'projects_tags', + foreignKey: 'tag_id', + otherKey: 'project_id', +}); module.exports = { - sequelize, - User, - Area, - Project, - Task, - Tag, - Note, - InboxItem, - TaskEvent -}; \ No newline at end of file + sequelize, + User, + Area, + Project, + Task, + Tag, + Note, + InboxItem, + TaskEvent, +}; diff --git a/backend/models/note.js b/backend/models/note.js index a704a4c..1e2ef25 100644 --- a/backend/models/note.js +++ b/backend/models/note.js @@ -1,47 +1,51 @@ const { DataTypes } = require('sequelize'); module.exports = (sequelize) => { - const Note = sequelize.define('Note', { - id: { - type: DataTypes.INTEGER, - primaryKey: true, - autoIncrement: true - }, - title: { - type: DataTypes.STRING, - allowNull: true - }, - content: { - type: DataTypes.TEXT, - allowNull: true - }, - user_id: { - type: DataTypes.INTEGER, - allowNull: false, - references: { - model: 'users', - key: 'id' - } - }, - project_id: { - type: DataTypes.INTEGER, - allowNull: true, - references: { - model: 'projects', - key: 'id' - } - } - }, { - tableName: 'notes', - indexes: [ - { - fields: ['user_id'] - }, - { - fields: ['project_id'] - } - ] - }); + const Note = sequelize.define( + 'Note', + { + id: { + type: DataTypes.INTEGER, + primaryKey: true, + autoIncrement: true, + }, + title: { + type: DataTypes.STRING, + allowNull: true, + }, + content: { + type: DataTypes.TEXT, + allowNull: true, + }, + user_id: { + type: DataTypes.INTEGER, + allowNull: false, + references: { + model: 'users', + key: 'id', + }, + }, + project_id: { + type: DataTypes.INTEGER, + allowNull: true, + references: { + model: 'projects', + key: 'id', + }, + }, + }, + { + tableName: 'notes', + indexes: [ + { + fields: ['user_id'], + }, + { + fields: ['project_id'], + }, + ], + } + ); - return Note; -}; \ No newline at end of file + return Note; +}; diff --git a/backend/models/project.js b/backend/models/project.js index 66c3b2e..997c883 100644 --- a/backend/models/project.js +++ b/backend/models/project.js @@ -1,73 +1,77 @@ const { DataTypes } = require('sequelize'); module.exports = (sequelize) => { - const Project = sequelize.define('Project', { - id: { - type: DataTypes.INTEGER, - primaryKey: true, - autoIncrement: true - }, - name: { - type: DataTypes.STRING, - allowNull: false - }, - description: { - type: DataTypes.TEXT, - allowNull: true - }, - active: { - type: DataTypes.BOOLEAN, - allowNull: false, - defaultValue: false - }, - pin_to_sidebar: { - type: DataTypes.BOOLEAN, - allowNull: false, - defaultValue: false - }, - priority: { - type: DataTypes.INTEGER, - allowNull: true, - validate: { - min: 0, - max: 2 - } - }, - due_date_at: { - type: DataTypes.DATE, - allowNull: true - }, - user_id: { - type: DataTypes.INTEGER, - allowNull: false, - references: { - model: 'users', - key: 'id' - } - }, - area_id: { - type: DataTypes.INTEGER, - allowNull: true, - references: { - model: 'areas', - key: 'id' - } - }, - image_url: { - type: DataTypes.TEXT, - allowNull: true - } - }, { - tableName: 'projects', - indexes: [ - { - fields: ['user_id'] - }, - { - fields: ['area_id'] - } - ] - }); + const Project = sequelize.define( + 'Project', + { + id: { + type: DataTypes.INTEGER, + primaryKey: true, + autoIncrement: true, + }, + name: { + type: DataTypes.STRING, + allowNull: false, + }, + description: { + type: DataTypes.TEXT, + allowNull: true, + }, + active: { + type: DataTypes.BOOLEAN, + allowNull: false, + defaultValue: false, + }, + pin_to_sidebar: { + type: DataTypes.BOOLEAN, + allowNull: false, + defaultValue: false, + }, + priority: { + type: DataTypes.INTEGER, + allowNull: true, + validate: { + min: 0, + max: 2, + }, + }, + due_date_at: { + type: DataTypes.DATE, + allowNull: true, + }, + user_id: { + type: DataTypes.INTEGER, + allowNull: false, + references: { + model: 'users', + key: 'id', + }, + }, + area_id: { + type: DataTypes.INTEGER, + allowNull: true, + references: { + model: 'areas', + key: 'id', + }, + }, + image_url: { + type: DataTypes.TEXT, + allowNull: true, + }, + }, + { + tableName: 'projects', + indexes: [ + { + fields: ['user_id'], + }, + { + fields: ['area_id'], + }, + ], + } + ); - return Project; -}; \ No newline at end of file + return Project; +}; diff --git a/backend/models/tag.js b/backend/models/tag.js index e4894ff..78b5986 100644 --- a/backend/models/tag.js +++ b/backend/models/tag.js @@ -1,32 +1,36 @@ const { DataTypes } = require('sequelize'); module.exports = (sequelize) => { - const Tag = sequelize.define('Tag', { - id: { - type: DataTypes.INTEGER, - primaryKey: true, - autoIncrement: true - }, - name: { - type: DataTypes.STRING, - allowNull: false - }, - user_id: { - type: DataTypes.INTEGER, - allowNull: false, - references: { - model: 'users', - key: 'id' - } - } - }, { - tableName: 'tags', - indexes: [ - { - fields: ['user_id'] - } - ] - }); + const Tag = sequelize.define( + 'Tag', + { + id: { + type: DataTypes.INTEGER, + primaryKey: true, + autoIncrement: true, + }, + name: { + type: DataTypes.STRING, + allowNull: false, + }, + user_id: { + type: DataTypes.INTEGER, + allowNull: false, + references: { + model: 'users', + key: 'id', + }, + }, + }, + { + tableName: 'tags', + indexes: [ + { + fields: ['user_id'], + }, + ], + } + ); - return Tag; -}; \ No newline at end of file + return Tag; +}; diff --git a/backend/models/task.js b/backend/models/task.js index 50cb9b3..1d5bf2a 100644 --- a/backend/models/task.js +++ b/backend/models/task.js @@ -1,219 +1,231 @@ const { DataTypes } = require('sequelize'); module.exports = (sequelize) => { - const Task = sequelize.define('Task', { - id: { - type: DataTypes.INTEGER, - primaryKey: true, - autoIncrement: true - }, - uuid: { - type: DataTypes.UUID, - allowNull: false, - unique: true, - defaultValue: DataTypes.UUIDV4 - }, - name: { - type: DataTypes.STRING, - allowNull: false - }, - description: { - type: DataTypes.TEXT, - allowNull: true - }, - due_date: { - type: DataTypes.DATE, - allowNull: true - }, - today: { - type: DataTypes.BOOLEAN, - allowNull: false, - defaultValue: false - }, - priority: { - type: DataTypes.INTEGER, - allowNull: true, - defaultValue: 0, - validate: { - min: 0, - max: 2 - } - }, - status: { - type: DataTypes.INTEGER, - allowNull: false, - defaultValue: 0, - validate: { - min: 0, - max: 4 - } - }, - note: { - type: DataTypes.TEXT, - allowNull: true - }, - recurrence_type: { - type: DataTypes.STRING, - allowNull: false, - defaultValue: 'none' - }, - recurrence_interval: { - type: DataTypes.INTEGER, - allowNull: true - }, - recurrence_end_date: { - type: DataTypes.DATE, - allowNull: true - }, - last_generated_date: { - type: DataTypes.DATE, - allowNull: true - }, - recurrence_weekday: { - type: DataTypes.INTEGER, - allowNull: true, - validate: { - min: 0, - max: 6 - } - }, - recurrence_month_day: { - type: DataTypes.INTEGER, - allowNull: true, - validate: { - min: -1, - max: 31 - } - }, - recurrence_week_of_month: { - type: DataTypes.INTEGER, - allowNull: true, - validate: { - min: 1, - max: 5 - } - }, - completion_based: { - type: DataTypes.BOOLEAN, - allowNull: false, - defaultValue: false - }, - user_id: { - type: DataTypes.INTEGER, - allowNull: false, - references: { - model: 'users', - key: 'id' - } - }, - project_id: { - type: DataTypes.INTEGER, - allowNull: true, - references: { - model: 'projects', - key: 'id' - } - }, - recurring_parent_id: { - type: DataTypes.INTEGER, - allowNull: true, - references: { - model: 'tasks', - key: 'id' - } - }, - completed_at: { - type: DataTypes.DATE, - allowNull: true - } - }, { - tableName: 'tasks', - indexes: [ - { - fields: ['user_id'] - }, - { - fields: ['project_id'] - }, - { - fields: ['recurrence_type'] - }, - { - fields: ['last_generated_date'] - } - ] - }); + const Task = sequelize.define( + 'Task', + { + id: { + type: DataTypes.INTEGER, + primaryKey: true, + autoIncrement: true, + }, + uuid: { + type: DataTypes.UUID, + allowNull: false, + unique: true, + defaultValue: DataTypes.UUIDV4, + }, + name: { + type: DataTypes.STRING, + allowNull: false, + }, + description: { + type: DataTypes.TEXT, + allowNull: true, + }, + due_date: { + type: DataTypes.DATE, + allowNull: true, + }, + today: { + type: DataTypes.BOOLEAN, + allowNull: false, + defaultValue: false, + }, + priority: { + type: DataTypes.INTEGER, + allowNull: true, + defaultValue: 0, + validate: { + min: 0, + max: 2, + }, + }, + status: { + type: DataTypes.INTEGER, + allowNull: false, + defaultValue: 0, + validate: { + min: 0, + max: 4, + }, + }, + note: { + type: DataTypes.TEXT, + allowNull: true, + }, + recurrence_type: { + type: DataTypes.STRING, + allowNull: false, + defaultValue: 'none', + }, + recurrence_interval: { + type: DataTypes.INTEGER, + allowNull: true, + }, + recurrence_end_date: { + type: DataTypes.DATE, + allowNull: true, + }, + last_generated_date: { + type: DataTypes.DATE, + allowNull: true, + }, + recurrence_weekday: { + type: DataTypes.INTEGER, + allowNull: true, + validate: { + min: 0, + max: 6, + }, + }, + recurrence_month_day: { + type: DataTypes.INTEGER, + allowNull: true, + validate: { + min: -1, + max: 31, + }, + }, + recurrence_week_of_month: { + type: DataTypes.INTEGER, + allowNull: true, + validate: { + min: 1, + max: 5, + }, + }, + completion_based: { + type: DataTypes.BOOLEAN, + allowNull: false, + defaultValue: false, + }, + user_id: { + type: DataTypes.INTEGER, + allowNull: false, + references: { + model: 'users', + key: 'id', + }, + }, + project_id: { + type: DataTypes.INTEGER, + allowNull: true, + references: { + model: 'projects', + key: 'id', + }, + }, + recurring_parent_id: { + type: DataTypes.INTEGER, + allowNull: true, + references: { + model: 'tasks', + key: 'id', + }, + }, + completed_at: { + type: DataTypes.DATE, + allowNull: true, + }, + }, + { + tableName: 'tasks', + indexes: [ + { + fields: ['user_id'], + }, + { + fields: ['project_id'], + }, + { + fields: ['recurrence_type'], + }, + { + fields: ['last_generated_date'], + }, + ], + } + ); - // Define associations - Task.associate = function(models) { - // Self-referencing association for recurring tasks - Task.belongsTo(models.Task, { - as: 'RecurringParent', - foreignKey: 'recurring_parent_id' - }); - - Task.hasMany(models.Task, { - as: 'RecurringChildren', - foreignKey: 'recurring_parent_id' - }); - }; + // Define associations + Task.associate = function (models) { + // Self-referencing association for recurring tasks + Task.belongsTo(models.Task, { + as: 'RecurringParent', + foreignKey: 'recurring_parent_id', + }); - // Define enum constants - Task.PRIORITY = { - LOW: 0, - MEDIUM: 1, - HIGH: 2 - }; - - Task.STATUS = { - NOT_STARTED: 0, - IN_PROGRESS: 1, - DONE: 2, - ARCHIVED: 3, - WAITING: 4 - }; - - Task.RECURRENCE_TYPE = { - NONE: 'none', - DAILY: 'daily', - WEEKLY: 'weekly', - MONTHLY: 'monthly', - MONTHLY_WEEKDAY: 'monthly_weekday', - MONTHLY_LAST_DAY: 'monthly_last_day' - }; - - // priority and status - const getPriorityName = (priorityValue) => { - const priorities = ['low', 'medium', 'high']; - return priorities[priorityValue] || 'low'; - }; - - const getStatusName = (statusValue) => { - const statuses = ['not_started', 'in_progress', 'done', 'archived', 'waiting']; - return statuses[statusValue] || 'not_started'; - }; - - const getPriorityValue = (priorityName) => { - const priorities = { 'low': 0, 'medium': 1, 'high': 2 }; - return priorities[priorityName] !== undefined ? priorities[priorityName] : 0; - }; - - const getStatusValue = (statusName) => { - const statuses = { - 'not_started': 0, - 'in_progress': 1, - 'done': 2, - 'archived': 3, - 'waiting': 4 + Task.hasMany(models.Task, { + as: 'RecurringChildren', + foreignKey: 'recurring_parent_id', + }); }; - return statuses[statusName] !== undefined ? statuses[statusName] : 0; - }; - // Attach utility functions to model - Task.getPriorityName = getPriorityName; - Task.getStatusName = getStatusName; - Task.getPriorityValue = getPriorityValue; - Task.getStatusValue = getStatusValue; + // Define enum constants + Task.PRIORITY = { + LOW: 0, + MEDIUM: 1, + HIGH: 2, + }; - return Task; -}; \ No newline at end of file + Task.STATUS = { + NOT_STARTED: 0, + IN_PROGRESS: 1, + DONE: 2, + ARCHIVED: 3, + WAITING: 4, + }; + + Task.RECURRENCE_TYPE = { + NONE: 'none', + DAILY: 'daily', + WEEKLY: 'weekly', + MONTHLY: 'monthly', + MONTHLY_WEEKDAY: 'monthly_weekday', + MONTHLY_LAST_DAY: 'monthly_last_day', + }; + + // priority and status + const getPriorityName = (priorityValue) => { + const priorities = ['low', 'medium', 'high']; + return priorities[priorityValue] || 'low'; + }; + + const getStatusName = (statusValue) => { + const statuses = [ + 'not_started', + 'in_progress', + 'done', + 'archived', + 'waiting', + ]; + return statuses[statusValue] || 'not_started'; + }; + + const getPriorityValue = (priorityName) => { + const priorities = { low: 0, medium: 1, high: 2 }; + return priorities[priorityName] !== undefined + ? priorities[priorityName] + : 0; + }; + + const getStatusValue = (statusName) => { + const statuses = { + not_started: 0, + in_progress: 1, + done: 2, + archived: 3, + waiting: 4, + }; + return statuses[statusName] !== undefined ? statuses[statusName] : 0; + }; + + // Attach utility functions to model + Task.getPriorityName = getPriorityName; + Task.getStatusName = getStatusName; + Task.getPriorityValue = getPriorityValue; + Task.getStatusValue = getStatusValue; + + return Task; +}; diff --git a/backend/models/task_event.js b/backend/models/task_event.js index 863dcb9..dc53d86 100644 --- a/backend/models/task_event.js +++ b/backend/models/task_event.js @@ -1,211 +1,282 @@ const { DataTypes } = require('sequelize'); module.exports = (sequelize) => { - const TaskEvent = sequelize.define('TaskEvent', { - id: { - type: DataTypes.INTEGER, - primaryKey: true, - autoIncrement: true - }, - task_id: { - type: DataTypes.INTEGER, - allowNull: false, - references: { - model: 'tasks', - key: 'id' - } - }, - user_id: { - type: DataTypes.INTEGER, - allowNull: false, - references: { - model: 'users', - key: 'id' - } - }, - event_type: { - type: DataTypes.STRING, - allowNull: false, - validate: { - isIn: [['created', 'status_changed', 'priority_changed', 'due_date_changed', - 'project_changed', 'name_changed', 'description_changed', 'note_changed', - 'completed', 'archived', 'deleted', 'restored', 'today_changed', - 'tags_changed', 'recurrence_changed', 'recurrence_type_changed', - 'completion_based_changed', 'recurrence_end_date_changed']] - } - }, - old_value: { - type: DataTypes.TEXT, - allowNull: true, - get() { - const rawValue = this.getDataValue('old_value'); - return rawValue ? JSON.parse(rawValue) : null; - }, - set(value) { - this.setDataValue('old_value', value ? JSON.stringify(value) : null); - } - }, - new_value: { - type: DataTypes.TEXT, - allowNull: true, - get() { - const rawValue = this.getDataValue('new_value'); - return rawValue ? JSON.parse(rawValue) : null; - }, - set(value) { - this.setDataValue('new_value', value ? JSON.stringify(value) : null); - } - }, - field_name: { - type: DataTypes.STRING, - allowNull: true, - validate: { - isIn: [['status', 'priority', 'due_date', 'project_id', 'name', 'description', - 'note', 'today', 'tags', 'recurrence_type', 'recurrence_interval', - 'recurrence_end_date', 'recurrence_weekday', 'recurrence_month_day', - 'recurrence_week_of_month', 'completion_based']] - } - }, - metadata: { - type: DataTypes.TEXT, - allowNull: true, - get() { - const rawValue = this.getDataValue('metadata'); - return rawValue ? JSON.parse(rawValue) : null; - }, - set(value) { - this.setDataValue('metadata', value ? JSON.stringify(value) : null); - } - } - }, { - tableName: 'task_events', - timestamps: true, - createdAt: 'created_at', - updatedAt: false, // We don't need updated_at for events (they're immutable) - indexes: [ - { - fields: ['task_id'] - }, - { - fields: ['user_id'] - }, - { - fields: ['event_type'] - }, - { - fields: ['created_at'] - }, - { - fields: ['task_id', 'event_type'] - }, - { - fields: ['task_id', 'created_at'] - } - ] - }); - - // Define associations - TaskEvent.associate = function(models) { - // TaskEvent belongs to Task - TaskEvent.belongsTo(models.Task, { - foreignKey: 'task_id', - as: 'Task' - }); - - // TaskEvent belongs to User - TaskEvent.belongsTo(models.User, { - foreignKey: 'user_id', - as: 'User' - }); - }; - - // Helper methods for common event types - TaskEvent.createStatusChangeEvent = async function(taskId, userId, oldStatus, newStatus, metadata = {}) { - return await TaskEvent.create({ - task_id: taskId, - user_id: userId, - event_type: 'status_changed', - field_name: 'status', - old_value: { status: oldStatus }, - new_value: { status: newStatus }, - metadata: metadata - }); - }; - - TaskEvent.createTaskCreatedEvent = async function(taskId, userId, taskData, metadata = {}) { - return await TaskEvent.create({ - task_id: taskId, - user_id: userId, - event_type: 'created', - field_name: null, - old_value: null, - new_value: taskData, - metadata: metadata - }); - }; - - TaskEvent.createFieldChangeEvent = async function(taskId, userId, fieldName, oldValue, newValue, metadata = {}) { - const eventType = fieldName === 'status' && newValue === 2 ? 'completed' : - fieldName === 'status' && newValue === 3 ? 'archived' : - `${fieldName}_changed`; - - return await TaskEvent.create({ - task_id: taskId, - user_id: userId, - event_type: eventType, - field_name: fieldName, - old_value: { [fieldName]: oldValue }, - new_value: { [fieldName]: newValue }, - metadata: metadata - }); - }; - - // Query helpers - TaskEvent.getTaskTimeline = async function(taskId) { - return await TaskEvent.findAll({ - where: { task_id: taskId }, - order: [['created_at', 'ASC']], - include: [{ - model: sequelize.models.User, - as: 'User', - attributes: ['id', 'name', 'email'] - }] - }); - }; - - TaskEvent.getCompletionTime = async function(taskId) { - const events = await TaskEvent.findAll({ - where: { - task_id: taskId, - event_type: ['status_changed', 'created', 'completed'] - }, - order: [['created_at', 'ASC']] - }); - - if (events.length === 0) return null; - - const startEvent = events.find(e => - e.event_type === 'created' || - (e.event_type === 'status_changed' && e.new_value?.status === 1) // in_progress + const TaskEvent = sequelize.define( + 'TaskEvent', + { + id: { + type: DataTypes.INTEGER, + primaryKey: true, + autoIncrement: true, + }, + task_id: { + type: DataTypes.INTEGER, + allowNull: false, + references: { + model: 'tasks', + key: 'id', + }, + }, + user_id: { + type: DataTypes.INTEGER, + allowNull: false, + references: { + model: 'users', + key: 'id', + }, + }, + event_type: { + type: DataTypes.STRING, + allowNull: false, + validate: { + isIn: [ + [ + 'created', + 'status_changed', + 'priority_changed', + 'due_date_changed', + 'project_changed', + 'name_changed', + 'description_changed', + 'note_changed', + 'completed', + 'archived', + 'deleted', + 'restored', + 'today_changed', + 'tags_changed', + 'recurrence_changed', + 'recurrence_type_changed', + 'completion_based_changed', + 'recurrence_end_date_changed', + ], + ], + }, + }, + old_value: { + type: DataTypes.TEXT, + allowNull: true, + get() { + const rawValue = this.getDataValue('old_value'); + return rawValue ? JSON.parse(rawValue) : null; + }, + set(value) { + this.setDataValue( + 'old_value', + value ? JSON.stringify(value) : null + ); + }, + }, + new_value: { + type: DataTypes.TEXT, + allowNull: true, + get() { + const rawValue = this.getDataValue('new_value'); + return rawValue ? JSON.parse(rawValue) : null; + }, + set(value) { + this.setDataValue( + 'new_value', + value ? JSON.stringify(value) : null + ); + }, + }, + field_name: { + type: DataTypes.STRING, + allowNull: true, + validate: { + isIn: [ + [ + 'status', + 'priority', + 'due_date', + 'project_id', + 'name', + 'description', + 'note', + 'today', + 'tags', + 'recurrence_type', + 'recurrence_interval', + 'recurrence_end_date', + 'recurrence_weekday', + 'recurrence_month_day', + 'recurrence_week_of_month', + 'completion_based', + ], + ], + }, + }, + metadata: { + type: DataTypes.TEXT, + allowNull: true, + get() { + const rawValue = this.getDataValue('metadata'); + return rawValue ? JSON.parse(rawValue) : null; + }, + set(value) { + this.setDataValue( + 'metadata', + value ? JSON.stringify(value) : null + ); + }, + }, + }, + { + tableName: 'task_events', + timestamps: true, + createdAt: 'created_at', + updatedAt: false, // We don't need updated_at for events (they're immutable) + indexes: [ + { + fields: ['task_id'], + }, + { + fields: ['user_id'], + }, + { + fields: ['event_type'], + }, + { + fields: ['created_at'], + }, + { + fields: ['task_id', 'event_type'], + }, + { + fields: ['task_id', 'created_at'], + }, + ], + } ); - const completedEvent = events.find(e => - e.event_type === 'completed' || - (e.event_type === 'status_changed' && e.new_value?.status === 2) // done - ); + // Define associations + TaskEvent.associate = function (models) { + // TaskEvent belongs to Task + TaskEvent.belongsTo(models.Task, { + foreignKey: 'task_id', + as: 'Task', + }); - if (!startEvent || !completedEvent) return null; - - const startTime = new Date(startEvent.created_at); - const endTime = new Date(completedEvent.created_at); - - return { - started_at: startTime, - completed_at: endTime, - duration_ms: endTime - startTime, - duration_hours: (endTime - startTime) / (1000 * 60 * 60) + // TaskEvent belongs to User + TaskEvent.belongsTo(models.User, { + foreignKey: 'user_id', + as: 'User', + }); }; - }; - return TaskEvent; -}; \ No newline at end of file + // Helper methods for common event types + TaskEvent.createStatusChangeEvent = async function ( + taskId, + userId, + oldStatus, + newStatus, + metadata = {} + ) { + return await TaskEvent.create({ + task_id: taskId, + user_id: userId, + event_type: 'status_changed', + field_name: 'status', + old_value: { status: oldStatus }, + new_value: { status: newStatus }, + metadata: metadata, + }); + }; + + TaskEvent.createTaskCreatedEvent = async function ( + taskId, + userId, + taskData, + metadata = {} + ) { + return await TaskEvent.create({ + task_id: taskId, + user_id: userId, + event_type: 'created', + field_name: null, + old_value: null, + new_value: taskData, + metadata: metadata, + }); + }; + + TaskEvent.createFieldChangeEvent = async function ( + taskId, + userId, + fieldName, + oldValue, + newValue, + metadata = {} + ) { + const eventType = + fieldName === 'status' && newValue === 2 + ? 'completed' + : fieldName === 'status' && newValue === 3 + ? 'archived' + : `${fieldName}_changed`; + + return await TaskEvent.create({ + task_id: taskId, + user_id: userId, + event_type: eventType, + field_name: fieldName, + old_value: { [fieldName]: oldValue }, + new_value: { [fieldName]: newValue }, + metadata: metadata, + }); + }; + + // Query helpers + TaskEvent.getTaskTimeline = async function (taskId) { + return await TaskEvent.findAll({ + where: { task_id: taskId }, + order: [['created_at', 'ASC']], + include: [ + { + model: sequelize.models.User, + as: 'User', + attributes: ['id', 'name', 'email'], + }, + ], + }); + }; + + TaskEvent.getCompletionTime = async function (taskId) { + const events = await TaskEvent.findAll({ + where: { + task_id: taskId, + event_type: ['status_changed', 'created', 'completed'], + }, + order: [['created_at', 'ASC']], + }); + + if (events.length === 0) return null; + + const startEvent = events.find( + (e) => + e.event_type === 'created' || + (e.event_type === 'status_changed' && e.new_value?.status === 1) // in_progress + ); + + const completedEvent = events.find( + (e) => + e.event_type === 'completed' || + (e.event_type === 'status_changed' && e.new_value?.status === 2) // done + ); + + if (!startEvent || !completedEvent) return null; + + const startTime = new Date(startEvent.created_at); + const endTime = new Date(completedEvent.created_at); + + return { + started_at: startTime, + completed_at: endTime, + duration_ms: endTime - startTime, + duration_hours: (endTime - startTime) / (1000 * 60 * 60), + }; + }; + + return TaskEvent; +}; diff --git a/backend/models/user.js b/backend/models/user.js index caf1980..a4abbda 100644 --- a/backend/models/user.js +++ b/backend/models/user.js @@ -2,135 +2,153 @@ const { DataTypes } = require('sequelize'); const bcrypt = require('bcrypt'); module.exports = (sequelize) => { - const User = sequelize.define('User', { - id: { - type: DataTypes.INTEGER, - primaryKey: true, - autoIncrement: true - }, - name: { - type: DataTypes.STRING, - allowNull: true - }, - email: { - type: DataTypes.STRING, - allowNull: false, - unique: true, - validate: { - isEmail: true - } - }, - password: { - type: DataTypes.VIRTUAL, - allowNull: true - }, - password_digest: { - type: DataTypes.STRING, - allowNull: false, - field: 'password_digest' - }, - appearance: { - type: DataTypes.STRING, - allowNull: false, - defaultValue: 'light', - validate: { - isIn: [['light', 'dark']] - } - }, - language: { - type: DataTypes.STRING, - allowNull: false, - defaultValue: 'en' - }, - timezone: { - type: DataTypes.STRING, - allowNull: false, - defaultValue: 'UTC' - }, - avatar_image: { - type: DataTypes.STRING, - allowNull: true - }, - telegram_bot_token: { - type: DataTypes.STRING, - allowNull: true - }, - telegram_chat_id: { - type: DataTypes.STRING, - allowNull: true - }, - task_summary_enabled: { - type: DataTypes.BOOLEAN, - allowNull: false, - defaultValue: false - }, - task_summary_frequency: { - type: DataTypes.STRING, - allowNull: true, - defaultValue: 'daily', - validate: { - isIn: [['daily', 'weekdays', 'weekly', '1h', '2h', '4h', '8h', '12h']] - } - }, - task_summary_last_run: { - type: DataTypes.DATE, - allowNull: true - }, - task_summary_next_run: { - type: DataTypes.DATE, - allowNull: true - }, - task_intelligence_enabled: { - type: DataTypes.BOOLEAN, - allowNull: false, - defaultValue: true - }, - auto_suggest_next_actions_enabled: { - type: DataTypes.BOOLEAN, - allowNull: false, - defaultValue: false - }, - pomodoro_enabled: { - type: DataTypes.BOOLEAN, - allowNull: false, - defaultValue: true - }, - today_settings: { - type: DataTypes.JSON, - allowNull: true, - defaultValue: { - showMetrics: false, - showProductivity: false, - showIntelligence: false, - showDueToday: true, - showCompleted: true, - showProgressBar: true, - showDailyQuote: true - } - } - }, { - tableName: 'users', - hooks: { - beforeValidate: async (user) => { - if (user.password) { - user.password_digest = await bcrypt.hash(user.password, 10); + const User = sequelize.define( + 'User', + { + id: { + type: DataTypes.INTEGER, + primaryKey: true, + autoIncrement: true, + }, + name: { + type: DataTypes.STRING, + allowNull: true, + }, + email: { + type: DataTypes.STRING, + allowNull: false, + unique: true, + validate: { + isEmail: true, + }, + }, + password: { + type: DataTypes.VIRTUAL, + allowNull: true, + }, + password_digest: { + type: DataTypes.STRING, + allowNull: false, + field: 'password_digest', + }, + appearance: { + type: DataTypes.STRING, + allowNull: false, + defaultValue: 'light', + validate: { + isIn: [['light', 'dark']], + }, + }, + language: { + type: DataTypes.STRING, + allowNull: false, + defaultValue: 'en', + }, + timezone: { + type: DataTypes.STRING, + allowNull: false, + defaultValue: 'UTC', + }, + avatar_image: { + type: DataTypes.STRING, + allowNull: true, + }, + telegram_bot_token: { + type: DataTypes.STRING, + allowNull: true, + }, + telegram_chat_id: { + type: DataTypes.STRING, + allowNull: true, + }, + task_summary_enabled: { + type: DataTypes.BOOLEAN, + allowNull: false, + defaultValue: false, + }, + task_summary_frequency: { + type: DataTypes.STRING, + allowNull: true, + defaultValue: 'daily', + validate: { + isIn: [ + [ + 'daily', + 'weekdays', + 'weekly', + '1h', + '2h', + '4h', + '8h', + '12h', + ], + ], + }, + }, + task_summary_last_run: { + type: DataTypes.DATE, + allowNull: true, + }, + task_summary_next_run: { + type: DataTypes.DATE, + allowNull: true, + }, + task_intelligence_enabled: { + type: DataTypes.BOOLEAN, + allowNull: false, + defaultValue: true, + }, + auto_suggest_next_actions_enabled: { + type: DataTypes.BOOLEAN, + allowNull: false, + defaultValue: false, + }, + pomodoro_enabled: { + type: DataTypes.BOOLEAN, + allowNull: false, + defaultValue: true, + }, + today_settings: { + type: DataTypes.JSON, + allowNull: true, + defaultValue: { + showMetrics: false, + showProductivity: false, + showIntelligence: false, + showDueToday: true, + showCompleted: true, + showProgressBar: true, + showDailyQuote: true, + }, + }, + }, + { + tableName: 'users', + hooks: { + beforeValidate: async (user) => { + if (user.password) { + user.password_digest = await bcrypt.hash( + user.password, + 10 + ); + } + }, + }, } - } - } - }); + ); - // password operations - const hashPassword = async (password) => { - return await bcrypt.hash(password, 10); - }; + // password operations + const hashPassword = async (password) => { + return await bcrypt.hash(password, 10); + }; - const checkPassword = async (password, hashedPassword) => { - return await bcrypt.compare(password, hashedPassword); - }; + const checkPassword = async (password, hashedPassword) => { + return await bcrypt.compare(password, hashedPassword); + }; - // Attach utility functions to model - User.hashPassword = hashPassword; - User.checkPassword = checkPassword; + // Attach utility functions to model + User.hashPassword = hashPassword; + User.checkPassword = checkPassword; - return User; -}; \ No newline at end of file + return User; +}; diff --git a/backend/package-lock.json b/backend/package-lock.json index a2239a3..acc317b 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -1,9025 +1,10147 @@ { - "name": "backend", - "version": "1.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "backend", - "version": "1.0.0", - "license": "ISC", - "dependencies": { - "bcrypt": "^6.0.0", - "cheerio": "^1.1.0", - "compression": "^1.8.0", - "connect-session-sequelize": "^7.1.7", - "cors": "^2.8.5", - "dotenv": "^16.5.0", - "express": "^4.18.2", - "express-session": "^1.18.1", - "googleapis": "^144.0.0", - "helmet": "^8.1.0", - "js-yaml": "^4.1.0", - "moment-timezone": "^0.6.0", - "morgan": "^1.10.0", - "multer": "^2.0.1", - "node-cron": "^4.1.0", - "recharts": "^2.15.4", - "sequelize": "^6.37.7", - "sqlite3": "^5.1.7", - "uuid": "^11.1.0" - }, - "devDependencies": { - "cross-env": "^7.0.3", - "jest": "^30.0.0", - "nodemon": "^3.0.1", - "sequelize-cli": "^6.6.2", - "supertest": "^7.1.1" - } - }, - "node_modules/@ampproject/remapping": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", - "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", - "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.27.1", - "js-tokens": "^4.0.0", - "picocolors": "^1.1.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.27.5", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.27.5.tgz", - "integrity": "sha512-KiRAp/VoJaWkkte84TvUd9qjdbZAdiqyvMxrGl1N6vzFogKmaLgoM3L1kgtLicp2HP5fBJS8JrZKLVIZGVJAVg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core": { - "version": "7.27.4", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.27.4.tgz", - "integrity": "sha512-bXYxrXFubeYdvB0NhD/NBB3Qi6aZeV20GOWVI47t2dkecCEoneR4NPVcb7abpXDEvejgrUfFtG6vG/zxAKmg+g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.27.3", - "@babel/helper-compilation-targets": "^7.27.2", - "@babel/helper-module-transforms": "^7.27.3", - "@babel/helpers": "^7.27.4", - "@babel/parser": "^7.27.4", - "@babel/template": "^7.27.2", - "@babel/traverse": "^7.27.4", - "@babel/types": "^7.27.3", - "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/core/node_modules/debug": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/@babel/core/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/@babel/core/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/@babel/generator": { - "version": "7.27.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.5.tgz", - "integrity": "sha512-ZGhA37l0e/g2s1Cnzdix0O3aLYm66eF8aufiVteOgnwxgnRP8GoyMj7VWsgWnQbVKXyge7hqrFh2K2TQM6t1Hw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.27.5", - "@babel/types": "^7.27.3", - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25", - "jsesc": "^3.0.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", - "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.27.2", - "@babel/helper-validator-option": "^7.27.1", - "browserslist": "^4.24.0", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets/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/@babel/helper-compilation-targets/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/@babel/helper-compilation-targets/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" - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", - "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.27.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.27.3.tgz", - "integrity": "sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1", - "@babel/traverse": "^7.27.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", - "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", - "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", - "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-option": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", - "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.27.6", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.6.tgz", - "integrity": "sha512-muE8Tt8M22638HU31A3CgfSUciwz1fhATfoVai05aPXGor//CdWDCbnlY1yvBPo07njuVOCNGCSp/GTt12lIug==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/template": "^7.27.2", - "@babel/types": "^7.27.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/parser": { - "version": "7.27.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.5.tgz", - "integrity": "sha512-OsQd175SxWkGlzbny8J3K8TnnDD0N3lrIUtB92xwyRpzaenGZhxDvxN/JgU00U3CDZNj9tPuDJ5H0WS4Nt3vKg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.27.3" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-bigint": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", - "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", - "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-class-static-block": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", - "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-import-attributes": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.27.1.tgz", - "integrity": "sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-import-meta": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", - "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz", - "integrity": "sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-private-property-in-object": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", - "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", - "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz", - "integrity": "sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/runtime": { - "version": "7.27.6", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.6.tgz", - "integrity": "sha512-vbavdySgbTTrmFE+EsiqUTzlOr5bzlnJtUv9PynGCAKvfQqjIXbvFdumPM/GxMDfyuGMJaJAU6TO4zc1Jf1i8Q==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/template": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", - "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/parser": "^7.27.2", - "@babel/types": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.27.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.4.tgz", - "integrity": "sha512-oNcu2QbHqts9BtOWJosOVJapWjBDSxGCpFvikNR5TGDYDQf3JwpIoMzIKrvfoti93cLfPJEG4tH9SPVeyCGgdA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.27.3", - "@babel/parser": "^7.27.4", - "@babel/template": "^7.27.2", - "@babel/types": "^7.27.3", - "debug": "^4.3.1", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse/node_modules/debug": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/@babel/traverse/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/@babel/types": { - "version": "7.27.6", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.6.tgz", - "integrity": "sha512-ETyHEk2VHHvl9b9jZP5IHPavHYk57EhanlRRuae9XCpb/j5bDCbPPMOBfCWhnl/7EDJz0jEMCi/RhccCE8r1+Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@bcoe/v8-coverage": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", - "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@emnapi/core": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.4.3.tgz", - "integrity": "sha512-4m62DuCE07lw01soJwPiBGC0nAww0Q+RY70VZ+n49yDIO13yyinhbWCeNnaob0lakDtWQzSdtNWzJeOJt2ma+g==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/wasi-threads": "1.0.2", - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/runtime": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.3.tgz", - "integrity": "sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@emnapi/wasi-threads": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.0.2.tgz", - "integrity": "sha512-5n3nTJblwRi8LlXkJ9eBzu+kZR8Yxcc7ubakyQTFzPMtIhFpUBRbsnc2Dv88IZDIbCDlBiWrknhB4Lsz7mg6BA==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@gar/promisify": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", - "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==", - "license": "MIT", - "optional": true - }, - "node_modules/@isaacs/cliui": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@isaacs/cliui/node_modules/ansi-regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@isaacs/cliui/node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@isaacs/cliui/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "license": "MIT", - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, - "license": "MIT", - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/console": { - "version": "30.0.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-30.0.0.tgz", - "integrity": "sha512-vfpJap6JZQ3I8sUN8dsFqNAKJYO4KIGxkcB+3Fw7Q/BJiWY5HwtMMiuT1oP0avsiDhjE/TCLaDgbGfHwDdBVeg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "30.0.0", - "@types/node": "*", - "chalk": "^4.1.2", - "jest-message-util": "30.0.0", - "jest-util": "30.0.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/core": { - "version": "30.0.0", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-30.0.0.tgz", - "integrity": "sha512-1zU39zFtWSl5ZuDK3Rd6P8S28MmS4F11x6Z4CURrgJ99iaAJg68hmdJ2SAHEEO6ociaNk43UhUYtHxWKEWoNYw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/console": "30.0.0", - "@jest/pattern": "30.0.0", - "@jest/reporters": "30.0.0", - "@jest/test-result": "30.0.0", - "@jest/transform": "30.0.0", - "@jest/types": "30.0.0", - "@types/node": "*", - "ansi-escapes": "^4.3.2", - "chalk": "^4.1.2", - "ci-info": "^4.2.0", - "exit-x": "^0.2.2", - "graceful-fs": "^4.2.11", - "jest-changed-files": "30.0.0", - "jest-config": "30.0.0", - "jest-haste-map": "30.0.0", - "jest-message-util": "30.0.0", - "jest-regex-util": "30.0.0", - "jest-resolve": "30.0.0", - "jest-resolve-dependencies": "30.0.0", - "jest-runner": "30.0.0", - "jest-runtime": "30.0.0", - "jest-snapshot": "30.0.0", - "jest-util": "30.0.0", - "jest-validate": "30.0.0", - "jest-watcher": "30.0.0", - "micromatch": "^4.0.8", - "pretty-format": "30.0.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/@jest/diff-sequences": { - "version": "30.0.0", - "resolved": "https://registry.npmjs.org/@jest/diff-sequences/-/diff-sequences-30.0.0.tgz", - "integrity": "sha512-xMbtoCeKJDto86GW6AiwVv7M4QAuI56R7dVBr1RNGYbOT44M2TIzOiske2RxopBqkumDY+A1H55pGvuribRY9A==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/environment": { - "version": "30.0.0", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-30.0.0.tgz", - "integrity": "sha512-09sFbMMgS5JxYnvgmmtwIHhvoyzvR5fUPrVl8nOCrC5KdzmmErTcAxfWyAhJ2bv3rvHNQaKiS+COSG+O7oNbXw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/fake-timers": "30.0.0", - "@jest/types": "30.0.0", - "@types/node": "*", - "jest-mock": "30.0.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/expect": { - "version": "30.0.0", - "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-30.0.0.tgz", - "integrity": "sha512-XZ3j6syhMeKiBknmmc8V3mNIb44kxLTbOQtaXA4IFdHy+vEN0cnXRzbRjdGBtrp4k1PWyMWNU3Fjz3iejrhpQg==", - "dev": true, - "license": "MIT", - "dependencies": { - "expect": "30.0.0", - "jest-snapshot": "30.0.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/expect-utils": { - "version": "30.0.0", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-30.0.0.tgz", - "integrity": "sha512-UiWfsqNi/+d7xepfOv8KDcbbzcYtkWBe3a3kVDtg6M1kuN6CJ7b4HzIp5e1YHrSaQaVS8sdCoyCMCZClTLNKFQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/get-type": "30.0.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/fake-timers": { - "version": "30.0.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-30.0.0.tgz", - "integrity": "sha512-yzBmJcrMHAMcAEbV2w1kbxmx8WFpEz8Cth3wjLMSkq+LO8VeGKRhpr5+BUp7PPK+x4njq/b6mVnDR8e/tPL5ng==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "30.0.0", - "@sinonjs/fake-timers": "^13.0.0", - "@types/node": "*", - "jest-message-util": "30.0.0", - "jest-mock": "30.0.0", - "jest-util": "30.0.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/get-type": { - "version": "30.0.0", - "resolved": "https://registry.npmjs.org/@jest/get-type/-/get-type-30.0.0.tgz", - "integrity": "sha512-VZWMjrBzqfDKngQ7sUctKeLxanAbsBFoZnPxNIG6CmxK7Gv6K44yqd0nzveNIBfuhGZMmk1n5PGbvdSTOu0yTg==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/globals": { - "version": "30.0.0", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-30.0.0.tgz", - "integrity": "sha512-OEzYes5A1xwBJVMPqFRa8NCao8Vr42nsUZuf/SpaJWoLE+4kyl6nCQZ1zqfipmCrIXQVALC5qJwKy/7NQQLPhw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/environment": "30.0.0", - "@jest/expect": "30.0.0", - "@jest/types": "30.0.0", - "jest-mock": "30.0.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/pattern": { - "version": "30.0.0", - "resolved": "https://registry.npmjs.org/@jest/pattern/-/pattern-30.0.0.tgz", - "integrity": "sha512-k+TpEThzLVXMkbdxf8KHjZ83Wl+G54ytVJoDIGWwS96Ql4xyASRjc6SU1hs5jHVql+hpyK9G8N7WuFhLpGHRpQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*", - "jest-regex-util": "30.0.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/reporters": { - "version": "30.0.0", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-30.0.0.tgz", - "integrity": "sha512-5WHNlLO0Ok+/o6ML5IzgVm1qyERtLHBNhwn67PAq92H4hZ+n5uW/BYj1VVwmTdxIcNrZLxdV9qtpdZkXf16HxA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "30.0.0", - "@jest/test-result": "30.0.0", - "@jest/transform": "30.0.0", - "@jest/types": "30.0.0", - "@jridgewell/trace-mapping": "^0.3.25", - "@types/node": "*", - "chalk": "^4.1.2", - "collect-v8-coverage": "^1.0.2", - "exit-x": "^0.2.2", - "glob": "^10.3.10", - "graceful-fs": "^4.2.11", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^6.0.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^5.0.0", - "istanbul-reports": "^3.1.3", - "jest-message-util": "30.0.0", - "jest-util": "30.0.0", - "jest-worker": "30.0.0", - "slash": "^3.0.0", - "string-length": "^4.0.2", - "v8-to-istanbul": "^9.0.1" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/@jest/reporters/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/@jest/reporters/node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", - "dev": true, - "license": "ISC", - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@jest/reporters/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@jest/reporters/node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/@jest/schemas": { - "version": "30.0.0", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.0.0.tgz", - "integrity": "sha512-NID2VRyaEkevCRz6badhfqYwri/RvMbiHY81rk3AkK/LaiB0LSxi1RdVZ7MpZdTjNugtZeGfpL0mLs9Kp3MrQw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@sinclair/typebox": "^0.34.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/snapshot-utils": { - "version": "30.0.0", - "resolved": "https://registry.npmjs.org/@jest/snapshot-utils/-/snapshot-utils-30.0.0.tgz", - "integrity": "sha512-C/QSFUmvZEYptg2Vin84FggAphwHvj6la39vkw1CNOZQORWZ7O/H0BXmdeeeGnvlXDYY8TlFM5jgFnxLAxpFjA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "30.0.0", - "chalk": "^4.1.2", - "graceful-fs": "^4.2.11", - "natural-compare": "^1.4.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/source-map": { - "version": "30.0.0", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-30.0.0.tgz", - "integrity": "sha512-oYBJ4d/NF4ZY3/7iq1VaeoERHRvlwKtrGClgescaXMIa1mmb+vfJd0xMgbW9yrI80IUA7qGbxpBWxlITrHkWoA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.25", - "callsites": "^3.1.0", - "graceful-fs": "^4.2.11" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/test-result": { - "version": "30.0.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-30.0.0.tgz", - "integrity": "sha512-685zco9HdgBaaWiB9T4xjLtBuN0Q795wgaQPpmuAeZPHwHZSoKFAUnozUtU+ongfi4l5VCz8AclOE5LAQdyjxQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/console": "30.0.0", - "@jest/types": "30.0.0", - "@types/istanbul-lib-coverage": "^2.0.6", - "collect-v8-coverage": "^1.0.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/test-sequencer": { - "version": "30.0.0", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-30.0.0.tgz", - "integrity": "sha512-Hmvv5Yg6UmghXIcVZIydkT0nAK7M/hlXx9WMHR5cLVwdmc14/qUQt3mC72T6GN0olPC6DhmKE6Cd/pHsgDbuqQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/test-result": "30.0.0", - "graceful-fs": "^4.2.11", - "jest-haste-map": "30.0.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/transform": { - "version": "30.0.0", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-30.0.0.tgz", - "integrity": "sha512-8xhpsCGYJsUjqpJOgLyMkeOSSlhqggFZEWAnZquBsvATtueoEs7CkMRxOUmJliF3E5x+mXmZ7gEEsHank029Og==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.27.4", - "@jest/types": "30.0.0", - "@jridgewell/trace-mapping": "^0.3.25", - "babel-plugin-istanbul": "^7.0.0", - "chalk": "^4.1.2", - "convert-source-map": "^2.0.0", - "fast-json-stable-stringify": "^2.1.0", - "graceful-fs": "^4.2.11", - "jest-haste-map": "30.0.0", - "jest-regex-util": "30.0.0", - "jest-util": "30.0.0", - "micromatch": "^4.0.8", - "pirates": "^4.0.7", - "slash": "^3.0.0", - "write-file-atomic": "^5.0.1" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jest/types": { - "version": "30.0.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.0.0.tgz", - "integrity": "sha512-1Nox8mAL52PKPfEnUQWBvKU/bp8FTT6AiDu76bFDEJj/qsRFSAVSldfCH3XYMqialti2zHXKvD5gN0AaHc0yKA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/pattern": "30.0.0", - "@jest/schemas": "30.0.0", - "@types/istanbul-lib-coverage": "^2.0.6", - "@types/istanbul-reports": "^3.0.4", - "@types/node": "*", - "@types/yargs": "^17.0.33", - "chalk": "^4.1.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", - "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "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/set-array": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", - "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@napi-rs/wasm-runtime": { - "version": "0.2.11", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.11.tgz", - "integrity": "sha512-9DPkXtvHydrcOsopiYpUgPHpmj0HWZKMUnL2dZqpvC42lsratuBG06V5ipyno0fUek5VlFsNQ+AcFATSrJXgMA==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@emnapi/core": "^1.4.3", - "@emnapi/runtime": "^1.4.3", - "@tybys/wasm-util": "^0.9.0" - } - }, - "node_modules/@noble/hashes": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", - "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^14.21.3 || >=16" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@npmcli/fs": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-1.1.1.tgz", - "integrity": "sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ==", - "license": "ISC", - "optional": true, - "dependencies": { - "@gar/promisify": "^1.0.1", - "semver": "^7.3.5" - } - }, - "node_modules/@npmcli/move-file": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.1.2.tgz", - "integrity": "sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==", - "deprecated": "This functionality has been moved to @npmcli/fs", - "license": "MIT", - "optional": true, - "dependencies": { - "mkdirp": "^1.0.4", - "rimraf": "^3.0.2" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@npmcli/move-file/node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "license": "MIT", - "optional": true, - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@one-ini/wasm": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@one-ini/wasm/-/wasm-0.1.1.tgz", - "integrity": "sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@paralleldrive/cuid2": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/@paralleldrive/cuid2/-/cuid2-2.2.2.tgz", - "integrity": "sha512-ZOBkgDwEdoYVlSeRbYYXs0S9MejQofiVYoTbKzy/6GQa39/q5tQU2IX46+shYnUkpEl3wc+J6wRlar7r2EK2xA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@noble/hashes": "^1.1.5" - } - }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">=14" - } - }, - "node_modules/@pkgr/core": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.7.tgz", - "integrity": "sha512-YLT9Zo3oNPJoBjBc4q8G2mjU4tqIbf5CEOORbUUr48dCD9q3umJ3IPlVqOqDakPfd2HuwccBaqlGhN4Gmr5OWg==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/pkgr" - } - }, - "node_modules/@sinclair/typebox": { - "version": "0.34.35", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.35.tgz", - "integrity": "sha512-C6ypdODf2VZkgRT6sFM8E1F8vR+HcffniX0Kp8MsU8PIfrlXbNCBz0jzj17GjdmjTx1OtZzdH8+iALL21UjF5A==", - "dev": true, - "license": "MIT" - }, - "node_modules/@sinonjs/commons": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", - "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "type-detect": "4.0.8" - } - }, - "node_modules/@sinonjs/fake-timers": { - "version": "13.0.5", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-13.0.5.tgz", - "integrity": "sha512-36/hTbH2uaWuGVERyC6da9YwGWnzUZXuPro/F2LfsdOsLnCojz/iSH8MxUt/FD2S5XBSVPhmArFUXcpCQ2Hkiw==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@sinonjs/commons": "^3.0.1" - } - }, - "node_modules/@tootallnate/once": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", - "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">= 6" - } - }, - "node_modules/@tybys/wasm-util": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.9.0.tgz", - "integrity": "sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw==", - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, - "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.20.7", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.7.tgz", - "integrity": "sha512-dkO5fhS7+/oos4ciWxyEyjWe48zmG6wbCheo/G2ZnHx4fs3EU6YC6UM8rk56gAjNJ9P3MTH2jo5jb92/K6wbng==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.20.7" - } - }, - "node_modules/@types/d3-array": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.1.tgz", - "integrity": "sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==", - "license": "MIT" - }, - "node_modules/@types/d3-color": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz", - "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==", - "license": "MIT" - }, - "node_modules/@types/d3-ease": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz", - "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==", - "license": "MIT" - }, - "node_modules/@types/d3-interpolate": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", - "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==", - "license": "MIT", - "dependencies": { - "@types/d3-color": "*" - } - }, - "node_modules/@types/d3-path": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz", - "integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==", - "license": "MIT" - }, - "node_modules/@types/d3-scale": { - "version": "4.0.9", - "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz", - "integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==", - "license": "MIT", - "dependencies": { - "@types/d3-time": "*" - } - }, - "node_modules/@types/d3-shape": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.7.tgz", - "integrity": "sha512-VLvUQ33C+3J+8p+Daf+nYSOsjB4GXp19/S/aGo60m9h1v6XaxjiT82lKVWJCfzhtuZ3yD7i/TPeC/fuKLLOSmg==", - "license": "MIT", - "dependencies": { - "@types/d3-path": "*" - } - }, - "node_modules/@types/d3-time": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz", - "integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==", - "license": "MIT" - }, - "node_modules/@types/d3-timer": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz", - "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==", - "license": "MIT" - }, - "node_modules/@types/debug": { - "version": "4.1.12", - "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", - "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", - "license": "MIT", - "dependencies": { - "@types/ms": "*" - } - }, - "node_modules/@types/istanbul-lib-coverage": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", - "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/istanbul-lib-report": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", - "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/istanbul-lib-coverage": "*" - } - }, - "node_modules/@types/istanbul-reports": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", - "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/istanbul-lib-report": "*" - } - }, - "node_modules/@types/ms": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", - "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", - "license": "MIT" - }, - "node_modules/@types/node": { - "version": "24.0.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.0.1.tgz", - "integrity": "sha512-MX4Zioh39chHlDJbKmEgydJDS3tspMP/lnQC67G3SWsTnb9NeYVWOjkxpOSy4oMfPs4StcWHwBrvUb4ybfnuaw==", - "license": "MIT", - "dependencies": { - "undici-types": "~7.8.0" - } - }, - "node_modules/@types/stack-utils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", - "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/validator": { - "version": "13.15.1", - "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.15.1.tgz", - "integrity": "sha512-9gG6ogYcoI2mCMLdcO0NYI0AYrbxIjv0MDmy/5Ywo6CpWWrqYayc+mmgxRsCgtcGJm9BSbXkMsmxGah1iGHAAQ==", - "license": "MIT" - }, - "node_modules/@types/yargs": { - "version": "17.0.33", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", - "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@types/yargs-parser": { - "version": "21.0.3", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", - "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@ungap/structured-clone": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", - "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", - "dev": true, - "license": "ISC" - }, - "node_modules/@unrs/resolver-binding-android-arm-eabi": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.9.0.tgz", - "integrity": "sha512-h1T2c2Di49ekF2TE8ZCoJkb+jwETKUIPDJ/nO3tJBKlLFPu+fyd93f0rGP/BvArKx2k2HlRM4kqkNarj3dvZlg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@unrs/resolver-binding-android-arm64": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.9.0.tgz", - "integrity": "sha512-sG1NHtgXtX8owEkJ11yn34vt0Xqzi3k9TJ8zppDmyG8GZV4kVWw44FHwKwHeEFl07uKPeC4ZoyuQaGh5ruJYPA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@unrs/resolver-binding-darwin-arm64": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.9.0.tgz", - "integrity": "sha512-nJ9z47kfFnCxN1z/oYZS7HSNsFh43y2asePzTEZpEvK7kGyuShSl3RRXnm/1QaqFL+iP+BjMwuB+DYUymOkA5A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@unrs/resolver-binding-darwin-x64": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.9.0.tgz", - "integrity": "sha512-TK+UA1TTa0qS53rjWn7cVlEKVGz2B6JYe0C++TdQjvWYIyx83ruwh0wd4LRxYBM5HeuAzXcylA9BH2trARXJTw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@unrs/resolver-binding-freebsd-x64": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.9.0.tgz", - "integrity": "sha512-6uZwzMRFcD7CcCd0vz3Hp+9qIL2jseE/bx3ZjaLwn8t714nYGwiE84WpaMCYjU+IQET8Vu/+BNAGtYD7BG/0yA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@unrs/resolver-binding-linux-arm-gnueabihf": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.9.0.tgz", - "integrity": "sha512-bPUBksQfrgcfv2+mm+AZinaKq8LCFvt5PThYqRotqSuuZK1TVKkhbVMS/jvSRfYl7jr3AoZLYbDkItxgqMKRkg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-arm-musleabihf": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.9.0.tgz", - "integrity": "sha512-uT6E7UBIrTdCsFQ+y0tQd3g5oudmrS/hds5pbU3h4s2t/1vsGWbbSKhBSCD9mcqaqkBwoqlECpUrRJCmldl8PA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-arm64-gnu": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.9.0.tgz", - "integrity": "sha512-vdqBh911wc5awE2bX2zx3eflbyv8U9xbE/jVKAm425eRoOVv/VseGZsqi3A3SykckSpF4wSROkbQPvbQFn8EsA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-arm64-musl": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.9.0.tgz", - "integrity": "sha512-/8JFZ/SnuDr1lLEVsxsuVwrsGquTvT51RZGvyDB/dOK3oYK2UqeXzgeyq6Otp8FZXQcEYqJwxb9v+gtdXn03eQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-ppc64-gnu": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.9.0.tgz", - "integrity": "sha512-FkJjybtrl+rajTw4loI3L6YqSOpeZfDls4SstL/5lsP2bka9TiHUjgMBjygeZEis1oC8LfJTS8FSgpKPaQx2tQ==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-riscv64-gnu": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.9.0.tgz", - "integrity": "sha512-w/NZfHNeDusbqSZ8r/hp8iL4S39h4+vQMc9/vvzuIKMWKppyUGKm3IST0Qv0aOZ1rzIbl9SrDeIqK86ZpUK37w==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-riscv64-musl": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.9.0.tgz", - "integrity": "sha512-bEPBosut8/8KQbUixPry8zg/fOzVOWyvwzOfz0C0Rw6dp+wIBseyiHKjkcSyZKv/98edrbMknBaMNJfA/UEdqw==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-s390x-gnu": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.9.0.tgz", - "integrity": "sha512-LDtMT7moE3gK753gG4pc31AAqGUC86j3AplaFusc717EUGF9ZFJ356sdQzzZzkBk1XzMdxFyZ4f/i35NKM/lFA==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-x64-gnu": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.9.0.tgz", - "integrity": "sha512-WmFd5KINHIXj8o1mPaT8QRjA9HgSXhN1gl9Da4IZihARihEnOylu4co7i/yeaIpcfsI6sYs33cNZKyHYDh0lrA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-linux-x64-musl": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.9.0.tgz", - "integrity": "sha512-CYuXbANW+WgzVRIl8/QvZmDaZxrqvOldOwlbUjIM4pQ46FJ0W5cinJ/Ghwa/Ng1ZPMJMk1VFdsD/XwmCGIXBWg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@unrs/resolver-binding-wasm32-wasi": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.9.0.tgz", - "integrity": "sha512-6Rp2WH0OoitMYR57Z6VE8Y6corX8C6QEMWLgOV6qXiJIeZ1F9WGXY/yQ8yDC4iTraotyLOeJ2Asea0urWj2fKQ==", - "cpu": [ - "wasm32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "dependencies": { - "@napi-rs/wasm-runtime": "^0.2.11" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@unrs/resolver-binding-win32-arm64-msvc": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.9.0.tgz", - "integrity": "sha512-rknkrTRuvujprrbPmGeHi8wYWxmNVlBoNW8+4XF2hXUnASOjmuC9FNF1tGbDiRQWn264q9U/oGtixyO3BT8adQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@unrs/resolver-binding-win32-ia32-msvc": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.9.0.tgz", - "integrity": "sha512-Ceymm+iBl+bgAICtgiHyMLz6hjxmLJKqBim8tDzpX61wpZOx2bPK6Gjuor7I2RiUynVjvvkoRIkrPyMwzBzF3A==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@unrs/resolver-binding-win32-x64-msvc": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.9.0.tgz", - "integrity": "sha512-k59o9ZyeyS0hAlcaKFezYSH2agQeRFEB7KoQLXl3Nb3rgkqT1NY9Vwy+SqODiLmYnEjxWJVRE/yq2jFVqdIxZw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", - "license": "ISC", - "optional": true - }, - "node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "license": "MIT", - "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/accepts/node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "license": "MIT", - "optional": true, - "dependencies": { - "debug": "4" - }, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/agent-base/node_modules/debug": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", - "license": "MIT", - "optional": true, - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/agent-base/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT", - "optional": true - }, - "node_modules/agentkeepalive": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.6.0.tgz", - "integrity": "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==", - "license": "MIT", - "optional": true, - "dependencies": { - "humanize-ms": "^1.2.1" - }, - "engines": { - "node": ">= 8.0.0" - } - }, - "node_modules/aggregate-error": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", - "license": "MIT", - "optional": true, - "dependencies": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "devOptional": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, - "license": "ISC", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/append-field": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", - "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==", - "license": "MIT" - }, - "node_modules/aproba": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", - "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", - "license": "ISC", - "optional": true - }, - "node_modules/are-we-there-yet": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz", - "integrity": "sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==", - "deprecated": "This package is no longer supported.", - "license": "ISC", - "optional": true, - "dependencies": { - "delegates": "^1.0.0", - "readable-stream": "^3.6.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "license": "Python-2.0" - }, - "node_modules/array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", - "license": "MIT" - }, - "node_modules/asap": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", - "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", - "dev": true, - "license": "MIT" - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "dev": true, - "license": "MIT" - }, - "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/babel-jest": { - "version": "30.0.0", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-30.0.0.tgz", - "integrity": "sha512-JQ0DhdFjODbSawDf0026uZuwaqfKkQzk+9mwWkq2XkKFIaMhFVOxlVmbFCOnnC76jATdxrff3IiUAvOAJec6tw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/transform": "30.0.0", - "@types/babel__core": "^7.20.5", - "babel-plugin-istanbul": "^7.0.0", - "babel-preset-jest": "30.0.0", - "chalk": "^4.1.2", - "graceful-fs": "^4.2.11", - "slash": "^3.0.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.11.0" - } - }, - "node_modules/babel-plugin-istanbul": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-7.0.0.tgz", - "integrity": "sha512-C5OzENSx/A+gt7t4VH1I2XsflxyPUmXRFPKBxt33xncdOmq7oROVM3bZv9Ysjjkv8OJYDMa+tKuKMvqU/H3xdw==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.3", - "istanbul-lib-instrument": "^6.0.2", - "test-exclude": "^6.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/babel-plugin-jest-hoist": { - "version": "30.0.0", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-30.0.0.tgz", - "integrity": "sha512-DSRm+US/FCB4xPDD6Rnslb6PAF9Bej1DZ+1u4aTiqJnk7ZX12eHsnDiIOqjGvITCq+u6wLqUhgS+faCNbVY8+g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/template": "^7.27.2", - "@babel/types": "^7.27.3", - "@types/babel__core": "^7.20.5" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/babel-preset-current-node-syntax": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.1.0.tgz", - "integrity": "sha512-ldYss8SbBlWva1bs28q78Ju5Zq1F+8BrqBZZ0VFhLBvhh6lCpC2o3gDJi/5DRLs9FgYZCnmPYIVFU4lRXCkyUw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-bigint": "^7.8.3", - "@babel/plugin-syntax-class-properties": "^7.12.13", - "@babel/plugin-syntax-class-static-block": "^7.14.5", - "@babel/plugin-syntax-import-attributes": "^7.24.7", - "@babel/plugin-syntax-import-meta": "^7.10.4", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5", - "@babel/plugin-syntax-top-level-await": "^7.14.5" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/babel-preset-jest": { - "version": "30.0.0", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-30.0.0.tgz", - "integrity": "sha512-hgEuu/W7gk8QOWUA9+m3Zk+WpGvKc1Egp6rFQEfYxEoM9Fk/q8nuTXNL65OkhwGrTApauEGgakOoWVXj+UfhKw==", - "dev": true, - "license": "MIT", - "dependencies": { - "babel-plugin-jest-hoist": "30.0.0", - "babel-preset-current-node-syntax": "^1.1.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.11.0" - } - }, - "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==", - "devOptional": true, - "license": "MIT" - }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" + "name": "backend", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "backend", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "bcrypt": "~6.0.0", + "compression": "~1.8.0", + "connect-session-sequelize": "~7.1.7", + "cors": "~2.8.5", + "dotenv": "~16.5.0", + "eslint": "^8.0.0", + "express": "~4.18.2", + "express-session": "~1.18.1", + "googleapis": "~144.0.0", + "helmet": "~8.1.0", + "js-yaml": "~4.1.0", + "moment-timezone": "~0.6.0", + "morgan": "~1.10.0", + "multer": "~2.0.1", + "node-cron": "~4.1.0", + "recharts": "~2.15.4", + "sequelize": "~6.37.7", + "sqlite3": "~5.1.7", + "uuid": "~11.1.0" + }, + "devDependencies": { + "cross-env": "~7.0.3", + "eslint-plugin-jest": "^29.0.1", + "eslint-plugin-prettier": "^5.5.1", + "jest": "~30.0.0", + "nodemon": "~3.0.1", + "prettier": "~3.6.2", + "sequelize-cli": "~6.6.2", + "supertest": "~7.1.1" + } }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/basic-auth": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", - "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", - "license": "MIT", - "dependencies": { - "safe-buffer": "5.1.2" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/basic-auth/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "license": "MIT" - }, - "node_modules/bcrypt": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-6.0.0.tgz", - "integrity": "sha512-cU8v/EGSrnH+HnxV2z0J7/blxH8gq7Xh2JFT6Aroax7UohdmiJJlxApMxtKfuI7z68NvvVcmR78k2LbT6efhRg==", - "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "node-addon-api": "^8.3.0", - "node-gyp-build": "^4.8.4" - }, - "engines": { - "node": ">= 18" - } - }, - "node_modules/bignumber.js": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.0.tgz", - "integrity": "sha512-EM7aMFTXbptt/wZdMlBv2t8IViwQL+h6SLHosp8Yf0dqJMTnY6iL32opnAB6kAdL0SZPuvcAzFr31o0c/R3/RA==", - "license": "MIT", - "engines": { - "node": "*" - } - }, - "node_modules/binary-extensions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", - "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/bindings": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", - "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", - "license": "MIT", - "dependencies": { - "file-uri-to-path": "1.0.0" - } - }, - "node_modules/bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "license": "MIT", - "dependencies": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, - "node_modules/bluebird": { - "version": "3.7.2", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", - "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", - "dev": true, - "license": "MIT" - }, - "node_modules/body-parser": { - "version": "1.20.3", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", - "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", - "license": "MIT", - "dependencies": { - "bytes": "3.1.2", - "content-type": "~1.0.5", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.13.0", - "raw-body": "2.5.2", - "type-is": "~1.6.18", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/body-parser/node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/boolbase": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", - "license": "ISC" - }, - "node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, - "license": "MIT", - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browserslist": { - "version": "4.25.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.0.tgz", - "integrity": "sha512-PJ8gYKeS5e/whHBh8xrwYK+dAvEj7JXtz6uTucnMRB8OiGTsKccFekoRrjajPBHV8oOY+2tI4uxeceSimKwMFA==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" + "node_modules/@babel/compat-data": { + "version": "7.27.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.27.7.tgz", + "integrity": "sha512-xgu/ySj2mTiUFmdE9yCMfBxLp4DHd5DwmbbD05YAuICfodYT3VvRxbrh81LGQ/8UpSdtMdfKMn3KouYDX59DGQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "caniuse-lite": "^1.0.30001718", - "electron-to-chromium": "^1.5.160", - "node-releases": "^2.0.19", - "update-browserslist-db": "^1.1.3" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/bser": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", - "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "node-int64": "^0.4.0" - } - }, - "node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" + "node_modules/@babel/core": { + "version": "7.27.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.27.7.tgz", + "integrity": "sha512-BU2f9tlKQ5CAthiMIgpzAh4eDTLWo1mqi9jqE2OxMG0E/OM199VJt2q8BztTxpnSW0i1ymdwLXRJnYzvDM5r2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.27.5", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-module-transforms": "^7.27.3", + "@babel/helpers": "^7.27.6", + "@babel/parser": "^7.27.7", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.27.7", + "@babel/types": "^7.27.7", + "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" + } }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" + "node_modules/@babel/core/node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, - "node_modules/buffer-equal-constant-time": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", - "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", - "license": "BSD-3-Clause" - }, - "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==", - "license": "MIT" - }, - "node_modules/busboy": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", - "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", - "dependencies": { - "streamsearch": "^1.1.0" - }, - "engines": { - "node": ">=10.16.0" - } - }, - "node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/cacache": { - "version": "15.3.0", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.3.0.tgz", - "integrity": "sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ==", - "license": "ISC", - "optional": true, - "dependencies": { - "@npmcli/fs": "^1.0.0", - "@npmcli/move-file": "^1.0.1", - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "glob": "^7.1.4", - "infer-owner": "^1.0.4", - "lru-cache": "^6.0.0", - "minipass": "^3.1.1", - "minipass-collect": "^1.0.2", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.2", - "mkdirp": "^1.0.3", - "p-map": "^4.0.0", - "promise-inflight": "^1.0.1", - "rimraf": "^3.0.2", - "ssri": "^8.0.1", - "tar": "^6.0.2", - "unique-filename": "^1.1.1" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/cacache/node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "license": "MIT", - "optional": true, - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/call-bind-apply-helpers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/call-bound": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", - "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", - "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/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001723", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001723.tgz", - "integrity": "sha512-1R/elMjtehrFejxwmexeXAtae5UO9iSyFn6G/I806CYC/BLyyBk1EPhrKBkWhy6wM6Xnm47dSJQec+tLJ39WHw==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" + "node_modules/@babel/core/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" }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + "node_modules/@babel/core/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" + } }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "CC-BY-4.0" - }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/chalk/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/chalk/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/char-regex": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", - "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/cheerio": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.1.0.tgz", - "integrity": "sha512-+0hMx9eYhJvWbgpKV9hN7jg0JcwydpopZE4hgi+KvQtByZXPp04NiCWU0LzcAbP63abZckIHkTQaXVF52mX3xQ==", - "license": "MIT", - "dependencies": { - "cheerio-select": "^2.1.0", - "dom-serializer": "^2.0.0", - "domhandler": "^5.0.3", - "domutils": "^3.2.2", - "encoding-sniffer": "^0.2.0", - "htmlparser2": "^10.0.0", - "parse5": "^7.3.0", - "parse5-htmlparser2-tree-adapter": "^7.1.0", - "parse5-parser-stream": "^7.1.2", - "undici": "^7.10.0", - "whatwg-mimetype": "^4.0.0" - }, - "engines": { - "node": ">=18.17" - }, - "funding": { - "url": "https://github.com/cheeriojs/cheerio?sponsor=1" - } - }, - "node_modules/cheerio-select": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz", - "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==", - "license": "BSD-2-Clause", - "dependencies": { - "boolbase": "^1.0.0", - "css-select": "^5.1.0", - "css-what": "^6.1.0", - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3", - "domutils": "^3.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "dev": true, - "license": "MIT", - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/chownr": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/ci-info": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.2.0.tgz", - "integrity": "sha512-cYY9mypksY8NRqgDB1XD1RiJL338v/551niynFTGkZOO2LHuB2OmOYxDIe/ttN9AHwrqdum1360G3ald0W9kCg==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/cjs-module-lexer": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-2.1.0.tgz", - "integrity": "sha512-UX0OwmYRYQQetfrLEZeewIFFI+wSTofC+pMBLNuH3RUuu/xzG1oz84UCEDOSoQlN3fZ4+AzmV50ZYvGqkMh9yA==", - "dev": true, - "license": "MIT" - }, - "node_modules/clean-stack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "node_modules/clsx": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", - "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", - "dev": true, - "license": "MIT", - "engines": { - "iojs": ">= 1.0.0", - "node": ">= 0.12.0" - } - }, - "node_modules/collect-v8-coverage": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", - "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, - "node_modules/color-support": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", - "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", - "license": "ISC", - "optional": true, - "bin": { - "color-support": "bin.js" - } - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, - "license": "MIT", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/commander": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", - "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14" - } - }, - "node_modules/component-emitter": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.1.tgz", - "integrity": "sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/compressible": { - "version": "2.0.18", - "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", - "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", - "license": "MIT", - "dependencies": { - "mime-db": ">= 1.43.0 < 2" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/compression": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/compression/-/compression-1.8.0.tgz", - "integrity": "sha512-k6WLKfunuqCYD3t6AsuPGvQWaKwuLLh2/xHNcX4qE+vIfDNXpSqnrhwA7O53R7WVQUnt8dVAIW+YHr7xTgOgGA==", - "license": "MIT", - "dependencies": { - "bytes": "3.1.2", - "compressible": "~2.0.18", - "debug": "2.6.9", - "negotiator": "~0.6.4", - "on-headers": "~1.0.2", - "safe-buffer": "5.2.1", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "devOptional": true, - "license": "MIT" - }, - "node_modules/concat-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", - "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", - "engines": [ - "node >= 6.0" - ], - "license": "MIT", - "dependencies": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.0.2", - "typedarray": "^0.0.6" - } - }, - "node_modules/config-chain": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", - "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ini": "^1.3.4", - "proto-list": "~1.2.1" - } - }, - "node_modules/connect-session-sequelize": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/connect-session-sequelize/-/connect-session-sequelize-7.1.7.tgz", - "integrity": "sha512-Wqq7rg0w+9bOVs6jC0nhZnssXJ3+iKNlDVWn2JfBuBPoY7oYaxzxfBKeUYrX6dHt3OWEWbZV6LJvapwi76iBQQ==", - "license": "MIT", - "dependencies": { - "debug": "^4.1.1" - }, - "engines": { - "node": ">= 10" - }, - "peerDependencies": { - "sequelize": ">= 6.1.0" - } - }, - "node_modules/connect-session-sequelize/node_modules/debug": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/connect-session-sequelize/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", - "license": "ISC", - "optional": true - }, - "node_modules/content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "license": "MIT", - "dependencies": { - "safe-buffer": "5.2.1" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "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/cookie": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", - "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", - "license": "MIT" - }, - "node_modules/cookiejar": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz", - "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==", - "dev": true, - "license": "MIT" - }, - "node_modules/cors": { - "version": "2.8.5", - "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", - "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", - "license": "MIT", - "dependencies": { - "object-assign": "^4", - "vary": "^1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/cross-env": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", - "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==", - "dev": true, - "license": "MIT", - "dependencies": { - "cross-spawn": "^7.0.1" - }, - "bin": { - "cross-env": "src/bin/cross-env.js", - "cross-env-shell": "src/bin/cross-env-shell.js" - }, - "engines": { - "node": ">=10.14", - "npm": ">=6", - "yarn": ">=1" - } - }, - "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/css-select": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", - "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", - "license": "BSD-2-Clause", - "dependencies": { - "boolbase": "^1.0.0", - "css-what": "^6.1.0", - "domhandler": "^5.0.2", - "domutils": "^3.0.1", - "nth-check": "^2.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/css-what": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", - "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", - "license": "BSD-2-Clause", - "engines": { - "node": ">= 6" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/csstype": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", - "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "license": "MIT" - }, - "node_modules/d3-array": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", - "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", - "license": "ISC", - "dependencies": { - "internmap": "1 - 2" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-color": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", - "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-ease": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", - "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-format": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz", - "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-interpolate": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", - "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", - "license": "ISC", - "dependencies": { - "d3-color": "1 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-path": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", - "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-scale": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", - "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", - "license": "ISC", - "dependencies": { - "d3-array": "2.10.0 - 3", - "d3-format": "1 - 3", - "d3-interpolate": "1.2.0 - 3", - "d3-time": "2.1.1 - 3", - "d3-time-format": "2 - 4" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-shape": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", - "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", - "license": "ISC", - "dependencies": { - "d3-path": "^3.1.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-time": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", - "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", - "license": "ISC", - "dependencies": { - "d3-array": "2 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-time-format": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", - "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", - "license": "ISC", - "dependencies": { - "d3-time": "1 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-timer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", - "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/decimal.js-light": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz", - "integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==", - "license": "MIT" - }, - "node_modules/decompress-response": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", - "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", - "license": "MIT", - "dependencies": { - "mimic-response": "^3.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/dedent": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.6.0.tgz", - "integrity": "sha512-F1Z+5UCFpmQUzJa11agbyPVMbpgT/qA3/SKyJ1jyBgm7dUcUEa8v9JwDkerSQXfakBwFljIxhOJqGkjUwZ9FSA==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "babel-plugin-macros": "^3.1.0" - }, - "peerDependenciesMeta": { - "babel-plugin-macros": { - "optional": true - } - } - }, - "node_modules/deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "license": "MIT", - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/deepmerge": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", - "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", - "license": "MIT", - "optional": true - }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", - "license": "MIT", - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/detect-libc": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", - "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==", - "license": "Apache-2.0", - "engines": { - "node": ">=8" - } - }, - "node_modules/detect-newline": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", - "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/dezalgo": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz", - "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==", - "dev": true, - "license": "ISC", - "dependencies": { - "asap": "^2.0.0", - "wrappy": "1" - } - }, - "node_modules/dom-helpers": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", - "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.8.7", - "csstype": "^3.0.2" - } - }, - "node_modules/dom-serializer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", - "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", - "license": "MIT", - "dependencies": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.2", - "entities": "^4.2.0" - }, - "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" - } - }, - "node_modules/domelementtype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", - "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], - "license": "BSD-2-Clause" - }, - "node_modules/domhandler": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", - "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", - "license": "BSD-2-Clause", - "dependencies": { - "domelementtype": "^2.3.0" - }, - "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" - } - }, - "node_modules/domutils": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", - "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", - "license": "BSD-2-Clause", - "dependencies": { - "dom-serializer": "^2.0.0", - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3" - }, - "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" - } - }, - "node_modules/dotenv": { - "version": "16.5.0", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.5.0.tgz", - "integrity": "sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://dotenvx.com" - } - }, - "node_modules/dottie": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/dottie/-/dottie-2.0.6.tgz", - "integrity": "sha512-iGCHkfUc5kFekGiqhe8B/mdaurD+lakO9txNnTvKtA6PISrw86LgqHvRzWYPyoE2Ph5aMIrCw9/uko6XHTKCwA==", - "license": "MIT" - }, - "node_modules/dunder-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true, - "license": "MIT" - }, - "node_modules/ecdsa-sig-formatter": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", - "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", - "license": "Apache-2.0", - "dependencies": { - "safe-buffer": "^5.0.1" - } - }, - "node_modules/editorconfig": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-1.0.4.tgz", - "integrity": "sha512-L9Qe08KWTlqYMVvMcTIvMAdl1cDUubzRNYL+WfA4bLDMHe4nemKkpmYzkznE1FwLKu0EEmy6obgQKzMJrg4x9Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@one-ini/wasm": "0.1.1", - "commander": "^10.0.0", - "minimatch": "9.0.1", - "semver": "^7.5.3" - }, - "bin": { - "editorconfig": "bin/editorconfig" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/editorconfig/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/editorconfig/node_modules/minimatch": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.1.tgz", - "integrity": "sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", - "license": "MIT" - }, - "node_modules/electron-to-chromium": { - "version": "1.5.167", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.167.tgz", - "integrity": "sha512-LxcRvnYO5ez2bMOFpbuuVuAI5QNeY1ncVytE/KXaL6ZNfzX1yPlAO0nSOyIHx2fVAuUprMqPs/TdVhUFZy7SIQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/emittery": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", - "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sindresorhus/emittery?sponsor=1" - } - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "devOptional": true, - "license": "MIT" - }, - "node_modules/encodeurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", - "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/encoding": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", - "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", - "license": "MIT", - "optional": true, - "dependencies": { - "iconv-lite": "^0.6.2" - } - }, - "node_modules/encoding-sniffer": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/encoding-sniffer/-/encoding-sniffer-0.2.1.tgz", - "integrity": "sha512-5gvq20T6vfpekVtqrYQsSCFZ1wEg5+wW0/QaZMWkFr6BqD3NfKs0rLCx4rrVlSWJeZb5NBJgVLswK/w2MWU+Gw==", - "license": "MIT", - "dependencies": { - "iconv-lite": "^0.6.3", - "whatwg-encoding": "^3.1.1" - }, - "funding": { - "url": "https://github.com/fb55/encoding-sniffer?sponsor=1" - } - }, - "node_modules/end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "license": "MIT", - "dependencies": { - "once": "^1.4.0" - } - }, - "node_modules/entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/env-paths": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", - "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/err-code": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", - "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", - "license": "MIT", - "optional": true - }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, - "node_modules/es-define-property": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-object-atoms": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-set-tostringtag": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", - "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", - "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/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/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", - "license": "MIT" - }, - "node_modules/escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, - "license": "BSD-2-Clause", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/eventemitter3": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", - "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", - "license": "MIT" - }, - "node_modules/execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dev": true, - "license": "MIT", - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/exit-x": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/exit-x/-/exit-x-0.2.2.tgz", - "integrity": "sha512-+I6B/IkJc1o/2tiURyz/ivu/O0nKNEArIUB5O7zBrlDVJr22SCLH3xTeEry428LvFhRzIA1g8izguxJ/gbNcVQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/expand-template": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", - "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", - "license": "(MIT OR WTFPL)", - "engines": { - "node": ">=6" - } - }, - "node_modules/expect": { - "version": "30.0.0", - "resolved": "https://registry.npmjs.org/expect/-/expect-30.0.0.tgz", - "integrity": "sha512-xCdPp6gwiR9q9lsPCHANarIkFTN/IMZso6Kkq03sOm9IIGtzK/UJqml0dkhHibGh8HKOj8BIDIpZ0BZuU7QK6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/expect-utils": "30.0.0", - "@jest/get-type": "30.0.0", - "jest-matcher-utils": "30.0.0", - "jest-message-util": "30.0.0", - "jest-mock": "30.0.0", - "jest-util": "30.0.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/express": { - "version": "4.21.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", - "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", - "license": "MIT", - "dependencies": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "1.20.3", - "content-disposition": "0.5.4", - "content-type": "~1.0.4", - "cookie": "0.7.1", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "2.0.0", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "1.3.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "merge-descriptors": "1.0.3", - "methods": "~1.1.2", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.12", - "proxy-addr": "~2.0.7", - "qs": "6.13.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "0.19.0", - "serve-static": "1.16.2", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.10.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/express-session": { - "version": "1.18.1", - "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.18.1.tgz", - "integrity": "sha512-a5mtTqEaZvBCL9A9aqkrtfz+3SMDhOVUnjafjo+s7A9Txkq+SVX2DLvSp1Zrv4uCXa3lMSK3viWnh9Gg07PBUA==", - "license": "MIT", - "dependencies": { - "cookie": "0.7.2", - "cookie-signature": "1.0.7", - "debug": "2.6.9", - "depd": "~2.0.0", - "on-headers": "~1.0.2", - "parseurl": "~1.3.3", - "safe-buffer": "5.2.1", - "uid-safe": "~2.1.5" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/express-session/node_modules/cookie-signature": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz", - "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==", - "license": "MIT" - }, - "node_modules/express/node_modules/cookie": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", - "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "license": "MIT" - }, - "node_modules/fast-equals": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.2.2.tgz", - "integrity": "sha512-V7/RktU11J3I36Nwq2JnZEM7tNm17eBJz+u25qdxBZeCKiX6BkVSZQjwWIr+IobgnZy+ag73tTZgZi7tr0LrBw==", - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "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-safe-stringify": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", - "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==", - "dev": true, - "license": "MIT" - }, - "node_modules/fb-watchman": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", - "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "bser": "2.1.1" - } - }, - "node_modules/file-uri-to-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", - "license": "MIT" - }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, - "license": "MIT", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/finalhandler": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", - "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", - "license": "MIT", - "dependencies": { - "debug": "2.6.9", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "statuses": "2.0.1", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/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/foreground-child/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/form-data": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.3.tgz", - "integrity": "sha512-qsITQPfmvMOSAdeyZ+12I1c+CKSstAFAwu+97zrnWAbIr5u8wfsExUzCesVLC8NgHuRUqNN4Zy6UPWUTRGslcA==", - "dev": true, - "license": "MIT", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "es-set-tostringtag": "^2.1.0", - "hasown": "^2.0.2", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/formidable": { - "version": "3.5.4", - "resolved": "https://registry.npmjs.org/formidable/-/formidable-3.5.4.tgz", - "integrity": "sha512-YikH+7CUTOtP44ZTnUhR7Ic2UASBPOqmaRkRKxRbywPTe5VxF7RRCck4af9wutiZ/QKM5nME9Bie2fFaPz5Gug==", - "dev": true, - "license": "MIT", - "dependencies": { - "@paralleldrive/cuid2": "^2.2.2", - "dezalgo": "^1.0.4", - "once": "^1.4.0" - }, - "engines": { - "node": ">=14.0.0" - }, - "funding": { - "url": "https://ko-fi.com/tunnckoCore/commissions" - } - }, - "node_modules/forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fs-constants": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", - "license": "MIT" - }, - "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/fs-minipass": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", - "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", - "license": "ISC", - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "devOptional": true, - "license": "ISC" - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/gauge": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz", - "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==", - "deprecated": "This package is no longer supported.", - "license": "ISC", - "optional": true, - "dependencies": { - "aproba": "^1.0.3 || ^2.0.0", - "color-support": "^1.1.3", - "console-control-strings": "^1.1.0", - "has-unicode": "^2.0.1", - "signal-exit": "^3.0.7", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1", - "wide-align": "^1.1.5" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/gaxios": { - "version": "6.7.1", - "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-6.7.1.tgz", - "integrity": "sha512-LDODD4TMYx7XXdpwxAVRAIAuB0bzv0s+ywFonY46k126qzQHT9ygyoa9tncmOiQmmDrik65UYsEkv3lbfqQ3yQ==", - "license": "Apache-2.0", - "dependencies": { - "extend": "^3.0.2", - "https-proxy-agent": "^7.0.1", - "is-stream": "^2.0.0", - "node-fetch": "^2.6.9", - "uuid": "^9.0.1" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/gaxios/node_modules/agent-base": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", - "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", - "license": "MIT", - "engines": { - "node": ">= 14" - } - }, - "node_modules/gaxios/node_modules/debug": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/gaxios/node_modules/https-proxy-agent": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", - "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", - "license": "MIT", - "dependencies": { - "agent-base": "^7.1.2", - "debug": "4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/gaxios/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/gaxios/node_modules/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "license": "MIT", - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/gcp-metadata": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-6.1.1.tgz", - "integrity": "sha512-a4tiq7E0/5fTjxPAaH4jpjkSv/uCaU2p5KC6HVGrvl0cDjA8iBZv4vv1gyzlmK0ZUKqwpOyQMKzZQe3lTit77A==", - "license": "Apache-2.0", - "dependencies": { - "gaxios": "^6.1.1", - "google-logging-utils": "^0.0.2", - "json-bigint": "^1.0.0" - }, - "engines": { - "node": ">=14" - } - }, - "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-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, - "license": "ISC", - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-intrinsic": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", - "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "function-bind": "^1.1.2", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "math-intrinsics": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-package-type": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/get-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", - "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/github-from-package": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", - "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", - "license": "MIT" - }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "devOptional": true, - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/google-auth-library": { - "version": "9.15.1", - "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-9.15.1.tgz", - "integrity": "sha512-Jb6Z0+nvECVz+2lzSMt9u98UsoakXxA2HGHMCxh+so3n90XgYWkq5dur19JAJV7ONiJY22yBTyJB1TSkvPq9Ng==", - "license": "Apache-2.0", - "dependencies": { - "base64-js": "^1.3.0", - "ecdsa-sig-formatter": "^1.0.11", - "gaxios": "^6.1.1", - "gcp-metadata": "^6.1.0", - "gtoken": "^7.0.0", - "jws": "^4.0.0" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/google-logging-utils": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-0.0.2.tgz", - "integrity": "sha512-NEgUnEcBiP5HrPzufUkBzJOD/Sxsco3rLNo1F1TNf7ieU8ryUzBhqba8r756CjLX7rn3fHl6iLEwPYuqpoKgQQ==", - "license": "Apache-2.0", - "engines": { - "node": ">=14" - } - }, - "node_modules/googleapis": { - "version": "144.0.0", - "resolved": "https://registry.npmjs.org/googleapis/-/googleapis-144.0.0.tgz", - "integrity": "sha512-ELcWOXtJxjPX4vsKMh+7V+jZvgPwYMlEhQFiu2sa9Qmt5veX8nwXPksOWGGN6Zk4xCiLygUyaz7xGtcMO+Onxw==", - "license": "Apache-2.0", - "dependencies": { - "google-auth-library": "^9.0.0", - "googleapis-common": "^7.0.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/googleapis-common": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/googleapis-common/-/googleapis-common-7.2.0.tgz", - "integrity": "sha512-/fhDZEJZvOV3X5jmD+fKxMqma5q2Q9nZNSF3kn1F18tpxmA86BcTxAGBQdM0N89Z3bEaIs+HVznSmFJEAmMTjA==", - "license": "Apache-2.0", - "dependencies": { - "extend": "^3.0.2", - "gaxios": "^6.0.3", - "google-auth-library": "^9.7.0", - "qs": "^6.7.0", - "url-template": "^2.0.8", - "uuid": "^9.0.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/googleapis-common/node_modules/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "license": "MIT", - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/gopd": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/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==", - "devOptional": true, - "license": "ISC" - }, - "node_modules/gtoken": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-7.1.0.tgz", - "integrity": "sha512-pCcEwRi+TKpMlxAQObHDQ56KawURgyAf6jtIY046fJ5tIv3zDe/LEIubckAO8fj6JnAxLdmWkUfNyulQ2iKdEw==", - "license": "MIT", - "dependencies": { - "gaxios": "^6.0.0", - "jws": "^4.0.0" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/has-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-tostringtag": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", - "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-symbols": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", - "license": "ISC", - "optional": true - }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/helmet": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/helmet/-/helmet-8.1.0.tgz", - "integrity": "sha512-jOiHyAZsmnr8LqoPGmCjYAaiuWwjAPLgY8ZX2XrmHawt99/u1y6RgrZMTeoPfpUbV96HOalYgz1qzkRbw54Pmg==", - "license": "MIT", - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true, - "license": "MIT" - }, - "node_modules/htmlparser2": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-10.0.0.tgz", - "integrity": "sha512-TwAZM+zE5Tq3lrEHvOlvwgj1XLWQCtaaibSN11Q+gGBAS7Y1uZSWwXXRe4iF6OXnaq1riyQAPFOBtYc77Mxq0g==", - "funding": [ - "https://github.com/fb55/htmlparser2?sponsor=1", - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], - "license": "MIT", - "dependencies": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3", - "domutils": "^3.2.1", - "entities": "^6.0.0" - } - }, - "node_modules/htmlparser2/node_modules/entities": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", - "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/http-cache-semantics": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", - "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==", - "license": "BSD-2-Clause", - "optional": true - }, - "node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "license": "MIT", - "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/http-proxy-agent": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", - "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", - "license": "MIT", - "optional": true, - "dependencies": { - "@tootallnate/once": "1", - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/http-proxy-agent/node_modules/debug": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", - "license": "MIT", - "optional": true, - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/http-proxy-agent/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT", - "optional": true - }, - "node_modules/https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", - "license": "MIT", - "optional": true, - "dependencies": { - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/https-proxy-agent/node_modules/debug": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", - "license": "MIT", - "optional": true, - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/https-proxy-agent/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT", - "optional": true - }, - "node_modules/human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=10.17.0" - } - }, - "node_modules/humanize-ms": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", - "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", - "license": "MIT", - "optional": true, - "dependencies": { - "ms": "^2.0.0" - } - }, - "node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" + "node_modules/@babel/generator": { + "version": "7.27.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.5.tgz", + "integrity": "sha512-ZGhA37l0e/g2s1Cnzdix0O3aLYm66eF8aufiVteOgnwxgnRP8GoyMj7VWsgWnQbVKXyge7hqrFh2K2TQM6t1Hw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.27.5", + "@babel/types": "^7.27.3", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" + "node_modules/@babel/helper-compilation-targets": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.27.2", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } }, - { - "type": "consulting", - "url": "https://feross.org/support" + "node_modules/@babel/helper-compilation-targets/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/@babel/helper-module-imports": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.27.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.27.3.tgz", + "integrity": "sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.27.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", + "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.27.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.6.tgz", + "integrity": "sha512-muE8Tt8M22638HU31A3CgfSUciwz1fhATfoVai05aPXGor//CdWDCbnlY1yvBPo07njuVOCNGCSp/GTt12lIug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.27.2", + "@babel/types": "^7.27.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.27.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.7.tgz", + "integrity": "sha512-qnzXzDXdr/po3bOTbTIQZ7+TxNKxpkN5IifVLXS+r7qwynkZfPyjZfE7hCXbo7IoO9TNcSyibgONsf2HauUd3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.27.7" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.27.1.tgz", + "integrity": "sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz", + "integrity": "sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz", + "integrity": "sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.27.6", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.6.tgz", + "integrity": "sha512-vbavdySgbTTrmFE+EsiqUTzlOr5bzlnJtUv9PynGCAKvfQqjIXbvFdumPM/GxMDfyuGMJaJAU6TO4zc1Jf1i8Q==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.27.7", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.7.tgz", + "integrity": "sha512-X6ZlfR/O/s5EQ/SnUSLzr+6kGnkg8HXGMzpgsMsrJVcfDtH1vIp6ctCN4eZ1LS5c0+te5Cb6Y514fASjMRJ1nw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.27.5", + "@babel/parser": "^7.27.7", + "@babel/template": "^7.27.2", + "@babel/types": "^7.27.7", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@babel/traverse/node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/traverse/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/@babel/types": { + "version": "7.27.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.7.tgz", + "integrity": "sha512-8OLQgDScAOHXnAz2cV+RfzzNMipuLVBz2biuAJFMV9bfkNf393je3VM8CLkjQodW5+iWsSJdSgSWT6rsZoXHPw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@emnapi/core": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.4.3.tgz", + "integrity": "sha512-4m62DuCE07lw01soJwPiBGC0nAww0Q+RY70VZ+n49yDIO13yyinhbWCeNnaob0lakDtWQzSdtNWzJeOJt2ma+g==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.0.2", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.3.tgz", + "integrity": "sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.0.2.tgz", + "integrity": "sha512-5n3nTJblwRi8LlXkJ9eBzu+kZR8Yxcc7ubakyQTFzPMtIhFpUBRbsnc2Dv88IZDIbCDlBiWrknhB4Lsz7mg6BA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", + "integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==", + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@eslint/eslintrc/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/@eslint/js": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", + "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", + "license": "MIT", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@gar/promisify": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", + "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==", + "license": "MIT", + "optional": true + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", + "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", + "deprecated": "Use @eslint/config-array instead", + "license": "Apache-2.0", + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.3", + "debug": "^4.3.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/config-array/node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@humanwhocodes/config-array/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "deprecated": "Use @eslint/object-schema instead", + "license": "BSD-3-Clause" + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/console": { + "version": "30.0.2", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-30.0.2.tgz", + "integrity": "sha512-krGElPU0FipAqpVZ/BRZOy0MZh/ARdJ0Nj+PiH1ykFY1+VpBlYNLjdjVA5CFKxnKR6PFqFutO4Z7cdK9BlGiDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "30.0.1", + "@types/node": "*", + "chalk": "^4.1.2", + "jest-message-util": "30.0.2", + "jest-util": "30.0.2", + "slash": "^3.0.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/core": { + "version": "30.0.3", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-30.0.3.tgz", + "integrity": "sha512-Mgs1N+NSHD3Fusl7bOq1jyxv1JDAUwjy+0DhVR93Q6xcBP9/bAQ+oZhXb5TTnP5sQzAHgb7ROCKQ2SnovtxYtg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "30.0.2", + "@jest/pattern": "30.0.1", + "@jest/reporters": "30.0.2", + "@jest/test-result": "30.0.2", + "@jest/transform": "30.0.2", + "@jest/types": "30.0.1", + "@types/node": "*", + "ansi-escapes": "^4.3.2", + "chalk": "^4.1.2", + "ci-info": "^4.2.0", + "exit-x": "^0.2.2", + "graceful-fs": "^4.2.11", + "jest-changed-files": "30.0.2", + "jest-config": "30.0.3", + "jest-haste-map": "30.0.2", + "jest-message-util": "30.0.2", + "jest-regex-util": "30.0.1", + "jest-resolve": "30.0.2", + "jest-resolve-dependencies": "30.0.3", + "jest-runner": "30.0.3", + "jest-runtime": "30.0.3", + "jest-snapshot": "30.0.3", + "jest-util": "30.0.2", + "jest-validate": "30.0.2", + "jest-watcher": "30.0.2", + "micromatch": "^4.0.8", + "pretty-format": "30.0.2", + "slash": "^3.0.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/diff-sequences": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/@jest/diff-sequences/-/diff-sequences-30.0.1.tgz", + "integrity": "sha512-n5H8QLDJ47QqbCNn5SuFjCRDrOLEZ0h8vAHCK5RL9Ls7Xa8AQLa/YxAc9UjFqoEDM48muwtBGjtMY5cr0PLDCw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/environment": { + "version": "30.0.2", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-30.0.2.tgz", + "integrity": "sha512-hRLhZRJNxBiOhxIKSq2UkrlhMt3/zVFQOAi5lvS8T9I03+kxsbflwHJEF+eXEYXCrRGRhHwECT7CDk6DyngsRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/fake-timers": "30.0.2", + "@jest/types": "30.0.1", + "@types/node": "*", + "jest-mock": "30.0.2" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/expect": { + "version": "30.0.3", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-30.0.3.tgz", + "integrity": "sha512-73BVLqfCeWjYWPEQoYjiRZ4xuQRhQZU0WdgvbyXGRHItKQqg5e6mt2y1kVhzLSuZpmUnccZHbGynoaL7IcLU3A==", + "dev": true, + "license": "MIT", + "dependencies": { + "expect": "30.0.3", + "jest-snapshot": "30.0.3" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/expect-utils": { + "version": "30.0.3", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-30.0.3.tgz", + "integrity": "sha512-SMtBvf2sfX2agcT0dA9pXwcUrKvOSDqBY4e4iRfT+Hya33XzV35YVg+98YQFErVGA/VR1Gto5Y2+A6G9LSQ3Yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/get-type": "30.0.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/fake-timers": { + "version": "30.0.2", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-30.0.2.tgz", + "integrity": "sha512-jfx0Xg7l0gmphTY9UKm5RtH12BlLYj/2Plj6wXjVW5Era4FZKfXeIvwC67WX+4q8UCFxYS20IgnMcFBcEU0DtA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "30.0.1", + "@sinonjs/fake-timers": "^13.0.0", + "@types/node": "*", + "jest-message-util": "30.0.2", + "jest-mock": "30.0.2", + "jest-util": "30.0.2" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/get-type": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/@jest/get-type/-/get-type-30.0.1.tgz", + "integrity": "sha512-AyYdemXCptSRFirI5EPazNxyPwAL0jXt3zceFjaj8NFiKP9pOi0bfXonf6qkf82z2t3QWPeLCWWw4stPBzctLw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/globals": { + "version": "30.0.3", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-30.0.3.tgz", + "integrity": "sha512-fIduqNyYpMeeSr5iEAiMn15KxCzvrmxl7X7VwLDRGj7t5CoHtbF+7K3EvKk32mOUIJ4kIvFRlaixClMH2h/Vaw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "30.0.2", + "@jest/expect": "30.0.3", + "@jest/types": "30.0.1", + "jest-mock": "30.0.2" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/pattern": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/@jest/pattern/-/pattern-30.0.1.tgz", + "integrity": "sha512-gWp7NfQW27LaBQz3TITS8L7ZCQ0TLvtmI//4OwlQRx4rnWxcPNIYjxZpDcN4+UlGxgm3jS5QPz8IPTCkb59wZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "jest-regex-util": "30.0.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/reporters": { + "version": "30.0.2", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-30.0.2.tgz", + "integrity": "sha512-l4QzS/oKf57F8WtPZK+vvF4Io6ukplc6XgNFu4Hd/QxaLEO9f+8dSFzUua62Oe0HKlCUjKHpltKErAgDiMJKsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "30.0.2", + "@jest/test-result": "30.0.2", + "@jest/transform": "30.0.2", + "@jest/types": "30.0.1", + "@jridgewell/trace-mapping": "^0.3.25", + "@types/node": "*", + "chalk": "^4.1.2", + "collect-v8-coverage": "^1.0.2", + "exit-x": "^0.2.2", + "glob": "^10.3.10", + "graceful-fs": "^4.2.11", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^6.0.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^5.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "30.0.2", + "jest-util": "30.0.2", + "jest-worker": "30.0.2", + "slash": "^3.0.0", + "string-length": "^4.0.2", + "v8-to-istanbul": "^9.0.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/schemas": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.0.1.tgz", + "integrity": "sha512-+g/1TKjFuGrf1Hh0QPCv0gISwBxJ+MQSNXmG9zjHy7BmFhtoJ9fdNhWJp3qUKRi93AOZHXtdxZgJ1vAtz6z65w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sinclair/typebox": "^0.34.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/snapshot-utils": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/@jest/snapshot-utils/-/snapshot-utils-30.0.1.tgz", + "integrity": "sha512-6Dpv7vdtoRiISEFwYF8/c7LIvqXD7xDXtLPNzC2xqAfBznKip0MQM+rkseKwUPUpv2PJ7KW/YsnwWXrIL2xF+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "30.0.1", + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", + "natural-compare": "^1.4.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/source-map": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-30.0.1.tgz", + "integrity": "sha512-MIRWMUUR3sdbP36oyNyhbThLHyJ2eEDClPCiHVbrYAe5g3CHRArIVpBw7cdSB5fr+ofSfIb2Tnsw8iEHL0PYQg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.25", + "callsites": "^3.1.0", + "graceful-fs": "^4.2.11" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/test-result": { + "version": "30.0.2", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-30.0.2.tgz", + "integrity": "sha512-KKMuBKkkZYP/GfHMhI+cH2/P3+taMZS3qnqqiPC1UXZTJskkCS+YU/ILCtw5anw1+YsTulDHFpDo70mmCedW8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "30.0.2", + "@jest/types": "30.0.1", + "@types/istanbul-lib-coverage": "^2.0.6", + "collect-v8-coverage": "^1.0.2" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/test-sequencer": { + "version": "30.0.2", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-30.0.2.tgz", + "integrity": "sha512-fbyU5HPka0rkalZ3MXVvq0hwZY8dx3Y6SCqR64zRmh+xXlDeFl0IdL4l9e7vp4gxEXTYHbwLFA1D+WW5CucaSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/test-result": "30.0.2", + "graceful-fs": "^4.2.11", + "jest-haste-map": "30.0.2", + "slash": "^3.0.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/transform": { + "version": "30.0.2", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-30.0.2.tgz", + "integrity": "sha512-kJIuhLMTxRF7sc0gPzPtCDib/V9KwW3I2U25b+lYCYMVqHHSrcZopS8J8H+znx9yixuFv+Iozl8raLt/4MoxrA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.27.4", + "@jest/types": "30.0.1", + "@jridgewell/trace-mapping": "^0.3.25", + "babel-plugin-istanbul": "^7.0.0", + "chalk": "^4.1.2", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.11", + "jest-haste-map": "30.0.2", + "jest-regex-util": "30.0.1", + "jest-util": "30.0.2", + "micromatch": "^4.0.8", + "pirates": "^4.0.7", + "slash": "^3.0.0", + "write-file-atomic": "^5.0.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jest/types": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.0.1.tgz", + "integrity": "sha512-HGwoYRVF0QSKJu1ZQX0o5ZrUrrhj0aOOFA8hXrumD7SIzjouevhawbTjmXdwOmURdGluU9DM/XvGm3NyFoiQjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/pattern": "30.0.1", + "@jest/schemas": "30.0.1", + "@types/istanbul-lib-coverage": "^2.0.6", + "@types/istanbul-reports": "^3.0.4", + "@types/node": "*", + "@types/yargs": "^17.0.33", + "chalk": "^4.1.2" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.11", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.11.tgz", + "integrity": "sha512-C512c1ytBTio4MrpWKlJpyFHT6+qfFL8SZ58zBzJ1OOzUEjHeF1BtjY2fH7n4x/g2OV/KiiMLAivOp1DXmiMMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.3.tgz", + "integrity": "sha512-AiR5uKpFxP3PjO4R19kQGIMwxyRyPuXmKEEy301V1C0+1rVjS94EZQXf1QKZYN8Q0YM+estSPhmx5JwNftv6nw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.28", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.28.tgz", + "integrity": "sha512-KNNHHwW3EIp4EDYOvYFGyIFfx36R2dNJYH4knnZlF8T5jdbD5Wx8xmSaQ2gP9URkJ04LGEtlcCtwArKcmFcwKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "0.2.11", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.11.tgz", + "integrity": "sha512-9DPkXtvHydrcOsopiYpUgPHpmj0HWZKMUnL2dZqpvC42lsratuBG06V5ipyno0fUek5VlFsNQ+AcFATSrJXgMA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.4.3", + "@emnapi/runtime": "^1.4.3", + "@tybys/wasm-util": "^0.9.0" + } + }, + "node_modules/@noble/hashes": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@npmcli/fs": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-1.1.1.tgz", + "integrity": "sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ==", + "license": "ISC", + "optional": true, + "dependencies": { + "@gar/promisify": "^1.0.1", + "semver": "^7.3.5" + } + }, + "node_modules/@npmcli/move-file": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.1.2.tgz", + "integrity": "sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==", + "deprecated": "This functionality has been moved to @npmcli/fs", + "license": "MIT", + "optional": true, + "dependencies": { + "mkdirp": "^1.0.4", + "rimraf": "^3.0.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@npmcli/move-file/node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "license": "MIT", + "optional": true, + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@one-ini/wasm": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@one-ini/wasm/-/wasm-0.1.1.tgz", + "integrity": "sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@paralleldrive/cuid2": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@paralleldrive/cuid2/-/cuid2-2.2.2.tgz", + "integrity": "sha512-ZOBkgDwEdoYVlSeRbYYXs0S9MejQofiVYoTbKzy/6GQa39/q5tQU2IX46+shYnUkpEl3wc+J6wRlar7r2EK2xA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@noble/hashes": "^1.1.5" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@pkgr/core": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.7.tgz", + "integrity": "sha512-YLT9Zo3oNPJoBjBc4q8G2mjU4tqIbf5CEOORbUUr48dCD9q3umJ3IPlVqOqDakPfd2HuwccBaqlGhN4Gmr5OWg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/pkgr" + } + }, + "node_modules/@sinclair/typebox": { + "version": "0.34.37", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.37.tgz", + "integrity": "sha512-2TRuQVgQYfy+EzHRTIvkhv2ADEouJ2xNS/Vq+W5EuuewBdOrvATvljZTxHWZSTYr2sTjTHpGvucaGAt67S2akw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "13.0.5", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-13.0.5.tgz", + "integrity": "sha512-36/hTbH2uaWuGVERyC6da9YwGWnzUZXuPro/F2LfsdOsLnCojz/iSH8MxUt/FD2S5XBSVPhmArFUXcpCQ2Hkiw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.1" + } + }, + "node_modules/@tootallnate/once": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@tybys/wasm-util": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.9.0.tgz", + "integrity": "sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "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.20.7", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.7.tgz", + "integrity": "sha512-dkO5fhS7+/oos4ciWxyEyjWe48zmG6wbCheo/G2ZnHx4fs3EU6YC6UM8rk56gAjNJ9P3MTH2jo5jb92/K6wbng==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.20.7" + } + }, + "node_modules/@types/d3-array": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.1.tgz", + "integrity": "sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==", + "license": "MIT" + }, + "node_modules/@types/d3-color": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz", + "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==", + "license": "MIT" + }, + "node_modules/@types/d3-ease": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz", + "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==", + "license": "MIT" + }, + "node_modules/@types/d3-interpolate": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", + "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==", + "license": "MIT", + "dependencies": { + "@types/d3-color": "*" + } + }, + "node_modules/@types/d3-path": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz", + "integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==", + "license": "MIT" + }, + "node_modules/@types/d3-scale": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz", + "integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==", + "license": "MIT", + "dependencies": { + "@types/d3-time": "*" + } + }, + "node_modules/@types/d3-shape": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.7.tgz", + "integrity": "sha512-VLvUQ33C+3J+8p+Daf+nYSOsjB4GXp19/S/aGo60m9h1v6XaxjiT82lKVWJCfzhtuZ3yD7i/TPeC/fuKLLOSmg==", + "license": "MIT", + "dependencies": { + "@types/d3-path": "*" + } + }, + "node_modules/@types/d3-time": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz", + "integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==", + "license": "MIT" + }, + "node_modules/@types/d3-timer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz", + "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==", + "license": "MIT" + }, + "node_modules/@types/debug": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", + "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", + "license": "MIT", + "dependencies": { + "@types/ms": "*" + } + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "24.0.8", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.0.8.tgz", + "integrity": "sha512-WytNrFSgWO/esSH9NbpWUfTMGQwCGIKfCmNlmFDNiI5gGhgMmEA+V1AEvKLeBNvvtBnailJtkrEa2OIISwrVAA==", + "license": "MIT", + "dependencies": { + "undici-types": "~7.8.0" + } + }, + "node_modules/@types/stack-utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/validator": { + "version": "13.15.2", + "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.15.2.tgz", + "integrity": "sha512-y7pa/oEJJ4iGYBxOpfAKn5b9+xuihvzDVnC/OSvlVnGxVg0pOqmjiMafiJ1KVNQEaPZf9HsEp5icEwGg8uIe5Q==", + "license": "MIT" + }, + "node_modules/@types/yargs": { + "version": "17.0.33", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", + "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.35.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.35.1.tgz", + "integrity": "sha512-VYxn/5LOpVxADAuP3NrnxxHYfzVtQzLKeldIhDhzC8UHaiQvYlXvKuVho1qLduFbJjjy5U5bkGwa3rUGUb1Q6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.35.1", + "@typescript-eslint/types": "^8.35.1", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/project-service/node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/project-service/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/@typescript-eslint/scope-manager": { + "version": "8.35.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.35.1.tgz", + "integrity": "sha512-s/Bpd4i7ht2934nG+UoSPlYXd08KYz3bmjLEb7Ye1UVob0d1ENiT3lY8bsCmik4RqfSbPw9xJJHbugpPpP5JUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.35.1", + "@typescript-eslint/visitor-keys": "8.35.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.35.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.35.1.tgz", + "integrity": "sha512-K5/U9VmT9dTHoNowWZpz+/TObS3xqC5h0xAIjXPw+MNcKV9qg6eSatEnmeAwkjHijhACH0/N7bkhKvbt1+DXWQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.35.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.35.1.tgz", + "integrity": "sha512-q/O04vVnKHfrrhNAscndAn1tuQhIkwqnaW+eu5waD5IPts2eX1dgJxgqcPx5BX109/qAz7IG6VrEPTOYKCNfRQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.35.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.35.1.tgz", + "integrity": "sha512-Vvpuvj4tBxIka7cPs6Y1uvM7gJgdF5Uu9F+mBJBPY4MhvjrjWGK4H0lVgLJd/8PWZ23FTqsaJaLEkBCFUk8Y9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.35.1", + "@typescript-eslint/tsconfig-utils": "8.35.1", + "@typescript-eslint/types": "8.35.1", + "@typescript-eslint/visitor-keys": "8.35.1", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/typescript-estree/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/@typescript-eslint/utils": { + "version": "8.35.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.35.1.tgz", + "integrity": "sha512-lhnwatFmOFcazAsUm3ZnZFpXSxiwoa1Lj50HphnDe1Et01NF4+hrdXONSUHIcbVu2eFb1bAf+5yjXkGVkXBKAQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.7.0", + "@typescript-eslint/scope-manager": "8.35.1", + "@typescript-eslint/types": "8.35.1", + "@typescript-eslint/typescript-estree": "8.35.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.35.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.35.1.tgz", + "integrity": "sha512-VRwixir4zBWCSTP/ljEo091lbpypz57PoeAQ9imjG+vbeof9LplljsL1mos4ccG6H9IjfrVGM359RozUnuFhpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.35.1", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@ungap/structured-clone": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", + "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", + "license": "ISC" + }, + "node_modules/@unrs/resolver-binding-android-arm-eabi": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm-eabi/-/resolver-binding-android-arm-eabi-1.9.2.tgz", + "integrity": "sha512-tS+lqTU3N0kkthU+rYp0spAYq15DU8ld9kXkaKg9sbQqJNF+WPMuNHZQGCgdxrUOEO0j22RKMwRVhF1HTl+X8A==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@unrs/resolver-binding-android-arm64": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-android-arm64/-/resolver-binding-android-arm64-1.9.2.tgz", + "integrity": "sha512-MffGiZULa/KmkNjHeuuflLVqfhqLv1vZLm8lWIyeADvlElJ/GLSOkoUX+5jf4/EGtfwrNFcEaB8BRas03KT0/Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@unrs/resolver-binding-darwin-arm64": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.9.2.tgz", + "integrity": "sha512-dzJYK5rohS1sYl1DHdJ3mwfwClJj5BClQnQSyAgEfggbUwA9RlROQSSbKBLqrGfsiC/VyrDPtbO8hh56fnkbsQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@unrs/resolver-binding-darwin-x64": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.9.2.tgz", + "integrity": "sha512-gaIMWK+CWtXcg9gUyznkdV54LzQ90S3X3dn8zlh+QR5Xy7Y+Efqw4Rs4im61K1juy4YNb67vmJsCDAGOnIeffQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@unrs/resolver-binding-freebsd-x64": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.9.2.tgz", + "integrity": "sha512-S7QpkMbVoVJb0xwHFwujnwCAEDe/596xqY603rpi/ioTn9VDgBHnCCxh+UFrr5yxuMH+dliHfjwCZJXOPJGPnw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm-gnueabihf": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.9.2.tgz", + "integrity": "sha512-+XPUMCuCCI80I46nCDFbGum0ZODP5NWGiwS3Pj8fOgsG5/ctz+/zzuBlq/WmGa+EjWZdue6CF0aWWNv84sE1uw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm-musleabihf": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.9.2.tgz", + "integrity": "sha512-sqvUyAd1JUpwbz33Ce2tuTLJKM+ucSsYpPGl2vuFwZnEIg0CmdxiZ01MHQ3j6ExuRqEDUCy8yvkDKvjYFPb8Zg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm64-gnu": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.9.2.tgz", + "integrity": "sha512-UYA0MA8ajkEDCFRQdng/FVx3F6szBvk3EPnkTTQuuO9lV1kPGuTB+V9TmbDxy5ikaEgyWKxa4CI3ySjklZ9lFA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-arm64-musl": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.9.2.tgz", + "integrity": "sha512-P/CO3ODU9YJIHFqAkHbquKtFst0COxdphc8TKGL5yCX75GOiVpGqd1d15ahpqu8xXVsqP4MGFP2C3LRZnnL5MA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-ppc64-gnu": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.9.2.tgz", + "integrity": "sha512-uKStFlOELBxBum2s1hODPtgJhY4NxYJE9pAeyBgNEzHgTqTiVBPjfTlPFJkfxyTjQEuxZbbJlJnMCrRgD7ubzw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-riscv64-gnu": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.9.2.tgz", + "integrity": "sha512-LkbNnZlhINfY9gK30AHs26IIVEZ9PEl9qOScYdmY2o81imJYI4IMnJiW0vJVtXaDHvBvxeAgEy5CflwJFIl3tQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-riscv64-musl": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.9.2.tgz", + "integrity": "sha512-vI+e6FzLyZHSLFNomPi+nT+qUWN4YSj8pFtQZSFTtmgFoxqB6NyjxSjAxEC1m93qn6hUXhIsh8WMp+fGgxCoRg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-s390x-gnu": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.9.2.tgz", + "integrity": "sha512-sSO4AlAYhSM2RAzBsRpahcJB1msc6uYLAtP6pesPbZtptF8OU/CbCPhSRW6cnYOGuVmEmWVW5xVboAqCnWTeHQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-x64-gnu": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.9.2.tgz", + "integrity": "sha512-jkSkwch0uPFva20Mdu8orbQjv2A3G88NExTN2oPTI1AJ+7mZfYW3cDCTyoH6OnctBKbBVeJCEqh0U02lTkqD5w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-linux-x64-musl": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.9.2.tgz", + "integrity": "sha512-Uk64NoiTpQbkpl+bXsbeyOPRpUoMdcUqa+hDC1KhMW7aN1lfW8PBlBH4mJ3n3Y47dYE8qi0XTxy1mBACruYBaw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@unrs/resolver-binding-wasm32-wasi": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.9.2.tgz", + "integrity": "sha512-EpBGwkcjDicjR/ybC0g8wO5adPNdVuMrNalVgYcWi+gYtC1XYNuxe3rufcO7dA76OHGeVabcO6cSkPJKVcbCXQ==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@napi-rs/wasm-runtime": "^0.2.11" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@unrs/resolver-binding-win32-arm64-msvc": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.9.2.tgz", + "integrity": "sha512-EdFbGn7o1SxGmN6aZw9wAkehZJetFPao0VGZ9OMBwKx6TkvDuj6cNeLimF/Psi6ts9lMOe+Dt6z19fZQ9Ye2fw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@unrs/resolver-binding-win32-ia32-msvc": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.9.2.tgz", + "integrity": "sha512-JY9hi1p7AG+5c/dMU8o2kWemM8I6VZxfGwn1GCtf3c5i+IKcMo2NQ8OjZ4Z3/itvY/Si3K10jOBQn7qsD/whUA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@unrs/resolver-binding-win32-x64-msvc": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.9.2.tgz", + "integrity": "sha512-ryoo+EB19lMxAd80ln9BVf8pdOAxLb97amrQ3SFN9OCRn/5M5wvwDgAe4i8ZjhpbiHoDeP8yavcTEnpKBo7lZg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/abbrev": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-2.0.0.tgz", + "integrity": "sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/accepts/node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/agent-base": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", + "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/agentkeepalive": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.6.0.tgz", + "integrity": "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "humanize-ms": "^1.2.1" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "license": "MIT", + "optional": true, + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-escapes/node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/append-field": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", + "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==", + "license": "MIT" + }, + "node_modules/aproba": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", + "license": "ISC", + "optional": true + }, + "node_modules/are-we-there-yet": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz", + "integrity": "sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==", + "deprecated": "This package is no longer supported.", + "license": "ISC", + "optional": true, + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "license": "Python-2.0" + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "license": "MIT" + }, + "node_modules/asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", + "dev": true, + "license": "MIT" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true, + "license": "MIT" + }, + "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/babel-jest": { + "version": "30.0.2", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-30.0.2.tgz", + "integrity": "sha512-A5kqR1/EUTidM2YC2YMEUDP2+19ppgOwK0IAd9Swc3q2KqFb5f9PtRUXVeZcngu0z5mDMyZ9zH2huJZSOMLiTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/transform": "30.0.2", + "@types/babel__core": "^7.20.5", + "babel-plugin-istanbul": "^7.0.0", + "babel-preset-jest": "30.0.1", + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", + "slash": "^3.0.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.11.0" + } + }, + "node_modules/babel-plugin-istanbul": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-7.0.0.tgz", + "integrity": "sha512-C5OzENSx/A+gt7t4VH1I2XsflxyPUmXRFPKBxt33xncdOmq7oROVM3bZv9Ysjjkv8OJYDMa+tKuKMvqU/H3xdw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-instrument": "^6.0.2", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/babel-plugin-jest-hoist": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-30.0.1.tgz", + "integrity": "sha512-zTPME3pI50NsFW8ZBaVIOeAxzEY7XHlmWeXXu9srI+9kNfzCUTy8MFan46xOGZY8NZThMqq+e3qZUKsvXbasnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.27.2", + "@babel/types": "^7.27.3", + "@types/babel__core": "^7.20.5" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/babel-preset-current-node-syntax": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.1.0.tgz", + "integrity": "sha512-ldYss8SbBlWva1bs28q78Ju5Zq1F+8BrqBZZ0VFhLBvhh6lCpC2o3gDJi/5DRLs9FgYZCnmPYIVFU4lRXCkyUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-import-attributes": "^7.24.7", + "@babel/plugin-syntax-import-meta": "^7.10.4", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/babel-preset-jest": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-30.0.1.tgz", + "integrity": "sha512-+YHejD5iTWI46cZmcc/YtX4gaKBtdqCHCVfuVinizVpbmyjO3zYmeuyFdfA8duRqQZfgCAMlsfmkVbJ+e2MAJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "babel-plugin-jest-hoist": "30.0.1", + "babel-preset-current-node-syntax": "^1.1.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.11.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/basic-auth": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", + "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.1.2" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/basic-auth/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "license": "MIT" + }, + "node_modules/bcrypt": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-6.0.0.tgz", + "integrity": "sha512-cU8v/EGSrnH+HnxV2z0J7/blxH8gq7Xh2JFT6Aroax7UohdmiJJlxApMxtKfuI7z68NvvVcmR78k2LbT6efhRg==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "node-addon-api": "^8.3.0", + "node-gyp-build": "^4.8.4" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/bignumber.js": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.0.tgz", + "integrity": "sha512-EM7aMFTXbptt/wZdMlBv2t8IViwQL+h6SLHosp8Yf0dqJMTnY6iL32opnAB6kAdL0SZPuvcAzFr31o0c/R3/RA==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "license": "MIT", + "dependencies": { + "file-uri-to-path": "1.0.0" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "license": "MIT", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", + "dev": true, + "license": "MIT" + }, + "node_modules/body-parser": { + "version": "1.20.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.2.tgz", + "integrity": "sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==", + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.11.0", + "raw-body": "2.5.2", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.25.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.1.tgz", + "integrity": "sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw==", + "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": { + "caniuse-lite": "^1.0.30001726", + "electron-to-chromium": "^1.5.173", + "node-releases": "^2.0.19", + "update-browserslist-db": "^1.1.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "node-int64": "^0.4.0" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "license": "BSD-3-Clause" + }, + "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==", + "license": "MIT" + }, + "node_modules/busboy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", + "dependencies": { + "streamsearch": "^1.1.0" + }, + "engines": { + "node": ">=10.16.0" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/cacache": { + "version": "15.3.0", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.3.0.tgz", + "integrity": "sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ==", + "license": "ISC", + "optional": true, + "dependencies": { + "@npmcli/fs": "^1.0.0", + "@npmcli/move-file": "^1.0.1", + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "glob": "^7.1.4", + "infer-owner": "^1.0.4", + "lru-cache": "^6.0.0", + "minipass": "^3.1.1", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.2", + "mkdirp": "^1.0.3", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^3.0.2", + "ssri": "^8.0.1", + "tar": "^6.0.2", + "unique-filename": "^1.1.1" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/cacache/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "license": "ISC", + "optional": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/cacache/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "license": "ISC", + "optional": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/cacache/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "license": "ISC", + "optional": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cacache/node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "license": "MIT", + "optional": true, + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/cacache/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC", + "optional": true + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "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/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001726", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001726.tgz", + "integrity": "sha512-VQAUIUzBiZ/UnlM28fSp2CRF3ivUn1BWEvxMcVTNwpw91Py1pGbPIyIKtd+tzct9C3ouceCVdGAXxZOpZAsgdw==", + "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/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/ci-info": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.2.0.tgz", + "integrity": "sha512-cYY9mypksY8NRqgDB1XD1RiJL338v/551niynFTGkZOO2LHuB2OmOYxDIe/ttN9AHwrqdum1360G3ald0W9kCg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cjs-module-lexer": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-2.1.0.tgz", + "integrity": "sha512-UX0OwmYRYQQetfrLEZeewIFFI+wSTofC+pMBLNuH3RUuu/xzG1oz84UCEDOSoQlN3fZ4+AzmV50ZYvGqkMh9yA==", + "dev": true, + "license": "MIT" + }, + "node_modules/clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/cliui/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/cliui/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/collect-v8-coverage": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", + "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "license": "MIT" + }, + "node_modules/color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "license": "ISC", + "optional": true, + "bin": { + "color-support": "bin.js" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/component-emitter": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.1.tgz", + "integrity": "sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", + "license": "MIT", + "dependencies": { + "mime-db": ">= 1.43.0 < 2" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/compression": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/compression/-/compression-1.8.0.tgz", + "integrity": "sha512-k6WLKfunuqCYD3t6AsuPGvQWaKwuLLh2/xHNcX4qE+vIfDNXpSqnrhwA7O53R7WVQUnt8dVAIW+YHr7xTgOgGA==", + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "compressible": "~2.0.18", + "debug": "2.6.9", + "negotiator": "~0.6.4", + "on-headers": "~1.0.2", + "safe-buffer": "5.2.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "license": "MIT" + }, + "node_modules/concat-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz", + "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==", + "engines": [ + "node >= 6.0" + ], + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.0.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/config-chain": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", + "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ini": "^1.3.4", + "proto-list": "~1.2.1" + } + }, + "node_modules/connect-session-sequelize": { + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/connect-session-sequelize/-/connect-session-sequelize-7.1.7.tgz", + "integrity": "sha512-Wqq7rg0w+9bOVs6jC0nhZnssXJ3+iKNlDVWn2JfBuBPoY7oYaxzxfBKeUYrX6dHt3OWEWbZV6LJvapwi76iBQQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.1.1" + }, + "engines": { + "node": ">= 10" + }, + "peerDependencies": { + "sequelize": ">= 6.1.0" + } + }, + "node_modules/connect-session-sequelize/node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/connect-session-sequelize/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", + "license": "ISC", + "optional": true + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "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/cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", + "license": "MIT" + }, + "node_modules/cookiejar": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz", + "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==", + "dev": true, + "license": "MIT" + }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/cross-env": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", + "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.1" + }, + "bin": { + "cross-env": "src/bin/cross-env.js", + "cross-env-shell": "src/bin/cross-env-shell.js" + }, + "engines": { + "node": ">=10.14", + "npm": ">=6", + "yarn": ">=1" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "license": "MIT" + }, + "node_modules/d3-array": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", + "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", + "license": "ISC", + "dependencies": { + "internmap": "1 - 2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-format": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz", + "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", + "license": "ISC", + "dependencies": { + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-shape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "license": "ISC", + "dependencies": { + "d3-path": "^3.1.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", + "license": "ISC", + "dependencies": { + "d3-array": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time-format": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "license": "ISC", + "dependencies": { + "d3-time": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-timer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/decimal.js-light": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz", + "integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==", + "license": "MIT" + }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "license": "MIT", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/dedent": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.6.0.tgz", + "integrity": "sha512-F1Z+5UCFpmQUzJa11agbyPVMbpgT/qA3/SKyJ1jyBgm7dUcUEa8v9JwDkerSQXfakBwFljIxhOJqGkjUwZ9FSA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" + }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "license": "MIT" + }, + "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/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", + "license": "MIT", + "optional": true + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "license": "MIT", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/detect-libc": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", + "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/dezalgo": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz", + "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==", + "dev": true, + "license": "ISC", + "dependencies": { + "asap": "^2.0.0", + "wrappy": "1" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/dom-helpers": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", + "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + } + }, + "node_modules/dotenv": { + "version": "16.5.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.5.0.tgz", + "integrity": "sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dottie": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/dottie/-/dottie-2.0.6.tgz", + "integrity": "sha512-iGCHkfUc5kFekGiqhe8B/mdaurD+lakO9txNnTvKtA6PISrw86LgqHvRzWYPyoE2Ph5aMIrCw9/uko6XHTKCwA==", + "license": "MIT" + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, + "license": "MIT" + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/editorconfig": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-1.0.4.tgz", + "integrity": "sha512-L9Qe08KWTlqYMVvMcTIvMAdl1cDUubzRNYL+WfA4bLDMHe4nemKkpmYzkznE1FwLKu0EEmy6obgQKzMJrg4x9Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@one-ini/wasm": "0.1.1", + "commander": "^10.0.0", + "minimatch": "9.0.1", + "semver": "^7.5.3" + }, + "bin": { + "editorconfig": "bin/editorconfig" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/editorconfig/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/editorconfig/node_modules/minimatch": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.1.tgz", + "integrity": "sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/electron-to-chromium": { + "version": "1.5.178", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.178.tgz", + "integrity": "sha512-wObbz/ar3Bc6e4X5vf0iO8xTN8YAjN/tgiAOJLr7yjYFtP9wAjq8Mb5h0yn6kResir+VYx2DXBj9NNobs0ETSA==", + "dev": true, + "license": "ISC" + }, + "node_modules/emittery": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", + "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/encoding": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", + "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", + "license": "MIT", + "optional": true, + "dependencies": { + "iconv-lite": "^0.6.2" + } + }, + "node_modules/encoding/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "license": "MIT", + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/err-code": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", + "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", + "license": "MIT", + "optional": true + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "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/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/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", + "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", + "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.1", + "@humanwhocodes/config-array": "^0.13.0", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-plugin-jest": { + "version": "29.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-29.0.1.tgz", + "integrity": "sha512-EE44T0OSMCeXhDrrdsbKAhprobKkPtJTbQz5yEktysNpHeDZTAL1SfDTNKmcFfJkY6yrQLtTKZALrD3j/Gpmiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/utils": "^8.0.0" + }, + "engines": { + "node": "^20.12.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "@typescript-eslint/eslint-plugin": "^8.0.0", + "eslint": "^8.57.0 || ^9.0.0", + "jest": "*" + }, + "peerDependenciesMeta": { + "@typescript-eslint/eslint-plugin": { + "optional": true + }, + "jest": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-prettier": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.1.tgz", + "integrity": "sha512-dobTkHT6XaEVOo8IO90Q4DOSxnm3Y151QxPJlM/vKC0bVy+d6cVWQZLlFiuZPP0wS6vZwSKeJgKkcS+KfMBlRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "prettier-linter-helpers": "^1.0.0", + "synckit": "^0.11.7" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint-plugin-prettier" + }, + "peerDependencies": { + "@types/eslint": ">=8.0.0", + "eslint": ">=8.0.0", + "eslint-config-prettier": ">= 7.0.0 <10.0.0 || >=10.1.0", + "prettier": ">=3.0.0" + }, + "peerDependenciesMeta": { + "@types/eslint": { + "optional": true + }, + "eslint-config-prettier": { + "optional": true + } + } + }, + "node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/eslint/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "license": "MIT" + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/execa/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/exit-x": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/exit-x/-/exit-x-0.2.2.tgz", + "integrity": "sha512-+I6B/IkJc1o/2tiURyz/ivu/O0nKNEArIUB5O7zBrlDVJr22SCLH3xTeEry428LvFhRzIA1g8izguxJ/gbNcVQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "license": "(MIT OR WTFPL)", + "engines": { + "node": ">=6" + } + }, + "node_modules/expect": { + "version": "30.0.3", + "resolved": "https://registry.npmjs.org/expect/-/expect-30.0.3.tgz", + "integrity": "sha512-HXg6NvK35/cSYZCUKAtmlgCFyqKM4frEPbzrav5hRqb0GMz0E0lS5hfzYjSaiaE5ysnp/qI2aeZkeyeIAOeXzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/expect-utils": "30.0.3", + "@jest/get-type": "30.0.1", + "jest-matcher-utils": "30.0.3", + "jest-message-util": "30.0.2", + "jest-mock": "30.0.2", + "jest-util": "30.0.2" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/express": { + "version": "4.18.3", + "resolved": "https://registry.npmjs.org/express/-/express-4.18.3.tgz", + "integrity": "sha512-6VyCijWQ+9O7WuVMTRBTl+cjNNIzD5cY5mQ1WM8r/LEkI2u8EYpOotESNwzNlyCn3g+dmjKYI6BmNneSr/FSRw==", + "license": "MIT", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.2", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.5.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.2.0", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.11.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/express-session": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/express-session/-/express-session-1.18.1.tgz", + "integrity": "sha512-a5mtTqEaZvBCL9A9aqkrtfz+3SMDhOVUnjafjo+s7A9Txkq+SVX2DLvSp1Zrv4uCXa3lMSK3viWnh9Gg07PBUA==", + "license": "MIT", + "dependencies": { + "cookie": "0.7.2", + "cookie-signature": "1.0.7", + "debug": "2.6.9", + "depd": "~2.0.0", + "on-headers": "~1.0.2", + "parseurl": "~1.3.3", + "safe-buffer": "5.2.1", + "uid-safe": "~2.1.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/express-session/node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express-session/node_modules/cookie-signature": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz", + "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==", + "license": "MIT" + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "license": "MIT" + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" + }, + "node_modules/fast-diff": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/fast-equals": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.2.2.tgz", + "integrity": "sha512-V7/RktU11J3I36Nwq2JnZEM7tNm17eBJz+u25qdxBZeCKiX6BkVSZQjwWIr+IobgnZy+ag73tTZgZi7tr0LrBw==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "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==", + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "license": "MIT" + }, + "node_modules/fast-safe-stringify": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", + "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==", + "dev": true, + "license": "MIT" + }, + "node_modules/fastq": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "bser": "2.1.1" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "license": "MIT", + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "license": "MIT" + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "license": "ISC" + }, + "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/form-data": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.3.tgz", + "integrity": "sha512-qsITQPfmvMOSAdeyZ+12I1c+CKSstAFAwu+97zrnWAbIr5u8wfsExUzCesVLC8NgHuRUqNN4Zy6UPWUTRGslcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/formidable": { + "version": "3.5.4", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-3.5.4.tgz", + "integrity": "sha512-YikH+7CUTOtP44ZTnUhR7Ic2UASBPOqmaRkRKxRbywPTe5VxF7RRCck4af9wutiZ/QKM5nME9Bie2fFaPz5Gug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@paralleldrive/cuid2": "^2.2.2", + "dezalgo": "^1.0.4", + "once": "^1.4.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "url": "https://ko-fi.com/tunnckoCore/commissions" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "license": "MIT" + }, + "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/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/fs-minipass/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fs-minipass/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC" + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gauge": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz", + "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==", + "deprecated": "This package is no longer supported.", + "license": "ISC", + "optional": true, + "dependencies": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.3", + "console-control-strings": "^1.1.0", + "has-unicode": "^2.0.1", + "signal-exit": "^3.0.7", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/gauge/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT", + "optional": true + }, + "node_modules/gauge/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "license": "ISC", + "optional": true + }, + "node_modules/gauge/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "optional": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/gaxios": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-6.7.1.tgz", + "integrity": "sha512-LDODD4TMYx7XXdpwxAVRAIAuB0bzv0s+ywFonY46k126qzQHT9ygyoa9tncmOiQmmDrik65UYsEkv3lbfqQ3yQ==", + "license": "Apache-2.0", + "dependencies": { + "extend": "^3.0.2", + "https-proxy-agent": "^7.0.1", + "is-stream": "^2.0.0", + "node-fetch": "^2.6.9", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/gaxios/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/gcp-metadata": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-6.1.1.tgz", + "integrity": "sha512-a4tiq7E0/5fTjxPAaH4jpjkSv/uCaU2p5KC6HVGrvl0cDjA8iBZv4vv1gyzlmK0ZUKqwpOyQMKzZQe3lTit77A==", + "license": "Apache-2.0", + "dependencies": { + "gaxios": "^6.1.1", + "google-logging-utils": "^0.0.2", + "json-bigint": "^1.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "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-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", + "license": "MIT" + }, + "node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", + "license": "MIT", + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/google-auth-library": { + "version": "9.15.1", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-9.15.1.tgz", + "integrity": "sha512-Jb6Z0+nvECVz+2lzSMt9u98UsoakXxA2HGHMCxh+so3n90XgYWkq5dur19JAJV7ONiJY22yBTyJB1TSkvPq9Ng==", + "license": "Apache-2.0", + "dependencies": { + "base64-js": "^1.3.0", + "ecdsa-sig-formatter": "^1.0.11", + "gaxios": "^6.1.1", + "gcp-metadata": "^6.1.0", + "gtoken": "^7.0.0", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/google-logging-utils": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-0.0.2.tgz", + "integrity": "sha512-NEgUnEcBiP5HrPzufUkBzJOD/Sxsco3rLNo1F1TNf7ieU8ryUzBhqba8r756CjLX7rn3fHl6iLEwPYuqpoKgQQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, + "node_modules/googleapis": { + "version": "144.0.0", + "resolved": "https://registry.npmjs.org/googleapis/-/googleapis-144.0.0.tgz", + "integrity": "sha512-ELcWOXtJxjPX4vsKMh+7V+jZvgPwYMlEhQFiu2sa9Qmt5veX8nwXPksOWGGN6Zk4xCiLygUyaz7xGtcMO+Onxw==", + "license": "Apache-2.0", + "dependencies": { + "google-auth-library": "^9.0.0", + "googleapis-common": "^7.0.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/googleapis-common": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/googleapis-common/-/googleapis-common-7.2.0.tgz", + "integrity": "sha512-/fhDZEJZvOV3X5jmD+fKxMqma5q2Q9nZNSF3kn1F18tpxmA86BcTxAGBQdM0N89Z3bEaIs+HVznSmFJEAmMTjA==", + "license": "Apache-2.0", + "dependencies": { + "extend": "^3.0.2", + "gaxios": "^6.0.3", + "google-auth-library": "^9.7.0", + "qs": "^6.7.0", + "url-template": "^2.0.8", + "uuid": "^9.0.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/googleapis-common/node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/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==", + "devOptional": true, + "license": "ISC" + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "license": "MIT" + }, + "node_modules/gtoken": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/gtoken/-/gtoken-7.1.0.tgz", + "integrity": "sha512-pCcEwRi+TKpMlxAQObHDQ56KawURgyAf6jtIY046fJ5tIv3zDe/LEIubckAO8fj6JnAxLdmWkUfNyulQ2iKdEw==", + "license": "MIT", + "dependencies": { + "gaxios": "^6.0.0", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", + "license": "ISC", + "optional": true + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/helmet": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/helmet/-/helmet-8.1.0.tgz", + "integrity": "sha512-jOiHyAZsmnr8LqoPGmCjYAaiuWwjAPLgY8ZX2XrmHawt99/u1y6RgrZMTeoPfpUbV96HOalYgz1qzkRbw54Pmg==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true, + "license": "MIT" + }, + "node_modules/http-cache-semantics": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", + "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==", + "license": "BSD-2-Clause", + "optional": true + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "license": "MIT", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-proxy-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "license": "MIT", + "optional": true, + "dependencies": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/http-proxy-agent/node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/http-proxy-agent/node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/http-proxy-agent/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT", + "optional": true + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/https-proxy-agent/node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/https-proxy-agent/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/humanize-ms": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "ms": "^2.0.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/ignore-by-default": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", + "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", + "dev": true, + "license": "ISC" + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-local": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", + "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/infer-owner": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", + "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", + "license": "ISC", + "optional": true + }, + "node_modules/inflection": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/inflection/-/inflection-1.13.4.tgz", + "integrity": "sha512-6I/HUDeYFfuNCVS3td055BaXBwKYuzw7K3ExVMStBowKo9oOAMJIXIHvdyR3iboTCp1b+1i5DSkIZTcwIktuDw==", + "engines": [ + "node >= 0.4.0" + ], + "license": "MIT" + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "license": "ISC" + }, + "node_modules/internmap": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", + "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/ip-address": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", + "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", + "license": "MIT", + "optional": true, + "dependencies": { + "jsbn": "1.1.0", + "sprintf-js": "^1.1.3" + }, + "engines": { + "node": ">= 12" + } + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-lambda": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", + "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==", + "license": "MIT", + "optional": true + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "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==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", + "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/core": "^7.23.9", + "@babel/parser": "^7.23.9", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.6.tgz", + "integrity": "sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.23", + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps/node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/istanbul-lib-source-maps/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/istanbul-reports": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", + "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jest": { + "version": "30.0.3", + "resolved": "https://registry.npmjs.org/jest/-/jest-30.0.3.tgz", + "integrity": "sha512-Uy8xfeE/WpT2ZLGDXQmaYNzw2v8NUKuYeKGtkS6sDxwsdQihdgYCXaKIYnph1h95DN5H35ubFDm0dfmsQnjn4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/core": "30.0.3", + "@jest/types": "30.0.1", + "import-local": "^3.2.0", + "jest-cli": "30.0.3" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-changed-files": { + "version": "30.0.2", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-30.0.2.tgz", + "integrity": "sha512-Ius/iRST9FKfJI+I+kpiDh8JuUlAISnRszF9ixZDIqJF17FckH5sOzKC8a0wd0+D+8em5ADRHA5V5MnfeDk2WA==", + "dev": true, + "license": "MIT", + "dependencies": { + "execa": "^5.1.1", + "jest-util": "30.0.2", + "p-limit": "^3.1.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-circus": { + "version": "30.0.3", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-30.0.3.tgz", + "integrity": "sha512-rD9qq2V28OASJHJWDRVdhoBdRs6k3u3EmBzDYcyuMby8XCO3Ll1uq9kyqM41ZcC4fMiPulMVh3qMw0cBvDbnyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "30.0.2", + "@jest/expect": "30.0.3", + "@jest/test-result": "30.0.2", + "@jest/types": "30.0.1", + "@types/node": "*", + "chalk": "^4.1.2", + "co": "^4.6.0", + "dedent": "^1.6.0", + "is-generator-fn": "^2.1.0", + "jest-each": "30.0.2", + "jest-matcher-utils": "30.0.3", + "jest-message-util": "30.0.2", + "jest-runtime": "30.0.3", + "jest-snapshot": "30.0.3", + "jest-util": "30.0.2", + "p-limit": "^3.1.0", + "pretty-format": "30.0.2", + "pure-rand": "^7.0.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.6" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-cli": { + "version": "30.0.3", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-30.0.3.tgz", + "integrity": "sha512-UWDSj0ayhumEAxpYRlqQLrssEi29kdQ+kddP94AuHhZknrE+mT0cR0J+zMHKFe9XPfX3dKQOc2TfWki3WhFTsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/core": "30.0.3", + "@jest/test-result": "30.0.2", + "@jest/types": "30.0.1", + "chalk": "^4.1.2", + "exit-x": "^0.2.2", + "import-local": "^3.2.0", + "jest-config": "30.0.3", + "jest-util": "30.0.2", + "jest-validate": "30.0.2", + "yargs": "^17.7.2" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-config": { + "version": "30.0.3", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-30.0.3.tgz", + "integrity": "sha512-j0L4oRCtJwNyZktXIqwzEiDVQXBbQ4dqXuLD/TZdn++hXIcIfZmjHgrViEy5s/+j4HvITmAXbexVZpQ/jnr0bg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.27.4", + "@jest/get-type": "30.0.1", + "@jest/pattern": "30.0.1", + "@jest/test-sequencer": "30.0.2", + "@jest/types": "30.0.1", + "babel-jest": "30.0.2", + "chalk": "^4.1.2", + "ci-info": "^4.2.0", + "deepmerge": "^4.3.1", + "glob": "^10.3.10", + "graceful-fs": "^4.2.11", + "jest-circus": "30.0.3", + "jest-docblock": "30.0.1", + "jest-environment-node": "30.0.2", + "jest-regex-util": "30.0.1", + "jest-resolve": "30.0.2", + "jest-runner": "30.0.3", + "jest-util": "30.0.2", + "jest-validate": "30.0.2", + "micromatch": "^4.0.8", + "parse-json": "^5.2.0", + "pretty-format": "30.0.2", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "peerDependencies": { + "@types/node": "*", + "esbuild-register": ">=3.4.0", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "esbuild-register": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/jest-diff": { + "version": "30.0.3", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-30.0.3.tgz", + "integrity": "sha512-Q1TAV0cUcBTic57SVnk/mug0/ASyAqtSIOkr7RAlxx97llRYsM74+E8N5WdGJUlwCKwgxPAkVjKh653h1+HA9A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/diff-sequences": "30.0.1", + "@jest/get-type": "30.0.1", + "chalk": "^4.1.2", + "pretty-format": "30.0.2" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-docblock": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-30.0.1.tgz", + "integrity": "sha512-/vF78qn3DYphAaIc3jy4gA7XSAz167n9Bm/wn/1XhTLW7tTBIzXtCJpb/vcmc73NIIeeohCbdL94JasyXUZsGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "detect-newline": "^3.1.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-each": { + "version": "30.0.2", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-30.0.2.tgz", + "integrity": "sha512-ZFRsTpe5FUWFQ9cWTMguCaiA6kkW5whccPy9JjD1ezxh+mJeqmz8naL8Fl/oSbNJv3rgB0x87WBIkA5CObIUZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/get-type": "30.0.1", + "@jest/types": "30.0.1", + "chalk": "^4.1.2", + "jest-util": "30.0.2", + "pretty-format": "30.0.2" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-environment-node": { + "version": "30.0.2", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-30.0.2.tgz", + "integrity": "sha512-XsGtZ0H+a70RsxAQkKuIh0D3ZlASXdZdhpOSBq9WRPq6lhe0IoQHGW0w9ZUaPiZQ/CpkIdprvlfV1QcXcvIQLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "30.0.2", + "@jest/fake-timers": "30.0.2", + "@jest/types": "30.0.1", + "@types/node": "*", + "jest-mock": "30.0.2", + "jest-util": "30.0.2", + "jest-validate": "30.0.2" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-haste-map": { + "version": "30.0.2", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-30.0.2.tgz", + "integrity": "sha512-telJBKpNLeCb4MaX+I5k496556Y2FiKR/QLZc0+MGBYl4k3OO0472drlV2LUe7c1Glng5HuAu+5GLYp//GpdOQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "30.0.1", + "@types/node": "*", + "anymatch": "^3.1.3", + "fb-watchman": "^2.0.2", + "graceful-fs": "^4.2.11", + "jest-regex-util": "30.0.1", + "jest-util": "30.0.2", + "jest-worker": "30.0.2", + "micromatch": "^4.0.8", + "walker": "^1.0.8" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.3" + } + }, + "node_modules/jest-leak-detector": { + "version": "30.0.2", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-30.0.2.tgz", + "integrity": "sha512-U66sRrAYdALq+2qtKffBLDWsQ/XoNNs2Lcr83sc9lvE/hEpNafJlq2lXCPUBMNqamMECNxSIekLfe69qg4KMIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/get-type": "30.0.1", + "pretty-format": "30.0.2" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-matcher-utils": { + "version": "30.0.3", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-30.0.3.tgz", + "integrity": "sha512-hMpVFGFOhYmIIRGJ0HgM9htC5qUiJ00famcc9sRFchJJiLZbbVKrAztcgE6VnXLRxA3XZ0bvNA7hQWh3oHXo/A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/get-type": "30.0.1", + "chalk": "^4.1.2", + "jest-diff": "30.0.3", + "pretty-format": "30.0.2" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-message-util": { + "version": "30.0.2", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-30.0.2.tgz", + "integrity": "sha512-vXywcxmr0SsKXF/bAD7t7nMamRvPuJkras00gqYeB1V0WllxZrbZ0paRr3XqpFU2sYYjD0qAaG2fRyn/CGZ0aw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@jest/types": "30.0.1", + "@types/stack-utils": "^2.0.3", + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", + "micromatch": "^4.0.8", + "pretty-format": "30.0.2", + "slash": "^3.0.0", + "stack-utils": "^2.0.6" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-mock": { + "version": "30.0.2", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-30.0.2.tgz", + "integrity": "sha512-PnZOHmqup/9cT/y+pXIVbbi8ID6U1XHRmbvR7MvUy4SLqhCbwpkmXhLbsWbGewHrV5x/1bF7YDjs+x24/QSvFA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "30.0.1", + "@types/node": "*", + "jest-util": "30.0.2" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-pnp-resolver": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "jest-resolve": "*" + }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } + } + }, + "node_modules/jest-regex-util": { + "version": "30.0.1", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-30.0.1.tgz", + "integrity": "sha512-jHEQgBXAgc+Gh4g0p3bCevgRCVRkB4VB70zhoAE48gxeSr1hfUOsM/C2WoJgVL7Eyg//hudYENbm3Ne+/dRVVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-resolve": { + "version": "30.0.2", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-30.0.2.tgz", + "integrity": "sha512-q/XT0XQvRemykZsvRopbG6FQUT6/ra+XV6rPijyjT6D0msOyCvR2A5PlWZLd+fH0U8XWKZfDiAgrUNDNX2BkCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.2", + "graceful-fs": "^4.2.11", + "jest-haste-map": "30.0.2", + "jest-pnp-resolver": "^1.2.3", + "jest-util": "30.0.2", + "jest-validate": "30.0.2", + "slash": "^3.0.0", + "unrs-resolver": "^1.7.11" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-resolve-dependencies": { + "version": "30.0.3", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-30.0.3.tgz", + "integrity": "sha512-FlL6u7LiHbF0Oe27k7DHYMq2T2aNpPhxnNo75F7lEtu4A6sSw+TKkNNUGNcVckdFoL0RCWREJsC1HsKDwKRZzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "jest-regex-util": "30.0.1", + "jest-snapshot": "30.0.3" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-runner": { + "version": "30.0.3", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-30.0.3.tgz", + "integrity": "sha512-CxYBzu9WStOBBXAKkLXGoUtNOWsiS1RRmUQb6SsdUdTcqVncOau7m8AJ4cW3Mz+YL1O9pOGPSYLyvl8HBdFmkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "30.0.2", + "@jest/environment": "30.0.2", + "@jest/test-result": "30.0.2", + "@jest/transform": "30.0.2", + "@jest/types": "30.0.1", + "@types/node": "*", + "chalk": "^4.1.2", + "emittery": "^0.13.1", + "exit-x": "^0.2.2", + "graceful-fs": "^4.2.11", + "jest-docblock": "30.0.1", + "jest-environment-node": "30.0.2", + "jest-haste-map": "30.0.2", + "jest-leak-detector": "30.0.2", + "jest-message-util": "30.0.2", + "jest-resolve": "30.0.2", + "jest-runtime": "30.0.3", + "jest-util": "30.0.2", + "jest-watcher": "30.0.2", + "jest-worker": "30.0.2", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-runtime": { + "version": "30.0.3", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-30.0.3.tgz", + "integrity": "sha512-Xjosq0C48G9XEQOtmgrjXJwPaUPaq3sPJwHDRaiC+5wi4ZWxO6Lx6jNkizK/0JmTulVNuxP8iYwt77LGnfg3/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "30.0.2", + "@jest/fake-timers": "30.0.2", + "@jest/globals": "30.0.3", + "@jest/source-map": "30.0.1", + "@jest/test-result": "30.0.2", + "@jest/transform": "30.0.2", + "@jest/types": "30.0.1", + "@types/node": "*", + "chalk": "^4.1.2", + "cjs-module-lexer": "^2.1.0", + "collect-v8-coverage": "^1.0.2", + "glob": "^10.3.10", + "graceful-fs": "^4.2.11", + "jest-haste-map": "30.0.2", + "jest-message-util": "30.0.2", + "jest-mock": "30.0.2", + "jest-regex-util": "30.0.1", + "jest-resolve": "30.0.2", + "jest-snapshot": "30.0.3", + "jest-util": "30.0.2", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-snapshot": { + "version": "30.0.3", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-30.0.3.tgz", + "integrity": "sha512-F05JCohd3OA1N9+5aEPXA6I0qOfZDGIx0zTq5Z4yMBg2i1p5ELfBusjYAWwTkC12c7dHcbyth4QAfQbS7cRjow==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.27.4", + "@babel/generator": "^7.27.5", + "@babel/plugin-syntax-jsx": "^7.27.1", + "@babel/plugin-syntax-typescript": "^7.27.1", + "@babel/types": "^7.27.3", + "@jest/expect-utils": "30.0.3", + "@jest/get-type": "30.0.1", + "@jest/snapshot-utils": "30.0.1", + "@jest/transform": "30.0.2", + "@jest/types": "30.0.1", + "babel-preset-current-node-syntax": "^1.1.0", + "chalk": "^4.1.2", + "expect": "30.0.3", + "graceful-fs": "^4.2.11", + "jest-diff": "30.0.3", + "jest-matcher-utils": "30.0.3", + "jest-message-util": "30.0.2", + "jest-util": "30.0.2", + "pretty-format": "30.0.2", + "semver": "^7.7.2", + "synckit": "^0.11.8" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-util": { + "version": "30.0.2", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-30.0.2.tgz", + "integrity": "sha512-8IyqfKS4MqprBuUpZNlFB5l+WFehc8bfCe1HSZFHzft2mOuND8Cvi9r1musli+u6F3TqanCZ/Ik4H4pXUolZIg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "30.0.1", + "@types/node": "*", + "chalk": "^4.1.2", + "ci-info": "^4.2.0", + "graceful-fs": "^4.2.11", + "picomatch": "^4.0.2" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-util/node_modules/picomatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/jest-validate": { + "version": "30.0.2", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-30.0.2.tgz", + "integrity": "sha512-noOvul+SFER4RIvNAwGn6nmV2fXqBq67j+hKGHKGFCmK4ks/Iy1FSrqQNBLGKlu4ZZIRL6Kg1U72N1nxuRCrGQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/get-type": "30.0.1", + "@jest/types": "30.0.1", + "camelcase": "^6.3.0", + "chalk": "^4.1.2", + "leven": "^3.1.0", + "pretty-format": "30.0.2" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-validate/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-watcher": { + "version": "30.0.2", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-30.0.2.tgz", + "integrity": "sha512-vYO5+E7jJuF+XmONr6CrbXdlYrgvZqtkn6pdkgjt/dU64UAdc0v1cAVaAeWtAfUUMScxNmnUjKPUMdCpNVASwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/test-result": "30.0.2", + "@jest/types": "30.0.1", + "@types/node": "*", + "ansi-escapes": "^4.3.2", + "chalk": "^4.1.2", + "emittery": "^0.13.1", + "jest-util": "30.0.2", + "string-length": "^4.0.2" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-worker": { + "version": "30.0.2", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-30.0.2.tgz", + "integrity": "sha512-RN1eQmx7qSLFA+o9pfJKlqViwL5wt+OL3Vff/A+/cPsmuw7NPwfgl33AP+/agRmHzPOFgXviRycR9kYwlcRQXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@ungap/structured-clone": "^1.3.0", + "jest-util": "30.0.2", + "merge-stream": "^2.0.0", + "supports-color": "^8.1.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/js-beautify": { + "version": "1.15.4", + "resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.15.4.tgz", + "integrity": "sha512-9/KXeZUKKJwqCXUdBxFJ3vPh467OCckSBmYDwSK/EtV090K+iMJ7zx2S3HLVDIWFQdqMIsZWbnaGiba18aWhaA==", + "dev": true, + "license": "MIT", + "dependencies": { + "config-chain": "^1.1.13", + "editorconfig": "^1.0.4", + "glob": "^10.4.2", + "js-cookie": "^3.0.5", + "nopt": "^7.2.1" + }, + "bin": { + "css-beautify": "js/bin/css-beautify.js", + "html-beautify": "js/bin/html-beautify.js", + "js-beautify": "js/bin/js-beautify.js" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/js-cookie": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz", + "integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "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/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsbn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", + "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==", + "license": "MIT", + "optional": true + }, + "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-bigint": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", + "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", + "license": "MIT", + "dependencies": { + "bignumber.js": "^9.0.0" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "license": "MIT" + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "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.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jwa": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", + "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==", + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "^1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", + "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", + "license": "MIT", + "dependencies": { + "jwa": "^2.0.0", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "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/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "license": "MIT" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "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/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-fetch-happen": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-9.1.0.tgz", + "integrity": "sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg==", + "license": "ISC", + "optional": true, + "dependencies": { + "agentkeepalive": "^4.1.3", + "cacache": "^15.2.0", + "http-cache-semantics": "^4.1.0", + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^6.0.0", + "minipass": "^3.1.3", + "minipass-collect": "^1.0.2", + "minipass-fetch": "^1.3.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.2", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^6.0.0", + "ssri": "^8.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/make-fetch-happen/node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/make-fetch-happen/node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/make-fetch-happen/node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "license": "MIT", + "optional": true, + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/make-fetch-happen/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "license": "ISC", + "optional": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/make-fetch-happen/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "license": "ISC", + "optional": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/make-fetch-happen/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT", + "optional": true + }, + "node_modules/make-fetch-happen/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC", + "optional": true + }, + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tmpl": "1.0.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==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==", + "license": "MIT" + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true, + "license": "MIT" + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types/node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/minipass-collect": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", + "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", + "license": "ISC", + "optional": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-collect/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "license": "ISC", + "optional": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-collect/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC", + "optional": true + }, + "node_modules/minipass-fetch": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-1.4.1.tgz", + "integrity": "sha512-CGH1eblLq26Y15+Azk7ey4xh0J/XfJfrCox5LDJiKqI2Q2iwOLOKrlmIaODiSQS8d18jalF6y2K2ePUm0CmShw==", + "license": "MIT", + "optional": true, + "dependencies": { + "minipass": "^3.1.0", + "minipass-sized": "^1.0.3", + "minizlib": "^2.0.0" + }, + "engines": { + "node": ">=8" + }, + "optionalDependencies": { + "encoding": "^0.1.12" + } + }, + "node_modules/minipass-fetch/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "license": "ISC", + "optional": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-fetch/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC", + "optional": true + }, + "node_modules/minipass-flush": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", + "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", + "license": "ISC", + "optional": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-flush/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "license": "ISC", + "optional": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-flush/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC", + "optional": true + }, + "node_modules/minipass-pipeline": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", + "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", + "license": "ISC", + "optional": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-pipeline/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "license": "ISC", + "optional": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-pipeline/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC", + "optional": true + }, + "node_modules/minipass-sized": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", + "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", + "license": "ISC", + "optional": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-sized/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "license": "ISC", + "optional": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-sized/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC", + "optional": true + }, + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "license": "MIT", + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minizlib/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC" + }, + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "license": "MIT", + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "license": "MIT" + }, + "node_modules/moment": { + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", + "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/moment-timezone": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.6.0.tgz", + "integrity": "sha512-ldA5lRNm3iJCWZcBCab4pnNL3HSZYXVb/3TYr75/1WCTWYuTqYUb5f/S384pncYjJ88lbO8Z4uPDvmoluHJc8Q==", + "license": "MIT", + "dependencies": { + "moment": "^2.29.4" + }, + "engines": { + "node": "*" + } + }, + "node_modules/morgan": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz", + "integrity": "sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==", + "license": "MIT", + "dependencies": { + "basic-auth": "~2.0.1", + "debug": "2.6.9", + "depd": "~2.0.0", + "on-finished": "~2.3.0", + "on-headers": "~1.0.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/morgan/node_modules/on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/multer": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/multer/-/multer-2.0.1.tgz", + "integrity": "sha512-Ug8bXeTIUlxurg8xLTEskKShvcKDZALo1THEX5E41pYCD2sCVub5/kIRIGqWNoqV6szyLyQKV6mD4QUrWE5GCQ==", + "license": "MIT", + "dependencies": { + "append-field": "^1.0.0", + "busboy": "^1.6.0", + "concat-stream": "^2.0.0", + "mkdirp": "^0.5.6", + "object-assign": "^4.1.1", + "type-is": "^1.6.18", + "xtend": "^4.0.2" + }, + "engines": { + "node": ">= 10.16.0" + } + }, + "node_modules/napi-build-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz", + "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==", + "license": "MIT" + }, + "node_modules/napi-postinstall": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.2.5.tgz", + "integrity": "sha512-kmsgUvCRIJohHjbZ3V8avP0I1Pekw329MVAMDzVxsrkjgdnqiwvMX5XwR+hWV66vsAtZ+iM+fVnq8RTQawUmCQ==", + "dev": true, + "license": "MIT", + "bin": { + "napi-postinstall": "lib/cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/napi-postinstall" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz", + "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/node-abi": { + "version": "3.75.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.75.0.tgz", + "integrity": "sha512-OhYaY5sDsIka7H7AtijtI9jwGYLyl29eQn/W623DiN/MIv5sUqc4g7BIDThX+gb7di9f6xK02nkp8sdfFWZLTg==", + "license": "MIT", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-addon-api": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.4.0.tgz", + "integrity": "sha512-D9DI/gXHvVmjHS08SVch0Em8G5S1P+QWtU31appcKT/8wFSPRcdHadIFSAntdMMVM5zz+/DL+bL/gz3UDppqtg==", + "license": "MIT", + "engines": { + "node": "^18 || ^20 || >= 21" + } + }, + "node_modules/node-cron": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/node-cron/-/node-cron-4.1.1.tgz", + "integrity": "sha512-oJj9CYV7teeCVs+y2Efi5IQ4FGmAYbsXQOehc1AGLlwteec8pC7DjBCUzSyRQ0LYa+CRCgmD+vtlWQcnPpXowA==", + "license": "ISC", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-gyp": { + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-8.4.1.tgz", + "integrity": "sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w==", + "license": "MIT", + "optional": true, + "dependencies": { + "env-paths": "^2.2.0", + "glob": "^7.1.4", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^9.1.0", + "nopt": "^5.0.0", + "npmlog": "^6.0.0", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.2", + "which": "^2.0.2" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" + }, + "engines": { + "node": ">= 10.12.0" + } + }, + "node_modules/node-gyp-build": { + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz", + "integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==", + "license": "MIT", + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, + "node_modules/node-gyp/node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "license": "ISC", + "optional": true + }, + "node_modules/node-gyp/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "license": "ISC", + "optional": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/node-gyp/node_modules/nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "license": "ISC", + "optional": true, + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", + "dev": true, + "license": "MIT" + }, + "node_modules/nodemon": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.0.3.tgz", + "integrity": "sha512-7jH/NXbFPxVaMwmBCC2B9F/V6X1VkEdNgx3iu9jji8WxWcvhMWkmhNWhI5077zknOnZnBzba9hZP6bCPJLSReQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "chokidar": "^3.5.2", + "debug": "^4", + "ignore-by-default": "^1.0.1", + "minimatch": "^3.1.2", + "pstree.remy": "^1.1.8", + "semver": "^7.5.3", + "simple-update-notifier": "^2.0.0", + "supports-color": "^5.5.0", + "touch": "^3.1.0", + "undefsafe": "^2.0.5" + }, + "bin": { + "nodemon": "bin/nodemon.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nodemon" + } + }, + "node_modules/nodemon/node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/nodemon/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/nodemon/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/nodemon/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/nopt": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-7.2.1.tgz", + "integrity": "sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==", + "dev": true, + "license": "ISC", + "dependencies": { + "abbrev": "^2.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npmlog": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz", + "integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==", + "deprecated": "This package is no longer supported.", + "license": "ISC", + "optional": true, + "dependencies": { + "are-we-there-yet": "^3.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^4.0.3", + "set-blocking": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/on-headers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", + "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/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/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "license": "MIT", + "engines": { + "node": ">=0.10.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==", + "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": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==", + "license": "MIT" + }, + "node_modules/pg-connection-string": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.9.1.tgz", + "integrity": "sha512-nkc6NpDcvPVpZXxrreI/FOtX3XemeLl8E0qFr6F2Lrm/I8WOnaWNhIPK2Z7OHpw7gh5XJThi6j6ppgNoaT1w4w==", + "license": "MIT" + }, + "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": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pkg-dir/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/prebuild-install": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", + "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==", + "license": "MIT", + "dependencies": { + "detect-libc": "^2.0.0", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^2.0.0", + "node-abi": "^3.3.0", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^4.0.0", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + }, + "bin": { + "prebuild-install": "bin.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", + "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-diff": "^1.1.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/pretty-format": { + "version": "30.0.2", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.0.2.tgz", + "integrity": "sha512-yC5/EBSOrTtqhCKfLHqoUIAXVRZnukHPwWBJWR7h84Q3Be1DRQZLncwcfLoPA5RPQ65qfiCMqgYwdUuQ//eVpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "30.0.1", + "ansi-styles": "^5.2.0", + "react-is": "^18.3.1" + }, + "engines": { + "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==", + "license": "ISC", + "optional": true + }, + "node_modules/promise-retry": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", + "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", + "license": "MIT", + "optional": true, + "dependencies": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/prop-types/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "license": "MIT" + }, + "node_modules/proto-list": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", + "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==", + "dev": true, + "license": "ISC" + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/pstree.remy": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", + "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", + "dev": true, + "license": "MIT" + }, + "node_modules/pump": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz", + "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==", + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/pure-rand": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-7.0.1.tgz", + "integrity": "sha512-oTUZM/NAZS8p7ANR3SHh30kXB+zK2r2BPcEn/awJIbOvq82WoMN4p62AWWp3Hhw50G0xMsw1mhIBLqHw64EcNQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "license": "MIT" + }, + "node_modules/qs": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz", + "integrity": "sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/random-bytes": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz", + "integrity": "sha512-iv7LhNVO047HzYR3InF6pUcUsPQiHTM1Qal51DcGSuZFBil1aBBWG5eHPNek7bvILMaYJ/8RU1e8w1AMdHmLQQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/rc/node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react": { + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz", + "integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz", + "integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==", + "license": "MIT", + "peer": true, + "dependencies": { + "scheduler": "^0.26.0" + }, + "peerDependencies": { + "react": "^19.1.0" + } + }, + "node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "license": "MIT" + }, + "node_modules/react-smooth": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/react-smooth/-/react-smooth-4.0.4.tgz", + "integrity": "sha512-gnGKTpYwqL0Iii09gHobNolvX4Kiq4PKx6eWBCYYix+8cdw+cGo3do906l1NBPKkSWx1DghC1dlWG9L2uGd61Q==", + "license": "MIT", + "dependencies": { + "fast-equals": "^5.0.1", + "prop-types": "^15.8.1", + "react-transition-group": "^4.4.5" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/react-transition-group": { + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", + "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", + "license": "BSD-3-Clause", + "dependencies": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + }, + "peerDependencies": { + "react": ">=16.6.0", + "react-dom": ">=16.6.0" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/recharts": { + "version": "2.15.4", + "resolved": "https://registry.npmjs.org/recharts/-/recharts-2.15.4.tgz", + "integrity": "sha512-UT/q6fwS3c1dHbXv2uFgYJ9BMFHu3fwnd7AYZaEQhXuYQ4hgsxLvsUXzGdKeZrW5xopzDCvuA2N41WJ88I7zIw==", + "license": "MIT", + "dependencies": { + "clsx": "^2.0.0", + "eventemitter3": "^4.0.1", + "lodash": "^4.17.21", + "react-is": "^18.3.1", + "react-smooth": "^4.0.4", + "recharts-scale": "^0.4.4", + "tiny-invariant": "^1.3.1", + "victory-vendor": "^36.6.8" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "react": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/recharts-scale": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/recharts-scale/-/recharts-scale-0.4.5.tgz", + "integrity": "sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w==", + "license": "MIT", + "dependencies": { + "decimal.js-light": "^2.4.1" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-cwd/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/retry-as-promised": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/retry-as-promised/-/retry-as-promised-7.1.1.tgz", + "integrity": "sha512-hMD7odLOt3LkTjcif8aRZqi/hybjpLNgSk5oF5FCowfCjok6LukpN2bDX7R5wDmbgBQFn7YoBxSagmtXHaJYJw==", + "license": "MIT" + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/scheduler": { + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz", + "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==", + "license": "MIT", + "peer": true + }, + "node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/sequelize": { + "version": "6.37.7", + "resolved": "https://registry.npmjs.org/sequelize/-/sequelize-6.37.7.tgz", + "integrity": "sha512-mCnh83zuz7kQxxJirtFD7q6Huy6liPanI67BSlbzSYgVNl5eXVdE2CN1FuAeZwG1SNpGsNRCV+bJAVVnykZAFA==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/sequelize" + } + ], + "license": "MIT", + "dependencies": { + "@types/debug": "^4.1.8", + "@types/validator": "^13.7.17", + "debug": "^4.3.4", + "dottie": "^2.0.6", + "inflection": "^1.13.4", + "lodash": "^4.17.21", + "moment": "^2.29.4", + "moment-timezone": "^0.5.43", + "pg-connection-string": "^2.6.1", + "retry-as-promised": "^7.0.4", + "semver": "^7.5.4", + "sequelize-pool": "^7.1.0", + "toposort-class": "^1.0.1", + "uuid": "^8.3.2", + "validator": "^13.9.0", + "wkx": "^0.5.0" + }, + "engines": { + "node": ">=10.0.0" + }, + "peerDependenciesMeta": { + "ibm_db": { + "optional": true + }, + "mariadb": { + "optional": true + }, + "mysql2": { + "optional": true + }, + "oracledb": { + "optional": true + }, + "pg": { + "optional": true + }, + "pg-hstore": { + "optional": true + }, + "snowflake-sdk": { + "optional": true + }, + "sqlite3": { + "optional": true + }, + "tedious": { + "optional": true + } + } + }, + "node_modules/sequelize-cli": { + "version": "6.6.3", + "resolved": "https://registry.npmjs.org/sequelize-cli/-/sequelize-cli-6.6.3.tgz", + "integrity": "sha512-1YYPrcSRt/bpMDDSKM5ubY1mnJ2TEwIaGZcqITw4hLtGtE64nIqaBnLtMvH8VKHg6FbWpXTiFNc2mS/BtQCXZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fs-extra": "^9.1.0", + "js-beautify": "1.15.4", + "lodash": "^4.17.21", + "picocolors": "^1.1.1", + "resolve": "^1.22.1", + "umzug": "^2.3.0", + "yargs": "^16.2.0" + }, + "bin": { + "sequelize": "lib/sequelize", + "sequelize-cli": "lib/sequelize" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/sequelize-cli/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/sequelize-cli/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/sequelize-cli/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/sequelize-cli/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/sequelize-cli/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/sequelize-cli/node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/sequelize-pool": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/sequelize-pool/-/sequelize-pool-7.1.0.tgz", + "integrity": "sha512-G9c0qlIWQSK29pR/5U2JF5dDQeqqHRragoyahj/Nx4KOOQ3CPPfzxnfqFPCSB7x5UgjOgnZ61nSxz+fjDpRlJg==", + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/sequelize/node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/sequelize/node_modules/moment-timezone": { + "version": "0.5.48", + "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.48.tgz", + "integrity": "sha512-f22b8LV1gbTO2ms2j2z13MuPogNoh5UzxL3nzNAYKGraILnbGc9NEE6dyiiiLv46DGRb8A4kg8UKWLjPthxBHw==", + "license": "MIT", + "dependencies": { + "moment": "^2.29.4" + }, + "engines": { + "node": "*" + } + }, + "node_modules/sequelize/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/sequelize/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "license": "MIT", + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.18.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "license": "ISC", + "optional": true + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "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.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "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==", + "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==", + "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/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/simple-get": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "node_modules/simple-update-notifier": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", + "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.5.tgz", + "integrity": "sha512-iF+tNDQla22geJdTyJB1wM/qrX9DMRwWrciEPwWLPRWAUEM8sQiyxgckLxWT1f7+9VabJS0jTGGr4QgBuvi6Ww==", + "license": "MIT", + "optional": true, + "dependencies": { + "ip-address": "^9.0.5", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks-proxy-agent": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-6.2.1.tgz", + "integrity": "sha512-a6KW9G+6B3nWZ1yB8G7pJwL3ggLy1uTzKAgCb7ttblwqdz9fMGJUuTy3uFzEP48FAs9FLILlmzDlE2JJhVQaXQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "agent-base": "^6.0.2", + "debug": "^4.3.3", + "socks": "^2.6.2" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/socks-proxy-agent/node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/socks-proxy-agent/node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socks-proxy-agent/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT", + "optional": true + }, + "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/source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", + "license": "BSD-3-Clause", + "optional": true + }, + "node_modules/sqlite3": { + "version": "5.1.7", + "resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-5.1.7.tgz", + "integrity": "sha512-GGIyOiFaG+TUra3JIfkI/zGP8yZYLPQ0pl1bH+ODjiX57sPhrLU5sQJn1y9bDKZUFYkX1crlrPfSYt0BKKdkog==", + "hasInstallScript": true, + "license": "BSD-3-Clause", + "dependencies": { + "bindings": "^1.5.0", + "node-addon-api": "^7.0.0", + "prebuild-install": "^7.1.1", + "tar": "^6.1.11" + }, + "optionalDependencies": { + "node-gyp": "8.x" + }, + "peerDependencies": { + "node-gyp": "8.x" + }, + "peerDependenciesMeta": { + "node-gyp": { + "optional": true + } + } + }, + "node_modules/sqlite3/node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "license": "MIT" + }, + "node_modules/ssri": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz", + "integrity": "sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==", + "license": "ISC", + "optional": true, + "dependencies": { + "minipass": "^3.1.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/ssri/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "license": "ISC", + "optional": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ssri/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC", + "optional": true + }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/stack-utils/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/streamsearch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/string-width/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/string-width/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/superagent": { + "version": "10.2.1", + "resolved": "https://registry.npmjs.org/superagent/-/superagent-10.2.1.tgz", + "integrity": "sha512-O+PCv11lgTNJUzy49teNAWLjBZfc+A1enOwTpLlH6/rsvKcTwcdTT8m9azGkVqM7HBl5jpyZ7KTPhHweokBcdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "component-emitter": "^1.3.0", + "cookiejar": "^2.1.4", + "debug": "^4.3.4", + "fast-safe-stringify": "^2.1.1", + "form-data": "^4.0.0", + "formidable": "^3.5.4", + "methods": "^1.1.2", + "mime": "2.6.0", + "qs": "^6.11.0" + }, + "engines": { + "node": ">=14.18.0" + } + }, + "node_modules/superagent/node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/superagent/node_modules/mime": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "dev": true, + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/superagent/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/supertest": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/supertest/-/supertest-7.1.1.tgz", + "integrity": "sha512-aI59HBTlG9e2wTjxGJV+DygfNLgnWbGdZxiA/sgrnNNikIW8lbDvCtF6RnhZoJ82nU7qv7ZLjrvWqCEm52fAmw==", + "dev": true, + "license": "MIT", + "dependencies": { + "methods": "^1.1.2", + "superagent": "^10.2.1" + }, + "engines": { + "node": ">=14.18.0" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "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/synckit": { + "version": "0.11.8", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.8.tgz", + "integrity": "sha512-+XZ+r1XGIJGeQk3VvXhT6xx/VpbHsRzsTkGgF6E5RX9TTXD0118l87puaEBZ566FhqblC6U0d4XnubznJDm30A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@pkgr/core": "^0.2.4" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/synckit" + } + }, + "node_modules/tar": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", + "license": "ISC", + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tar-fs": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.3.tgz", + "integrity": "sha512-090nwYJDmlhwFwEW3QQl+vaNnxsO2yVsd45eTKRBzSzu+hlb1w2K9inVq5b0ngXuLVqQ4ApvsUHHnu/zQNkWAg==", + "license": "MIT", + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "node_modules/tar-fs/node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "license": "ISC" + }, + "node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "license": "MIT", + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tar/node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "license": "ISC", + "engines": { + "node": ">=8" + } + }, + "node_modules/tar/node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "license": "MIT", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tar/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC" + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "license": "ISC", + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/test-exclude/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "license": "MIT" + }, + "node_modules/tiny-invariant": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", + "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==", + "license": "MIT" + }, + "node_modules/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/toposort-class": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toposort-class/-/toposort-class-1.0.1.tgz", + "integrity": "sha512-OsLcGGbYF3rMjPUf8oKktyvCiUxSbqMMS39m33MAjLTC1DVIH6x3WSt63/M77ihI09+Sdfk1AXvfhCEeUmC7mg==", + "license": "MIT" + }, + "node_modules/touch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz", + "integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==", + "dev": true, + "license": "ISC", + "bin": { + "nodetouch": "bin/nodetouch.js" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "license": "MIT" + }, + "node_modules/ts-api-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", + "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD", + "optional": true + }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "license": "MIT", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", + "license": "MIT" + }, + "node_modules/typescript": { + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", + "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/uid-safe": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz", + "integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==", + "license": "MIT", + "dependencies": { + "random-bytes": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/umzug": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/umzug/-/umzug-2.3.0.tgz", + "integrity": "sha512-Z274K+e8goZK8QJxmbRPhl89HPO1K+ORFtm6rySPhFKfKc5GHhqdzD0SGhSWHkzoXasqJuItdhorSvY7/Cgflw==", + "dev": true, + "license": "MIT", + "dependencies": { + "bluebird": "^3.7.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/undefsafe": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", + "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", + "dev": true, + "license": "MIT" + }, + "node_modules/undici-types": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.8.0.tgz", + "integrity": "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==", + "license": "MIT" + }, + "node_modules/unique-filename": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", + "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", + "license": "ISC", + "optional": true, + "dependencies": { + "unique-slug": "^2.0.0" + } + }, + "node_modules/unique-slug": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", + "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", + "license": "ISC", + "optional": true, + "dependencies": { + "imurmurhash": "^0.1.4" + } + }, + "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/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/unrs-resolver": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.9.2.tgz", + "integrity": "sha512-VUyWiTNQD7itdiMuJy+EuLEErLj3uwX/EpHQF8EOf33Dq3Ju6VW1GXm+swk6+1h7a49uv9fKZ+dft9jU7esdLA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "napi-postinstall": "^0.2.4" + }, + "funding": { + "url": "https://opencollective.com/unrs-resolver" + }, + "optionalDependencies": { + "@unrs/resolver-binding-android-arm-eabi": "1.9.2", + "@unrs/resolver-binding-android-arm64": "1.9.2", + "@unrs/resolver-binding-darwin-arm64": "1.9.2", + "@unrs/resolver-binding-darwin-x64": "1.9.2", + "@unrs/resolver-binding-freebsd-x64": "1.9.2", + "@unrs/resolver-binding-linux-arm-gnueabihf": "1.9.2", + "@unrs/resolver-binding-linux-arm-musleabihf": "1.9.2", + "@unrs/resolver-binding-linux-arm64-gnu": "1.9.2", + "@unrs/resolver-binding-linux-arm64-musl": "1.9.2", + "@unrs/resolver-binding-linux-ppc64-gnu": "1.9.2", + "@unrs/resolver-binding-linux-riscv64-gnu": "1.9.2", + "@unrs/resolver-binding-linux-riscv64-musl": "1.9.2", + "@unrs/resolver-binding-linux-s390x-gnu": "1.9.2", + "@unrs/resolver-binding-linux-x64-gnu": "1.9.2", + "@unrs/resolver-binding-linux-x64-musl": "1.9.2", + "@unrs/resolver-binding-wasm32-wasi": "1.9.2", + "@unrs/resolver-binding-win32-arm64-msvc": "1.9.2", + "@unrs/resolver-binding-win32-ia32-msvc": "1.9.2", + "@unrs/resolver-binding-win32-x64-msvc": "1.9.2" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", + "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/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/url-template": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/url-template/-/url-template-2.0.8.tgz", + "integrity": "sha512-XdVKMF4SJ0nP/O7XIPB0JwAEuT9lDIYnNsK8yGVe43y0AWoKeJNdv3ZNWh7ksJ6KqQFjOO6ox/VEitLnaVNufw==", + "license": "BSD" + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/uuid": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", + "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/esm/bin/uuid" + } + }, + "node_modules/v8-to-istanbul": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", + "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", + "dev": true, + "license": "ISC", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/validator": { + "version": "13.15.15", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.15.15.tgz", + "integrity": "sha512-BgWVbCI72aIQy937xbawcs+hrVaN/CZ2UwutgaJ36hGqRrLNM+f5LUT/YPRbo8IV/ASeFzXszezV+y2+rq3l8A==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/victory-vendor": { + "version": "36.9.2", + "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-36.9.2.tgz", + "integrity": "sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ==", + "license": "MIT AND ISC", + "dependencies": { + "@types/d3-array": "^3.0.3", + "@types/d3-ease": "^3.0.0", + "@types/d3-interpolate": "^3.0.1", + "@types/d3-scale": "^4.0.2", + "@types/d3-shape": "^3.1.0", + "@types/d3-time": "^3.0.0", + "@types/d3-timer": "^3.0.0", + "d3-array": "^3.1.6", + "d3-ease": "^3.0.1", + "d3-interpolate": "^3.0.1", + "d3-scale": "^4.0.2", + "d3-shape": "^3.1.0", + "d3-time": "^3.0.0", + "d3-timer": "^3.0.1" + } + }, + "node_modules/walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "makeerror": "1.0.12" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "license": "BSD-2-Clause" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "license": "ISC", + "optional": true, + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, + "node_modules/wide-align/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT", + "optional": true + }, + "node_modules/wide-align/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "optional": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wkx": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/wkx/-/wkx-0.5.0.tgz", + "integrity": "sha512-Xng/d4Ichh8uN4l0FToV/258EjMGU9MGcA0HV2d9B/ZpZB3lqQm7nkOdZdm5GhKtLLhAE7PiVQwN4eN+2YJJUg==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, + "node_modules/write-file-atomic": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz", + "integrity": "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "license": "MIT", + "engines": { + "node": ">=0.4" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "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" + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/yargs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } } - ], - "license": "BSD-3-Clause" - }, - "node_modules/ignore-by-default": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", - "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", - "dev": true, - "license": "ISC" - }, - "node_modules/import-local": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", - "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", - "dev": true, - "license": "MIT", - "dependencies": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - }, - "bin": { - "import-local-fixture": "fixtures/cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "devOptional": true, - "license": "MIT", - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/infer-owner": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", - "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", - "license": "ISC", - "optional": true - }, - "node_modules/inflection": { - "version": "1.13.4", - "resolved": "https://registry.npmjs.org/inflection/-/inflection-1.13.4.tgz", - "integrity": "sha512-6I/HUDeYFfuNCVS3td055BaXBwKYuzw7K3ExVMStBowKo9oOAMJIXIHvdyR3iboTCp1b+1i5DSkIZTcwIktuDw==", - "engines": [ - "node >= 0.4.0" - ], - "license": "MIT" - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", - "devOptional": true, - "license": "ISC", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "license": "ISC" - }, - "node_modules/ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "license": "ISC" - }, - "node_modules/internmap": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", - "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/ip-address": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", - "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", - "license": "MIT", - "optional": true, - "dependencies": { - "jsbn": "1.1.0", - "sprintf-js": "^1.1.3" - }, - "engines": { - "node": ">= 12" - } - }, - "node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "license": "MIT", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true, - "license": "MIT" - }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "license": "MIT", - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-core-module": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", - "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", - "dev": true, - "license": "MIT", - "dependencies": { - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "devOptional": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-generator-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", - "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-lambda": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", - "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==", - "license": "MIT", - "optional": true - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, - "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==", - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "devOptional": true, - "license": "ISC" - }, - "node_modules/istanbul-lib-coverage": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", - "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-instrument": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", - "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@babel/core": "^7.23.9", - "@babel/parser": "^7.23.9", - "@istanbuljs/schema": "^0.1.3", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^7.5.4" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-report": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", - "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^4.0.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-report/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-report/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-source-maps": { - "version": "5.0.6", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.6.tgz", - "integrity": "sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.23", - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-source-maps/node_modules/debug": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/istanbul-lib-source-maps/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/istanbul-reports": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", - "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jackspeak": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", - "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" - } - }, - "node_modules/jest": { - "version": "30.0.0", - "resolved": "https://registry.npmjs.org/jest/-/jest-30.0.0.tgz", - "integrity": "sha512-/3G2iFwsUY95vkflmlDn/IdLyLWqpQXcftptooaPH4qkyU52V7qVYf1BjmdSPlp1+0fs6BmNtrGaSFwOfV07ew==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/core": "30.0.0", - "@jest/types": "30.0.0", - "import-local": "^3.2.0", - "jest-cli": "30.0.0" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/jest-changed-files": { - "version": "30.0.0", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-30.0.0.tgz", - "integrity": "sha512-rzGpvCdPdEV1Ma83c1GbZif0L2KAm3vXSXGRlpx7yCt0vhruwCNouKNRh3SiVcISHP1mb3iJzjb7tAEnNu1laQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "execa": "^5.1.1", - "jest-util": "30.0.0", - "p-limit": "^3.1.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-circus": { - "version": "30.0.0", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-30.0.0.tgz", - "integrity": "sha512-nTwah78qcKVyndBS650hAkaEmwWGaVsMMoWdJwMnH77XArRJow2Ir7hc+8p/mATtxVZuM9OTkA/3hQocRIK5Dw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/environment": "30.0.0", - "@jest/expect": "30.0.0", - "@jest/test-result": "30.0.0", - "@jest/types": "30.0.0", - "@types/node": "*", - "chalk": "^4.1.2", - "co": "^4.6.0", - "dedent": "^1.6.0", - "is-generator-fn": "^2.1.0", - "jest-each": "30.0.0", - "jest-matcher-utils": "30.0.0", - "jest-message-util": "30.0.0", - "jest-runtime": "30.0.0", - "jest-snapshot": "30.0.0", - "jest-util": "30.0.0", - "p-limit": "^3.1.0", - "pretty-format": "30.0.0", - "pure-rand": "^7.0.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.6" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-cli": { - "version": "30.0.0", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-30.0.0.tgz", - "integrity": "sha512-fWKAgrhlwVVCfeizsmIrPRTBYTzO82WSba3gJniZNR3PKXADgdC0mmCSK+M+t7N8RCXOVfY6kvCkvjUNtzmHYQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/core": "30.0.0", - "@jest/test-result": "30.0.0", - "@jest/types": "30.0.0", - "chalk": "^4.1.2", - "exit-x": "^0.2.2", - "import-local": "^3.2.0", - "jest-config": "30.0.0", - "jest-util": "30.0.0", - "jest-validate": "30.0.0", - "yargs": "^17.7.2" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/jest-cli/node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/jest-cli/node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/jest-cli/node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/jest-config": { - "version": "30.0.0", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-30.0.0.tgz", - "integrity": "sha512-p13a/zun+sbOMrBnTEUdq/5N7bZMOGd1yMfqtAJniPNuzURMay4I+vxZLK1XSDbjvIhmeVdG8h8RznqYyjctyg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.27.4", - "@jest/get-type": "30.0.0", - "@jest/pattern": "30.0.0", - "@jest/test-sequencer": "30.0.0", - "@jest/types": "30.0.0", - "babel-jest": "30.0.0", - "chalk": "^4.1.2", - "ci-info": "^4.2.0", - "deepmerge": "^4.3.1", - "glob": "^10.3.10", - "graceful-fs": "^4.2.11", - "jest-circus": "30.0.0", - "jest-docblock": "30.0.0", - "jest-environment-node": "30.0.0", - "jest-regex-util": "30.0.0", - "jest-resolve": "30.0.0", - "jest-runner": "30.0.0", - "jest-util": "30.0.0", - "jest-validate": "30.0.0", - "micromatch": "^4.0.8", - "parse-json": "^5.2.0", - "pretty-format": "30.0.0", - "slash": "^3.0.0", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - }, - "peerDependencies": { - "@types/node": "*", - "esbuild-register": ">=3.4.0", - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "esbuild-register": { - "optional": true - }, - "ts-node": { - "optional": true - } - } - }, - "node_modules/jest-config/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/jest-config/node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", - "dev": true, - "license": "ISC", - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/jest-config/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/jest-config/node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/jest-config/node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/jest-diff": { - "version": "30.0.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-30.0.0.tgz", - "integrity": "sha512-TgT1+KipV8JTLXXeFX0qSvIJR/UXiNNojjxb/awh3vYlBZyChU/NEmyKmq+wijKjWEztyrGJFL790nqMqNjTHA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/diff-sequences": "30.0.0", - "@jest/get-type": "30.0.0", - "chalk": "^4.1.2", - "pretty-format": "30.0.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-docblock": { - "version": "30.0.0", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-30.0.0.tgz", - "integrity": "sha512-By/iQ0nvTzghEecGzUMCp1axLtBh+8wB4Hpoi5o+x1stycjEmPcH1mHugL4D9Q+YKV++vKeX/3ZTW90QC8ICPg==", - "dev": true, - "license": "MIT", - "dependencies": { - "detect-newline": "^3.1.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-each": { - "version": "30.0.0", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-30.0.0.tgz", - "integrity": "sha512-qkFEW3cfytEjG2KtrhwtldZfXYnWSanO8xUMXLe4A6yaiHMHJUalk0Yyv4MQH6aeaxgi4sGVrukvF0lPMM7U1w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/get-type": "30.0.0", - "@jest/types": "30.0.0", - "chalk": "^4.1.2", - "jest-util": "30.0.0", - "pretty-format": "30.0.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-environment-node": { - "version": "30.0.0", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-30.0.0.tgz", - "integrity": "sha512-sF6lxyA25dIURyDk4voYmGU9Uwz2rQKMfjxKnDd19yk+qxKGrimFqS5YsPHWTlAVBo+YhWzXsqZoaMzrTFvqfg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/environment": "30.0.0", - "@jest/fake-timers": "30.0.0", - "@jest/types": "30.0.0", - "@types/node": "*", - "jest-mock": "30.0.0", - "jest-util": "30.0.0", - "jest-validate": "30.0.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-haste-map": { - "version": "30.0.0", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-30.0.0.tgz", - "integrity": "sha512-p4bXAhXTawTsADgQgTpbymdLaTyPW1xWNu1oIGG7/N3LIAbZVkH2JMJqS8/IUcnGR8Kc7WFE+vWbJvsqGCWZXw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "30.0.0", - "@types/node": "*", - "anymatch": "^3.1.3", - "fb-watchman": "^2.0.2", - "graceful-fs": "^4.2.11", - "jest-regex-util": "30.0.0", - "jest-util": "30.0.0", - "jest-worker": "30.0.0", - "micromatch": "^4.0.8", - "walker": "^1.0.8" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - }, - "optionalDependencies": { - "fsevents": "^2.3.3" - } - }, - "node_modules/jest-leak-detector": { - "version": "30.0.0", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-30.0.0.tgz", - "integrity": "sha512-E/ly1azdVVbZrS0T6FIpyYHvsdek4FNaThJTtggjV/8IpKxh3p9NLndeUZy2+sjAI3ncS+aM0uLLon/dBg8htA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/get-type": "30.0.0", - "pretty-format": "30.0.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-matcher-utils": { - "version": "30.0.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-30.0.0.tgz", - "integrity": "sha512-m5mrunqopkrqwG1mMdJxe1J4uGmS9AHHKYUmoxeQOxBcLjEvirIrIDwuKmUYrecPHVB/PUBpXs2gPoeA2FSSLQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/get-type": "30.0.0", - "chalk": "^4.1.2", - "jest-diff": "30.0.0", - "pretty-format": "30.0.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-message-util": { - "version": "30.0.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-30.0.0.tgz", - "integrity": "sha512-pV3qcrb4utEsa/U7UI2VayNzSDQcmCllBZLSoIucrESRu0geKThFZOjjh0kACDJFJRAQwsK7GVsmS6SpEceD8w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@jest/types": "30.0.0", - "@types/stack-utils": "^2.0.3", - "chalk": "^4.1.2", - "graceful-fs": "^4.2.11", - "micromatch": "^4.0.8", - "pretty-format": "30.0.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.6" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-mock": { - "version": "30.0.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-30.0.0.tgz", - "integrity": "sha512-W2sRA4ALXILrEetEOh2ooZG6fZ01iwVs0OWMKSSWRcUlaLr4ESHuiKXDNTg+ZVgOq8Ei5445i/Yxrv59VT+XkA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "30.0.0", - "@types/node": "*", - "jest-util": "30.0.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-pnp-resolver": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", - "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - }, - "peerDependencies": { - "jest-resolve": "*" - }, - "peerDependenciesMeta": { - "jest-resolve": { - "optional": true - } - } - }, - "node_modules/jest-regex-util": { - "version": "30.0.0", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-30.0.0.tgz", - "integrity": "sha512-rT84010qRu/5OOU7a9TeidC2Tp3Qgt9Sty4pOZ/VSDuEmRupIjKZAb53gU3jr4ooMlhwScrgC9UixJxWzVu9oQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-resolve": { - "version": "30.0.0", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-30.0.0.tgz", - "integrity": "sha512-zwWl1P15CcAfuQCEuxszjiKdsValhnWcj/aXg/R3aMHs8HVoCWHC4B/+5+1BirMoOud8NnN85GSP2LEZCbj3OA==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^4.1.2", - "graceful-fs": "^4.2.11", - "jest-haste-map": "30.0.0", - "jest-pnp-resolver": "^1.2.3", - "jest-util": "30.0.0", - "jest-validate": "30.0.0", - "slash": "^3.0.0", - "unrs-resolver": "^1.7.11" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-resolve-dependencies": { - "version": "30.0.0", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-30.0.0.tgz", - "integrity": "sha512-Yhh7odCAUNXhluK1bCpwIlHrN1wycYaTlZwq1GdfNBEESNNI/z1j1a7dUEWHbmB9LGgv0sanxw3JPmWU8NeebQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "jest-regex-util": "30.0.0", - "jest-snapshot": "30.0.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-runner": { - "version": "30.0.0", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-30.0.0.tgz", - "integrity": "sha512-xbhmvWIc8X1IQ8G7xTv0AQJXKjBVyxoVJEJgy7A4RXsSaO+k/1ZSBbHwjnUhvYqMvwQPomWssDkUx6EoidEhlw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/console": "30.0.0", - "@jest/environment": "30.0.0", - "@jest/test-result": "30.0.0", - "@jest/transform": "30.0.0", - "@jest/types": "30.0.0", - "@types/node": "*", - "chalk": "^4.1.2", - "emittery": "^0.13.1", - "exit-x": "^0.2.2", - "graceful-fs": "^4.2.11", - "jest-docblock": "30.0.0", - "jest-environment-node": "30.0.0", - "jest-haste-map": "30.0.0", - "jest-leak-detector": "30.0.0", - "jest-message-util": "30.0.0", - "jest-resolve": "30.0.0", - "jest-runtime": "30.0.0", - "jest-util": "30.0.0", - "jest-watcher": "30.0.0", - "jest-worker": "30.0.0", - "p-limit": "^3.1.0", - "source-map-support": "0.5.13" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-runtime": { - "version": "30.0.0", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-30.0.0.tgz", - "integrity": "sha512-/O07qVgFrFAOGKGigojmdR3jUGz/y3+a/v9S/Yi2MHxsD+v6WcPppglZJw0gNJkRBArRDK8CFAwpM/VuEiiRjA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/environment": "30.0.0", - "@jest/fake-timers": "30.0.0", - "@jest/globals": "30.0.0", - "@jest/source-map": "30.0.0", - "@jest/test-result": "30.0.0", - "@jest/transform": "30.0.0", - "@jest/types": "30.0.0", - "@types/node": "*", - "chalk": "^4.1.2", - "cjs-module-lexer": "^2.1.0", - "collect-v8-coverage": "^1.0.2", - "glob": "^10.3.10", - "graceful-fs": "^4.2.11", - "jest-haste-map": "30.0.0", - "jest-message-util": "30.0.0", - "jest-mock": "30.0.0", - "jest-regex-util": "30.0.0", - "jest-resolve": "30.0.0", - "jest-snapshot": "30.0.0", - "jest-util": "30.0.0", - "slash": "^3.0.0", - "strip-bom": "^4.0.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-runtime/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/jest-runtime/node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", - "dev": true, - "license": "ISC", - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/jest-runtime/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/jest-runtime/node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/jest-snapshot": { - "version": "30.0.0", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-30.0.0.tgz", - "integrity": "sha512-6oCnzjpvfj/UIOMTqKZ6gedWAUgaycMdV8Y8h2dRJPvc2wSjckN03pzeoonw8y33uVngfx7WMo1ygdRGEKOT7w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.27.4", - "@babel/generator": "^7.27.5", - "@babel/plugin-syntax-jsx": "^7.27.1", - "@babel/plugin-syntax-typescript": "^7.27.1", - "@babel/types": "^7.27.3", - "@jest/expect-utils": "30.0.0", - "@jest/get-type": "30.0.0", - "@jest/snapshot-utils": "30.0.0", - "@jest/transform": "30.0.0", - "@jest/types": "30.0.0", - "babel-preset-current-node-syntax": "^1.1.0", - "chalk": "^4.1.2", - "expect": "30.0.0", - "graceful-fs": "^4.2.11", - "jest-diff": "30.0.0", - "jest-matcher-utils": "30.0.0", - "jest-message-util": "30.0.0", - "jest-util": "30.0.0", - "pretty-format": "30.0.0", - "semver": "^7.7.2", - "synckit": "^0.11.8" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-util": { - "version": "30.0.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-30.0.0.tgz", - "integrity": "sha512-fhNBBM9uSUbd4Lzsf8l/kcAdaHD/4SgoI48en3HXcBEMwKwoleKFMZ6cYEYs21SB779PRuRCyNLmymApAm8tZw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "30.0.0", - "@types/node": "*", - "chalk": "^4.1.2", - "ci-info": "^4.2.0", - "graceful-fs": "^4.2.11", - "picomatch": "^4.0.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-util/node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/jest-validate": { - "version": "30.0.0", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-30.0.0.tgz", - "integrity": "sha512-d6OkzsdlWItHAikUDs1hlLmpOIRhsZoXTCliV2XXalVQ3ZOeb9dy0CQ6AKulJu/XOZqpOEr/FiMH+FeOBVV+nw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/get-type": "30.0.0", - "@jest/types": "30.0.0", - "camelcase": "^6.3.0", - "chalk": "^4.1.2", - "leven": "^3.1.0", - "pretty-format": "30.0.0" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-validate/node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/jest-watcher": { - "version": "30.0.0", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-30.0.0.tgz", - "integrity": "sha512-fbAkojcyS53bOL/B7XYhahORq9cIaPwOgd/p9qW/hybbC8l6CzxfWJJxjlPBAIVN8dRipLR0zdhpGQdam+YBtw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/test-result": "30.0.0", - "@jest/types": "30.0.0", - "@types/node": "*", - "ansi-escapes": "^4.3.2", - "chalk": "^4.1.2", - "emittery": "^0.13.1", - "jest-util": "30.0.0", - "string-length": "^4.0.2" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-worker": { - "version": "30.0.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-30.0.0.tgz", - "integrity": "sha512-VZvxfWIybIvwK8N/Bsfe43LfQgd/rD0c4h5nLUx78CAqPxIQcW2qDjsVAC53iUR8yxzFIeCFFvWOh8en8hGzdg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*", - "@ungap/structured-clone": "^1.3.0", - "jest-util": "30.0.0", - "merge-stream": "^2.0.0", - "supports-color": "^8.1.1" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/jest-worker/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-worker/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/js-beautify": { - "version": "1.15.4", - "resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.15.4.tgz", - "integrity": "sha512-9/KXeZUKKJwqCXUdBxFJ3vPh467OCckSBmYDwSK/EtV090K+iMJ7zx2S3HLVDIWFQdqMIsZWbnaGiba18aWhaA==", - "dev": true, - "license": "MIT", - "dependencies": { - "config-chain": "^1.1.13", - "editorconfig": "^1.0.4", - "glob": "^10.4.2", - "js-cookie": "^3.0.5", - "nopt": "^7.2.1" - }, - "bin": { - "css-beautify": "js/bin/css-beautify.js", - "html-beautify": "js/bin/html-beautify.js", - "js-beautify": "js/bin/js-beautify.js" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/js-beautify/node_modules/abbrev": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-2.0.0.tgz", - "integrity": "sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/js-beautify/node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/js-beautify/node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", - "dev": true, - "license": "ISC", - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/js-beautify/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/js-beautify/node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/js-beautify/node_modules/nopt": { - "version": "7.2.1", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-7.2.1.tgz", - "integrity": "sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==", - "dev": true, - "license": "ISC", - "dependencies": { - "abbrev": "^2.0.0" - }, - "bin": { - "nopt": "bin/nopt.js" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/js-cookie": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz", - "integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14" - } - }, - "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/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsbn": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", - "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==", - "license": "MIT", - "optional": true - }, - "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-bigint": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", - "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", - "license": "MIT", - "dependencies": { - "bignumber.js": "^9.0.0" - } - }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "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.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/jwa": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", - "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==", - "license": "MIT", - "dependencies": { - "buffer-equal-constant-time": "^1.0.1", - "ecdsa-sig-formatter": "1.0.11", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/jws": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", - "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", - "license": "MIT", - "dependencies": { - "jwa": "^2.0.0", - "safe-buffer": "^5.0.1" - } - }, - "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/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true, - "license": "MIT" - }, - "node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "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": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "license": "ISC", - "optional": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/make-dir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", - "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", - "dev": true, - "license": "MIT", - "dependencies": { - "semver": "^7.5.3" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/make-fetch-happen": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-9.1.0.tgz", - "integrity": "sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg==", - "license": "ISC", - "optional": true, - "dependencies": { - "agentkeepalive": "^4.1.3", - "cacache": "^15.2.0", - "http-cache-semantics": "^4.1.0", - "http-proxy-agent": "^4.0.1", - "https-proxy-agent": "^5.0.0", - "is-lambda": "^1.0.1", - "lru-cache": "^6.0.0", - "minipass": "^3.1.3", - "minipass-collect": "^1.0.2", - "minipass-fetch": "^1.3.2", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "negotiator": "^0.6.2", - "promise-retry": "^2.0.1", - "socks-proxy-agent": "^6.0.0", - "ssri": "^8.0.0" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/makeerror": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", - "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "tmpl": "1.0.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==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/merge-descriptors": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", - "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true, - "license": "MIT" - }, - "node_modules/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "dev": true, - "license": "MIT", - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "license": "MIT", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/mime-db": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", - "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types/node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/mimic-response": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", - "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "devOptional": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/minipass-collect": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", - "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", - "license": "ISC", - "optional": true, - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/minipass-fetch": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-1.4.1.tgz", - "integrity": "sha512-CGH1eblLq26Y15+Azk7ey4xh0J/XfJfrCox5LDJiKqI2Q2iwOLOKrlmIaODiSQS8d18jalF6y2K2ePUm0CmShw==", - "license": "MIT", - "optional": true, - "dependencies": { - "minipass": "^3.1.0", - "minipass-sized": "^1.0.3", - "minizlib": "^2.0.0" - }, - "engines": { - "node": ">=8" - }, - "optionalDependencies": { - "encoding": "^0.1.12" - } - }, - "node_modules/minipass-flush": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", - "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", - "license": "ISC", - "optional": true, - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/minipass-pipeline": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", - "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", - "license": "ISC", - "optional": true, - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/minipass-sized": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", - "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", - "license": "ISC", - "optional": true, - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/minizlib": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", - "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", - "license": "MIT", - "dependencies": { - "minipass": "^3.0.0", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", - "license": "MIT", - "dependencies": { - "minimist": "^1.2.6" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, - "node_modules/mkdirp-classic": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", - "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", - "license": "MIT" - }, - "node_modules/moment": { - "version": "2.30.1", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", - "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", - "license": "MIT", - "engines": { - "node": "*" - } - }, - "node_modules/moment-timezone": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.6.0.tgz", - "integrity": "sha512-ldA5lRNm3iJCWZcBCab4pnNL3HSZYXVb/3TYr75/1WCTWYuTqYUb5f/S384pncYjJ88lbO8Z4uPDvmoluHJc8Q==", - "license": "MIT", - "dependencies": { - "moment": "^2.29.4" - }, - "engines": { - "node": "*" - } - }, - "node_modules/morgan": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz", - "integrity": "sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==", - "license": "MIT", - "dependencies": { - "basic-auth": "~2.0.1", - "debug": "2.6.9", - "depd": "~2.0.0", - "on-finished": "~2.3.0", - "on-headers": "~1.0.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/morgan/node_modules/on-finished": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", - "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", - "license": "MIT", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "node_modules/multer": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/multer/-/multer-2.0.1.tgz", - "integrity": "sha512-Ug8bXeTIUlxurg8xLTEskKShvcKDZALo1THEX5E41pYCD2sCVub5/kIRIGqWNoqV6szyLyQKV6mD4QUrWE5GCQ==", - "license": "MIT", - "dependencies": { - "append-field": "^1.0.0", - "busboy": "^1.6.0", - "concat-stream": "^2.0.0", - "mkdirp": "^0.5.6", - "object-assign": "^4.1.1", - "type-is": "^1.6.18", - "xtend": "^4.0.2" - }, - "engines": { - "node": ">= 10.16.0" - } - }, - "node_modules/napi-build-utils": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz", - "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==", - "license": "MIT" - }, - "node_modules/napi-postinstall": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.2.4.tgz", - "integrity": "sha512-ZEzHJwBhZ8qQSbknHqYcdtQVr8zUgGyM/q6h6qAyhtyVMNrSgDhrC4disf03dYW0e+czXyLnZINnCTEkWy0eJg==", - "dev": true, - "license": "MIT", - "bin": { - "napi-postinstall": "lib/cli.js" - }, - "engines": { - "node": "^12.20.0 || ^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/napi-postinstall" - } - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true, - "license": "MIT" - }, - "node_modules/negotiator": { - "version": "0.6.4", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz", - "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/node-abi": { - "version": "3.75.0", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.75.0.tgz", - "integrity": "sha512-OhYaY5sDsIka7H7AtijtI9jwGYLyl29eQn/W623DiN/MIv5sUqc4g7BIDThX+gb7di9f6xK02nkp8sdfFWZLTg==", - "license": "MIT", - "dependencies": { - "semver": "^7.3.5" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/node-addon-api": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.4.0.tgz", - "integrity": "sha512-D9DI/gXHvVmjHS08SVch0Em8G5S1P+QWtU31appcKT/8wFSPRcdHadIFSAntdMMVM5zz+/DL+bL/gz3UDppqtg==", - "license": "MIT", - "engines": { - "node": "^18 || ^20 || >= 21" - } - }, - "node_modules/node-cron": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/node-cron/-/node-cron-4.1.0.tgz", - "integrity": "sha512-OS+3ORu+h03/haS6Di8Qr7CrVs4YaKZZOynZwQpyPZDnR3tqRbwJmuP2gVR16JfhLgyNlloAV1VTrrWlRogCFA==", - "license": "ISC", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/node-fetch": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "license": "MIT", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, - "node_modules/node-gyp": { - "version": "8.4.1", - "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-8.4.1.tgz", - "integrity": "sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w==", - "license": "MIT", - "optional": true, - "dependencies": { - "env-paths": "^2.2.0", - "glob": "^7.1.4", - "graceful-fs": "^4.2.6", - "make-fetch-happen": "^9.1.0", - "nopt": "^5.0.0", - "npmlog": "^6.0.0", - "rimraf": "^3.0.2", - "semver": "^7.3.5", - "tar": "^6.1.2", - "which": "^2.0.2" - }, - "bin": { - "node-gyp": "bin/node-gyp.js" - }, - "engines": { - "node": ">= 10.12.0" - } - }, - "node_modules/node-gyp-build": { - "version": "4.8.4", - "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz", - "integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==", - "license": "MIT", - "bin": { - "node-gyp-build": "bin.js", - "node-gyp-build-optional": "optional.js", - "node-gyp-build-test": "build-test.js" - } - }, - "node_modules/node-int64": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", - "dev": true, - "license": "MIT" - }, - "node_modules/node-releases": { - "version": "2.0.19", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", - "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", - "dev": true, - "license": "MIT" - }, - "node_modules/nodemon": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.10.tgz", - "integrity": "sha512-WDjw3pJ0/0jMFmyNDp3gvY2YizjLmmOUQo6DEBY+JgdvW/yQ9mEeSw6H5ythl5Ny2ytb7f9C2nIbjSxMNzbJXw==", - "dev": true, - "license": "MIT", - "dependencies": { - "chokidar": "^3.5.2", - "debug": "^4", - "ignore-by-default": "^1.0.1", - "minimatch": "^3.1.2", - "pstree.remy": "^1.1.8", - "semver": "^7.5.3", - "simple-update-notifier": "^2.0.0", - "supports-color": "^5.5.0", - "touch": "^3.1.0", - "undefsafe": "^2.0.5" - }, - "bin": { - "nodemon": "bin/nodemon.js" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/nodemon" - } - }, - "node_modules/nodemon/node_modules/debug": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/nodemon/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/nopt": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", - "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", - "license": "ISC", - "optional": true, - "dependencies": { - "abbrev": "1" - }, - "bin": { - "nopt": "bin/nopt.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/npmlog": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz", - "integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==", - "deprecated": "This package is no longer supported.", - "license": "ISC", - "optional": true, - "dependencies": { - "are-we-there-yet": "^3.0.0", - "console-control-strings": "^1.1.0", - "gauge": "^4.0.3", - "set-blocking": "^2.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/nth-check": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", - "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", - "license": "BSD-2-Clause", - "dependencies": { - "boolbase": "^1.0.0" - }, - "funding": { - "url": "https://github.com/fb55/nth-check?sponsor=1" - } - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "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==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "license": "MIT", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/on-headers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", - "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "license": "ISC", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/p-locate/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-map": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", - "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", - "license": "MIT", - "optional": true, - "dependencies": { - "aggregate-error": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/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/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/parse5": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", - "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", - "license": "MIT", - "dependencies": { - "entities": "^6.0.0" - }, - "funding": { - "url": "https://github.com/inikulin/parse5?sponsor=1" - } - }, - "node_modules/parse5-htmlparser2-tree-adapter": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.1.0.tgz", - "integrity": "sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==", - "license": "MIT", - "dependencies": { - "domhandler": "^5.0.3", - "parse5": "^7.0.0" - }, - "funding": { - "url": "https://github.com/inikulin/parse5?sponsor=1" - } - }, - "node_modules/parse5-parser-stream": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/parse5-parser-stream/-/parse5-parser-stream-7.1.2.tgz", - "integrity": "sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow==", - "license": "MIT", - "dependencies": { - "parse5": "^7.0.0" - }, - "funding": { - "url": "https://github.com/inikulin/parse5?sponsor=1" - } - }, - "node_modules/parse5/node_modules/entities": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", - "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "devOptional": true, - "license": "MIT", - "engines": { - "node": ">=0.10.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": "1.11.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", - "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" - }, - "engines": { - "node": ">=16 || 14 >=14.18" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/path-scurry/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/path-scurry/node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/path-to-regexp": { - "version": "0.1.12", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", - "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", - "license": "MIT" - }, - "node_modules/pg-connection-string": { - "version": "2.9.0", - "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.9.0.tgz", - "integrity": "sha512-P2DEBKuvh5RClafLngkAuGe9OUlFV7ebu8w1kmaaOgPcpJd1RIFh7otETfI6hAR8YupOLFTY7nuvvIn7PLciUQ==", - "license": "MIT" - }, - "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": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pirates": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", - "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "find-up": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/prebuild-install": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", - "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==", - "license": "MIT", - "dependencies": { - "detect-libc": "^2.0.0", - "expand-template": "^2.0.3", - "github-from-package": "0.0.0", - "minimist": "^1.2.3", - "mkdirp-classic": "^0.5.3", - "napi-build-utils": "^2.0.0", - "node-abi": "^3.3.0", - "pump": "^3.0.0", - "rc": "^1.2.7", - "simple-get": "^4.0.0", - "tar-fs": "^2.0.0", - "tunnel-agent": "^0.6.0" - }, - "bin": { - "prebuild-install": "bin.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/pretty-format": { - "version": "30.0.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-30.0.0.tgz", - "integrity": "sha512-18NAOUr4ZOQiIR+BgI5NhQE7uREdx4ZyV0dyay5izh4yfQ+1T7BSvggxvRGoXocrRyevqW5OhScUjbi9GB8R8Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/schemas": "30.0.0", - "ansi-styles": "^5.2.0", - "react-is": "^18.3.1" - }, - "engines": { - "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0" - } - }, - "node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/promise-inflight": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", - "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==", - "license": "ISC", - "optional": true - }, - "node_modules/promise-retry": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", - "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", - "license": "MIT", - "optional": true, - "dependencies": { - "err-code": "^2.0.2", - "retry": "^0.12.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/prop-types": { - "version": "15.8.1", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", - "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "license": "MIT", - "dependencies": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.13.1" - } - }, - "node_modules/prop-types/node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "license": "MIT" - }, - "node_modules/proto-list": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", - "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==", - "dev": true, - "license": "ISC" - }, - "node_modules/proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "license": "MIT", - "dependencies": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/pstree.remy": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", - "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", - "dev": true, - "license": "MIT" - }, - "node_modules/pump": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz", - "integrity": "sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==", - "license": "MIT", - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "node_modules/pure-rand": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-7.0.1.tgz", - "integrity": "sha512-oTUZM/NAZS8p7ANR3SHh30kXB+zK2r2BPcEn/awJIbOvq82WoMN4p62AWWp3Hhw50G0xMsw1mhIBLqHw64EcNQ==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/dubzzz" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fast-check" - } - ], - "license": "MIT" - }, - "node_modules/qs": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", - "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", - "license": "BSD-3-Clause", - "dependencies": { - "side-channel": "^1.0.6" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/random-bytes": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz", - "integrity": "sha512-iv7LhNVO047HzYR3InF6pUcUsPQiHTM1Qal51DcGSuZFBil1aBBWG5eHPNek7bvILMaYJ/8RU1e8w1AMdHmLQQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/raw-body": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", - "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", - "license": "MIT", - "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/raw-body/node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", - "dependencies": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "bin": { - "rc": "cli.js" - } - }, - "node_modules/react": { - "version": "19.1.0", - "resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz", - "integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-dom": { - "version": "19.1.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz", - "integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==", - "license": "MIT", - "peer": true, - "dependencies": { - "scheduler": "^0.26.0" - }, - "peerDependencies": { - "react": "^19.1.0" - } - }, - "node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "license": "MIT" - }, - "node_modules/react-smooth": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/react-smooth/-/react-smooth-4.0.4.tgz", - "integrity": "sha512-gnGKTpYwqL0Iii09gHobNolvX4Kiq4PKx6eWBCYYix+8cdw+cGo3do906l1NBPKkSWx1DghC1dlWG9L2uGd61Q==", - "license": "MIT", - "dependencies": { - "fast-equals": "^5.0.1", - "prop-types": "^15.8.1", - "react-transition-group": "^4.4.5" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" - } - }, - "node_modules/react-transition-group": { - "version": "4.4.5", - "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", - "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", - "license": "BSD-3-Clause", - "dependencies": { - "@babel/runtime": "^7.5.5", - "dom-helpers": "^5.0.1", - "loose-envify": "^1.4.0", - "prop-types": "^15.6.2" - }, - "peerDependencies": { - "react": ">=16.6.0", - "react-dom": ">=16.6.0" - } - }, - "node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "license": "MIT", - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/recharts": { - "version": "2.15.4", - "resolved": "https://registry.npmjs.org/recharts/-/recharts-2.15.4.tgz", - "integrity": "sha512-UT/q6fwS3c1dHbXv2uFgYJ9BMFHu3fwnd7AYZaEQhXuYQ4hgsxLvsUXzGdKeZrW5xopzDCvuA2N41WJ88I7zIw==", - "license": "MIT", - "dependencies": { - "clsx": "^2.0.0", - "eventemitter3": "^4.0.1", - "lodash": "^4.17.21", - "react-is": "^18.3.1", - "react-smooth": "^4.0.4", - "recharts-scale": "^0.4.4", - "tiny-invariant": "^1.3.1", - "victory-vendor": "^36.6.8" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "react": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", - "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" - } - }, - "node_modules/recharts-scale": { - "version": "0.4.5", - "resolved": "https://registry.npmjs.org/recharts-scale/-/recharts-scale-0.4.5.tgz", - "integrity": "sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w==", - "license": "MIT", - "dependencies": { - "decimal.js-light": "^2.4.1" - } - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/resolve": { - "version": "1.22.10", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", - "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-core-module": "^2.16.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-cwd": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", - "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", - "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/retry-as-promised": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/retry-as-promised/-/retry-as-promised-7.1.1.tgz", - "integrity": "sha512-hMD7odLOt3LkTjcif8aRZqi/hybjpLNgSk5oF5FCowfCjok6LukpN2bDX7R5wDmbgBQFn7YoBxSagmtXHaJYJw==", - "license": "MIT" - }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", - "license": "ISC", - "optional": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "license": "MIT" - }, - "node_modules/scheduler": { - "version": "0.26.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz", - "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==", - "license": "MIT", - "peer": true - }, - "node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/send": { - "version": "0.19.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", - "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", - "license": "MIT", - "dependencies": { - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "2.4.1", - "range-parser": "~1.2.1", - "statuses": "2.0.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/send/node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/send/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/sequelize": { - "version": "6.37.7", - "resolved": "https://registry.npmjs.org/sequelize/-/sequelize-6.37.7.tgz", - "integrity": "sha512-mCnh83zuz7kQxxJirtFD7q6Huy6liPanI67BSlbzSYgVNl5eXVdE2CN1FuAeZwG1SNpGsNRCV+bJAVVnykZAFA==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/sequelize" - } - ], - "license": "MIT", - "dependencies": { - "@types/debug": "^4.1.8", - "@types/validator": "^13.7.17", - "debug": "^4.3.4", - "dottie": "^2.0.6", - "inflection": "^1.13.4", - "lodash": "^4.17.21", - "moment": "^2.29.4", - "moment-timezone": "^0.5.43", - "pg-connection-string": "^2.6.1", - "retry-as-promised": "^7.0.4", - "semver": "^7.5.4", - "sequelize-pool": "^7.1.0", - "toposort-class": "^1.0.1", - "uuid": "^8.3.2", - "validator": "^13.9.0", - "wkx": "^0.5.0" - }, - "engines": { - "node": ">=10.0.0" - }, - "peerDependenciesMeta": { - "ibm_db": { - "optional": true - }, - "mariadb": { - "optional": true - }, - "mysql2": { - "optional": true - }, - "oracledb": { - "optional": true - }, - "pg": { - "optional": true - }, - "pg-hstore": { - "optional": true - }, - "snowflake-sdk": { - "optional": true - }, - "sqlite3": { - "optional": true - }, - "tedious": { - "optional": true - } - } - }, - "node_modules/sequelize-cli": { - "version": "6.6.3", - "resolved": "https://registry.npmjs.org/sequelize-cli/-/sequelize-cli-6.6.3.tgz", - "integrity": "sha512-1YYPrcSRt/bpMDDSKM5ubY1mnJ2TEwIaGZcqITw4hLtGtE64nIqaBnLtMvH8VKHg6FbWpXTiFNc2mS/BtQCXZw==", - "dev": true, - "license": "MIT", - "dependencies": { - "fs-extra": "^9.1.0", - "js-beautify": "1.15.4", - "lodash": "^4.17.21", - "picocolors": "^1.1.1", - "resolve": "^1.22.1", - "umzug": "^2.3.0", - "yargs": "^16.2.0" - }, - "bin": { - "sequelize": "lib/sequelize", - "sequelize-cli": "lib/sequelize" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/sequelize-pool": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/sequelize-pool/-/sequelize-pool-7.1.0.tgz", - "integrity": "sha512-G9c0qlIWQSK29pR/5U2JF5dDQeqqHRragoyahj/Nx4KOOQ3CPPfzxnfqFPCSB7x5UgjOgnZ61nSxz+fjDpRlJg==", - "license": "MIT", - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/sequelize/node_modules/debug": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/sequelize/node_modules/moment-timezone": { - "version": "0.5.48", - "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.48.tgz", - "integrity": "sha512-f22b8LV1gbTO2ms2j2z13MuPogNoh5UzxL3nzNAYKGraILnbGc9NEE6dyiiiLv46DGRb8A4kg8UKWLjPthxBHw==", - "license": "MIT", - "dependencies": { - "moment": "^2.29.4" - }, - "engines": { - "node": "*" - } - }, - "node_modules/sequelize/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/sequelize/node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "license": "MIT", - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/serve-static": { - "version": "1.16.2", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", - "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", - "license": "MIT", - "dependencies": { - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.19.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", - "license": "ISC", - "optional": true - }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", - "license": "ISC" - }, - "node_modules/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.0", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", - "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3", - "side-channel-list": "^1.0.0", - "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.0", - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", - "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3" - }, - "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==", - "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==", - "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": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "devOptional": true, - "license": "ISC" - }, - "node_modules/simple-concat": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", - "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/simple-get": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", - "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "decompress-response": "^6.0.0", - "once": "^1.3.1", - "simple-concat": "^1.0.0" - } - }, - "node_modules/simple-update-notifier": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", - "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "semver": "^7.5.3" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/smart-buffer": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", - "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">= 6.0.0", - "npm": ">= 3.0.0" - } - }, - "node_modules/socks": { - "version": "2.8.5", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.5.tgz", - "integrity": "sha512-iF+tNDQla22geJdTyJB1wM/qrX9DMRwWrciEPwWLPRWAUEM8sQiyxgckLxWT1f7+9VabJS0jTGGr4QgBuvi6Ww==", - "license": "MIT", - "optional": true, - "dependencies": { - "ip-address": "^9.0.5", - "smart-buffer": "^4.2.0" - }, - "engines": { - "node": ">= 10.0.0", - "npm": ">= 3.0.0" - } - }, - "node_modules/socks-proxy-agent": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-6.2.1.tgz", - "integrity": "sha512-a6KW9G+6B3nWZ1yB8G7pJwL3ggLy1uTzKAgCb7ttblwqdz9fMGJUuTy3uFzEP48FAs9FLILlmzDlE2JJhVQaXQ==", - "license": "MIT", - "optional": true, - "dependencies": { - "agent-base": "^6.0.2", - "debug": "^4.3.3", - "socks": "^2.6.2" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/socks-proxy-agent/node_modules/debug": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", - "license": "MIT", - "optional": true, - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/socks-proxy-agent/node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT", - "optional": true - }, - "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/source-map-support": { - "version": "0.5.13", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", - "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", - "dev": true, - "license": "MIT", - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/sprintf-js": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", - "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", - "license": "BSD-3-Clause", - "optional": true - }, - "node_modules/sqlite3": { - "version": "5.1.7", - "resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-5.1.7.tgz", - "integrity": "sha512-GGIyOiFaG+TUra3JIfkI/zGP8yZYLPQ0pl1bH+ODjiX57sPhrLU5sQJn1y9bDKZUFYkX1crlrPfSYt0BKKdkog==", - "hasInstallScript": true, - "license": "BSD-3-Clause", - "dependencies": { - "bindings": "^1.5.0", - "node-addon-api": "^7.0.0", - "prebuild-install": "^7.1.1", - "tar": "^6.1.11" - }, - "optionalDependencies": { - "node-gyp": "8.x" - }, - "peerDependencies": { - "node-gyp": "8.x" - }, - "peerDependenciesMeta": { - "node-gyp": { - "optional": true - } - } - }, - "node_modules/sqlite3/node_modules/node-addon-api": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", - "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", - "license": "MIT" - }, - "node_modules/ssri": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz", - "integrity": "sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==", - "license": "ISC", - "optional": true, - "dependencies": { - "minipass": "^3.1.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/stack-utils": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", - "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "escape-string-regexp": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/streamsearch": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", - "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, - "node_modules/string-length": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", - "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "char-regex": "^1.0.2", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width-cjs": { - "name": "string-width", - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi-cjs": { - "name": "strip-ansi", - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/superagent": { - "version": "10.2.1", - "resolved": "https://registry.npmjs.org/superagent/-/superagent-10.2.1.tgz", - "integrity": "sha512-O+PCv11lgTNJUzy49teNAWLjBZfc+A1enOwTpLlH6/rsvKcTwcdTT8m9azGkVqM7HBl5jpyZ7KTPhHweokBcdg==", - "dev": true, - "license": "MIT", - "dependencies": { - "component-emitter": "^1.3.0", - "cookiejar": "^2.1.4", - "debug": "^4.3.4", - "fast-safe-stringify": "^2.1.1", - "form-data": "^4.0.0", - "formidable": "^3.5.4", - "methods": "^1.1.2", - "mime": "2.6.0", - "qs": "^6.11.0" - }, - "engines": { - "node": ">=14.18.0" - } - }, - "node_modules/superagent/node_modules/debug": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/superagent/node_modules/mime": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", - "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", - "dev": true, - "license": "MIT", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/superagent/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/supertest": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/supertest/-/supertest-7.1.1.tgz", - "integrity": "sha512-aI59HBTlG9e2wTjxGJV+DygfNLgnWbGdZxiA/sgrnNNikIW8lbDvCtF6RnhZoJ82nU7qv7ZLjrvWqCEm52fAmw==", - "dev": true, - "license": "MIT", - "dependencies": { - "methods": "^1.1.2", - "superagent": "^10.2.1" - }, - "engines": { - "node": ">=14.18.0" - } - }, - "node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/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/synckit": { - "version": "0.11.8", - "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.8.tgz", - "integrity": "sha512-+XZ+r1XGIJGeQk3VvXhT6xx/VpbHsRzsTkGgF6E5RX9TTXD0118l87puaEBZ566FhqblC6U0d4XnubznJDm30A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@pkgr/core": "^0.2.4" - }, - "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/synckit" - } - }, - "node_modules/tar": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", - "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", - "license": "ISC", - "dependencies": { - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "minipass": "^5.0.0", - "minizlib": "^2.1.1", - "mkdirp": "^1.0.3", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/tar-fs": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.3.tgz", - "integrity": "sha512-090nwYJDmlhwFwEW3QQl+vaNnxsO2yVsd45eTKRBzSzu+hlb1w2K9inVq5b0ngXuLVqQ4ApvsUHHnu/zQNkWAg==", - "license": "MIT", - "dependencies": { - "chownr": "^1.1.1", - "mkdirp-classic": "^0.5.2", - "pump": "^3.0.0", - "tar-stream": "^2.1.4" - } - }, - "node_modules/tar-fs/node_modules/chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", - "license": "ISC" - }, - "node_modules/tar-stream": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", - "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", - "license": "MIT", - "dependencies": { - "bl": "^4.0.3", - "end-of-stream": "^1.4.1", - "fs-constants": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/tar/node_modules/minipass": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", - "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", - "license": "ISC", - "engines": { - "node": ">=8" - } - }, - "node_modules/tar/node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "license": "MIT", - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", - "dev": true, - "license": "ISC", - "dependencies": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/tiny-invariant": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", - "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==", - "license": "MIT" - }, - "node_modules/tmpl": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", - "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "license": "MIT", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/toposort-class": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toposort-class/-/toposort-class-1.0.1.tgz", - "integrity": "sha512-OsLcGGbYF3rMjPUf8oKktyvCiUxSbqMMS39m33MAjLTC1DVIH6x3WSt63/M77ihI09+Sdfk1AXvfhCEeUmC7mg==", - "license": "MIT" - }, - "node_modules/touch": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz", - "integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==", - "dev": true, - "license": "ISC", - "bin": { - "nodetouch": "bin/nodetouch.js" - } - }, - "node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "license": "MIT" - }, - "node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "dev": true, - "license": "0BSD", - "optional": true - }, - "node_modules/tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", - "license": "Apache-2.0", - "dependencies": { - "safe-buffer": "^5.0.1" - }, - "engines": { - "node": "*" - } - }, - "node_modules/type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "license": "MIT", - "dependencies": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/typedarray": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", - "license": "MIT" - }, - "node_modules/uid-safe": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz", - "integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==", - "license": "MIT", - "dependencies": { - "random-bytes": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/umzug": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/umzug/-/umzug-2.3.0.tgz", - "integrity": "sha512-Z274K+e8goZK8QJxmbRPhl89HPO1K+ORFtm6rySPhFKfKc5GHhqdzD0SGhSWHkzoXasqJuItdhorSvY7/Cgflw==", - "dev": true, - "license": "MIT", - "dependencies": { - "bluebird": "^3.7.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/undefsafe": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", - "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", - "dev": true, - "license": "MIT" - }, - "node_modules/undici": { - "version": "7.10.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-7.10.0.tgz", - "integrity": "sha512-u5otvFBOBZvmdjWLVW+5DAc9Nkq8f24g0O9oY7qw2JVIF1VocIFoyz9JFkuVOS2j41AufeO0xnlweJ2RLT8nGw==", - "license": "MIT", - "engines": { - "node": ">=20.18.1" - } - }, - "node_modules/undici-types": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.8.0.tgz", - "integrity": "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==", - "license": "MIT" - }, - "node_modules/unique-filename": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", - "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", - "license": "ISC", - "optional": true, - "dependencies": { - "unique-slug": "^2.0.0" - } - }, - "node_modules/unique-slug": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", - "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", - "license": "ISC", - "optional": true, - "dependencies": { - "imurmurhash": "^0.1.4" - } - }, - "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/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/unrs-resolver": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.9.0.tgz", - "integrity": "sha512-wqaRu4UnzBD2ABTC1kLfBjAqIDZ5YUTr/MLGa7By47JV1bJDSW7jq/ZSLigB7enLe7ubNaJhtnBXgrc/50cEhg==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "napi-postinstall": "^0.2.2" - }, - "funding": { - "url": "https://opencollective.com/unrs-resolver" - }, - "optionalDependencies": { - "@unrs/resolver-binding-android-arm-eabi": "1.9.0", - "@unrs/resolver-binding-android-arm64": "1.9.0", - "@unrs/resolver-binding-darwin-arm64": "1.9.0", - "@unrs/resolver-binding-darwin-x64": "1.9.0", - "@unrs/resolver-binding-freebsd-x64": "1.9.0", - "@unrs/resolver-binding-linux-arm-gnueabihf": "1.9.0", - "@unrs/resolver-binding-linux-arm-musleabihf": "1.9.0", - "@unrs/resolver-binding-linux-arm64-gnu": "1.9.0", - "@unrs/resolver-binding-linux-arm64-musl": "1.9.0", - "@unrs/resolver-binding-linux-ppc64-gnu": "1.9.0", - "@unrs/resolver-binding-linux-riscv64-gnu": "1.9.0", - "@unrs/resolver-binding-linux-riscv64-musl": "1.9.0", - "@unrs/resolver-binding-linux-s390x-gnu": "1.9.0", - "@unrs/resolver-binding-linux-x64-gnu": "1.9.0", - "@unrs/resolver-binding-linux-x64-musl": "1.9.0", - "@unrs/resolver-binding-wasm32-wasi": "1.9.0", - "@unrs/resolver-binding-win32-arm64-msvc": "1.9.0", - "@unrs/resolver-binding-win32-ia32-msvc": "1.9.0", - "@unrs/resolver-binding-win32-x64-msvc": "1.9.0" - } - }, - "node_modules/update-browserslist-db": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", - "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", - "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/url-template": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/url-template/-/url-template-2.0.8.tgz", - "integrity": "sha512-XdVKMF4SJ0nP/O7XIPB0JwAEuT9lDIYnNsK8yGVe43y0AWoKeJNdv3ZNWh7ksJ6KqQFjOO6ox/VEitLnaVNufw==", - "license": "BSD" - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "license": "MIT" - }, - "node_modules/utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", - "license": "MIT", - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/uuid": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", - "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "license": "MIT", - "bin": { - "uuid": "dist/esm/bin/uuid" - } - }, - "node_modules/v8-to-istanbul": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", - "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", - "dev": true, - "license": "ISC", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.12", - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^2.0.0" - }, - "engines": { - "node": ">=10.12.0" - } - }, - "node_modules/validator": { - "version": "13.15.15", - "resolved": "https://registry.npmjs.org/validator/-/validator-13.15.15.tgz", - "integrity": "sha512-BgWVbCI72aIQy937xbawcs+hrVaN/CZ2UwutgaJ36hGqRrLNM+f5LUT/YPRbo8IV/ASeFzXszezV+y2+rq3l8A==", - "license": "MIT", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/victory-vendor": { - "version": "36.9.2", - "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-36.9.2.tgz", - "integrity": "sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ==", - "license": "MIT AND ISC", - "dependencies": { - "@types/d3-array": "^3.0.3", - "@types/d3-ease": "^3.0.0", - "@types/d3-interpolate": "^3.0.1", - "@types/d3-scale": "^4.0.2", - "@types/d3-shape": "^3.1.0", - "@types/d3-time": "^3.0.0", - "@types/d3-timer": "^3.0.0", - "d3-array": "^3.1.6", - "d3-ease": "^3.0.1", - "d3-interpolate": "^3.0.1", - "d3-scale": "^4.0.2", - "d3-shape": "^3.1.0", - "d3-time": "^3.0.0", - "d3-timer": "^3.0.1" - } - }, - "node_modules/walker": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", - "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "makeerror": "1.0.12" - } - }, - "node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", - "license": "BSD-2-Clause" - }, - "node_modules/whatwg-encoding": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", - "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", - "license": "MIT", - "dependencies": { - "iconv-lite": "0.6.3" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/whatwg-mimetype": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", - "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", - "license": "MIT", - "engines": { - "node": ">=18" - } - }, - "node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "license": "MIT", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "devOptional": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/wide-align": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", - "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", - "license": "ISC", - "optional": true, - "dependencies": { - "string-width": "^1.0.2 || 2 || 3 || 4" - } - }, - "node_modules/wkx": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/wkx/-/wkx-0.5.0.tgz", - "integrity": "sha512-Xng/d4Ichh8uN4l0FToV/258EjMGU9MGcA0HV2d9B/ZpZB3lqQm7nkOdZdm5GhKtLLhAE7PiVQwN4eN+2YJJUg==", - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs": { - "name": "wrap-ansi", - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "license": "ISC" - }, - "node_modules/write-file-atomic": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.1.tgz", - "integrity": "sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==", - "dev": true, - "license": "ISC", - "dependencies": { - "imurmurhash": "^0.1.4", - "signal-exit": "^4.0.1" - }, - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/write-file-atomic/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/xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "license": "MIT", - "engines": { - "node": ">=0.4" - } - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "license": "ISC" - }, - "node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", - "dev": true, - "license": "MIT", - "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } } - } } diff --git a/backend/package.json b/backend/package.json index 298e53a..196e8ac 100644 --- a/backend/package.json +++ b/backend/package.json @@ -1,59 +1,65 @@ { - "name": "backend", - "version": "1.0.0", - "description": "", - "main": "index.js", - "scripts": { - "start": "node app.js", - "dev": "nodemon app.js", - "test": "cross-env NODE_ENV=test jest", - "test:watch": "cross-env NODE_ENV=test jest --watch", - "test:coverage": "cross-env NODE_ENV=test jest --coverage", - "test:unit": "cross-env NODE_ENV=test jest tests/unit", - "test:integration": "cross-env NODE_ENV=test jest tests/integration", - "test:telegram-duplicates": "node scripts/test-telegram-duplicates.js", - "db:init": "node scripts/db-init.js", - "db:sync": "node scripts/db-sync.js", - "db:migrate": "node scripts/db-migrate.js", - "db:reset": "node scripts/db-reset.js", - "db:status": "node scripts/db-status.js", - "user:create": "node scripts/user-create.js", - "migration:create": "node scripts/migration-create.js", - "migration:run": "npx sequelize-cli db:migrate", - "migration:undo": "npx sequelize-cli db:migrate:undo", - "migration:undo:all": "npx sequelize-cli db:migrate:undo:all", - "migration:status": "npx sequelize-cli db:migrate:status", - "seed:dev": "node scripts/seed-dev-data.js" - }, - "keywords": [], - "author": "", - "license": "ISC", - "dependencies": { - "bcrypt": "^6.0.0", - "cheerio": "^1.1.0", - "compression": "^1.8.0", - "connect-session-sequelize": "^7.1.7", - "cors": "^2.8.5", - "dotenv": "^16.5.0", - "express": "^4.18.2", - "express-session": "^1.18.1", - "googleapis": "^144.0.0", - "helmet": "^8.1.0", - "js-yaml": "^4.1.0", - "moment-timezone": "^0.6.0", - "morgan": "^1.10.0", - "multer": "^2.0.1", - "node-cron": "^4.1.0", - "recharts": "^2.15.4", - "sequelize": "^6.37.7", - "sqlite3": "^5.1.7", - "uuid": "^11.1.0" - }, - "devDependencies": { - "cross-env": "^7.0.3", - "jest": "^30.0.0", - "nodemon": "^3.0.1", - "sequelize-cli": "^6.6.2", - "supertest": "^7.1.1" - } + "name": "backend", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "start": "node app.js", + "dev": "nodemon app.js", + "test": "cross-env NODE_ENV=test jest", + "test:watch": "cross-env NODE_ENV=test jest --watch", + "test:coverage": "cross-env NODE_ENV=test jest --coverage", + "test:unit": "cross-env NODE_ENV=test jest tests/unit", + "test:integration": "cross-env NODE_ENV=test jest tests/integration", + "test:telegram-duplicates": "node scripts/test-telegram-duplicates.js", + "db:init": "node scripts/db-init.js", + "db:sync": "node scripts/db-sync.js", + "db:migrate": "node scripts/db-migrate.js", + "db:reset": "node scripts/db-reset.js", + "db:status": "node scripts/db-status.js", + "user:create": "node scripts/user-create.js", + "migration:create": "node scripts/migration-create.js", + "migration:run": "npx sequelize-cli db:migrate", + "migration:undo": "npx sequelize-cli db:migrate:undo", + "migration:undo:all": "npx sequelize-cli db:migrate:undo:all", + "migration:status": "npx sequelize-cli db:migrate:status", + "seed:dev": "node scripts/seed-dev-data.js", + "lint": "eslint .", + "lint-fix": "eslint . --fix", + "format": "prettier --write ." + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "bcrypt": "~6.0.0", + "compression": "~1.8.0", + "connect-session-sequelize": "~7.1.7", + "cors": "~2.8.5", + "dotenv": "~16.5.0", + "eslint": "^8.0.0", + "express": "~4.18.2", + "express-session": "~1.18.1", + "googleapis": "~144.0.0", + "helmet": "~8.1.0", + "js-yaml": "~4.1.0", + "moment-timezone": "~0.6.0", + "morgan": "~1.10.0", + "multer": "~2.0.1", + "node-cron": "~4.1.0", + "recharts": "~2.15.4", + "sequelize": "~6.37.7", + "sqlite3": "~5.1.7", + "uuid": "~11.1.0" + }, + "devDependencies": { + "cross-env": "~7.0.3", + "eslint-plugin-jest": "^29.0.1", + "eslint-plugin-prettier": "^5.5.1", + "jest": "~30.0.0", + "nodemon": "~3.0.1", + "prettier": "~3.6.2", + "sequelize-cli": "~6.6.2", + "supertest": "~7.1.1" + } } diff --git a/backend/routes/areas.js b/backend/routes/areas.js index 84607b8..dcab673 100644 --- a/backend/routes/areas.js +++ b/backend/routes/areas.js @@ -4,127 +4,135 @@ const router = express.Router(); // GET /api/areas router.get('/areas', async (req, res) => { - try { - if (!req.session || !req.session.userId) { - return res.status(401).json({ error: 'Authentication required' }); + try { + if (!req.session || !req.session.userId) { + return res.status(401).json({ error: 'Authentication required' }); + } + + const areas = await Area.findAll({ + where: { user_id: req.session.userId }, + order: [['name', 'ASC']], + }); + + res.json(areas); + } catch (error) { + console.error('Error fetching areas:', error); + res.status(500).json({ error: 'Internal server error' }); } - - const areas = await Area.findAll({ - where: { user_id: req.session.userId }, - order: [['name', 'ASC']] - }); - - res.json(areas); - } catch (error) { - console.error('Error fetching areas:', error); - res.status(500).json({ error: 'Internal server error' }); - } }); // GET /api/areas/:id router.get('/areas/:id', async (req, res) => { - try { - if (!req.session || !req.session.userId) { - return res.status(401).json({ error: 'Authentication required' }); + try { + if (!req.session || !req.session.userId) { + return res.status(401).json({ error: 'Authentication required' }); + } + + const area = await Area.findOne({ + where: { id: req.params.id, user_id: req.session.userId }, + }); + + if (!area) { + return res.status(404).json({ + error: "Area not found or doesn't belong to the current user.", + }); + } + + res.json(area); + } catch (error) { + console.error('Error fetching area:', error); + res.status(500).json({ error: 'Internal server error' }); } - - const area = await Area.findOne({ - where: { id: req.params.id, user_id: req.session.userId } - }); - - if (!area) { - return res.status(404).json({ error: "Area not found or doesn't belong to the current user." }); - } - - res.json(area); - } catch (error) { - console.error('Error fetching area:', error); - res.status(500).json({ error: 'Internal server error' }); - } }); // POST /api/areas router.post('/areas', async (req, res) => { - try { - if (!req.session || !req.session.userId) { - return res.status(401).json({ error: 'Authentication required' }); + try { + if (!req.session || !req.session.userId) { + return res.status(401).json({ error: 'Authentication required' }); + } + + const { name, description } = req.body; + + if (!name || !name.trim()) { + return res.status(400).json({ error: 'Area name is required.' }); + } + + const area = await Area.create({ + name: name.trim(), + description: description || '', + user_id: req.session.userId, + }); + + res.status(201).json(area); + } catch (error) { + console.error('Error creating area:', error); + res.status(400).json({ + error: 'There was a problem creating the area.', + details: error.errors + ? error.errors.map((e) => e.message) + : [error.message], + }); } - - const { name, description } = req.body; - - if (!name || !name.trim()) { - return res.status(400).json({ error: 'Area name is required.' }); - } - - const area = await Area.create({ - name: name.trim(), - description: description || '', - user_id: req.session.userId - }); - - res.status(201).json(area); - } catch (error) { - console.error('Error creating area:', error); - res.status(400).json({ - error: 'There was a problem creating the area.', - details: error.errors ? error.errors.map(e => e.message) : [error.message] - }); - } }); // PATCH /api/areas/:id router.patch('/areas/:id', async (req, res) => { - try { - if (!req.session || !req.session.userId) { - return res.status(401).json({ error: 'Authentication required' }); + try { + if (!req.session || !req.session.userId) { + return res.status(401).json({ error: 'Authentication required' }); + } + + const area = await Area.findOne({ + where: { id: req.params.id, user_id: req.session.userId }, + }); + + if (!area) { + return res.status(404).json({ error: 'Area not found.' }); + } + + const { name, description } = req.body; + const updateData = {}; + + if (name !== undefined) updateData.name = name; + if (description !== undefined) updateData.description = description; + + await area.update(updateData); + res.json(area); + } catch (error) { + console.error('Error updating area:', error); + res.status(400).json({ + error: 'There was a problem updating the area.', + details: error.errors + ? error.errors.map((e) => e.message) + : [error.message], + }); } - - const area = await Area.findOne({ - where: { id: req.params.id, user_id: req.session.userId } - }); - - if (!area) { - return res.status(404).json({ error: 'Area not found.' }); - } - - const { name, description } = req.body; - const updateData = {}; - - if (name !== undefined) updateData.name = name; - if (description !== undefined) updateData.description = description; - - await area.update(updateData); - res.json(area); - } catch (error) { - console.error('Error updating area:', error); - res.status(400).json({ - error: 'There was a problem updating the area.', - details: error.errors ? error.errors.map(e => e.message) : [error.message] - }); - } }); // DELETE /api/areas/:id router.delete('/areas/:id', async (req, res) => { - try { - if (!req.session || !req.session.userId) { - return res.status(401).json({ error: 'Authentication required' }); + try { + if (!req.session || !req.session.userId) { + return res.status(401).json({ error: 'Authentication required' }); + } + + const area = await Area.findOne({ + where: { id: req.params.id, user_id: req.session.userId }, + }); + + if (!area) { + return res.status(404).json({ error: 'Area not found.' }); + } + + await area.destroy(); + res.status(204).send(); + } catch (error) { + console.error('Error deleting area:', error); + res.status(400).json({ + error: 'There was a problem deleting the area.', + }); } - - const area = await Area.findOne({ - where: { id: req.params.id, user_id: req.session.userId } - }); - - if (!area) { - return res.status(404).json({ error: 'Area not found.' }); - } - - await area.destroy(); - res.status(204).send(); - } catch (error) { - console.error('Error deleting area:', error); - res.status(400).json({ error: 'There was a problem deleting the area.' }); - } }); -module.exports = router; \ No newline at end of file +module.exports = router; diff --git a/backend/routes/auth.js b/backend/routes/auth.js index 3af9917..7b41d05 100644 --- a/backend/routes/auth.js +++ b/backend/routes/auth.js @@ -4,75 +4,78 @@ const router = express.Router(); // Get current user router.get('/current_user', async (req, res) => { - try { - if (req.session && req.session.userId) { - const user = await User.findByPk(req.session.userId); - if (user) { - return res.json({ - user: { - id: user.id, - email: user.email, - language: user.language, - appearance: user.appearance, - timezone: user.timezone - } - }); - } + try { + if (req.session && req.session.userId) { + const user = await User.findByPk(req.session.userId); + if (user) { + return res.json({ + user: { + id: user.id, + email: user.email, + language: user.language, + appearance: user.appearance, + timezone: user.timezone, + }, + }); + } + } + + res.json({ user: null }); + } catch (error) { + console.error('Error fetching current user:', error); + res.status(500).json({ error: 'Internal server error' }); } - - res.json({ user: null }); - } catch (error) { - console.error('Error fetching current user:', error); - res.status(500).json({ error: 'Internal server error' }); - } }); // Login router.post('/login', async (req, res) => { - try { - const { email, password } = req.body; + try { + const { email, password } = req.body; - if (!email || !password) { - return res.status(400).json({ error: 'Invalid login parameters.' }); + if (!email || !password) { + return res.status(400).json({ error: 'Invalid login parameters.' }); + } + + const user = await User.findOne({ where: { email } }); + if (!user) { + return res.status(401).json({ errors: ['Invalid credentials'] }); + } + + const isValidPassword = await User.checkPassword( + password, + user.password_digest + ); + if (!isValidPassword) { + return res.status(401).json({ errors: ['Invalid credentials'] }); + } + + req.session.userId = user.id; + + res.json({ + user: { + id: user.id, + email: user.email, + language: user.language, + appearance: user.appearance, + timezone: user.timezone, + }, + }); + } catch (error) { + console.error('Login error:', error); + res.status(500).json({ error: 'Internal server error' }); } - - const user = await User.findOne({ where: { email } }); - if (!user) { - return res.status(401).json({ errors: ['Invalid credentials'] }); - } - - const isValidPassword = await User.checkPassword(password, user.password_digest); - if (!isValidPassword) { - return res.status(401).json({ errors: ['Invalid credentials'] }); - } - - req.session.userId = user.id; - - res.json({ - user: { - id: user.id, - email: user.email, - language: user.language, - appearance: user.appearance, - timezone: user.timezone - } - }); - } catch (error) { - console.error('Login error:', error); - res.status(500).json({ error: 'Internal server error' }); - } }); // Logout router.get('/logout', (req, res) => { - req.session.destroy((err) => { - if (err) { - console.error('Logout error:', err); - return res.status(500).json({ error: 'Could not log out' }); - } - - res.json({ message: 'Logged out successfully' }); - }); + req.session.destroy((err) => { + if (err) { + console.error('Logout error:', err); + return res.status(500).json({ error: 'Could not log out' }); + } + + res.json({ message: 'Logged out successfully' }); + }); }); -module.exports = router; \ No newline at end of file +module.exports = router; diff --git a/backend/routes/calendar.js b/backend/routes/calendar.js index d61d402..f8e05dd 100644 --- a/backend/routes/calendar.js +++ b/backend/routes/calendar.js @@ -8,132 +8,149 @@ const SCOPES = ['https://www.googleapis.com/auth/calendar.readonly']; // OAuth2 client setup const getOAuth2Client = () => { - return new google.auth.OAuth2( - process.env.GOOGLE_CLIENT_ID, - process.env.GOOGLE_CLIENT_SECRET, - process.env.GOOGLE_REDIRECT_URI || 'http://localhost:3002/api/calendar/oauth/callback' - ); + return new google.auth.OAuth2( + process.env.GOOGLE_CLIENT_ID, + process.env.GOOGLE_CLIENT_SECRET, + process.env.GOOGLE_REDIRECT_URI || + 'http://localhost:3002/api/calendar/oauth/callback' + ); }; // GET /api/calendar/auth - Start OAuth flow (Demo mode) router.get('/auth', requireAuth, (req, res) => { - try { - // Check if Google credentials are configured - if (!process.env.GOOGLE_CLIENT_ID || !process.env.GOOGLE_CLIENT_SECRET) { - // Demo mode - simulate successful connection - console.log('Demo mode: Simulating Google Calendar connection for user:', req.currentUser.id); - - // Simulate the callback redirect with success - const frontendUrl = process.env.FRONTEND_URL || 'http://localhost:8080'; - return res.json({ - authUrl: `${frontendUrl}/calendar?demo=true&connected=true`, - demo: true, - message: 'Demo mode: Google Calendar integration simulated' - }); + try { + // Check if Google credentials are configured + if ( + !process.env.GOOGLE_CLIENT_ID || + !process.env.GOOGLE_CLIENT_SECRET + ) { + // Demo mode - simulate successful connection + console.log( + 'Demo mode: Simulating Google Calendar connection for user:', + req.currentUser.id + ); + + // Simulate the callback redirect with success + const frontendUrl = + process.env.FRONTEND_URL || 'http://localhost:8080'; + return res.json({ + authUrl: `${frontendUrl}/calendar?demo=true&connected=true`, + demo: true, + message: 'Demo mode: Google Calendar integration simulated', + }); + } + + // Production mode with real Google OAuth + const oauth2Client = getOAuth2Client(); + + const authUrl = oauth2Client.generateAuthUrl({ + access_type: 'offline', + scope: SCOPES, + state: JSON.stringify({ userId: req.currentUser.id }), + }); + + res.json({ authUrl }); + } catch (error) { + console.error('Error generating auth URL:', error); + res.status(500).json({ error: 'Failed to generate authorization URL' }); } - - // Production mode with real Google OAuth - const oauth2Client = getOAuth2Client(); - - const authUrl = oauth2Client.generateAuthUrl({ - access_type: 'offline', - scope: SCOPES, - state: JSON.stringify({ userId: req.currentUser.id }) - }); - - res.json({ authUrl }); - } catch (error) { - console.error('Error generating auth URL:', error); - res.status(500).json({ error: 'Failed to generate authorization URL' }); - } }); // GET /api/calendar/oauth/callback - Handle OAuth callback router.get('/oauth/callback', async (req, res) => { - try { - const { code, state } = req.query; - - if (!code) { - return res.status(400).json({ error: 'Authorization code not provided' }); + try { + const { code, state } = req.query; + + if (!code) { + return res + .status(400) + .json({ error: 'Authorization code not provided' }); + } + + const oauth2Client = getOAuth2Client(); + const { tokens } = await oauth2Client.getToken(code); + + // Parse state to get user ID + const { userId } = JSON.parse(state); + + // Here you would typically save the tokens to the database + // For now, we'll just return them (in production, store securely) + console.log('Google Calendar tokens received for user:', userId); + console.log('Tokens:', tokens); + + // TODO: Save tokens to database associated with user + // await saveGoogleTokensForUser(userId, tokens); + + // Redirect to frontend with success + res.redirect( + `${process.env.FRONTEND_URL || 'http://localhost:8080'}/calendar?connected=true` + ); + } catch (error) { + console.error('Error handling OAuth callback:', error); + res.redirect( + `${process.env.FRONTEND_URL || 'http://localhost:8080'}/calendar?error=auth_failed` + ); } - - const oauth2Client = getOAuth2Client(); - const { tokens } = await oauth2Client.getToken(code); - - // Parse state to get user ID - const { userId } = JSON.parse(state); - - // Here you would typically save the tokens to the database - // For now, we'll just return them (in production, store securely) - console.log('Google Calendar tokens received for user:', userId); - console.log('Tokens:', tokens); - - // TODO: Save tokens to database associated with user - // await saveGoogleTokensForUser(userId, tokens); - - // Redirect to frontend with success - res.redirect(`${process.env.FRONTEND_URL || 'http://localhost:8080'}/calendar?connected=true`); - } catch (error) { - console.error('Error handling OAuth callback:', error); - res.redirect(`${process.env.FRONTEND_URL || 'http://localhost:8080'}/calendar?error=auth_failed`); - } }); // GET /api/calendar/status - Check connection status router.get('/status', requireAuth, async (req, res) => { - try { - // Check if we're in demo mode or have real Google integration - if (!process.env.GOOGLE_CLIENT_ID || !process.env.GOOGLE_CLIENT_SECRET) { - // Demo mode - check if user has been "connected" in this session - // For demo purposes, we'll simulate connection status - res.json({ - connected: false, // Will be set to true after demo connection - email: null, - demo: true - }); - return; - } + try { + // Check if we're in demo mode or have real Google integration + if ( + !process.env.GOOGLE_CLIENT_ID || + !process.env.GOOGLE_CLIENT_SECRET + ) { + // Demo mode - check if user has been "connected" in this session + // For demo purposes, we'll simulate connection status + res.json({ + connected: false, // Will be set to true after demo connection + email: null, + demo: true, + }); + return; + } - // TODO: Check if user has valid Google Calendar tokens in database - // const tokens = await getGoogleTokensForUser(req.currentUser.id); - - res.json({ - connected: false, // Change to true when tokens exist and are valid - email: null // Return connected Google account email when available - }); - } catch (error) { - console.error('Error checking calendar status:', error); - res.status(500).json({ error: 'Failed to check calendar status' }); - } + // TODO: Check if user has valid Google Calendar tokens in database + // const tokens = await getGoogleTokensForUser(req.currentUser.id); + + res.json({ + connected: false, // Change to true when tokens exist and are valid + email: null, // Return connected Google account email when available + }); + } catch (error) { + console.error('Error checking calendar status:', error); + res.status(500).json({ error: 'Failed to check calendar status' }); + } }); // GET /api/calendar/events - Get events from Google Calendar router.get('/events', requireAuth, async (req, res) => { - try { - const { start, end } = req.query; - - // TODO: Get tokens from database - // const tokens = await getGoogleTokensForUser(req.currentUser.id); - // if (!tokens) { - // return res.status(401).json({ error: 'Google Calendar not connected' }); - // } + try { + const { start, end } = req.query; - // For now, return sample data - const sampleEvents = [ - { - id: 'google-1', - title: 'Google Calendar Event', - start: new Date().toISOString(), - end: new Date(Date.now() + 60 * 60 * 1000).toISOString(), - type: 'google', - color: '#ea4335' - } - ]; + // TODO: Get tokens from database + // const tokens = await getGoogleTokensForUser(req.currentUser.id); + // if (!tokens) { + // return res.status(401).json({ error: 'Google Calendar not connected' }); + // } - res.json({ events: sampleEvents }); + // For now, return sample data + const sampleEvents = [ + { + id: 'google-1', + title: 'Google Calendar Event', + start: new Date().toISOString(), + end: new Date(Date.now() + 60 * 60 * 1000).toISOString(), + type: 'google', + color: '#ea4335', + }, + ]; - // TODO: Implement actual Google Calendar API call - /* + res.json({ events: sampleEvents }); + + // TODO: Implement actual Google Calendar API call + /* const oauth2Client = getOAuth2Client(); oauth2Client.setCredentials(tokens); @@ -161,23 +178,23 @@ router.get('/events', requireAuth, async (req, res) => { res.json({ events }); */ - } catch (error) { - console.error('Error fetching calendar events:', error); - res.status(500).json({ error: 'Failed to fetch calendar events' }); - } + } catch (error) { + console.error('Error fetching calendar events:', error); + res.status(500).json({ error: 'Failed to fetch calendar events' }); + } }); // POST /api/calendar/disconnect - Disconnect Google Calendar router.post('/disconnect', requireAuth, async (req, res) => { - try { - // TODO: Remove tokens from database - // await removeGoogleTokensForUser(req.currentUser.id); - - res.json({ success: true, message: 'Google Calendar disconnected' }); - } catch (error) { - console.error('Error disconnecting calendar:', error); - res.status(500).json({ error: 'Failed to disconnect calendar' }); - } + try { + // TODO: Remove tokens from database + // await removeGoogleTokensForUser(req.currentUser.id); + + res.json({ success: true, message: 'Google Calendar disconnected' }); + } catch (error) { + console.error('Error disconnecting calendar:', error); + res.status(500).json({ error: 'Failed to disconnect calendar' }); + } }); -module.exports = router; \ No newline at end of file +module.exports = router; diff --git a/backend/routes/inbox.js b/backend/routes/inbox.js index a7bde05..a64d136 100644 --- a/backend/routes/inbox.js +++ b/backend/routes/inbox.js @@ -4,154 +4,162 @@ const router = express.Router(); // GET /api/inbox router.get('/inbox', async (req, res) => { - try { - if (!req.session || !req.session.userId) { - return res.status(401).json({ error: 'Authentication required' }); + try { + if (!req.session || !req.session.userId) { + return res.status(401).json({ error: 'Authentication required' }); + } + + const items = await InboxItem.findAll({ + where: { + user_id: req.session.userId, + status: 'added', + }, + order: [['created_at', 'DESC']], + }); + + res.json(items); + } catch (error) { + console.error('Error fetching inbox items:', error); + res.status(500).json({ error: 'Internal server error' }); } - - const items = await InboxItem.findAll({ - where: { - user_id: req.session.userId, - status: 'added' - }, - order: [['created_at', 'DESC']] - }); - - res.json(items); - } catch (error) { - console.error('Error fetching inbox items:', error); - res.status(500).json({ error: 'Internal server error' }); - } }); // POST /api/inbox router.post('/inbox', async (req, res) => { - try { - if (!req.session || !req.session.userId) { - return res.status(401).json({ error: 'Authentication required' }); + try { + if (!req.session || !req.session.userId) { + return res.status(401).json({ error: 'Authentication required' }); + } + + const { content, source } = req.body; + + if (!content || !content.trim()) { + return res.status(400).json({ error: 'Content is required' }); + } + + const item = await InboxItem.create({ + content: content.trim(), + source: source || 'tududi', + user_id: req.session.userId, + }); + + res.status(201).json(item); + } catch (error) { + console.error('Error creating inbox item:', error); + res.status(400).json({ + error: 'There was a problem creating the inbox item.', + details: error.errors + ? error.errors.map((e) => e.message) + : [error.message], + }); } - - const { content, source } = req.body; - - if (!content || !content.trim()) { - return res.status(400).json({ error: 'Content is required' }); - } - - const item = await InboxItem.create({ - content: content.trim(), - source: source || 'tududi', - user_id: req.session.userId - }); - - res.status(201).json(item); - } catch (error) { - console.error('Error creating inbox item:', error); - res.status(400).json({ - error: 'There was a problem creating the inbox item.', - details: error.errors ? error.errors.map(e => e.message) : [error.message] - }); - } }); // GET /api/inbox/:id router.get('/inbox/:id', async (req, res) => { - try { - if (!req.session || !req.session.userId) { - return res.status(401).json({ error: 'Authentication required' }); + try { + if (!req.session || !req.session.userId) { + return res.status(401).json({ error: 'Authentication required' }); + } + + const item = await InboxItem.findOne({ + where: { id: req.params.id, user_id: req.session.userId }, + }); + + if (!item) { + return res.status(404).json({ error: 'Inbox item not found.' }); + } + + res.json(item); + } catch (error) { + console.error('Error fetching inbox item:', error); + res.status(500).json({ error: 'Internal server error' }); } - - const item = await InboxItem.findOne({ - where: { id: req.params.id, user_id: req.session.userId } - }); - - if (!item) { - return res.status(404).json({ error: 'Inbox item not found.' }); - } - - res.json(item); - } catch (error) { - console.error('Error fetching inbox item:', error); - res.status(500).json({ error: 'Internal server error' }); - } }); // PATCH /api/inbox/:id router.patch('/inbox/:id', async (req, res) => { - try { - if (!req.session || !req.session.userId) { - return res.status(401).json({ error: 'Authentication required' }); + try { + if (!req.session || !req.session.userId) { + return res.status(401).json({ error: 'Authentication required' }); + } + + const item = await InboxItem.findOne({ + where: { id: req.params.id, user_id: req.session.userId }, + }); + + if (!item) { + return res.status(404).json({ error: 'Inbox item not found.' }); + } + + const { content, status } = req.body; + const updateData = {}; + + if (content !== undefined) updateData.content = content; + if (status !== undefined) updateData.status = status; + + await item.update(updateData); + res.json(item); + } catch (error) { + console.error('Error updating inbox item:', error); + res.status(400).json({ + error: 'There was a problem updating the inbox item.', + details: error.errors + ? error.errors.map((e) => e.message) + : [error.message], + }); } - - const item = await InboxItem.findOne({ - where: { id: req.params.id, user_id: req.session.userId } - }); - - if (!item) { - return res.status(404).json({ error: 'Inbox item not found.' }); - } - - const { content, status } = req.body; - const updateData = {}; - - if (content !== undefined) updateData.content = content; - if (status !== undefined) updateData.status = status; - - await item.update(updateData); - res.json(item); - } catch (error) { - console.error('Error updating inbox item:', error); - res.status(400).json({ - error: 'There was a problem updating the inbox item.', - details: error.errors ? error.errors.map(e => e.message) : [error.message] - }); - } }); // DELETE /api/inbox/:id router.delete('/inbox/:id', async (req, res) => { - try { - if (!req.session || !req.session.userId) { - return res.status(401).json({ error: 'Authentication required' }); + try { + if (!req.session || !req.session.userId) { + return res.status(401).json({ error: 'Authentication required' }); + } + + const item = await InboxItem.findOne({ + where: { id: req.params.id, user_id: req.session.userId }, + }); + + if (!item) { + return res.status(404).json({ error: 'Inbox item not found.' }); + } + + // Mark as deleted instead of actual deletion + await item.update({ status: 'deleted' }); + res.json({ message: 'Inbox item successfully deleted' }); + } catch (error) { + console.error('Error deleting inbox item:', error); + res.status(400).json({ + error: 'There was a problem deleting the inbox item.', + }); } - - const item = await InboxItem.findOne({ - where: { id: req.params.id, user_id: req.session.userId } - }); - - if (!item) { - return res.status(404).json({ error: 'Inbox item not found.' }); - } - - // Mark as deleted instead of actual deletion - await item.update({ status: 'deleted' }); - res.json({ message: 'Inbox item successfully deleted' }); - } catch (error) { - console.error('Error deleting inbox item:', error); - res.status(400).json({ error: 'There was a problem deleting the inbox item.' }); - } }); // PATCH /api/inbox/:id/process router.patch('/inbox/:id/process', async (req, res) => { - try { - if (!req.session || !req.session.userId) { - return res.status(401).json({ error: 'Authentication required' }); + try { + if (!req.session || !req.session.userId) { + return res.status(401).json({ error: 'Authentication required' }); + } + + const item = await InboxItem.findOne({ + where: { id: req.params.id, user_id: req.session.userId }, + }); + + if (!item) { + return res.status(404).json({ error: 'Inbox item not found.' }); + } + + await item.update({ status: 'processed' }); + res.json(item); + } catch (error) { + console.error('Error processing inbox item:', error); + res.status(400).json({ + error: 'There was a problem processing the inbox item.', + }); } - - const item = await InboxItem.findOne({ - where: { id: req.params.id, user_id: req.session.userId } - }); - - if (!item) { - return res.status(404).json({ error: 'Inbox item not found.' }); - } - - await item.update({ status: 'processed' }); - res.json(item); - } catch (error) { - console.error('Error processing inbox item:', error); - res.status(400).json({ error: 'There was a problem processing the inbox item.' }); - } }); -module.exports = router; \ No newline at end of file +module.exports = router; diff --git a/backend/routes/notes.js b/backend/routes/notes.js index ea080d6..add2b73 100644 --- a/backend/routes/notes.js +++ b/backend/routes/notes.js @@ -5,238 +5,246 @@ const router = express.Router(); // Helper function to update note tags async function updateNoteTags(note, tagsArray, userId) { - if (!tagsArray || tagsArray.length === 0) { - await note.setTags([]); - return; - } + if (!tagsArray || tagsArray.length === 0) { + await note.setTags([]); + return; + } - try { - const tagNames = tagsArray.filter((name, index, arr) => arr.indexOf(name) === index); // unique - const tags = await Promise.all( - tagNames.map(async (name) => { - const [tag] = await Tag.findOrCreate({ - where: { name, user_id: userId }, - defaults: { name, user_id: userId } - }); - return tag; - }) - ); - await note.setTags(tags); - } catch (error) { - console.error('Failed to update tags:', error.message); - } + try { + const tagNames = tagsArray.filter( + (name, index, arr) => arr.indexOf(name) === index + ); // unique + const tags = await Promise.all( + tagNames.map(async (name) => { + const [tag] = await Tag.findOrCreate({ + where: { name, user_id: userId }, + defaults: { name, user_id: userId }, + }); + return tag; + }) + ); + await note.setTags(tags); + } catch (error) { + console.error('Failed to update tags:', error.message); + } } // GET /api/notes router.get('/notes', async (req, res) => { - try { - if (!req.session || !req.session.userId) { - return res.status(401).json({ error: 'Authentication required' }); + try { + if (!req.session || !req.session.userId) { + return res.status(401).json({ error: 'Authentication required' }); + } + + const orderBy = req.query.order_by || 'title:asc'; + const [orderColumn, orderDirection] = orderBy.split(':'); + + let whereClause = { user_id: req.session.userId }; + let includeClause = [ + { model: Tag, through: { attributes: [] } }, + { model: Project, required: false, attributes: ['id', 'name'] }, + ]; + + // Filter by tag + if (req.query.tag) { + includeClause[0].where = { name: req.query.tag }; + includeClause[0].required = true; + } + + const notes = await Note.findAll({ + where: whereClause, + include: includeClause, + order: [[orderColumn, orderDirection.toUpperCase()]], + distinct: true, + }); + + res.json(notes); + } catch (error) { + console.error('Error fetching notes:', error); + res.status(500).json({ error: 'Internal server error' }); } - - const orderBy = req.query.order_by || 'title:asc'; - const [orderColumn, orderDirection] = orderBy.split(':'); - - let whereClause = { user_id: req.session.userId }; - let includeClause = [ - { model: Tag, through: { attributes: [] } }, - { model: Project, required: false, attributes: ['id', 'name'] } - ]; - - // Filter by tag - if (req.query.tag) { - includeClause[0].where = { name: req.query.tag }; - includeClause[0].required = true; - } - - const notes = await Note.findAll({ - where: whereClause, - include: includeClause, - order: [[orderColumn, orderDirection.toUpperCase()]], - distinct: true - }); - - res.json(notes); - } catch (error) { - console.error('Error fetching notes:', error); - res.status(500).json({ error: 'Internal server error' }); - } }); // GET /api/note/:id router.get('/note/:id', async (req, res) => { - try { - if (!req.session || !req.session.userId) { - return res.status(401).json({ error: 'Authentication required' }); + try { + if (!req.session || !req.session.userId) { + return res.status(401).json({ error: 'Authentication required' }); + } + + const note = await Note.findOne({ + where: { id: req.params.id, user_id: req.session.userId }, + include: [ + { model: Tag, through: { attributes: [] } }, + { model: Project, required: false, attributes: ['id', 'name'] }, + ], + }); + + if (!note) { + return res.status(404).json({ error: 'Note not found.' }); + } + + res.json(note); + } catch (error) { + console.error('Error fetching note:', error); + res.status(500).json({ error: 'Internal server error' }); } - - const note = await Note.findOne({ - where: { id: req.params.id, user_id: req.session.userId }, - include: [ - { model: Tag, through: { attributes: [] } }, - { model: Project, required: false, attributes: ['id', 'name'] } - ] - }); - - if (!note) { - return res.status(404).json({ error: 'Note not found.' }); - } - - res.json(note); - } catch (error) { - console.error('Error fetching note:', error); - res.status(500).json({ error: 'Internal server error' }); - } }); // POST /api/note router.post('/note', async (req, res) => { - try { - if (!req.session || !req.session.userId) { - return res.status(401).json({ error: 'Authentication required' }); + try { + if (!req.session || !req.session.userId) { + return res.status(401).json({ error: 'Authentication required' }); + } + + const { title, content, project_id, tags } = req.body; + + const noteAttributes = { + title, + content, + user_id: req.session.userId, + }; + + // Handle project assignment + if (project_id && project_id.toString().trim()) { + const project = await Project.findOne({ + where: { id: project_id, user_id: req.session.userId }, + }); + if (!project) { + return res.status(400).json({ error: 'Invalid project.' }); + } + noteAttributes.project_id = project_id; + } + + const note = await Note.create(noteAttributes); + + // Handle tags - can be array of strings or array of objects with name property + let tagNames = []; + if (Array.isArray(tags)) { + if (tags.every((t) => typeof t === 'string')) { + tagNames = tags; + } else if (tags.every((t) => typeof t === 'object' && t.name)) { + tagNames = tags.map((t) => t.name); + } + } + + await updateNoteTags(note, tagNames, req.session.userId); + + // Reload note with associations + const noteWithAssociations = await Note.findByPk(note.id, { + include: [ + { model: Tag, through: { attributes: [] } }, + { model: Project, required: false, attributes: ['id', 'name'] }, + ], + }); + + res.status(201).json(noteWithAssociations); + } catch (error) { + console.error('Error creating note:', error); + res.status(400).json({ + error: 'There was a problem creating the note.', + details: error.errors + ? error.errors.map((e) => e.message) + : [error.message], + }); } - - const { title, content, project_id, tags } = req.body; - - const noteAttributes = { - title, - content, - user_id: req.session.userId - }; - - // Handle project assignment - if (project_id && project_id.toString().trim()) { - const project = await Project.findOne({ - where: { id: project_id, user_id: req.session.userId } - }); - if (!project) { - return res.status(400).json({ error: 'Invalid project.' }); - } - noteAttributes.project_id = project_id; - } - - const note = await Note.create(noteAttributes); - - // Handle tags - can be array of strings or array of objects with name property - let tagNames = []; - if (Array.isArray(tags)) { - if (tags.every(t => typeof t === 'string')) { - tagNames = tags; - } else if (tags.every(t => typeof t === 'object' && t.name)) { - tagNames = tags.map(t => t.name); - } - } - - await updateNoteTags(note, tagNames, req.session.userId); - - // Reload note with associations - const noteWithAssociations = await Note.findByPk(note.id, { - include: [ - { model: Tag, through: { attributes: [] } }, - { model: Project, required: false, attributes: ['id', 'name'] } - ] - }); - - res.status(201).json(noteWithAssociations); - } catch (error) { - console.error('Error creating note:', error); - res.status(400).json({ - error: 'There was a problem creating the note.', - details: error.errors ? error.errors.map(e => e.message) : [error.message] - }); - } }); // PATCH /api/note/:id router.patch('/note/:id', async (req, res) => { - try { - if (!req.session || !req.session.userId) { - return res.status(401).json({ error: 'Authentication required' }); - } + try { + if (!req.session || !req.session.userId) { + return res.status(401).json({ error: 'Authentication required' }); + } - const note = await Note.findOne({ - where: { id: req.params.id, user_id: req.session.userId } - }); - - if (!note) { - return res.status(404).json({ error: 'Note not found.' }); - } - - const { title, content, project_id, tags } = req.body; - - const updateData = {}; - if (title !== undefined) updateData.title = title; - if (content !== undefined) updateData.content = content; - - // Handle project assignment - if (project_id !== undefined) { - if (project_id && project_id.toString().trim()) { - const project = await Project.findOne({ - where: { id: project_id, user_id: req.session.userId } + const note = await Note.findOne({ + where: { id: req.params.id, user_id: req.session.userId }, }); - if (!project) { - return res.status(400).json({ error: 'Invalid project.' }); + + if (!note) { + return res.status(404).json({ error: 'Note not found.' }); } - updateData.project_id = project_id; - } else { - updateData.project_id = null; - } - } - await note.update(updateData); + const { title, content, project_id, tags } = req.body; - // Handle tags if provided - if (tags !== undefined) { - let tagNames = []; - if (Array.isArray(tags)) { - if (tags.every(t => typeof t === 'string')) { - tagNames = tags; - } else if (tags.every(t => typeof t === 'object' && t.name)) { - tagNames = tags.map(t => t.name); + const updateData = {}; + if (title !== undefined) updateData.title = title; + if (content !== undefined) updateData.content = content; + + // Handle project assignment + if (project_id !== undefined) { + if (project_id && project_id.toString().trim()) { + const project = await Project.findOne({ + where: { id: project_id, user_id: req.session.userId }, + }); + if (!project) { + return res.status(400).json({ error: 'Invalid project.' }); + } + updateData.project_id = project_id; + } else { + updateData.project_id = null; + } } - } - await updateNoteTags(note, tagNames, req.session.userId); + + await note.update(updateData); + + // Handle tags if provided + if (tags !== undefined) { + let tagNames = []; + if (Array.isArray(tags)) { + if (tags.every((t) => typeof t === 'string')) { + tagNames = tags; + } else if (tags.every((t) => typeof t === 'object' && t.name)) { + tagNames = tags.map((t) => t.name); + } + } + await updateNoteTags(note, tagNames, req.session.userId); + } + + // Reload note with associations + const noteWithAssociations = await Note.findByPk(note.id, { + include: [ + { model: Tag, through: { attributes: [] } }, + { model: Project, required: false, attributes: ['id', 'name'] }, + ], + }); + + res.json(noteWithAssociations); + } catch (error) { + console.error('Error updating note:', error); + res.status(400).json({ + error: 'There was a problem updating the note.', + details: error.errors + ? error.errors.map((e) => e.message) + : [error.message], + }); } - - // Reload note with associations - const noteWithAssociations = await Note.findByPk(note.id, { - include: [ - { model: Tag, through: { attributes: [] } }, - { model: Project, required: false, attributes: ['id', 'name'] } - ] - }); - - res.json(noteWithAssociations); - } catch (error) { - console.error('Error updating note:', error); - res.status(400).json({ - error: 'There was a problem updating the note.', - details: error.errors ? error.errors.map(e => e.message) : [error.message] - }); - } }); // DELETE /api/note/:id router.delete('/note/:id', async (req, res) => { - try { - if (!req.session || !req.session.userId) { - return res.status(401).json({ error: 'Authentication required' }); + try { + if (!req.session || !req.session.userId) { + return res.status(401).json({ error: 'Authentication required' }); + } + + const note = await Note.findOne({ + where: { id: req.params.id, user_id: req.session.userId }, + }); + + if (!note) { + return res.status(404).json({ error: 'Note not found.' }); + } + + await note.destroy(); + res.json({ message: 'Note deleted successfully.' }); + } catch (error) { + console.error('Error deleting note:', error); + res.status(400).json({ + error: 'There was a problem deleting the note.', + }); } - - const note = await Note.findOne({ - where: { id: req.params.id, user_id: req.session.userId } - }); - - if (!note) { - return res.status(404).json({ error: 'Note not found.' }); - } - - await note.destroy(); - res.json({ message: 'Note deleted successfully.' }); - } catch (error) { - console.error('Error deleting note:', error); - res.status(400).json({ error: 'There was a problem deleting the note.' }); - } }); -module.exports = router; \ No newline at end of file +module.exports = router; diff --git a/backend/routes/projects.js b/backend/routes/projects.js index dacbca6..468e406 100644 --- a/backend/routes/projects.js +++ b/backend/routes/projects.js @@ -8,376 +8,422 @@ const router = express.Router(); // Helper function to safely format dates const formatDate = (date) => { - if (!date) return null; - try { - const dateObj = new Date(date); - if (isNaN(dateObj.getTime())) return null; - return dateObj.toISOString(); - } catch (error) { - return null; - } + if (!date) return null; + try { + const dateObj = new Date(date); + if (isNaN(dateObj.getTime())) return null; + return dateObj.toISOString(); + } catch (error) { + return null; + } }; // Configure multer for file uploads const storage = multer.diskStorage({ - destination: function (req, file, cb) { - const uploadDir = path.join(__dirname, '../uploads/projects'); - if (!fs.existsSync(uploadDir)) { - fs.mkdirSync(uploadDir, { recursive: true }); - } - cb(null, uploadDir); - }, - filename: function (req, file, cb) { - const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1E9); - cb(null, 'project-' + uniqueSuffix + path.extname(file.originalname)); - } + destination: function (req, file, cb) { + const uploadDir = path.join(__dirname, '../uploads/projects'); + if (!fs.existsSync(uploadDir)) { + fs.mkdirSync(uploadDir, { recursive: true }); + } + cb(null, uploadDir); + }, + filename: function (req, file, cb) { + const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1e9); + cb(null, 'project-' + uniqueSuffix + path.extname(file.originalname)); + }, }); const upload = multer({ - storage: storage, - limits: { - fileSize: 5 * 1024 * 1024, // 5MB limit - }, - fileFilter: function (req, file, cb) { - const allowedTypes = /jpeg|jpg|png|gif|webp/; - const extname = allowedTypes.test(path.extname(file.originalname).toLowerCase()); - const mimetype = allowedTypes.test(file.mimetype); - - if (mimetype && extname) { - return cb(null, true); - } else { - cb(new Error('Only image files are allowed!')); - } - } + storage: storage, + limits: { + fileSize: 5 * 1024 * 1024, // 5MB limit + }, + fileFilter: function (req, file, cb) { + const allowedTypes = /jpeg|jpg|png|gif|webp/; + const extname = allowedTypes.test( + path.extname(file.originalname).toLowerCase() + ); + const mimetype = allowedTypes.test(file.mimetype); + + if (mimetype && extname) { + return cb(null, true); + } else { + cb(new Error('Only image files are allowed!')); + } + }, }); // Helper function to update project tags async function updateProjectTags(project, tagsData, userId) { - if (!tagsData) return; + if (!tagsData) return; - const tagNames = tagsData - .map(tag => tag.name) - .filter(name => name && name.trim()) - .filter((name, index, arr) => arr.indexOf(name) === index); // unique + const tagNames = tagsData + .map((tag) => tag.name) + .filter((name) => name && name.trim()) + .filter((name, index, arr) => arr.indexOf(name) === index); // unique - if (tagNames.length === 0) { - await project.setTags([]); - return; - } + if (tagNames.length === 0) { + await project.setTags([]); + return; + } - // Find existing tags - const existingTags = await Tag.findAll({ - where: { user_id: userId, name: tagNames } - }); + // Find existing tags + const existingTags = await Tag.findAll({ + where: { user_id: userId, name: tagNames }, + }); - // Create new tags - const existingTagNames = existingTags.map(tag => tag.name); - const newTagNames = tagNames.filter(name => !existingTagNames.includes(name)); - - const createdTags = await Promise.all( - newTagNames.map(name => Tag.create({ name, user_id: userId })) - ); + // Create new tags + const existingTagNames = existingTags.map((tag) => tag.name); + const newTagNames = tagNames.filter( + (name) => !existingTagNames.includes(name) + ); - // Set all tags to project - const allTags = [...existingTags, ...createdTags]; - await project.setTags(allTags); + const createdTags = await Promise.all( + newTagNames.map((name) => Tag.create({ name, user_id: userId })) + ); + + // Set all tags to project + const allTags = [...existingTags, ...createdTags]; + await project.setTags(allTags); } // POST /api/upload/project-image router.post('/upload/project-image', upload.single('image'), (req, res) => { - try { - if (!req.session || !req.session.userId) { - return res.status(401).json({ error: 'Authentication required' }); - } + try { + if (!req.session || !req.session.userId) { + return res.status(401).json({ error: 'Authentication required' }); + } - if (!req.file) { - return res.status(400).json({ error: 'No image file provided' }); - } + if (!req.file) { + return res.status(400).json({ error: 'No image file provided' }); + } - // Return the relative URL that can be accessed from the frontend - const imageUrl = `/api/uploads/projects/${req.file.filename}`; - res.json({ imageUrl }); - } catch (error) { - console.error('Error uploading image:', error); - res.status(500).json({ error: 'Failed to upload image' }); - } + // Return the relative URL that can be accessed from the frontend + const imageUrl = `/api/uploads/projects/${req.file.filename}`; + res.json({ imageUrl }); + } catch (error) { + console.error('Error uploading image:', error); + res.status(500).json({ error: 'Failed to upload image' }); + } }); // GET /api/projects router.get('/projects', async (req, res) => { - try { - if (!req.session || !req.session.userId) { - return res.status(401).json({ error: 'Authentication required' }); - } - - const { active, pin_to_sidebar, area_id } = req.query; - - let whereClause = { user_id: req.session.userId }; - - // Filter by active status - if (active === 'true') { - whereClause.active = true; - } else if (active === 'false') { - whereClause.active = false; - } - - // Filter by pinned status - if (pin_to_sidebar === 'true') { - whereClause.pin_to_sidebar = true; - } else if (pin_to_sidebar === 'false') { - whereClause.pin_to_sidebar = false; - } - - // Filter by area - if (area_id && area_id !== '') { - whereClause.area_id = area_id; - } - - const projects = await Project.findAll({ - where: whereClause, - include: [ - { - model: Task, - required: false, - attributes: ['id', 'status'] - }, - { - model: Area, - required: false, - attributes: ['name'] - }, - { - model: Tag, - attributes: ['id', 'name'], - through: { attributes: [] } + try { + if (!req.session || !req.session.userId) { + return res.status(401).json({ error: 'Authentication required' }); } - ], - order: [['name', 'ASC']] - }); - const { grouped } = req.query; - - // Calculate task status counts for each project - const taskStatusCounts = {}; - const enhancedProjects = projects.map(project => { - const tasks = project.Tasks || []; - const taskStatus = { - total: tasks.length, - done: tasks.filter(t => t.status === 2).length, - in_progress: tasks.filter(t => t.status === 1).length, - not_started: tasks.filter(t => t.status === 0).length - }; - - taskStatusCounts[project.id] = taskStatus; - - const projectJson = project.toJSON(); - return { - ...projectJson, - tags: projectJson.Tags || [], // Normalize Tags to tags - due_date_at: formatDate(project.due_date_at), - task_status: taskStatus, - completion_percentage: taskStatus.total > 0 ? Math.round((taskStatus.done / taskStatus.total) * 100) : 0 - }; - }); + const { active, pin_to_sidebar, area_id } = req.query; - // If grouped=true, return grouped format - if (grouped === 'true') { - const groupedProjects = {}; - enhancedProjects.forEach(project => { - const areaName = project.Area ? project.Area.name : 'No Area'; - if (!groupedProjects[areaName]) { - groupedProjects[areaName] = []; + let whereClause = { user_id: req.session.userId }; + + // Filter by active status + if (active === 'true') { + whereClause.active = true; + } else if (active === 'false') { + whereClause.active = false; } - groupedProjects[areaName].push(project); - }); - res.json(groupedProjects); - } else { - res.json({ - projects: enhancedProjects - }); + + // Filter by pinned status + if (pin_to_sidebar === 'true') { + whereClause.pin_to_sidebar = true; + } else if (pin_to_sidebar === 'false') { + whereClause.pin_to_sidebar = false; + } + + // Filter by area + if (area_id && area_id !== '') { + whereClause.area_id = area_id; + } + + const projects = await Project.findAll({ + where: whereClause, + include: [ + { + model: Task, + required: false, + attributes: ['id', 'status'], + }, + { + model: Area, + required: false, + attributes: ['name'], + }, + { + model: Tag, + attributes: ['id', 'name'], + through: { attributes: [] }, + }, + ], + order: [['name', 'ASC']], + }); + + const { grouped } = req.query; + + // Calculate task status counts for each project + const taskStatusCounts = {}; + const enhancedProjects = projects.map((project) => { + const tasks = project.Tasks || []; + const taskStatus = { + total: tasks.length, + done: tasks.filter((t) => t.status === 2).length, + in_progress: tasks.filter((t) => t.status === 1).length, + not_started: tasks.filter((t) => t.status === 0).length, + }; + + taskStatusCounts[project.id] = taskStatus; + + const projectJson = project.toJSON(); + return { + ...projectJson, + tags: projectJson.Tags || [], // Normalize Tags to tags + due_date_at: formatDate(project.due_date_at), + task_status: taskStatus, + completion_percentage: + taskStatus.total > 0 + ? Math.round((taskStatus.done / taskStatus.total) * 100) + : 0, + }; + }); + + // If grouped=true, return grouped format + if (grouped === 'true') { + const groupedProjects = {}; + enhancedProjects.forEach((project) => { + const areaName = project.Area ? project.Area.name : 'No Area'; + if (!groupedProjects[areaName]) { + groupedProjects[areaName] = []; + } + groupedProjects[areaName].push(project); + }); + res.json(groupedProjects); + } else { + res.json({ + projects: enhancedProjects, + }); + } + } catch (error) { + console.error('Error fetching projects:', error); + res.status(500).json({ error: 'Internal server error' }); } - } catch (error) { - console.error('Error fetching projects:', error); - res.status(500).json({ error: 'Internal server error' }); - } }); // GET /api/project/:id router.get('/project/:id', async (req, res) => { - try { - if (!req.session || !req.session.userId) { - return res.status(401).json({ error: 'Authentication required' }); + try { + if (!req.session || !req.session.userId) { + return res.status(401).json({ error: 'Authentication required' }); + } + + const project = await Project.findOne({ + where: { id: req.params.id, user_id: req.session.userId }, + include: [ + { + model: Task, + required: false, + include: [ + { + model: Tag, + attributes: ['id', 'name'], + through: { attributes: [] }, + required: false, + }, + ], + }, + { model: Area, required: false, attributes: ['id', 'name'] }, + { + model: Tag, + attributes: ['id', 'name'], + through: { attributes: [] }, + }, + ], + }); + + if (!project) { + return res.status(404).json({ error: 'Project not found' }); + } + + const projectJson = project.toJSON(); + const result = { + ...projectJson, + tags: projectJson.Tags || [], // Normalize Tags to tags + due_date_at: formatDate(project.due_date_at), + }; + + res.json(result); + } catch (error) { + console.error('Error fetching project:', error); + res.status(500).json({ error: 'Internal server error' }); } - - const project = await Project.findOne({ - where: { id: req.params.id, user_id: req.session.userId }, - include: [ - { - model: Task, - required: false, - include: [ - { - model: Tag, - attributes: ['id', 'name'], - through: { attributes: [] }, - required: false - } - ] - }, - { model: Area, required: false, attributes: ['id', 'name'] }, - { model: Tag, attributes: ['id', 'name'], through: { attributes: [] } } - ] - }); - - if (!project) { - return res.status(404).json({ error: 'Project not found' }); - } - - const projectJson = project.toJSON(); - const result = { - ...projectJson, - tags: projectJson.Tags || [], // Normalize Tags to tags - due_date_at: formatDate(project.due_date_at) - }; - - res.json(result); - } catch (error) { - console.error('Error fetching project:', error); - res.status(500).json({ error: 'Internal server error' }); - } }); // POST /api/project router.post('/project', async (req, res) => { - try { - if (!req.session || !req.session.userId) { - return res.status(401).json({ error: 'Authentication required' }); + try { + if (!req.session || !req.session.userId) { + return res.status(401).json({ error: 'Authentication required' }); + } + + const { + name, + description, + area_id, + priority, + due_date_at, + image_url, + tags, + Tags, + } = req.body; + + // Handle both tags and Tags (Sequelize association format) + const tagsData = tags || Tags; + + if (!name || !name.trim()) { + return res.status(400).json({ error: 'Project name is required' }); + } + + const projectData = { + name: name.trim(), + description: description || '', + area_id: area_id || null, + active: true, + pin_to_sidebar: false, + priority: priority || null, + due_date_at: due_date_at || null, + image_url: image_url || null, + user_id: req.session.userId, + }; + + const project = await Project.create(projectData); + await updateProjectTags(project, tagsData, req.session.userId); + + // Reload project with associations + const projectWithAssociations = await Project.findByPk(project.id, { + include: [ + { + model: Tag, + attributes: ['id', 'name'], + through: { attributes: [] }, + }, + ], + }); + + const projectJson = projectWithAssociations.toJSON(); + + res.status(201).json({ + ...projectJson, + tags: projectJson.Tags || [], // Normalize Tags to tags + due_date_at: formatDate(projectWithAssociations.due_date_at), + }); + } catch (error) { + console.error('Error creating project:', error); + res.status(400).json({ + error: 'There was a problem creating the project.', + details: error.errors + ? error.errors.map((e) => e.message) + : [error.message], + }); } - - const { name, description, area_id, priority, due_date_at, image_url, tags, Tags } = req.body; - - // Handle both tags and Tags (Sequelize association format) - const tagsData = tags || Tags; - - if (!name || !name.trim()) { - return res.status(400).json({ error: 'Project name is required' }); - } - - const projectData = { - name: name.trim(), - description: description || '', - area_id: area_id || null, - active: true, - pin_to_sidebar: false, - priority: priority || null, - due_date_at: due_date_at || null, - image_url: image_url || null, - user_id: req.session.userId - }; - - const project = await Project.create(projectData); - await updateProjectTags(project, tagsData, req.session.userId); - - // Reload project with associations - const projectWithAssociations = await Project.findByPk(project.id, { - include: [ - { model: Tag, attributes: ['id', 'name'], through: { attributes: [] } } - ] - }); - - const projectJson = projectWithAssociations.toJSON(); - - res.status(201).json({ - ...projectJson, - tags: projectJson.Tags || [], // Normalize Tags to tags - due_date_at: formatDate(projectWithAssociations.due_date_at) - }); - } catch (error) { - console.error('Error creating project:', error); - res.status(400).json({ - error: 'There was a problem creating the project.', - details: error.errors ? error.errors.map(e => e.message) : [error.message] - }); - } }); // PATCH /api/project/:id router.patch('/project/:id', async (req, res) => { - try { - if (!req.session || !req.session.userId) { - return res.status(401).json({ error: 'Authentication required' }); + try { + if (!req.session || !req.session.userId) { + return res.status(401).json({ error: 'Authentication required' }); + } + + const project = await Project.findOne({ + where: { id: req.params.id, user_id: req.session.userId }, + }); + + if (!project) { + return res.status(404).json({ error: 'Project not found.' }); + } + + const { + name, + description, + area_id, + active, + pin_to_sidebar, + priority, + due_date_at, + image_url, + tags, + Tags, + } = req.body; + + // Handle both tags and Tags (Sequelize association format) + const tagsData = tags || Tags; + + const updateData = {}; + if (name !== undefined) updateData.name = name; + if (description !== undefined) updateData.description = description; + if (area_id !== undefined) updateData.area_id = area_id; + if (active !== undefined) updateData.active = active; + if (pin_to_sidebar !== undefined) + updateData.pin_to_sidebar = pin_to_sidebar; + if (priority !== undefined) updateData.priority = priority; + if (due_date_at !== undefined) updateData.due_date_at = due_date_at; + if (image_url !== undefined) updateData.image_url = image_url; + + await project.update(updateData); + await updateProjectTags(project, tagsData, req.session.userId); + + // Reload project with associations + const projectWithAssociations = await Project.findByPk(project.id, { + include: [ + { + model: Tag, + attributes: ['id', 'name'], + through: { attributes: [] }, + }, + ], + }); + + const projectJson = projectWithAssociations.toJSON(); + + res.json({ + ...projectJson, + tags: projectJson.Tags || [], // Normalize Tags to tags + due_date_at: formatDate(projectWithAssociations.due_date_at), + }); + } catch (error) { + console.error('Error updating project:', error); + res.status(400).json({ + error: 'There was a problem updating the project.', + details: error.errors + ? error.errors.map((e) => e.message) + : [error.message], + }); } - - const project = await Project.findOne({ - where: { id: req.params.id, user_id: req.session.userId } - }); - - if (!project) { - return res.status(404).json({ error: 'Project not found.' }); - } - - const { name, description, area_id, active, pin_to_sidebar, priority, due_date_at, image_url, tags, Tags } = req.body; - - // Handle both tags and Tags (Sequelize association format) - const tagsData = tags || Tags; - - const updateData = {}; - if (name !== undefined) updateData.name = name; - if (description !== undefined) updateData.description = description; - if (area_id !== undefined) updateData.area_id = area_id; - if (active !== undefined) updateData.active = active; - if (pin_to_sidebar !== undefined) updateData.pin_to_sidebar = pin_to_sidebar; - if (priority !== undefined) updateData.priority = priority; - if (due_date_at !== undefined) updateData.due_date_at = due_date_at; - if (image_url !== undefined) updateData.image_url = image_url; - - await project.update(updateData); - await updateProjectTags(project, tagsData, req.session.userId); - - // Reload project with associations - const projectWithAssociations = await Project.findByPk(project.id, { - include: [ - { model: Tag, attributes: ['id', 'name'], through: { attributes: [] } } - ] - }); - - const projectJson = projectWithAssociations.toJSON(); - - res.json({ - ...projectJson, - tags: projectJson.Tags || [], // Normalize Tags to tags - due_date_at: formatDate(projectWithAssociations.due_date_at) - }); - } catch (error) { - console.error('Error updating project:', error); - res.status(400).json({ - error: 'There was a problem updating the project.', - details: error.errors ? error.errors.map(e => e.message) : [error.message] - }); - } }); // DELETE /api/project/:id router.delete('/project/:id', async (req, res) => { - try { - if (!req.session || !req.session.userId) { - return res.status(401).json({ error: 'Authentication required' }); + try { + if (!req.session || !req.session.userId) { + return res.status(401).json({ error: 'Authentication required' }); + } + + const project = await Project.findOne({ + where: { id: req.params.id, user_id: req.session.userId }, + }); + + if (!project) { + return res.status(404).json({ error: 'Project not found.' }); + } + + await project.destroy(); + res.json({ message: 'Project successfully deleted' }); + } catch (error) { + console.error('Error deleting project:', error); + res.status(400).json({ + error: 'There was a problem deleting the project.', + }); } - - const project = await Project.findOne({ - where: { id: req.params.id, user_id: req.session.userId } - }); - - if (!project) { - return res.status(404).json({ error: 'Project not found.' }); - } - - await project.destroy(); - res.json({ message: 'Project successfully deleted' }); - } catch (error) { - console.error('Error deleting project:', error); - res.status(400).json({ error: 'There was a problem deleting the project.' }); - } }); -module.exports = router; \ No newline at end of file +module.exports = router; diff --git a/backend/routes/quotes.js b/backend/routes/quotes.js index f596909..e90701b 100644 --- a/backend/routes/quotes.js +++ b/backend/routes/quotes.js @@ -4,27 +4,27 @@ const quotesService = require('../services/quotesService'); // GET /api/quotes/random - Get a random quote router.get('/quotes/random', (req, res) => { - try { - const quote = quotesService.getRandomQuote(); - res.json({ quote }); - } catch (error) { - console.error('Error getting random quote:', error); - res.status(500).json({ error: 'Internal server error' }); - } + try { + const quote = quotesService.getRandomQuote(); + res.json({ quote }); + } catch (error) { + console.error('Error getting random quote:', error); + res.status(500).json({ error: 'Internal server error' }); + } }); // GET /api/quotes - Get all quotes router.get('/quotes', (req, res) => { - try { - const quotes = quotesService.getAllQuotes(); - res.json({ - quotes, - count: quotesService.getQuotesCount() - }); - } catch (error) { - console.error('Error getting quotes:', error); - res.status(500).json({ error: 'Internal server error' }); - } + try { + const quotes = quotesService.getAllQuotes(); + res.json({ + quotes, + count: quotesService.getQuotesCount(), + }); + } catch (error) { + console.error('Error getting quotes:', error); + res.status(500).json({ error: 'Internal server error' }); + } }); -module.exports = router; \ No newline at end of file +module.exports = router; diff --git a/backend/routes/tags.js b/backend/routes/tags.js index f924a61..6c1cb66 100644 --- a/backend/routes/tags.js +++ b/backend/routes/tags.js @@ -4,152 +4,161 @@ const router = express.Router(); // GET /api/tags router.get('/tags', async (req, res) => { - try { - const tags = await Tag.findAll({ - where: { user_id: req.currentUser.id }, - attributes: ['id', 'name'], - order: [['name', 'ASC']] - }); + try { + const tags = await Tag.findAll({ + where: { user_id: req.currentUser.id }, + attributes: ['id', 'name'], + order: [['name', 'ASC']], + }); - res.json(tags); - } catch (error) { - console.error('Error fetching tags:', error); - res.status(500).json({ error: 'Internal server error' }); - } + res.json(tags); + } catch (error) { + console.error('Error fetching tags:', error); + res.status(500).json({ error: 'Internal server error' }); + } }); // GET /api/tag/:id router.get('/tag/:id', async (req, res) => { - try { - const tag = await Tag.findOne({ - where: { id: req.params.id, user_id: req.currentUser.id }, - attributes: ['id', 'name'] - }); + try { + const tag = await Tag.findOne({ + where: { id: req.params.id, user_id: req.currentUser.id }, + attributes: ['id', 'name'], + }); - if (!tag) { - return res.status(404).json({ error: 'Tag not found' }); + if (!tag) { + return res.status(404).json({ error: 'Tag not found' }); + } + + res.json(tag); + } catch (error) { + console.error('Error fetching tag:', error); + res.status(500).json({ error: 'Internal server error' }); } - - res.json(tag); - } catch (error) { - console.error('Error fetching tag:', error); - res.status(500).json({ error: 'Internal server error' }); - } }); // POST /api/tag router.post('/tag', async (req, res) => { - try { - const { name } = req.body; + try { + const { name } = req.body; - if (!name || !name.trim()) { - return res.status(400).json({ error: 'Tag name is required' }); + if (!name || !name.trim()) { + return res.status(400).json({ error: 'Tag name is required' }); + } + + const tag = await Tag.create({ + name: name.trim(), + user_id: req.currentUser.id, + }); + + res.status(201).json({ + id: tag.id, + name: tag.name, + }); + } catch (error) { + console.error('Error creating tag:', error); + res.status(400).json({ + error: 'There was a problem creating the tag.', + }); } - - const tag = await Tag.create({ - name: name.trim(), - user_id: req.currentUser.id - }); - - res.status(201).json({ - id: tag.id, - name: tag.name - }); - } catch (error) { - console.error('Error creating tag:', error); - res.status(400).json({ error: 'There was a problem creating the tag.' }); - } }); // PATCH /api/tag/:id router.patch('/tag/:id', async (req, res) => { - try { - const tag = await Tag.findOne({ - where: { id: req.params.id, user_id: req.currentUser.id } - }); + try { + const tag = await Tag.findOne({ + where: { id: req.params.id, user_id: req.currentUser.id }, + }); - if (!tag) { - return res.status(404).json({ error: 'Tag not found' }); + if (!tag) { + return res.status(404).json({ error: 'Tag not found' }); + } + + const { name } = req.body; + + if (!name || !name.trim()) { + return res.status(400).json({ error: 'Tag name is required' }); + } + + await tag.update({ name: name.trim() }); + + res.json({ + id: tag.id, + name: tag.name, + }); + } catch (error) { + console.error('Error updating tag:', error); + res.status(400).json({ + error: 'There was a problem updating the tag.', + }); } - - const { name } = req.body; - - if (!name || !name.trim()) { - return res.status(400).json({ error: 'Tag name is required' }); - } - - await tag.update({ name: name.trim() }); - - res.json({ - id: tag.id, - name: tag.name - }); - } catch (error) { - console.error('Error updating tag:', error); - res.status(400).json({ error: 'There was a problem updating the tag.' }); - } }); // DELETE /api/tag/:id router.delete('/tag/:id', async (req, res) => { - const transaction = await sequelize.transaction(); - - try { - const tag = await Tag.findOne({ - where: { id: req.params.id, user_id: req.currentUser.id } - }); + const transaction = await sequelize.transaction(); - if (!tag) { - await transaction.rollback(); - return res.status(404).json({ error: 'Tag not found' }); - } + try { + const tag = await Tag.findOne({ + where: { id: req.params.id, user_id: req.currentUser.id }, + }); - // Use transaction to ensure all deletions happen atomically - // Remove all associations before deleting the tag by manually deleting from junction tables - // Only delete from tables that exist - try { - await sequelize.query('DELETE FROM tasks_tags WHERE tag_id = ?', { - replacements: [tag.id], - type: sequelize.QueryTypes.DELETE, - transaction - }); - } catch (error) { - // Ignore if table doesn't exist - console.log('tasks_tags table not found, skipping'); - } - - try { - await sequelize.query('DELETE FROM notes_tags WHERE tag_id = ?', { - replacements: [tag.id], - type: sequelize.QueryTypes.DELETE, - transaction - }); - } catch (error) { - // Ignore if table doesn't exist - console.log('notes_tags table not found, skipping'); - } - - try { - await sequelize.query('DELETE FROM projects_tags WHERE tag_id = ?', { - replacements: [tag.id], - type: sequelize.QueryTypes.DELETE, - transaction - }); - } catch (error) { - // Ignore if table doesn't exist - console.log('projects_tags table not found, skipping'); - } + if (!tag) { + await transaction.rollback(); + return res.status(404).json({ error: 'Tag not found' }); + } - // Now safely delete the tag - await tag.destroy({ transaction }); - - await transaction.commit(); - res.json({ message: 'Tag successfully deleted' }); - } catch (error) { - await transaction.rollback(); - console.error('Error deleting tag:', error); - res.status(400).json({ error: 'There was a problem deleting the tag.' }); - } + // Use transaction to ensure all deletions happen atomically + // Remove all associations before deleting the tag by manually deleting from junction tables + // Only delete from tables that exist + try { + await sequelize.query('DELETE FROM tasks_tags WHERE tag_id = ?', { + replacements: [tag.id], + type: sequelize.QueryTypes.DELETE, + transaction, + }); + } catch (error) { + // Ignore if table doesn't exist + console.log('tasks_tags table not found, skipping'); + } + + try { + await sequelize.query('DELETE FROM notes_tags WHERE tag_id = ?', { + replacements: [tag.id], + type: sequelize.QueryTypes.DELETE, + transaction, + }); + } catch (error) { + // Ignore if table doesn't exist + console.log('notes_tags table not found, skipping'); + } + + try { + await sequelize.query( + 'DELETE FROM projects_tags WHERE tag_id = ?', + { + replacements: [tag.id], + type: sequelize.QueryTypes.DELETE, + transaction, + } + ); + } catch (error) { + // Ignore if table doesn't exist + console.log('projects_tags table not found, skipping'); + } + + // Now safely delete the tag + await tag.destroy({ transaction }); + + await transaction.commit(); + res.json({ message: 'Tag successfully deleted' }); + } catch (error) { + await transaction.rollback(); + console.error('Error deleting tag:', error); + res.status(400).json({ + error: 'There was a problem deleting the tag.', + }); + } }); -module.exports = router; \ No newline at end of file +module.exports = router; diff --git a/backend/routes/task-events.js b/backend/routes/task-events.js index 07aab25..dfa767e 100644 --- a/backend/routes/task-events.js +++ b/backend/routes/task-events.js @@ -5,149 +5,166 @@ const router = express.Router(); // GET /api/task/:id/timeline - Get task event timeline router.get('/task/:id/timeline', async (req, res) => { - try { - const timeline = await TaskEventService.getTaskTimeline(req.params.id); - - // Filter to only show events for tasks owned by the current user - const userTimeline = timeline.filter(event => event.user_id === req.currentUser.id); - - res.json(userTimeline); - } catch (error) { - console.error('Error fetching task timeline:', error); - res.status(500).json({ error: 'Failed to fetch task timeline' }); - } + try { + const timeline = await TaskEventService.getTaskTimeline(req.params.id); + + // Filter to only show events for tasks owned by the current user + const userTimeline = timeline.filter( + (event) => event.user_id === req.currentUser.id + ); + + res.json(userTimeline); + } catch (error) { + console.error('Error fetching task timeline:', error); + res.status(500).json({ error: 'Failed to fetch task timeline' }); + } }); // GET /api/task/:id/completion-time - Get task completion analytics router.get('/task/:id/completion-time', async (req, res) => { - try { - const completionTime = await TaskEventService.getTaskCompletionTime(req.params.id); - - if (!completionTime) { - return res.status(404).json({ error: 'Task completion data not found' }); - } + try { + const completionTime = await TaskEventService.getTaskCompletionTime( + req.params.id + ); - res.json(completionTime); - } catch (error) { - console.error('Error fetching task completion time:', error); - res.status(500).json({ error: 'Failed to fetch task completion time' }); - } + if (!completionTime) { + return res + .status(404) + .json({ error: 'Task completion data not found' }); + } + + res.json(completionTime); + } catch (error) { + console.error('Error fetching task completion time:', error); + res.status(500).json({ error: 'Failed to fetch task completion time' }); + } }); // GET /api/user/productivity-metrics - Get user productivity metrics router.get('/user/productivity-metrics', async (req, res) => { - try { - const { startDate, endDate } = req.query; - - const metrics = await TaskEventService.getUserProductivityMetrics( - req.currentUser.id, - startDate ? new Date(startDate) : null, - endDate ? new Date(endDate) : null - ); + try { + const { startDate, endDate } = req.query; - res.json(metrics); - } catch (error) { - console.error('Error fetching productivity metrics:', error); - res.status(500).json({ error: 'Failed to fetch productivity metrics' }); - } + const metrics = await TaskEventService.getUserProductivityMetrics( + req.currentUser.id, + startDate ? new Date(startDate) : null, + endDate ? new Date(endDate) : null + ); + + res.json(metrics); + } catch (error) { + console.error('Error fetching productivity metrics:', error); + res.status(500).json({ error: 'Failed to fetch productivity metrics' }); + } }); // GET /api/user/activity-summary - Get task activity summary router.get('/user/activity-summary', async (req, res) => { - try { - const { startDate, endDate } = req.query; - - if (!startDate || !endDate) { - return res.status(400).json({ error: 'startDate and endDate are required' }); + try { + const { startDate, endDate } = req.query; + + if (!startDate || !endDate) { + return res + .status(400) + .json({ error: 'startDate and endDate are required' }); + } + + const activitySummary = await TaskEventService.getTaskActivitySummary( + req.currentUser.id, + new Date(startDate), + new Date(endDate) + ); + + res.json(activitySummary); + } catch (error) { + console.error('Error fetching activity summary:', error); + res.status(500).json({ error: 'Failed to fetch activity summary' }); } - - const activitySummary = await TaskEventService.getTaskActivitySummary( - req.currentUser.id, - new Date(startDate), - new Date(endDate) - ); - - res.json(activitySummary); - } catch (error) { - console.error('Error fetching activity summary:', error); - res.status(500).json({ error: 'Failed to fetch activity summary' }); - } }); // GET /api/tasks/completion-analytics - Get completion time analytics for multiple tasks router.get('/tasks/completion-analytics', async (req, res) => { - try { - const { limit = 50, offset = 0, projectId } = req.query; - - // Get completed tasks for the user - const { Task, Project } = require('../models'); - const { Op } = require('sequelize'); - - const whereClause = { - user_id: req.currentUser.id, - status: 2 // completed - }; - - if (projectId) { - whereClause.project_id = projectId; - } - - const completedTasks = await Task.findAll({ - where: whereClause, - include: [ - { model: Project, attributes: ['name'], required: false } - ], - order: [['completed_at', 'DESC']], - limit: parseInt(limit), - offset: parseInt(offset) - }); + try { + const { limit = 50, offset = 0, projectId } = req.query; - // Get completion time analytics for each task - const analytics = []; - for (const task of completedTasks) { - const completionTime = await TaskEventService.getTaskCompletionTime(task.id); - if (completionTime) { - analytics.push({ - task_id: task.id, - task_name: task.name, - project_name: task.Project?.name || null, - ...completionTime + // Get completed tasks for the user + const { Task, Project } = require('../models'); + const { Op } = require('sequelize'); + + const whereClause = { + user_id: req.currentUser.id, + status: 2, // completed + }; + + if (projectId) { + whereClause.project_id = projectId; + } + + const completedTasks = await Task.findAll({ + where: whereClause, + include: [ + { model: Project, attributes: ['name'], required: false }, + ], + order: [['completed_at', 'DESC']], + limit: parseInt(limit), + offset: parseInt(offset), }); - } + + // Get completion time analytics for each task + const analytics = []; + for (const task of completedTasks) { + const completionTime = await TaskEventService.getTaskCompletionTime( + task.id + ); + if (completionTime) { + analytics.push({ + task_id: task.id, + task_name: task.name, + project_name: task.Project?.name || null, + ...completionTime, + }); + } + } + + // Calculate summary statistics + const summary = { + total_tasks: analytics.length, + average_completion_hours: + analytics.length > 0 + ? analytics.reduce((sum, a) => sum + a.duration_hours, 0) / + analytics.length + : 0, + median_completion_hours: 0, + fastest_completion: + analytics.length > 0 + ? Math.min(...analytics.map((a) => a.duration_hours)) + : 0, + slowest_completion: + analytics.length > 0 + ? Math.max(...analytics.map((a) => a.duration_hours)) + : 0, + }; + + // Calculate median + if (analytics.length > 0) { + const sorted = analytics + .map((a) => a.duration_hours) + .sort((a, b) => a - b); + const middle = Math.floor(sorted.length / 2); + summary.median_completion_hours = + sorted.length % 2 === 0 + ? (sorted[middle - 1] + sorted[middle]) / 2 + : sorted[middle]; + } + + res.json({ + tasks: analytics, + summary, + }); + } catch (error) { + console.error('Error fetching completion analytics:', error); + res.status(500).json({ error: 'Failed to fetch completion analytics' }); } - - // Calculate summary statistics - const summary = { - total_tasks: analytics.length, - average_completion_hours: analytics.length > 0 - ? analytics.reduce((sum, a) => sum + a.duration_hours, 0) / analytics.length - : 0, - median_completion_hours: 0, - fastest_completion: analytics.length > 0 - ? Math.min(...analytics.map(a => a.duration_hours)) - : 0, - slowest_completion: analytics.length > 0 - ? Math.max(...analytics.map(a => a.duration_hours)) - : 0 - }; - - // Calculate median - if (analytics.length > 0) { - const sorted = analytics.map(a => a.duration_hours).sort((a, b) => a - b); - const middle = Math.floor(sorted.length / 2); - summary.median_completion_hours = sorted.length % 2 === 0 - ? (sorted[middle - 1] + sorted[middle]) / 2 - : sorted[middle]; - } - - res.json({ - tasks: analytics, - summary - }); - } catch (error) { - console.error('Error fetching completion analytics:', error); - res.status(500).json({ error: 'Failed to fetch completion analytics' }); - } }); -module.exports = router; \ No newline at end of file +module.exports = router; diff --git a/backend/routes/tasks.js b/backend/routes/tasks.js index 559c3fd..dd5f18f 100644 --- a/backend/routes/tasks.js +++ b/backend/routes/tasks.js @@ -8,1065 +8,1375 @@ const router = express.Router(); // Helper function to serialize task with today move count async function serializeTask(task) { - const taskJson = task.toJSON(); - const todayMoveCount = await TaskEventService.getTaskTodayMoveCount(task.id); - - return { - ...taskJson, - tags: taskJson.Tags || [], - due_date: task.due_date ? task.due_date.toISOString().split('T')[0] : null, - today_move_count: todayMoveCount - }; + const taskJson = task.toJSON(); + const todayMoveCount = await TaskEventService.getTaskTodayMoveCount( + task.id + ); + + return { + ...taskJson, + tags: taskJson.Tags || [], + due_date: task.due_date + ? task.due_date.toISOString().split('T')[0] + : null, + today_move_count: todayMoveCount, + }; } // Helper function to update task tags async function updateTaskTags(task, tagsData, userId) { - if (!tagsData) return; + if (!tagsData) return; - const tagNames = tagsData - .map(tag => tag.name) - .filter(name => name && name.trim()) - .filter((name, index, arr) => arr.indexOf(name) === index); // unique + const tagNames = tagsData + .map((tag) => tag.name) + .filter((name) => name && name.trim()) + .filter((name, index, arr) => arr.indexOf(name) === index); // unique - if (tagNames.length === 0) { - await task.setTags([]); - return; - } + if (tagNames.length === 0) { + await task.setTags([]); + return; + } - // Find existing tags - const existingTags = await Tag.findAll({ - where: { user_id: userId, name: tagNames } - }); + // Find existing tags + const existingTags = await Tag.findAll({ + where: { user_id: userId, name: tagNames }, + }); - // Create new tags - const existingTagNames = existingTags.map(tag => tag.name); - const newTagNames = tagNames.filter(name => !existingTagNames.includes(name)); - - const createdTags = await Promise.all( - newTagNames.map(name => Tag.create({ name, user_id: userId })) - ); + // Create new tags + const existingTagNames = existingTags.map((tag) => tag.name); + const newTagNames = tagNames.filter( + (name) => !existingTagNames.includes(name) + ); - // Set all tags to task - const allTags = [...existingTags, ...createdTags]; - await task.setTags(allTags); + const createdTags = await Promise.all( + newTagNames.map((name) => Tag.create({ name, user_id: userId })) + ); + + // Set all tags to task + const allTags = [...existingTags, ...createdTags]; + await task.setTags(allTags); } // Filter tasks by parameters async function filterTasksByParams(params, userId) { - let whereClause = { user_id: userId }; - let includeClause = [ - { model: Tag, attributes: ['id', 'name'], through: { attributes: [] } }, - { model: Project, attributes: ['name'], required: false } - ]; + let whereClause = { user_id: userId }; + let includeClause = [ + { model: Tag, attributes: ['id', 'name'], through: { attributes: [] } }, + { model: Project, attributes: ['name'], required: false }, + ]; - // Filter by type - switch (params.type) { - case 'today': - whereClause.status = { [Op.notIn]: [Task.STATUS.DONE, Task.STATUS.ARCHIVED, 'done', 'archived'] }; // Exclude completed and archived tasks (both integer and string values) - break; - case 'upcoming': - whereClause.due_date = { - [Op.between]: [new Date(), new Date(Date.now() + 7 * 24 * 60 * 60 * 1000)] - }; - whereClause.status = { [Op.notIn]: [Task.STATUS.DONE, 'done'] }; - break; - case 'next': - whereClause.due_date = null; - whereClause.project_id = null; - whereClause.status = { [Op.notIn]: [Task.STATUS.DONE, 'done'] }; - break; - case 'inbox': - whereClause[Op.or] = [ - { due_date: null }, - { project_id: null } - ]; - whereClause.status = { [Op.notIn]: [Task.STATUS.DONE, 'done'] }; - break; - case 'someday': - whereClause.due_date = null; - whereClause.status = { [Op.notIn]: [Task.STATUS.DONE, 'done'] }; - break; - case 'waiting': - whereClause.status = Task.STATUS.WAITING; - break; - default: - if (params.status === 'done') { - whereClause.status = { [Op.in]: [Task.STATUS.DONE, 'done'] }; - } else { - whereClause.status = { [Op.notIn]: [Task.STATUS.DONE, 'done'] }; - } - } - - // Filter by tag - if (params.tag) { - includeClause[0].where = { name: params.tag }; - includeClause[0].required = true; - } - - let orderClause = [['created_at', 'ASC']]; - - // Apply ordering - if (params.order_by) { - const [orderColumn, orderDirection = 'asc'] = params.order_by.split(':'); - const allowedColumns = ['created_at', 'updated_at', 'name', 'priority', 'status', 'due_date']; - - if (!allowedColumns.includes(orderColumn)) { - throw new Error('Invalid order column specified.'); + // Filter by type + switch (params.type) { + case 'today': + whereClause.status = { + [Op.notIn]: [ + Task.STATUS.DONE, + Task.STATUS.ARCHIVED, + 'done', + 'archived', + ], + }; // Exclude completed and archived tasks (both integer and string values) + break; + case 'upcoming': + whereClause.due_date = { + [Op.between]: [ + new Date(), + new Date(Date.now() + 7 * 24 * 60 * 60 * 1000), + ], + }; + whereClause.status = { [Op.notIn]: [Task.STATUS.DONE, 'done'] }; + break; + case 'next': + whereClause.due_date = null; + whereClause.project_id = null; + whereClause.status = { [Op.notIn]: [Task.STATUS.DONE, 'done'] }; + break; + case 'inbox': + whereClause[Op.or] = [{ due_date: null }, { project_id: null }]; + whereClause.status = { [Op.notIn]: [Task.STATUS.DONE, 'done'] }; + break; + case 'someday': + whereClause.due_date = null; + whereClause.status = { [Op.notIn]: [Task.STATUS.DONE, 'done'] }; + break; + case 'waiting': + whereClause.status = Task.STATUS.WAITING; + break; + default: + if (params.status === 'done') { + whereClause.status = { [Op.in]: [Task.STATUS.DONE, 'done'] }; + } else { + whereClause.status = { [Op.notIn]: [Task.STATUS.DONE, 'done'] }; + } } - if (orderColumn === 'due_date') { - orderClause = [ - [sequelize.literal('CASE WHEN due_date IS NULL THEN 1 ELSE 0 END'), 'ASC'], - ['due_date', orderDirection.toUpperCase()] - ]; - } else { - orderClause = [[orderColumn, orderDirection.toUpperCase()]]; + // Filter by tag + if (params.tag) { + includeClause[0].where = { name: params.tag }; + includeClause[0].required = true; } - } - return await Task.findAll({ - where: whereClause, - include: includeClause, - order: orderClause, - distinct: true - }); + let orderClause = [['created_at', 'ASC']]; + + // Apply ordering + if (params.order_by) { + const [orderColumn, orderDirection = 'asc'] = + params.order_by.split(':'); + const allowedColumns = [ + 'created_at', + 'updated_at', + 'name', + 'priority', + 'status', + 'due_date', + ]; + + if (!allowedColumns.includes(orderColumn)) { + throw new Error('Invalid order column specified.'); + } + + if (orderColumn === 'due_date') { + orderClause = [ + [ + sequelize.literal( + 'CASE WHEN due_date IS NULL THEN 1 ELSE 0 END' + ), + 'ASC', + ], + ['due_date', orderDirection.toUpperCase()], + ]; + } else { + orderClause = [[orderColumn, orderDirection.toUpperCase()]]; + } + } + + return await Task.findAll({ + where: whereClause, + include: includeClause, + order: orderClause, + distinct: true, + }); } // Compute task metrics async function computeTaskMetrics(userId, userTimezone = 'UTC') { - console.log('Computing metrics for user', userId, 'with timezone:', userTimezone); - const totalOpenTasks = await Task.count({ - where: { user_id: userId, status: { [Op.ne]: Task.STATUS.DONE } } - }); + console.log( + 'Computing metrics for user', + userId, + 'with timezone:', + userTimezone + ); + const totalOpenTasks = await Task.count({ + where: { user_id: userId, status: { [Op.ne]: Task.STATUS.DONE } }, + }); - const oneMonthAgo = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000); - const tasksPendingOverMonth = await Task.count({ - where: { - user_id: userId, - status: { [Op.ne]: Task.STATUS.DONE }, - created_at: { [Op.lt]: oneMonthAgo } - } - }); + const oneMonthAgo = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000); + const tasksPendingOverMonth = await Task.count({ + where: { + user_id: userId, + status: { [Op.ne]: Task.STATUS.DONE }, + created_at: { [Op.lt]: oneMonthAgo }, + }, + }); - const tasksInProgress = await Task.findAll({ - where: { - user_id: userId, - status: { [Op.in]: [Task.STATUS.IN_PROGRESS, 'in_progress'] } - }, - include: [ - { - model: Tag, - attributes: ['id', 'name'], - through: { attributes: [] }, - required: false - }, - { - model: Project, - attributes: ['id', 'name', 'active'], - required: false - } - ], - order: [['priority', 'DESC']] - }); + const tasksInProgress = await Task.findAll({ + where: { + user_id: userId, + status: { [Op.in]: [Task.STATUS.IN_PROGRESS, 'in_progress'] }, + }, + include: [ + { + model: Tag, + attributes: ['id', 'name'], + through: { attributes: [] }, + required: false, + }, + { + model: Project, + attributes: ['id', 'name', 'active'], + required: false, + }, + ], + order: [['priority', 'DESC']], + }); - // Get tasks in today plan - const todayPlanTasks = await Task.findAll({ - where: { - user_id: userId, - today: true, - status: { [Op.notIn]: [Task.STATUS.DONE, Task.STATUS.ARCHIVED, 'done', 'archived'] } - }, - include: [ - { - model: Tag, - attributes: ['id', 'name'], - through: { attributes: [] }, - required: false - }, - { - model: Project, - attributes: ['id', 'name', 'active'], - required: false - } - ], - order: [['priority', 'DESC'], ['created_at', 'ASC']] - }); + // Get tasks in today plan + const todayPlanTasks = await Task.findAll({ + where: { + user_id: userId, + today: true, + status: { + [Op.notIn]: [ + Task.STATUS.DONE, + Task.STATUS.ARCHIVED, + 'done', + 'archived', + ], + }, + }, + include: [ + { + model: Tag, + attributes: ['id', 'name'], + through: { attributes: [] }, + required: false, + }, + { + model: Project, + attributes: ['id', 'name', 'active'], + required: false, + }, + ], + order: [ + ['priority', 'DESC'], + ['created_at', 'ASC'], + ], + }); - const today = new Date(); - today.setHours(23, 59, 59, 999); + const today = new Date(); + today.setHours(23, 59, 59, 999); - const tasksDueToday = await Task.findAll({ - where: { - user_id: userId, - status: { [Op.notIn]: [Task.STATUS.DONE, Task.STATUS.ARCHIVED, 'done', 'archived'] }, - [Op.or]: [ - { due_date: { [Op.lte]: today } }, - sequelize.literal(`EXISTS ( + const tasksDueToday = await Task.findAll({ + where: { + user_id: userId, + status: { + [Op.notIn]: [ + Task.STATUS.DONE, + Task.STATUS.ARCHIVED, + 'done', + 'archived', + ], + }, + [Op.or]: [ + { due_date: { [Op.lte]: today } }, + sequelize.literal(`EXISTS ( SELECT 1 FROM projects WHERE projects.id = Task.project_id AND projects.due_date_at <= '${today.toISOString()}' - )`) - ] - }, - include: [ - { - model: Tag, - attributes: ['id', 'name'], - through: { attributes: [] }, - required: false - }, - { - model: Project, - attributes: ['id', 'name', 'active'], - required: false - } - ] - }); - - // Get suggested tasks only if user has a meaningful task base - let suggestedTasks = []; - - // Only show suggested tasks if: - // 1. User has at least 3 total tasks, OR - // 2. User has at least 1 project with tasks - if (totalOpenTasks >= 3 || (tasksInProgress.length > 0 || tasksDueToday.length > 0)) { - const excludedTaskIds = [ - ...tasksInProgress.map(t => t.id), - ...tasksDueToday.map(t => t.id) - ]; - - // Get task IDs that have "someday" tag - const somedayTaskIds = await sequelize.query( - `SELECT DISTINCT task_id FROM tasks_tags - JOIN tags ON tasks_tags.tag_id = tags.id - WHERE tags.name = 'someday' AND tags.user_id = ?`, - { - replacements: [userId], - type: sequelize.QueryTypes.SELECT - } - ).then(results => results.map(r => r.task_id)); - - // Get tasks without projects (excluding someday tagged tasks) - const nonProjectTasks = await Task.findAll({ - where: { - user_id: userId, - status: { [Op.in]: [Task.STATUS.NOT_STARTED, Task.STATUS.WAITING] }, - id: { [Op.notIn]: [...excludedTaskIds, ...somedayTaskIds] }, - [Op.or]: [ - { project_id: null }, - { project_id: '' } - ] - }, - include: [ - { - model: Tag, - attributes: ['id', 'name'], - through: { attributes: [] }, - required: false - }, - { - model: Project, - attributes: ['id', 'name', 'active'], - required: false - } - ], - order: [['priority', 'DESC'], ['created_at', 'ASC']], - limit: 6 - }); - - // Get tasks with projects (excluding someday tagged tasks) - const projectTasks = await Task.findAll({ - where: { - user_id: userId, - status: { [Op.in]: [Task.STATUS.NOT_STARTED, Task.STATUS.WAITING] }, - id: { [Op.notIn]: [...excludedTaskIds, ...somedayTaskIds] }, - project_id: { [Op.not]: null, [Op.ne]: '' } - }, - include: [ - { - model: Tag, - attributes: ['id', 'name'], - through: { attributes: [] }, - required: false - }, - { - model: Project, - attributes: ['id', 'name', 'active'], - required: false - } - ], - order: [['priority', 'DESC'], ['created_at', 'ASC']], - limit: 6 - }); - - // Check if we have enough suggestions (at least 6 total) - let combinedTasks = [...nonProjectTasks, ...projectTasks]; - - // If we don't have enough suggestions, include someday tasks as fallback - if (combinedTasks.length < 6) { - const usedTaskIds = [...excludedTaskIds, ...combinedTasks.map(t => t.id)]; - - const somedayFallbackTasks = await Task.findAll({ - where: { - user_id: userId, - status: { [Op.in]: [Task.STATUS.NOT_STARTED, Task.STATUS.WAITING] }, - id: { - [Op.notIn]: usedTaskIds, - [Op.in]: somedayTaskIds - } + )`), + ], }, include: [ - { - model: Tag, - attributes: ['id', 'name'], - through: { attributes: [] }, - required: false - }, - { - model: Project, - attributes: ['id', 'name', 'active'], - required: false - } + { + model: Tag, + attributes: ['id', 'name'], + through: { attributes: [] }, + required: false, + }, + { + model: Project, + attributes: ['id', 'name', 'active'], + required: false, + }, ], - order: [['priority', 'DESC'], ['created_at', 'ASC']], - limit: 12 - combinedTasks.length - }); + }); - combinedTasks = [...combinedTasks, ...somedayFallbackTasks]; + // Get suggested tasks only if user has a meaningful task base + let suggestedTasks = []; + + // Only show suggested tasks if: + // 1. User has at least 3 total tasks, OR + // 2. User has at least 1 project with tasks + if ( + totalOpenTasks >= 3 || + tasksInProgress.length > 0 || + tasksDueToday.length > 0 + ) { + const excludedTaskIds = [ + ...tasksInProgress.map((t) => t.id), + ...tasksDueToday.map((t) => t.id), + ]; + + // Get task IDs that have "someday" tag + const somedayTaskIds = await sequelize + .query( + `SELECT DISTINCT task_id FROM tasks_tags + JOIN tags ON tasks_tags.tag_id = tags.id + WHERE tags.name = 'someday' AND tags.user_id = ?`, + { + replacements: [userId], + type: sequelize.QueryTypes.SELECT, + } + ) + .then((results) => results.map((r) => r.task_id)); + + // Get tasks without projects (excluding someday tagged tasks) + const nonProjectTasks = await Task.findAll({ + where: { + user_id: userId, + status: { + [Op.in]: [Task.STATUS.NOT_STARTED, Task.STATUS.WAITING], + }, + id: { [Op.notIn]: [...excludedTaskIds, ...somedayTaskIds] }, + [Op.or]: [{ project_id: null }, { project_id: '' }], + }, + include: [ + { + model: Tag, + attributes: ['id', 'name'], + through: { attributes: [] }, + required: false, + }, + { + model: Project, + attributes: ['id', 'name', 'active'], + required: false, + }, + ], + order: [ + ['priority', 'DESC'], + ['created_at', 'ASC'], + ], + limit: 6, + }); + + // Get tasks with projects (excluding someday tagged tasks) + const projectTasks = await Task.findAll({ + where: { + user_id: userId, + status: { + [Op.in]: [Task.STATUS.NOT_STARTED, Task.STATUS.WAITING], + }, + id: { [Op.notIn]: [...excludedTaskIds, ...somedayTaskIds] }, + project_id: { [Op.not]: null, [Op.ne]: '' }, + }, + include: [ + { + model: Tag, + attributes: ['id', 'name'], + through: { attributes: [] }, + required: false, + }, + { + model: Project, + attributes: ['id', 'name', 'active'], + required: false, + }, + ], + order: [ + ['priority', 'DESC'], + ['created_at', 'ASC'], + ], + limit: 6, + }); + + // Check if we have enough suggestions (at least 6 total) + let combinedTasks = [...nonProjectTasks, ...projectTasks]; + + // If we don't have enough suggestions, include someday tasks as fallback + if (combinedTasks.length < 6) { + const usedTaskIds = [ + ...excludedTaskIds, + ...combinedTasks.map((t) => t.id), + ]; + + const somedayFallbackTasks = await Task.findAll({ + where: { + user_id: userId, + status: { + [Op.in]: [Task.STATUS.NOT_STARTED, Task.STATUS.WAITING], + }, + id: { + [Op.notIn]: usedTaskIds, + [Op.in]: somedayTaskIds, + }, + }, + include: [ + { + model: Tag, + attributes: ['id', 'name'], + through: { attributes: [] }, + required: false, + }, + { + model: Project, + attributes: ['id', 'name', 'active'], + required: false, + }, + ], + order: [ + ['priority', 'DESC'], + ['created_at', 'ASC'], + ], + limit: 12 - combinedTasks.length, + }); + + combinedTasks = [...combinedTasks, ...somedayFallbackTasks]; + } + + suggestedTasks = combinedTasks; } - suggestedTasks = combinedTasks; - - } + // Get tasks completed today - use user's timezone + const todayInUserTz = moment.tz(userTimezone); + const todayStart = todayInUserTz.clone().startOf('day').utc().toDate(); + const todayEnd = todayInUserTz.clone().endOf('day').utc().toDate(); - // Get tasks completed today - use user's timezone - const todayInUserTz = moment.tz(userTimezone); - const todayStart = todayInUserTz.clone().startOf('day').utc().toDate(); - const todayEnd = todayInUserTz.clone().endOf('day').utc().toDate(); + const tasksCompletedToday = await Task.findAll({ + where: { + user_id: userId, + status: Task.STATUS.DONE, + completed_at: { + [Op.between]: [todayStart, todayEnd], + }, + }, + include: [ + { + model: Tag, + attributes: ['id', 'name'], + through: { attributes: [] }, + required: false, + }, + { + model: Project, + attributes: ['id', 'name', 'active'], + required: false, + }, + ], + order: [['completed_at', 'DESC']], + }); - const tasksCompletedToday = await Task.findAll({ - where: { - user_id: userId, - status: Task.STATUS.DONE, - completed_at: { - [Op.between]: [todayStart, todayEnd] - } - }, - include: [ - { - model: Tag, - attributes: ['id', 'name'], - through: { attributes: [] }, - required: false - }, - { - model: Project, - attributes: ['id', 'name', 'active'], - required: false - } - ], - order: [['completed_at', 'DESC']] - }); + // Get weekly completion data (last 7 days) - use user's timezone + const weekStartInUserTz = moment.tz(userTimezone).subtract(6, 'days'); + const weekStart = weekStartInUserTz.clone().startOf('day').utc().toDate(); + const weekEnd = todayInUserTz.clone().endOf('day').utc().toDate(); - // Get weekly completion data (last 7 days) - use user's timezone - const weekStartInUserTz = moment.tz(userTimezone).subtract(6, 'days'); - const weekStart = weekStartInUserTz.clone().startOf('day').utc().toDate(); - const weekEnd = todayInUserTz.clone().endOf('day').utc().toDate(); + // For SQLite, we'll fetch the raw data and process it in JavaScript + const weeklyCompletionsRaw = await Task.findAll({ + where: { + user_id: userId, + status: Task.STATUS.DONE, + completed_at: { + [Op.between]: [weekStart, weekEnd], + }, + }, + attributes: ['completed_at'], + raw: true, + }); - // For SQLite, we'll fetch the raw data and process it in JavaScript - const weeklyCompletionsRaw = await Task.findAll({ - where: { - user_id: userId, - status: Task.STATUS.DONE, - completed_at: { - [Op.between]: [weekStart, weekEnd] - } - }, - attributes: ['completed_at'], - raw: true - }); + // Process the data in JavaScript to group by date in user's timezone + const dateCountMap = {}; + weeklyCompletionsRaw.forEach((task) => { + // Parse the completed_at field more reliably - convert to Date first, then to moment + const completedDate = new Date(task.completed_at); + const dateInUserTz = moment(completedDate) + .tz(userTimezone) + .format('YYYY-MM-DD'); + dateCountMap[dateInUserTz] = (dateCountMap[dateInUserTz] || 0) + 1; + }); - // Process the data in JavaScript to group by date in user's timezone - const dateCountMap = {}; - weeklyCompletionsRaw.forEach(task => { - // Parse the completed_at field more reliably - convert to Date first, then to moment - const completedDate = new Date(task.completed_at); - const dateInUserTz = moment(completedDate).tz(userTimezone).format('YYYY-MM-DD'); - dateCountMap[dateInUserTz] = (dateCountMap[dateInUserTz] || 0) + 1; - }); + // Convert to the format expected by the rest of the code + const weeklyCompletions = Object.entries(dateCountMap).map( + ([date, count]) => ({ + date, + count: count.toString(), + }) + ); - // Convert to the format expected by the rest of the code - const weeklyCompletions = Object.entries(dateCountMap).map(([date, count]) => ({ - date, - count: count.toString() - })); + // Process weekly completion data to ensure all 7 days are represented + const weeklyData = []; + for (let i = 6; i >= 0; i--) { + const dateInUserTz = moment.tz(userTimezone).subtract(i, 'days'); + const dateString = dateInUserTz.format('YYYY-MM-DD'); - // Process weekly completion data to ensure all 7 days are represented - const weeklyData = []; - for (let i = 6; i >= 0; i--) { - const dateInUserTz = moment.tz(userTimezone).subtract(i, 'days'); - const dateString = dateInUserTz.format('YYYY-MM-DD'); - - const found = weeklyCompletions.find(item => item.date === dateString); - const dayData = { - date: dateString, - count: found ? parseInt(found.count) : 0, - dayName: dateInUserTz.format('ddd') // Short day name + const found = weeklyCompletions.find( + (item) => item.date === dateString + ); + const dayData = { + date: dateString, + count: found ? parseInt(found.count) : 0, + dayName: dateInUserTz.format('ddd'), // Short day name + }; + weeklyData.push(dayData); + } + + return { + total_open_tasks: totalOpenTasks, + tasks_pending_over_month: tasksPendingOverMonth, + tasks_in_progress_count: tasksInProgress.length, + tasks_in_progress: tasksInProgress, + tasks_due_today: tasksDueToday, + today_plan_tasks: todayPlanTasks, + suggested_tasks: suggestedTasks, + tasks_completed_today: tasksCompletedToday, + weekly_completions: weeklyData, }; - weeklyData.push(dayData); - } - - return { - total_open_tasks: totalOpenTasks, - tasks_pending_over_month: tasksPendingOverMonth, - tasks_in_progress_count: tasksInProgress.length, - tasks_in_progress: tasksInProgress, - tasks_due_today: tasksDueToday, - today_plan_tasks: todayPlanTasks, - suggested_tasks: suggestedTasks, - tasks_completed_today: tasksCompletedToday, - weekly_completions: weeklyData - }; } // GET /api/tasks router.get('/tasks', async (req, res) => { - try { - const tasks = await filterTasksByParams(req.query, req.currentUser.id); - const metrics = await computeTaskMetrics(req.currentUser.id, req.currentUser.timezone); + try { + const tasks = await filterTasksByParams(req.query, req.currentUser.id); + const metrics = await computeTaskMetrics( + req.currentUser.id, + req.currentUser.timezone + ); - res.json({ - tasks: await Promise.all(tasks.map(task => serializeTask(task))), - metrics: { - total_open_tasks: metrics.total_open_tasks, - tasks_pending_over_month: metrics.tasks_pending_over_month, - tasks_in_progress_count: metrics.tasks_in_progress_count, - tasks_in_progress: await Promise.all(metrics.tasks_in_progress.map(task => serializeTask(task))), - tasks_due_today: await Promise.all(metrics.tasks_due_today.map(task => serializeTask(task))), - today_plan_tasks: await Promise.all(metrics.today_plan_tasks.map(task => serializeTask(task))), - suggested_tasks: await Promise.all(metrics.suggested_tasks.map(task => serializeTask(task))), - tasks_completed_today: await Promise.all(metrics.tasks_completed_today.map(async task => { - const serialized = await serializeTask(task); - return { - ...serialized, - completed_at: task.completed_at ? task.completed_at.toISOString() : null - }; - })), - weekly_completions: metrics.weekly_completions - } - }); - } catch (error) { - console.error('Error fetching tasks:', error); - if (error.message === 'Invalid order column specified.') { - return res.status(400).json({ error: error.message }); + res.json({ + tasks: await Promise.all(tasks.map((task) => serializeTask(task))), + metrics: { + total_open_tasks: metrics.total_open_tasks, + tasks_pending_over_month: metrics.tasks_pending_over_month, + tasks_in_progress_count: metrics.tasks_in_progress_count, + tasks_in_progress: await Promise.all( + metrics.tasks_in_progress.map((task) => serializeTask(task)) + ), + tasks_due_today: await Promise.all( + metrics.tasks_due_today.map((task) => serializeTask(task)) + ), + today_plan_tasks: await Promise.all( + metrics.today_plan_tasks.map((task) => serializeTask(task)) + ), + suggested_tasks: await Promise.all( + metrics.suggested_tasks.map((task) => serializeTask(task)) + ), + tasks_completed_today: await Promise.all( + metrics.tasks_completed_today.map(async (task) => { + const serialized = await serializeTask(task); + return { + ...serialized, + completed_at: task.completed_at + ? task.completed_at.toISOString() + : null, + }; + }) + ), + weekly_completions: metrics.weekly_completions, + }, + }); + } catch (error) { + console.error('Error fetching tasks:', error); + if (error.message === 'Invalid order column specified.') { + return res.status(400).json({ error: error.message }); + } + res.status(500).json({ error: 'Internal server error' }); } - res.status(500).json({ error: 'Internal server error' }); - } }); // GET /api/task/uuid/:uuid router.get('/task/uuid/:uuid', async (req, res) => { - try { - const task = await Task.findOne({ - where: { uuid: req.params.uuid, user_id: req.currentUser.id }, - include: [ - { model: Tag, attributes: ['id', 'name'], through: { attributes: [] } }, - { model: Project, attributes: ['name'], required: false } - ] - }); + try { + const task = await Task.findOne({ + where: { uuid: req.params.uuid, user_id: req.currentUser.id }, + include: [ + { + model: Tag, + attributes: ['id', 'name'], + through: { attributes: [] }, + }, + { model: Project, attributes: ['name'], required: false }, + ], + }); - if (!task) { - return res.status(404).json({ error: 'Task not found.' }); + if (!task) { + return res.status(404).json({ error: 'Task not found.' }); + } + + const serializedTask = await serializeTask(task); + + res.json(serializedTask); + } catch (error) { + console.error('Error fetching task by UUID:', error); + res.status(500).json({ error: 'Internal server error' }); } - - const serializedTask = await serializeTask(task); - - res.json(serializedTask); - } catch (error) { - console.error('Error fetching task by UUID:', error); - res.status(500).json({ error: 'Internal server error' }); - } }); // GET /api/task/:id router.get('/task/:id', async (req, res) => { - try { - const task = await Task.findOne({ - where: { id: req.params.id, user_id: req.currentUser.id }, - include: [ - { model: Tag, attributes: ['id', 'name'], through: { attributes: [] } }, - { model: Project, attributes: ['name'], required: false } - ] - }); + try { + const task = await Task.findOne({ + where: { id: req.params.id, user_id: req.currentUser.id }, + include: [ + { + model: Tag, + attributes: ['id', 'name'], + through: { attributes: [] }, + }, + { model: Project, attributes: ['name'], required: false }, + ], + }); - if (!task) { - return res.status(404).json({ error: 'Task not found.' }); + if (!task) { + return res.status(404).json({ error: 'Task not found.' }); + } + + const serializedTask = await serializeTask(task); + + res.json(serializedTask); + } catch (error) { + console.error('Error fetching task:', error); + res.status(500).json({ error: 'Internal server error' }); } - - const serializedTask = await serializeTask(task); - - res.json(serializedTask); - } catch (error) { - console.error('Error fetching task:', error); - res.status(500).json({ error: 'Internal server error' }); - } }); // POST /api/task router.post('/task', async (req, res) => { - try { - const { - name, - priority, - due_date, - status, - note, - project_id, - tags, - Tags, - today, - recurrence_type, - recurrence_interval, - recurrence_end_date, - recurrence_weekday, - recurrence_month_day, - recurrence_week_of_month, - completion_based - } = req.body; - - // Handle both tags and Tags (Sequelize association format) - const tagsData = tags || Tags; - - // Validate required fields - if (!name || name.trim() === '') { - return res.status(400).json({ error: 'Task name is required.' }); - } - - const taskAttributes = { - name: name.trim(), - priority: priority !== undefined ? (typeof priority === 'string' ? Task.getPriorityValue(priority) : priority) : Task.PRIORITY.LOW, - due_date: due_date || null, - status: status !== undefined ? (typeof status === 'string' ? Task.getStatusValue(status) : status) : Task.STATUS.NOT_STARTED, - note, - today: today !== undefined ? today : false, - user_id: req.currentUser.id, - recurrence_type: recurrence_type || 'none', - recurrence_interval: recurrence_interval || null, - recurrence_end_date: recurrence_end_date || null, - recurrence_weekday: recurrence_weekday !== undefined ? recurrence_weekday : null, - recurrence_month_day: recurrence_month_day !== undefined ? recurrence_month_day : null, - recurrence_week_of_month: recurrence_week_of_month !== undefined ? recurrence_week_of_month : null, - completion_based: completion_based || false - }; - - // Handle project assignment - if (project_id && project_id.toString().trim()) { - const project = await Project.findOne({ - where: { id: project_id, user_id: req.currentUser.id } - }); - if (!project) { - return res.status(400).json({ error: 'Invalid project.' }); - } - taskAttributes.project_id = project_id; - } - - const task = await Task.create(taskAttributes); - await updateTaskTags(task, tagsData, req.currentUser.id); - - // Log task creation event try { - await TaskEventService.logTaskCreated(task.id, req.currentUser.id, { - name: task.name, - status: task.status, - priority: task.priority, - due_date: task.due_date, - project_id: task.project_id - }, { source: 'web' }); - } catch (eventError) { - console.error('Error logging task creation event:', eventError); - // Don't fail the request if event logging fails + const { + name, + priority, + due_date, + status, + note, + project_id, + tags, + Tags, + today, + recurrence_type, + recurrence_interval, + recurrence_end_date, + recurrence_weekday, + recurrence_month_day, + recurrence_week_of_month, + completion_based, + } = req.body; + + // Handle both tags and Tags (Sequelize association format) + const tagsData = tags || Tags; + + // Validate required fields + if (!name || name.trim() === '') { + return res.status(400).json({ error: 'Task name is required.' }); + } + + const taskAttributes = { + name: name.trim(), + priority: + priority !== undefined + ? typeof priority === 'string' + ? Task.getPriorityValue(priority) + : priority + : Task.PRIORITY.LOW, + due_date: due_date || null, + status: + status !== undefined + ? typeof status === 'string' + ? Task.getStatusValue(status) + : status + : Task.STATUS.NOT_STARTED, + note, + today: today !== undefined ? today : false, + user_id: req.currentUser.id, + recurrence_type: recurrence_type || 'none', + recurrence_interval: recurrence_interval || null, + recurrence_end_date: recurrence_end_date || null, + recurrence_weekday: + recurrence_weekday !== undefined ? recurrence_weekday : null, + recurrence_month_day: + recurrence_month_day !== undefined + ? recurrence_month_day + : null, + recurrence_week_of_month: + recurrence_week_of_month !== undefined + ? recurrence_week_of_month + : null, + completion_based: completion_based || false, + }; + + // Handle project assignment + if (project_id && project_id.toString().trim()) { + const project = await Project.findOne({ + where: { id: project_id, user_id: req.currentUser.id }, + }); + if (!project) { + return res.status(400).json({ error: 'Invalid project.' }); + } + taskAttributes.project_id = project_id; + } + + const task = await Task.create(taskAttributes); + await updateTaskTags(task, tagsData, req.currentUser.id); + + // Log task creation event + try { + await TaskEventService.logTaskCreated( + task.id, + req.currentUser.id, + { + name: task.name, + status: task.status, + priority: task.priority, + due_date: task.due_date, + project_id: task.project_id, + }, + { source: 'web' } + ); + } catch (eventError) { + console.error('Error logging task creation event:', eventError); + // Don't fail the request if event logging fails + } + + // Reload task with associations + const taskWithAssociations = await Task.findByPk(task.id, { + include: [ + { + model: Tag, + attributes: ['name'], + through: { attributes: [] }, + }, + { model: Project, attributes: ['name'], required: false }, + ], + }); + + const taskJson = taskWithAssociations.toJSON(); + + res.status(201).json({ + ...taskJson, + tags: taskJson.Tags || [], + due_date: taskWithAssociations.due_date + ? taskWithAssociations.due_date.toISOString().split('T')[0] + : null, + }); + } catch (error) { + console.error('Error creating task:', error); + res.status(400).json({ + error: 'There was a problem creating the task.', + details: error.errors + ? error.errors.map((e) => e.message) + : [error.message], + }); } - - // Reload task with associations - const taskWithAssociations = await Task.findByPk(task.id, { - include: [ - { model: Tag, attributes: ['name'], through: { attributes: [] } }, - { model: Project, attributes: ['name'], required: false } - ] - }); - - const taskJson = taskWithAssociations.toJSON(); - - res.status(201).json({ - ...taskJson, - tags: taskJson.Tags || [], - due_date: taskWithAssociations.due_date ? taskWithAssociations.due_date.toISOString().split('T')[0] : null - }); - } catch (error) { - console.error('Error creating task:', error); - res.status(400).json({ - error: 'There was a problem creating the task.', - details: error.errors ? error.errors.map(e => e.message) : [error.message] - }); - } }); // PATCH /api/task/:id router.patch('/task/:id', async (req, res) => { - try { - const { - name, - priority, - status, - note, - due_date, - project_id, - tags, - Tags, - today, - recurrence_type, - recurrence_interval, - recurrence_end_date, - recurrence_weekday, - recurrence_month_day, - recurrence_week_of_month, - completion_based, - update_parent_recurrence - } = req.body; - - // Handle both tags and Tags (Sequelize association format) - const tagsData = tags || Tags; - - const task = await Task.findOne({ - where: { id: req.params.id, user_id: req.currentUser.id }, - include: [ - { model: Tag, attributes: ['id', 'name'], through: { attributes: [] } } - ] - }); - - if (!task) { - return res.status(404).json({ error: 'Task not found.' }); - } - - // Capture old values for event logging - const oldValues = { - name: task.name, - status: task.status, - priority: task.priority, - due_date: task.due_date, - project_id: task.project_id, - note: task.note, - recurrence_type: task.recurrence_type, - recurrence_interval: task.recurrence_interval, - recurrence_end_date: task.recurrence_end_date, - recurrence_weekday: task.recurrence_weekday, - recurrence_month_day: task.recurrence_month_day, - recurrence_week_of_month: task.recurrence_week_of_month, - completion_based: task.completion_based, - tags: task.Tags ? task.Tags.map(tag => ({ id: tag.id, name: tag.name })) : [] - }; - - // Handle updating parent recurrence settings if this is a child task - if (update_parent_recurrence && task.recurring_parent_id) { - const parentTask = await Task.findOne({ - where: { id: task.recurring_parent_id, user_id: req.currentUser.id } - }); - - if (parentTask) { - await parentTask.update({ - recurrence_type: recurrence_type !== undefined ? recurrence_type : parentTask.recurrence_type, - recurrence_interval: recurrence_interval !== undefined ? recurrence_interval : parentTask.recurrence_interval, - recurrence_end_date: recurrence_end_date !== undefined ? recurrence_end_date : parentTask.recurrence_end_date, - recurrence_weekday: recurrence_weekday !== undefined ? recurrence_weekday : parentTask.recurrence_weekday, - recurrence_month_day: recurrence_month_day !== undefined ? recurrence_month_day : parentTask.recurrence_month_day, - recurrence_week_of_month: recurrence_week_of_month !== undefined ? recurrence_week_of_month : parentTask.recurrence_week_of_month, - completion_based: completion_based !== undefined ? completion_based : parentTask.completion_based - }); - console.log(`Updated parent task ${parentTask.id} recurrence settings from child task ${task.id}`); - } - } - - const taskAttributes = { - name, - priority: priority !== undefined ? (typeof priority === 'string' ? Task.getPriorityValue(priority) : priority) : undefined, - status: status !== undefined ? (typeof status === 'string' ? Task.getStatusValue(status) : status) : Task.STATUS.NOT_STARTED, - note, - due_date: due_date || null, - today: today !== undefined ? today : task.today, - recurrence_type: recurrence_type !== undefined ? recurrence_type : task.recurrence_type, - recurrence_interval: recurrence_interval !== undefined ? recurrence_interval : task.recurrence_interval, - recurrence_end_date: recurrence_end_date !== undefined ? recurrence_end_date : task.recurrence_end_date, - recurrence_weekday: recurrence_weekday !== undefined ? recurrence_weekday : task.recurrence_weekday, - recurrence_month_day: recurrence_month_day !== undefined ? recurrence_month_day : task.recurrence_month_day, - recurrence_week_of_month: recurrence_week_of_month !== undefined ? recurrence_week_of_month : task.recurrence_week_of_month, - completion_based: completion_based !== undefined ? completion_based : task.completion_based - }; - - // Set completed_at when task is marked as done - if (status !== undefined) { - const newStatus = typeof status === 'string' ? Task.getStatusValue(status) : status; - const oldStatus = typeof task.status === 'string' ? Task.getStatusValue(task.status) : task.status; - - if (newStatus === Task.STATUS.DONE && oldStatus !== Task.STATUS.DONE) { - // Task is being completed - taskAttributes.completed_at = new Date(); - } else if (newStatus !== Task.STATUS.DONE && oldStatus === Task.STATUS.DONE) { - // Task is being uncompleted - taskAttributes.completed_at = null; - } - } - - // Handle project assignment - if (project_id && project_id.toString().trim()) { - const project = await Project.findOne({ - where: { id: project_id, user_id: req.currentUser.id } - }); - if (!project) { - return res.status(400).json({ error: 'Invalid project.' }); - } - taskAttributes.project_id = project_id; - } else { - taskAttributes.project_id = null; - } - - await task.update(taskAttributes); - await updateTaskTags(task, tagsData, req.currentUser.id); - - // Log task update events try { - const changes = {}; - - // Check for changes in each field - if (name !== undefined && name !== oldValues.name) { - changes.name = { oldValue: oldValues.name, newValue: name }; - } - if (status !== undefined && status !== oldValues.status) { - changes.status = { oldValue: oldValues.status, newValue: status }; - } - if (priority !== undefined && priority !== oldValues.priority) { - changes.priority = { oldValue: oldValues.priority, newValue: priority }; - } - if (due_date !== undefined) { - // Normalize dates for comparison (convert to YYYY-MM-DD format) - const oldDateStr = oldValues.due_date ? oldValues.due_date.toISOString().split('T')[0] : null; - const newDateStr = due_date || null; - - if (oldDateStr !== newDateStr) { - changes.due_date = { oldValue: oldValues.due_date, newValue: due_date }; - } - } - if (project_id !== undefined && project_id !== oldValues.project_id) { - changes.project_id = { oldValue: oldValues.project_id, newValue: project_id }; - } - if (note !== undefined && note !== oldValues.note) { - changes.note = { oldValue: oldValues.note, newValue: note }; - } - - // Check recurrence field changes - if (recurrence_type !== undefined && recurrence_type !== oldValues.recurrence_type) { - changes.recurrence_type = { oldValue: oldValues.recurrence_type, newValue: recurrence_type }; - } - if (recurrence_interval !== undefined && recurrence_interval !== oldValues.recurrence_interval) { - changes.recurrence_interval = { oldValue: oldValues.recurrence_interval, newValue: recurrence_interval }; - } - if (recurrence_end_date !== undefined && recurrence_end_date !== oldValues.recurrence_end_date) { - changes.recurrence_end_date = { oldValue: oldValues.recurrence_end_date, newValue: recurrence_end_date }; - } - if (recurrence_weekday !== undefined && recurrence_weekday !== oldValues.recurrence_weekday) { - changes.recurrence_weekday = { oldValue: oldValues.recurrence_weekday, newValue: recurrence_weekday }; - } - if (recurrence_month_day !== undefined && recurrence_month_day !== oldValues.recurrence_month_day) { - changes.recurrence_month_day = { oldValue: oldValues.recurrence_month_day, newValue: recurrence_month_day }; - } - if (recurrence_week_of_month !== undefined && recurrence_week_of_month !== oldValues.recurrence_week_of_month) { - changes.recurrence_week_of_month = { oldValue: oldValues.recurrence_week_of_month, newValue: recurrence_week_of_month }; - } - if (completion_based !== undefined && completion_based !== oldValues.completion_based) { - changes.completion_based = { oldValue: oldValues.completion_based, newValue: completion_based }; - } + const { + name, + priority, + status, + note, + due_date, + project_id, + tags, + Tags, + today, + recurrence_type, + recurrence_interval, + recurrence_end_date, + recurrence_weekday, + recurrence_month_day, + recurrence_week_of_month, + completion_based, + update_parent_recurrence, + } = req.body; - // Log all changes - if (Object.keys(changes).length > 0) { - await TaskEventService.logTaskUpdate(task.id, req.currentUser.id, changes, { source: 'web' }); - } + // Handle both tags and Tags (Sequelize association format) + const tagsData = tags || Tags; - // Check for tag changes (this is more complex due to the array comparison) - if (tagsData) { - const newTags = tagsData.map(tag => ({ id: tag.id, name: tag.name })); - const oldTagNames = oldValues.tags.map(tag => tag.name).sort(); - const newTagNames = newTags.map(tag => tag.name).sort(); - - if (JSON.stringify(oldTagNames) !== JSON.stringify(newTagNames)) { - await TaskEventService.logEvent({ - taskId: task.id, - userId: req.currentUser.id, - eventType: 'tags_changed', - fieldName: 'tags', - oldValue: oldValues.tags, - newValue: newTags, - metadata: { source: 'web', action: 'tags_update' } - }); + const task = await Task.findOne({ + where: { id: req.params.id, user_id: req.currentUser.id }, + include: [ + { + model: Tag, + attributes: ['id', 'name'], + through: { attributes: [] }, + }, + ], + }); + + if (!task) { + return res.status(404).json({ error: 'Task not found.' }); } - } - } catch (eventError) { - console.error('Error logging task update events:', eventError); - // Don't fail the request if event logging fails + + // Capture old values for event logging + const oldValues = { + name: task.name, + status: task.status, + priority: task.priority, + due_date: task.due_date, + project_id: task.project_id, + note: task.note, + recurrence_type: task.recurrence_type, + recurrence_interval: task.recurrence_interval, + recurrence_end_date: task.recurrence_end_date, + recurrence_weekday: task.recurrence_weekday, + recurrence_month_day: task.recurrence_month_day, + recurrence_week_of_month: task.recurrence_week_of_month, + completion_based: task.completion_based, + tags: task.Tags + ? task.Tags.map((tag) => ({ id: tag.id, name: tag.name })) + : [], + }; + + // Handle updating parent recurrence settings if this is a child task + if (update_parent_recurrence && task.recurring_parent_id) { + const parentTask = await Task.findOne({ + where: { + id: task.recurring_parent_id, + user_id: req.currentUser.id, + }, + }); + + if (parentTask) { + await parentTask.update({ + recurrence_type: + recurrence_type !== undefined + ? recurrence_type + : parentTask.recurrence_type, + recurrence_interval: + recurrence_interval !== undefined + ? recurrence_interval + : parentTask.recurrence_interval, + recurrence_end_date: + recurrence_end_date !== undefined + ? recurrence_end_date + : parentTask.recurrence_end_date, + recurrence_weekday: + recurrence_weekday !== undefined + ? recurrence_weekday + : parentTask.recurrence_weekday, + recurrence_month_day: + recurrence_month_day !== undefined + ? recurrence_month_day + : parentTask.recurrence_month_day, + recurrence_week_of_month: + recurrence_week_of_month !== undefined + ? recurrence_week_of_month + : parentTask.recurrence_week_of_month, + completion_based: + completion_based !== undefined + ? completion_based + : parentTask.completion_based, + }); + console.log( + `Updated parent task ${parentTask.id} recurrence settings from child task ${task.id}` + ); + } + } + + const taskAttributes = { + name, + priority: + priority !== undefined + ? typeof priority === 'string' + ? Task.getPriorityValue(priority) + : priority + : undefined, + status: + status !== undefined + ? typeof status === 'string' + ? Task.getStatusValue(status) + : status + : Task.STATUS.NOT_STARTED, + note, + due_date: due_date || null, + today: today !== undefined ? today : task.today, + recurrence_type: + recurrence_type !== undefined + ? recurrence_type + : task.recurrence_type, + recurrence_interval: + recurrence_interval !== undefined + ? recurrence_interval + : task.recurrence_interval, + recurrence_end_date: + recurrence_end_date !== undefined + ? recurrence_end_date + : task.recurrence_end_date, + recurrence_weekday: + recurrence_weekday !== undefined + ? recurrence_weekday + : task.recurrence_weekday, + recurrence_month_day: + recurrence_month_day !== undefined + ? recurrence_month_day + : task.recurrence_month_day, + recurrence_week_of_month: + recurrence_week_of_month !== undefined + ? recurrence_week_of_month + : task.recurrence_week_of_month, + completion_based: + completion_based !== undefined + ? completion_based + : task.completion_based, + }; + + // Set completed_at when task is marked as done + if (status !== undefined) { + const newStatus = + typeof status === 'string' + ? Task.getStatusValue(status) + : status; + const oldStatus = + typeof task.status === 'string' + ? Task.getStatusValue(task.status) + : task.status; + + if ( + newStatus === Task.STATUS.DONE && + oldStatus !== Task.STATUS.DONE + ) { + // Task is being completed + taskAttributes.completed_at = new Date(); + } else if ( + newStatus !== Task.STATUS.DONE && + oldStatus === Task.STATUS.DONE + ) { + // Task is being uncompleted + taskAttributes.completed_at = null; + } + } + + // Handle project assignment + if (project_id && project_id.toString().trim()) { + const project = await Project.findOne({ + where: { id: project_id, user_id: req.currentUser.id }, + }); + if (!project) { + return res.status(400).json({ error: 'Invalid project.' }); + } + taskAttributes.project_id = project_id; + } else { + taskAttributes.project_id = null; + } + + await task.update(taskAttributes); + await updateTaskTags(task, tagsData, req.currentUser.id); + + // Log task update events + try { + const changes = {}; + + // Check for changes in each field + if (name !== undefined && name !== oldValues.name) { + changes.name = { oldValue: oldValues.name, newValue: name }; + } + if (status !== undefined && status !== oldValues.status) { + changes.status = { + oldValue: oldValues.status, + newValue: status, + }; + } + if (priority !== undefined && priority !== oldValues.priority) { + changes.priority = { + oldValue: oldValues.priority, + newValue: priority, + }; + } + if (due_date !== undefined) { + // Normalize dates for comparison (convert to YYYY-MM-DD format) + const oldDateStr = oldValues.due_date + ? oldValues.due_date.toISOString().split('T')[0] + : null; + const newDateStr = due_date || null; + + if (oldDateStr !== newDateStr) { + changes.due_date = { + oldValue: oldValues.due_date, + newValue: due_date, + }; + } + } + if ( + project_id !== undefined && + project_id !== oldValues.project_id + ) { + changes.project_id = { + oldValue: oldValues.project_id, + newValue: project_id, + }; + } + if (note !== undefined && note !== oldValues.note) { + changes.note = { oldValue: oldValues.note, newValue: note }; + } + + // Check recurrence field changes + if ( + recurrence_type !== undefined && + recurrence_type !== oldValues.recurrence_type + ) { + changes.recurrence_type = { + oldValue: oldValues.recurrence_type, + newValue: recurrence_type, + }; + } + if ( + recurrence_interval !== undefined && + recurrence_interval !== oldValues.recurrence_interval + ) { + changes.recurrence_interval = { + oldValue: oldValues.recurrence_interval, + newValue: recurrence_interval, + }; + } + if ( + recurrence_end_date !== undefined && + recurrence_end_date !== oldValues.recurrence_end_date + ) { + changes.recurrence_end_date = { + oldValue: oldValues.recurrence_end_date, + newValue: recurrence_end_date, + }; + } + if ( + recurrence_weekday !== undefined && + recurrence_weekday !== oldValues.recurrence_weekday + ) { + changes.recurrence_weekday = { + oldValue: oldValues.recurrence_weekday, + newValue: recurrence_weekday, + }; + } + if ( + recurrence_month_day !== undefined && + recurrence_month_day !== oldValues.recurrence_month_day + ) { + changes.recurrence_month_day = { + oldValue: oldValues.recurrence_month_day, + newValue: recurrence_month_day, + }; + } + if ( + recurrence_week_of_month !== undefined && + recurrence_week_of_month !== oldValues.recurrence_week_of_month + ) { + changes.recurrence_week_of_month = { + oldValue: oldValues.recurrence_week_of_month, + newValue: recurrence_week_of_month, + }; + } + if ( + completion_based !== undefined && + completion_based !== oldValues.completion_based + ) { + changes.completion_based = { + oldValue: oldValues.completion_based, + newValue: completion_based, + }; + } + + // Log all changes + if (Object.keys(changes).length > 0) { + await TaskEventService.logTaskUpdate( + task.id, + req.currentUser.id, + changes, + { source: 'web' } + ); + } + + // Check for tag changes (this is more complex due to the array comparison) + if (tagsData) { + const newTags = tagsData.map((tag) => ({ + id: tag.id, + name: tag.name, + })); + const oldTagNames = oldValues.tags + .map((tag) => tag.name) + .sort(); + const newTagNames = newTags.map((tag) => tag.name).sort(); + + if ( + JSON.stringify(oldTagNames) !== JSON.stringify(newTagNames) + ) { + await TaskEventService.logEvent({ + taskId: task.id, + userId: req.currentUser.id, + eventType: 'tags_changed', + fieldName: 'tags', + oldValue: oldValues.tags, + newValue: newTags, + metadata: { source: 'web', action: 'tags_update' }, + }); + } + } + } catch (eventError) { + console.error('Error logging task update events:', eventError); + // Don't fail the request if event logging fails + } + + // Reload task with associations + const taskWithAssociations = await Task.findByPk(task.id, { + include: [ + { + model: Tag, + attributes: ['id', 'name'], + through: { attributes: [] }, + }, + { model: Project, attributes: ['name'], required: false }, + ], + }); + + const taskJson = taskWithAssociations.toJSON(); + + res.json({ + ...taskJson, + tags: taskJson.Tags || [], // Normalize Tags to tags + due_date: taskWithAssociations.due_date + ? taskWithAssociations.due_date.toISOString().split('T')[0] + : null, + }); + } catch (error) { + console.error('Error updating task:', error); + res.status(400).json({ + error: 'There was a problem updating the task.', + details: error.errors + ? error.errors.map((e) => e.message) + : [error.message], + }); } - - // Reload task with associations - const taskWithAssociations = await Task.findByPk(task.id, { - include: [ - { model: Tag, attributes: ['id', 'name'], through: { attributes: [] } }, - { model: Project, attributes: ['name'], required: false } - ] - }); - - const taskJson = taskWithAssociations.toJSON(); - - res.json({ - ...taskJson, - tags: taskJson.Tags || [], // Normalize Tags to tags - due_date: taskWithAssociations.due_date ? taskWithAssociations.due_date.toISOString().split('T')[0] : null - }); - } catch (error) { - console.error('Error updating task:', error); - res.status(400).json({ - error: 'There was a problem updating the task.', - details: error.errors ? error.errors.map(e => e.message) : [error.message] - }); - } }); // PATCH /api/task/:id/toggle_completion router.patch('/task/:id/toggle_completion', async (req, res) => { - try { - const task = await Task.findOne({ - where: { id: req.params.id, user_id: req.currentUser.id } - }); + try { + const task = await Task.findOne({ + where: { id: req.params.id, user_id: req.currentUser.id }, + }); - if (!task) { - return res.status(404).json({ error: 'Task not found.' }); + if (!task) { + return res.status(404).json({ error: 'Task not found.' }); + } + + console.log('🎯 Toggle completion called for task:', { + id: task.id, + name: task.name, + currentStatus: task.status, + recurrence_type: task.recurrence_type, + completion_based: task.completion_based, + }); + + const newStatus = + task.status === Task.STATUS.DONE || task.status === 'done' + ? task.note + ? Task.STATUS.IN_PROGRESS + : Task.STATUS.NOT_STARTED + : Task.STATUS.DONE; + + console.log('📝 Status changing from', task.status, 'to', newStatus); + + // Set completed_at when task is completed/uncompleted + const updateData = { status: newStatus }; + if (newStatus === Task.STATUS.DONE) { + updateData.completed_at = new Date(); + } else if (task.status === Task.STATUS.DONE || task.status === 'done') { + updateData.completed_at = null; + } + + await task.update(updateData); + + // Handle recurring task completion + let nextTask = null; + if (newStatus === Task.STATUS.DONE || newStatus === 'done') { + console.log( + '✅ Task marked as done, calling RecurringTaskService...' + ); + nextTask = await RecurringTaskService.handleTaskCompletion(task); + } else { + console.log( + '❌ Task not marked as done, skipping RecurringTaskService' + ); + } + + const response = { + ...task.toJSON(), + due_date: task.due_date + ? task.due_date.toISOString().split('T')[0] + : null, + }; + + if (nextTask) { + response.next_task = { + ...nextTask.toJSON(), + due_date: nextTask.due_date + ? nextTask.due_date.toISOString().split('T')[0] + : null, + }; + } + + res.json(response); + } catch (error) { + console.error('Error toggling task completion:', error); + res.status(422).json({ error: 'Unable to update task' }); } - - console.log('🎯 Toggle completion called for task:', { - id: task.id, - name: task.name, - currentStatus: task.status, - recurrence_type: task.recurrence_type, - completion_based: task.completion_based - }); - - const newStatus = (task.status === Task.STATUS.DONE || task.status === 'done') - ? (task.note ? Task.STATUS.IN_PROGRESS : Task.STATUS.NOT_STARTED) - : Task.STATUS.DONE; - - console.log('📝 Status changing from', task.status, 'to', newStatus); - - // Set completed_at when task is completed/uncompleted - const updateData = { status: newStatus }; - if (newStatus === Task.STATUS.DONE) { - updateData.completed_at = new Date(); - } else if (task.status === Task.STATUS.DONE || task.status === 'done') { - updateData.completed_at = null; - } - - await task.update(updateData); - - // Handle recurring task completion - let nextTask = null; - if (newStatus === Task.STATUS.DONE || newStatus === 'done') { - console.log('✅ Task marked as done, calling RecurringTaskService...'); - nextTask = await RecurringTaskService.handleTaskCompletion(task); - } else { - console.log('❌ Task not marked as done, skipping RecurringTaskService'); - } - - const response = { - ...task.toJSON(), - due_date: task.due_date ? task.due_date.toISOString().split('T')[0] : null - }; - - if (nextTask) { - response.next_task = { - ...nextTask.toJSON(), - due_date: nextTask.due_date ? nextTask.due_date.toISOString().split('T')[0] : null - }; - } - - res.json(response); - } catch (error) { - console.error('Error toggling task completion:', error); - res.status(422).json({ error: 'Unable to update task' }); - } }); // DELETE /api/task/:id router.delete('/task/:id', async (req, res) => { - try { - const task = await Task.findOne({ - where: { id: req.params.id, user_id: req.currentUser.id } - }); - - if (!task) { - return res.status(404).json({ error: 'Task not found.' }); - } - - console.log(`Attempting to delete task ${req.params.id}`); - - // Check for child tasks - prevent deletion of parent tasks with children - const childTasks = await Task.findAll({ - where: { recurring_parent_id: req.params.id } - }); - console.log(`Found ${childTasks.length} child tasks`); - - // If this is a recurring parent task with children, prevent deletion - if (childTasks.length > 0) { - console.log(`Cannot delete task ${req.params.id} - has ${childTasks.length} child tasks`); - return res.status(400).json({ error: 'There was a problem deleting the task.' }); - } - - const taskEvents = await TaskEvent.findAll({ - where: { task_id: req.params.id } - }); - console.log(`Found ${taskEvents.length} task events`); - - const tagAssociations = await sequelize.query( - 'SELECT COUNT(*) as count FROM tasks_tags WHERE task_id = ?', - { replacements: [req.params.id], type: sequelize.QueryTypes.SELECT } - ); - console.log(`Found ${tagAssociations[0].count} tag associations`); - - // Check SQLite foreign key list for tasks table - const foreignKeys = await sequelize.query( - 'PRAGMA foreign_key_list(tasks)', - { type: sequelize.QueryTypes.SELECT } - ); - console.log('Foreign keys in tasks table:', foreignKeys); - - // Find all tables that reference tasks - const allTables = await sequelize.query( - "SELECT name FROM sqlite_master WHERE type='table'", - { type: sequelize.QueryTypes.SELECT } - ); - - for (const table of allTables) { - if (table.name !== 'tasks') { - try { - const fks = await sequelize.query( - `PRAGMA foreign_key_list(${table.name})`, - { type: sequelize.QueryTypes.SELECT } - ); - const taskRefs = fks.filter(fk => fk.table === 'tasks'); - if (taskRefs.length > 0) { - console.log(`Table ${table.name} references tasks:`, taskRefs); - // Check if this table has any records referencing our task - for (const fk of taskRefs) { - const count = await sequelize.query( - `SELECT COUNT(*) as count FROM ${table.name} WHERE ${fk.from} = ?`, - { replacements: [req.params.id], type: sequelize.QueryTypes.SELECT } - ); - console.log(` ${table.name}.${fk.from} -> tasks.${fk.to}: ${count[0].count} references`); - } - } - } catch (error) { - // Skip tables that might not exist or have issues - } - } - } - - // Temporarily disable foreign key constraints for this operation - await sequelize.query('PRAGMA foreign_keys = OFF'); - try { - // Use force delete to bypass foreign key constraints - await TaskEvent.destroy({ - where: { task_id: req.params.id }, - force: true - }); + const task = await Task.findOne({ + where: { id: req.params.id, user_id: req.currentUser.id }, + }); - await sequelize.query( - 'DELETE FROM tasks_tags WHERE task_id = ?', - { replacements: [req.params.id] } - ); + if (!task) { + return res.status(404).json({ error: 'Task not found.' }); + } - await Task.update( - { recurring_parent_id: null }, - { where: { recurring_parent_id: req.params.id } } - ); + console.log(`Attempting to delete task ${req.params.id}`); - // Delete the task itself - await task.destroy({ force: true }); - - } finally { - // Re-enable foreign key constraints - await sequelize.query('PRAGMA foreign_keys = ON'); + // Check for child tasks - prevent deletion of parent tasks with children + const childTasks = await Task.findAll({ + where: { recurring_parent_id: req.params.id }, + }); + console.log(`Found ${childTasks.length} child tasks`); + + // If this is a recurring parent task with children, prevent deletion + if (childTasks.length > 0) { + console.log( + `Cannot delete task ${req.params.id} - has ${childTasks.length} child tasks` + ); + return res + .status(400) + .json({ error: 'There was a problem deleting the task.' }); + } + + const taskEvents = await TaskEvent.findAll({ + where: { task_id: req.params.id }, + }); + console.log(`Found ${taskEvents.length} task events`); + + const tagAssociations = await sequelize.query( + 'SELECT COUNT(*) as count FROM tasks_tags WHERE task_id = ?', + { replacements: [req.params.id], type: sequelize.QueryTypes.SELECT } + ); + console.log(`Found ${tagAssociations[0].count} tag associations`); + + // Check SQLite foreign key list for tasks table + const foreignKeys = await sequelize.query( + 'PRAGMA foreign_key_list(tasks)', + { type: sequelize.QueryTypes.SELECT } + ); + console.log('Foreign keys in tasks table:', foreignKeys); + + // Find all tables that reference tasks + const allTables = await sequelize.query( + "SELECT name FROM sqlite_master WHERE type='table'", + { type: sequelize.QueryTypes.SELECT } + ); + + for (const table of allTables) { + if (table.name !== 'tasks') { + try { + const fks = await sequelize.query( + `PRAGMA foreign_key_list(${table.name})`, + { type: sequelize.QueryTypes.SELECT } + ); + const taskRefs = fks.filter((fk) => fk.table === 'tasks'); + if (taskRefs.length > 0) { + console.log( + `Table ${table.name} references tasks:`, + taskRefs + ); + // Check if this table has any records referencing our task + for (const fk of taskRefs) { + const count = await sequelize.query( + `SELECT COUNT(*) as count FROM ${table.name} WHERE ${fk.from} = ?`, + { + replacements: [req.params.id], + type: sequelize.QueryTypes.SELECT, + } + ); + console.log( + ` ${table.name}.${fk.from} -> tasks.${fk.to}: ${count[0].count} references` + ); + } + } + } catch (error) { + // Skip tables that might not exist or have issues + } + } + } + + // Temporarily disable foreign key constraints for this operation + await sequelize.query('PRAGMA foreign_keys = OFF'); + + try { + // Use force delete to bypass foreign key constraints + await TaskEvent.destroy({ + where: { task_id: req.params.id }, + force: true, + }); + + await sequelize.query('DELETE FROM tasks_tags WHERE task_id = ?', { + replacements: [req.params.id], + }); + + await Task.update( + { recurring_parent_id: null }, + { where: { recurring_parent_id: req.params.id } } + ); + + // Delete the task itself + await task.destroy({ force: true }); + } finally { + // Re-enable foreign key constraints + await sequelize.query('PRAGMA foreign_keys = ON'); + } + + console.log(`Successfully deleted task ${req.params.id}`); + res.json({ message: 'Task successfully deleted' }); + } catch (error) { + console.error('Error deleting task:', error); + res.status(400).json({ + error: 'There was a problem deleting the task.', + }); } - - console.log(`Successfully deleted task ${req.params.id}`); - res.json({ message: 'Task successfully deleted' }); - } catch (error) { - console.error('Error deleting task:', error); - res.status(400).json({ error: 'There was a problem deleting the task.' }); - } }); // POST /api/tasks/generate-recurring router.post('/tasks/generate-recurring', async (req, res) => { - try { - const newTasks = await RecurringTaskService.generateRecurringTasks(req.currentUser.id); - - res.json({ - message: `Generated ${newTasks.length} recurring tasks`, - tasks: newTasks.map(task => ({ - ...task.toJSON(), - due_date: task.due_date ? task.due_date.toISOString().split('T')[0] : null - })) - }); - } catch (error) { - console.error('Error generating recurring tasks:', error); - res.status(500).json({ error: 'Failed to generate recurring tasks' }); - } + try { + const newTasks = await RecurringTaskService.generateRecurringTasks( + req.currentUser.id + ); + + res.json({ + message: `Generated ${newTasks.length} recurring tasks`, + tasks: newTasks.map((task) => ({ + ...task.toJSON(), + due_date: task.due_date + ? task.due_date.toISOString().split('T')[0] + : null, + })), + }); + } catch (error) { + console.error('Error generating recurring tasks:', error); + res.status(500).json({ error: 'Failed to generate recurring tasks' }); + } }); // PATCH /api/task/:id/toggle-today router.patch('/task/:id/toggle-today', async (req, res) => { - try { - const task = await Task.findOne({ - where: { id: req.params.id, user_id: req.currentUser.id } - }); - - if (!task) { - return res.status(404).json({ error: 'Task not found.' }); - } - - // Toggle the today flag - const newTodayValue = !task.today; - await task.update({ today: newTodayValue }); - - // Log the change try { - await TaskEventService.logEvent({ - taskId: task.id, - userId: req.currentUser.id, - eventType: 'today_changed', - fieldName: 'today', - oldValue: !newTodayValue, - newValue: newTodayValue, - metadata: { source: 'web', action: 'toggle_today' } - }); - } catch (eventError) { - console.error('Error logging today toggle event:', eventError); - // Don't fail the request if event logging fails - } + const task = await Task.findOne({ + where: { id: req.params.id, user_id: req.currentUser.id }, + }); - res.json({ - ...task.toJSON(), - due_date: task.due_date ? task.due_date.toISOString().split('T')[0] : null - }); - } catch (error) { - console.error('Error toggling task today flag:', error); - res.status(500).json({ error: 'Failed to update task today flag' }); - } + if (!task) { + return res.status(404).json({ error: 'Task not found.' }); + } + + // Toggle the today flag + const newTodayValue = !task.today; + await task.update({ today: newTodayValue }); + + // Log the change + try { + await TaskEventService.logEvent({ + taskId: task.id, + userId: req.currentUser.id, + eventType: 'today_changed', + fieldName: 'today', + oldValue: !newTodayValue, + newValue: newTodayValue, + metadata: { source: 'web', action: 'toggle_today' }, + }); + } catch (eventError) { + console.error('Error logging today toggle event:', eventError); + // Don't fail the request if event logging fails + } + + res.json({ + ...task.toJSON(), + due_date: task.due_date + ? task.due_date.toISOString().split('T')[0] + : null, + }); + } catch (error) { + console.error('Error toggling task today flag:', error); + res.status(500).json({ error: 'Failed to update task today flag' }); + } }); -module.exports = router; \ No newline at end of file +module.exports = router; diff --git a/backend/routes/telegram.js b/backend/routes/telegram.js index 4273cfa..c6ca3e4 100644 --- a/backend/routes/telegram.js +++ b/backend/routes/telegram.js @@ -5,105 +5,113 @@ const router = express.Router(); // POST /api/telegram/start-polling router.post('/telegram/start-polling', async (req, res) => { - try { - if (!req.session || !req.session.userId) { - return res.status(401).json({ error: 'Authentication required' }); - } + try { + if (!req.session || !req.session.userId) { + return res.status(401).json({ error: 'Authentication required' }); + } - const user = await User.findByPk(req.session.userId); - if (!user || !user.telegram_bot_token) { - return res.status(400).json({ error: 'Telegram bot token not set.' }); - } + const user = await User.findByPk(req.session.userId); + if (!user || !user.telegram_bot_token) { + return res + .status(400) + .json({ error: 'Telegram bot token not set.' }); + } - const success = await telegramPoller.addUser(user); + const success = await telegramPoller.addUser(user); - if (success) { - res.json({ - success: true, - message: 'Telegram polling started', - status: telegramPoller.getStatus() - }); - } else { - res.status(500).json({ error: 'Failed to start Telegram polling.' }); + if (success) { + res.json({ + success: true, + message: 'Telegram polling started', + status: telegramPoller.getStatus(), + }); + } else { + res.status(500).json({ + error: 'Failed to start Telegram polling.', + }); + } + } catch (error) { + console.error('Error starting Telegram polling:', error); + res.status(500).json({ error: 'Failed to start Telegram polling.' }); } - } catch (error) { - console.error('Error starting Telegram polling:', error); - res.status(500).json({ error: 'Failed to start Telegram polling.' }); - } }); // POST /api/telegram/stop-polling router.post('/telegram/stop-polling', async (req, res) => { - try { - if (!req.session || !req.session.userId) { - return res.status(401).json({ error: 'Authentication required' }); + try { + if (!req.session || !req.session.userId) { + return res.status(401).json({ error: 'Authentication required' }); + } + + const success = telegramPoller.removeUser(req.session.userId); + + res.json({ + success: true, + message: 'Telegram polling stopped', + status: telegramPoller.getStatus(), + }); + } catch (error) { + console.error('Error stopping Telegram polling:', error); + res.status(500).json({ error: 'Failed to stop Telegram polling.' }); } - - const success = telegramPoller.removeUser(req.session.userId); - - res.json({ - success: true, - message: 'Telegram polling stopped', - status: telegramPoller.getStatus() - }); - } catch (error) { - console.error('Error stopping Telegram polling:', error); - res.status(500).json({ error: 'Failed to stop Telegram polling.' }); - } }); // GET /api/telegram/polling-status router.get('/telegram/polling-status', async (req, res) => { - try { - if (!req.session || !req.session.userId) { - return res.status(401).json({ error: 'Authentication required' }); - } + try { + if (!req.session || !req.session.userId) { + return res.status(401).json({ error: 'Authentication required' }); + } - res.json({ - success: true, - status: telegramPoller.getStatus() - }); - } catch (error) { - console.error('Error getting Telegram polling status:', error); - res.status(500).json({ error: 'Internal server error' }); - } + res.json({ + success: true, + status: telegramPoller.getStatus(), + }); + } catch (error) { + console.error('Error getting Telegram polling status:', error); + res.status(500).json({ error: 'Internal server error' }); + } }); // POST /api/telegram/setup router.post('/telegram/setup', async (req, res) => { - try { - if (!req.session || !req.session.userId) { - return res.status(401).json({ error: 'Authentication required' }); + try { + if (!req.session || !req.session.userId) { + return res.status(401).json({ error: 'Authentication required' }); + } + + const { token } = req.body; + + if (!token) { + return res + .status(400) + .json({ error: 'Telegram bot token is required.' }); + } + + const user = await User.findByPk(req.session.userId); + if (!user) { + return res.status(404).json({ error: 'User not found.' }); + } + + // Basic token validation - check if it looks like a Telegram bot token + if (!/^\d+:[A-Za-z0-9_-]{35}$/.test(token)) { + return res + .status(400) + .json({ error: 'Invalid Telegram bot token format.' }); + } + + // Update user's telegram bot token + await user.update({ telegram_bot_token: token }); + + res.json({ + success: true, + message: 'Telegram bot token updated successfully', + token: token, + }); + } catch (error) { + console.error('Error setting up Telegram:', error); + res.status(500).json({ error: 'Internal server error' }); } - - const { token } = req.body; - - if (!token) { - return res.status(400).json({ error: 'Telegram bot token is required.' }); - } - - const user = await User.findByPk(req.session.userId); - if (!user) { - return res.status(404).json({ error: 'User not found.' }); - } - - // Basic token validation - check if it looks like a Telegram bot token - if (!/^\d+:[A-Za-z0-9_-]{35}$/.test(token)) { - return res.status(400).json({ error: 'Invalid Telegram bot token format.' }); - } - - // Update user's telegram bot token - await user.update({ telegram_bot_token: token }); - - res.json({ - success: true, - message: 'Telegram bot token updated successfully', - token: token - }); - } catch (error) { - console.error('Error setting up Telegram:', error); - res.status(500).json({ error: 'Internal server error' }); - } }); -module.exports = router; \ No newline at end of file +module.exports = router; diff --git a/backend/routes/url.js b/backend/routes/url.js index 2e7f1e8..03825d2 100644 --- a/backend/routes/url.js +++ b/backend/routes/url.js @@ -6,335 +6,378 @@ const router = express.Router(); // Fast regex-based metadata extraction (much faster than cheerio for head content) function extractMetadataFromHtml(html) { - try { - // Extract title with priority: og:title > twitter:title > title tag - let title = null; - - // Try og:title first - const ogTitleMatch = html.match(/]+property=["']og:title["'][^>]+content=["']([^"']+)["']/i); - if (ogTitleMatch) { - title = ogTitleMatch[1]; - } else { - // Try twitter:title - const twitterTitleMatch = html.match(/]+name=["']twitter:title["'][^>]+content=["']([^"']+)["']/i); - if (twitterTitleMatch) { - title = twitterTitleMatch[1]; - } else { - // Fallback to title tag - const titleMatch = html.match(/]*>([^<]+)<\/title>/i); - if (titleMatch) { - title = titleMatch[1].trim(); + try { + // Extract title with priority: og:title > twitter:title > title tag + let title = null; + + // Try og:title first + const ogTitleMatch = html.match( + /]+property=["']og:title["'][^>]+content=["']([^"']+)["']/i + ); + if (ogTitleMatch) { + title = ogTitleMatch[1]; + } else { + // Try twitter:title + const twitterTitleMatch = html.match( + /]+name=["']twitter:title["'][^>]+content=["']([^"']+)["']/i + ); + if (twitterTitleMatch) { + title = twitterTitleMatch[1]; + } else { + // Fallback to title tag + const titleMatch = html.match(/]*>([^<]+)<\/title>/i); + if (titleMatch) { + title = titleMatch[1].trim(); + } + } } - } - } - - // Clean up title - if (title) { - title = title.trim(); - // Decode common HTML entities - title = title - .replace(/</g, '<') - .replace(/>/g, '>') - .replace(/&/g, '&') - .replace(/"/g, '"') - .replace(/'/g, "'"); - - if (title.length > 100) { - title = title.substring(0, 100) + '...'; - } - } - - // Extract image with priority: og:image > twitter:image - let image = null; - const ogImageMatch = html.match(/]+property=["']og:image["'][^>]+content=["']([^"']+)["']/i); - if (ogImageMatch) { - image = ogImageMatch[1]; - } else { - const twitterImageMatch = html.match(/]+name=["']twitter:image["'][^>]+content=["']([^"']+)["']/i); - if (twitterImageMatch) { - image = twitterImageMatch[1]; - } - } - - // Extract description - let description = null; - const ogDescMatch = html.match(/]+property=["']og:description["'][^>]+content=["']([^"']+)["']/i); - if (ogDescMatch) { - description = ogDescMatch[1]; - } else { - const twitterDescMatch = html.match(/]+name=["']twitter:description["'][^>]+content=["']([^"']+)["']/i); - if (twitterDescMatch) { - description = twitterDescMatch[1]; - } else { - const metaDescMatch = html.match(/]+name=["']description["'][^>]+content=["']([^"']+)["']/i); - if (metaDescMatch) { - description = metaDescMatch[1]; + + // Clean up title + if (title) { + title = title.trim(); + // Decode common HTML entities + title = title + .replace(/</g, '<') + .replace(/>/g, '>') + .replace(/&/g, '&') + .replace(/"/g, '"') + .replace(/'/g, "'"); + + if (title.length > 100) { + title = title.substring(0, 100) + '...'; + } } - } + + // Extract image with priority: og:image > twitter:image + let image = null; + const ogImageMatch = html.match( + /]+property=["']og:image["'][^>]+content=["']([^"']+)["']/i + ); + if (ogImageMatch) { + image = ogImageMatch[1]; + } else { + const twitterImageMatch = html.match( + /]+name=["']twitter:image["'][^>]+content=["']([^"']+)["']/i + ); + if (twitterImageMatch) { + image = twitterImageMatch[1]; + } + } + + // Extract description + let description = null; + const ogDescMatch = html.match( + /]+property=["']og:description["'][^>]+content=["']([^"']+)["']/i + ); + if (ogDescMatch) { + description = ogDescMatch[1]; + } else { + const twitterDescMatch = html.match( + /]+name=["']twitter:description["'][^>]+content=["']([^"']+)["']/i + ); + if (twitterDescMatch) { + description = twitterDescMatch[1]; + } else { + const metaDescMatch = html.match( + /]+name=["']description["'][^>]+content=["']([^"']+)["']/i + ); + if (metaDescMatch) { + description = metaDescMatch[1]; + } + } + } + + if (description && description.length > 150) { + description = description.substring(0, 150) + '...'; + } + + return { + title, + image, + description, + }; + } catch (error) { + console.error('Error parsing HTML:', error); + return { title: null, image: null, description: null }; } - - if (description && description.length > 150) { - description = description.substring(0, 150) + '...'; - } - - return { - title, - image, - description - }; - } catch (error) { - console.error('Error parsing HTML:', error); - return { title: null, image: null, description: null }; - } } // Helper function to check if text is a URL function isUrl(text) { - const urlRegex = /^(https?:\/\/)?[a-z0-9]+([-.]{1}[a-z0-9]+)*\.[a-z]{2,5}(:[0-9]{1,5})?(\/.*)?$/i; - return urlRegex.test(text.trim()); + const urlRegex = + /^(https?:\/\/)?[a-z0-9]+([-.]{1}[a-z0-9]+)*\.[a-z]{2,5}(:[0-9]{1,5})?(\/.*)?$/i; + return urlRegex.test(text.trim()); } // Helper function to resolve relative URLs to absolute URLs function resolveUrl(baseUrl, relativeUrl) { - try { - return new URL(relativeUrl, baseUrl).href; - } catch { - return relativeUrl; - } + try { + return new URL(relativeUrl, baseUrl).href; + } catch { + return relativeUrl; + } } // Helper function to handle YouTube URLs specially function handleYouTubeUrl(url) { - const youtubeRegex = /(?:youtube\.com\/watch\?v=|youtu\.be\/)([a-zA-Z0-9_-]{11})/; - const match = url.match(youtubeRegex); - - if (match) { - const videoId = match[1]; - - // For now, return basic YouTube info - this is fast and reliable - return { - title: 'YouTube Video', - image: `https://img.youtube.com/vi/${videoId}/maxresdefault.jpg`, - description: 'YouTube video' - }; - } - - return null; + const youtubeRegex = + /(?:youtube\.com\/watch\?v=|youtu\.be\/)([a-zA-Z0-9_-]{11})/; + const match = url.match(youtubeRegex); + + if (match) { + const videoId = match[1]; + + // For now, return basic YouTube info - this is fast and reliable + return { + title: 'YouTube Video', + image: `https://img.youtube.com/vi/${videoId}/maxresdefault.jpg`, + description: 'YouTube video', + }; + } + + return null; } // Helper function to fetch URL metadata with redirect handling async function fetchUrlMetadata(url, maxRedirects = 5) { - return new Promise((resolve) => { - // Add protocol if missing - if (!url.startsWith('http://') && !url.startsWith('https://')) { - url = 'http://' + url; - } + return new Promise((resolve) => { + // Add protocol if missing + if (!url.startsWith('http://') && !url.startsWith('https://')) { + url = 'http://' + url; + } - // Handle YouTube URLs specially to avoid anti-bot issues - if (url.includes('youtube.com') || url.includes('youtu.be')) { - const youtubeMetadata = handleYouTubeUrl(url); - if (youtubeMetadata) { - resolve(youtubeMetadata); - } else { - resolve(null); - } - return; - } - - // Global timeout for the entire operation - const globalTimeout = setTimeout(() => { - resolve(null); - }, 3000); // 3 second max for entire operation - - function makeRequest(currentUrl, redirectCount = 0) { - if (redirectCount > maxRedirects) { - clearTimeout(globalTimeout); - resolve(null); - return; - } - - try { - const urlObj = new URL(currentUrl); - const isHttps = urlObj.protocol === 'https:'; - const client = isHttps ? https : http; - - const options = { - hostname: urlObj.hostname, - port: urlObj.port || (isHttps ? 443 : 80), - path: urlObj.pathname + urlObj.search, - method: 'GET', - timeout: 2000, // Reduced from 5000ms to 2000ms - headers: { - 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36' - } - }; - - const req = client.request(options, (res) => { - // Handle redirects (301, 302, 303, 307, 308) - if ([301, 302, 303, 307, 308].includes(res.statusCode) && res.headers.location) { - const redirectUrl = new URL(res.headers.location, currentUrl).href; - makeRequest(redirectUrl, redirectCount + 1); + // Handle YouTube URLs specially to avoid anti-bot issues + if (url.includes('youtube.com') || url.includes('youtu.be')) { + const youtubeMetadata = handleYouTubeUrl(url); + if (youtubeMetadata) { + resolve(youtubeMetadata); + } else { + resolve(null); + } return; - } + } - // If not a successful response, resolve with null - if (res.statusCode < 200 || res.statusCode >= 400) { - clearTimeout(globalTimeout); + // Global timeout for the entire operation + const globalTimeout = setTimeout(() => { resolve(null); - return; - } + }, 3000); // 3 second max for entire operation - let data = ''; - let totalBytes = 0; - const maxBytes = 20000; // Reduced from 100KB to 20KB - most meta tags are in head - let foundMeta = false; - - res.on('data', (chunk) => { - totalBytes += chunk.length; - if (totalBytes > maxBytes) { - clearTimeout(globalTimeout); - req.destroy(); - return; + function makeRequest(currentUrl, redirectCount = 0) { + if (redirectCount > maxRedirects) { + clearTimeout(globalTimeout); + resolve(null); + return; } - data += chunk; - - // Early termination if we've found essential meta tags and closed head - if (!foundMeta && (data.includes('og:title') || data.includes('twitter:title') || data.includes(''))) { - foundMeta = true; + + try { + const urlObj = new URL(currentUrl); + const isHttps = urlObj.protocol === 'https:'; + const client = isHttps ? https : http; + + const options = { + hostname: urlObj.hostname, + port: urlObj.port || (isHttps ? 443 : 80), + path: urlObj.pathname + urlObj.search, + method: 'GET', + timeout: 2000, // Reduced from 5000ms to 2000ms + headers: { + 'User-Agent': + 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36', + }, + }; + + const req = client.request(options, (res) => { + // Handle redirects (301, 302, 303, 307, 308) + if ( + [301, 302, 303, 307, 308].includes(res.statusCode) && + res.headers.location + ) { + const redirectUrl = new URL( + res.headers.location, + currentUrl + ).href; + makeRequest(redirectUrl, redirectCount + 1); + return; + } + + // If not a successful response, resolve with null + if (res.statusCode < 200 || res.statusCode >= 400) { + clearTimeout(globalTimeout); + resolve(null); + return; + } + + let data = ''; + let totalBytes = 0; + const maxBytes = 20000; // Reduced from 100KB to 20KB - most meta tags are in head + let foundMeta = false; + + res.on('data', (chunk) => { + totalBytes += chunk.length; + if (totalBytes > maxBytes) { + clearTimeout(globalTimeout); + req.destroy(); + return; + } + data += chunk; + + // Early termination if we've found essential meta tags and closed head + if ( + !foundMeta && + (data.includes('og:title') || + data.includes('twitter:title') || + data.includes('')) + ) { + foundMeta = true; + } + + // Stop early if we have meta tags and hit end of head + if (foundMeta && data.includes('')) { + clearTimeout(globalTimeout); + req.destroy(); + return; + } + }); + + res.on('end', () => { + clearTimeout(globalTimeout); + const metadata = extractMetadataFromHtml(data); + + // Resolve relative image URLs to absolute + if ( + metadata.image && + !metadata.image.startsWith('http') + ) { + metadata.image = resolveUrl( + currentUrl, + metadata.image + ); + } + + resolve(metadata); + }); + }); + + req.on('error', (err) => { + clearTimeout(globalTimeout); + resolve(null); + }); + + req.on('timeout', () => { + clearTimeout(globalTimeout); + req.destroy(); + resolve(null); + }); + + req.end(); + } catch (error) { + clearTimeout(globalTimeout); + resolve(null); } - - // Stop early if we have meta tags and hit end of head - if (foundMeta && data.includes('')) { - clearTimeout(globalTimeout); - req.destroy(); - return; - } - }); + } - res.on('end', () => { - clearTimeout(globalTimeout); - const metadata = extractMetadataFromHtml(data); - - // Resolve relative image URLs to absolute - if (metadata.image && !metadata.image.startsWith('http')) { - metadata.image = resolveUrl(currentUrl, metadata.image); - } - - resolve(metadata); - }); - }); - - req.on('error', (err) => { - clearTimeout(globalTimeout); - resolve(null); - }); - - req.on('timeout', () => { - clearTimeout(globalTimeout); - req.destroy(); - resolve(null); - }); - - req.end(); - } catch (error) { - clearTimeout(globalTimeout); - resolve(null); - } - } - - makeRequest(url); - }); + makeRequest(url); + }); } // GET /api/url/title router.get('/url/title', async (req, res) => { - try { - if (!req.session || !req.session.userId) { - return res.status(401).json({ error: 'Authentication required' }); - } + try { + if (!req.session || !req.session.userId) { + return res.status(401).json({ error: 'Authentication required' }); + } - const { url } = req.query; - - if (!url) { - return res.status(400).json({ error: 'URL parameter is required' }); - } + const { url } = req.query; - const metadata = await fetchUrlMetadata(url); + if (!url) { + return res.status(400).json({ error: 'URL parameter is required' }); + } - if (metadata && metadata.title) { - res.json({ - url, - title: metadata.title, - image: metadata.image, - description: metadata.description - }); - } else { - res.json({ url, title: null, image: null, description: null, error: 'Could not extract metadata' }); + const metadata = await fetchUrlMetadata(url); + + if (metadata && metadata.title) { + res.json({ + url, + title: metadata.title, + image: metadata.image, + description: metadata.description, + }); + } else { + res.json({ + url, + title: null, + image: null, + description: null, + error: 'Could not extract metadata', + }); + } + } catch (error) { + console.error('Error extracting URL title:', error); + res.status(500).json({ error: 'Internal server error' }); } - } catch (error) { - console.error('Error extracting URL title:', error); - res.status(500).json({ error: 'Internal server error' }); - } }); // POST /api/url/extract-from-text router.post('/url/extract-from-text', async (req, res) => { - try { - if (!req.session || !req.session.userId) { - return res.status(401).json({ error: 'Authentication required' }); - } + try { + if (!req.session || !req.session.userId) { + return res.status(401).json({ error: 'Authentication required' }); + } - const { text } = req.body; + const { text } = req.body; - if (!text) { - return res.status(400).json({ error: 'Text parameter is required' }); - } + if (!text) { + return res + .status(400) + .json({ error: 'Text parameter is required' }); + } - // Enhanced URL extraction - look for URLs with or without protocol - const urlWithProtocolRegex = /(https?:\/\/[^\s]+)/gi; - const urlWithoutProtocolRegex = /(?:^|\s)((?:www\.)?[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}(?::[0-9]{1,5})?(?:\/[^\s]*)?)/gi; - - let urls = text.match(urlWithProtocolRegex); - - // If no URLs with protocol found, look for URLs without protocol - if (!urls) { - const matches = text.match(urlWithoutProtocolRegex); - if (matches) { - // Clean up the matches (remove leading whitespace) - urls = matches.map(match => match.trim()); - } - } + // Enhanced URL extraction - look for URLs with or without protocol + const urlWithProtocolRegex = /(https?:\/\/[^\s]+)/gi; + const urlWithoutProtocolRegex = + /(?:^|\s)((?:www\.)?[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}(?::[0-9]{1,5})?(?:\/[^\s]*)?)/gi; - if (urls && urls.length > 0) { - const firstUrl = urls[0]; - const metadata = await fetchUrlMetadata(firstUrl); - - if (metadata && metadata.title) { - res.json({ - found: true, - url: firstUrl, - title: metadata.title, - image: metadata.image, - description: metadata.description, - originalText: text - }); - } else { - res.json({ - found: true, - url: firstUrl, - title: null, - image: null, - description: null, - originalText: text - }); - } - } else { - res.json({ found: false }); + let urls = text.match(urlWithProtocolRegex); + + // If no URLs with protocol found, look for URLs without protocol + if (!urls) { + const matches = text.match(urlWithoutProtocolRegex); + if (matches) { + // Clean up the matches (remove leading whitespace) + urls = matches.map((match) => match.trim()); + } + } + + if (urls && urls.length > 0) { + const firstUrl = urls[0]; + const metadata = await fetchUrlMetadata(firstUrl); + + if (metadata && metadata.title) { + res.json({ + found: true, + url: firstUrl, + title: metadata.title, + image: metadata.image, + description: metadata.description, + originalText: text, + }); + } else { + res.json({ + found: true, + url: firstUrl, + title: null, + image: null, + description: null, + originalText: text, + }); + } + } else { + res.json({ found: false }); + } + } catch (error) { + console.error('Error extracting URL from text:', error); + res.status(500).json({ error: 'Internal server error' }); } - } catch (error) { - console.error('Error extracting URL from text:', error); - res.status(500).json({ error: 'Internal server error' }); - } }); -module.exports = router; \ No newline at end of file +module.exports = router; diff --git a/backend/routes/users.js b/backend/routes/users.js index 405ddc2..2781c7e 100644 --- a/backend/routes/users.js +++ b/backend/routes/users.js @@ -3,332 +3,423 @@ const { User } = require('../models'); const taskSummaryService = require('../services/taskSummaryService'); const router = express.Router(); -const VALID_FREQUENCIES = ['daily', 'weekdays', 'weekly', '1h', '2h', '4h', '8h', '12h']; +const VALID_FREQUENCIES = [ + 'daily', + 'weekdays', + 'weekly', + '1h', + '2h', + '4h', + '8h', + '12h', +]; // GET /api/profile router.get('/profile', async (req, res) => { - try { - if (!req.session || !req.session.userId) { - return res.status(401).json({ error: 'Authentication required' }); + try { + if (!req.session || !req.session.userId) { + return res.status(401).json({ error: 'Authentication required' }); + } + + const user = await User.findByPk(req.session.userId, { + attributes: [ + 'id', + 'email', + 'appearance', + 'language', + 'timezone', + 'avatar_image', + 'telegram_bot_token', + 'telegram_chat_id', + 'task_summary_enabled', + 'task_summary_frequency', + 'task_intelligence_enabled', + 'auto_suggest_next_actions_enabled', + 'pomodoro_enabled', + 'today_settings', + ], + }); + + if (!user) { + return res.status(404).json({ error: 'Profile not found.' }); + } + + // Parse today_settings if it's a string + if (user.today_settings && typeof user.today_settings === 'string') { + try { + user.today_settings = JSON.parse(user.today_settings); + } catch (error) { + console.error('Error parsing today_settings:', error); + user.today_settings = null; + } + } + + res.json(user); + } catch (error) { + console.error('Error fetching profile:', error); + res.status(500).json({ error: 'Internal server error' }); } - - const user = await User.findByPk(req.session.userId, { - attributes: [ - 'id', 'email', 'appearance', 'language', 'timezone', - 'avatar_image', 'telegram_bot_token', 'telegram_chat_id', - 'task_summary_enabled', 'task_summary_frequency', 'task_intelligence_enabled', - 'auto_suggest_next_actions_enabled', 'pomodoro_enabled', 'today_settings' - ] - }); - - if (!user) { - return res.status(404).json({ error: 'Profile not found.' }); - } - - // Parse today_settings if it's a string - if (user.today_settings && typeof user.today_settings === 'string') { - try { - user.today_settings = JSON.parse(user.today_settings); - } catch (error) { - console.error('Error parsing today_settings:', error); - user.today_settings = null; - } - } - - res.json(user); - } catch (error) { - console.error('Error fetching profile:', error); - res.status(500).json({ error: 'Internal server error' }); - } }); // PATCH /api/profile router.patch('/profile', async (req, res) => { - try { - if (!req.session || !req.session.userId) { - return res.status(401).json({ error: 'Authentication required' }); - } + try { + if (!req.session || !req.session.userId) { + return res.status(401).json({ error: 'Authentication required' }); + } - const user = await User.findByPk(req.session.userId); - if (!user) { - return res.status(404).json({ error: 'Profile not found.' }); - } + const user = await User.findByPk(req.session.userId); + if (!user) { + return res.status(404).json({ error: 'Profile not found.' }); + } - const { appearance, language, timezone, avatar_image, telegram_bot_token, task_intelligence_enabled, task_summary_enabled, task_summary_frequency, auto_suggest_next_actions_enabled, pomodoro_enabled, currentPassword, newPassword } = req.body; - - const allowedUpdates = {}; - if (appearance !== undefined) allowedUpdates.appearance = appearance; - if (language !== undefined) allowedUpdates.language = language; - if (timezone !== undefined) allowedUpdates.timezone = timezone; - if (avatar_image !== undefined) allowedUpdates.avatar_image = avatar_image; - if (telegram_bot_token !== undefined) allowedUpdates.telegram_bot_token = telegram_bot_token; - if (task_intelligence_enabled !== undefined) allowedUpdates.task_intelligence_enabled = task_intelligence_enabled; - if (task_summary_enabled !== undefined) allowedUpdates.task_summary_enabled = task_summary_enabled; - if (task_summary_frequency !== undefined) allowedUpdates.task_summary_frequency = task_summary_frequency; - if (auto_suggest_next_actions_enabled !== undefined) allowedUpdates.auto_suggest_next_actions_enabled = auto_suggest_next_actions_enabled; - if (pomodoro_enabled !== undefined) allowedUpdates.pomodoro_enabled = pomodoro_enabled; + const { + appearance, + language, + timezone, + avatar_image, + telegram_bot_token, + task_intelligence_enabled, + task_summary_enabled, + task_summary_frequency, + auto_suggest_next_actions_enabled, + pomodoro_enabled, + currentPassword, + newPassword, + } = req.body; - // Handle password change if provided - if (currentPassword && newPassword) { - if (newPassword.length < 6) { - return res.status(400).json({ - field: 'newPassword', - error: 'Password must be at least 6 characters' + const allowedUpdates = {}; + if (appearance !== undefined) allowedUpdates.appearance = appearance; + if (language !== undefined) allowedUpdates.language = language; + if (timezone !== undefined) allowedUpdates.timezone = timezone; + if (avatar_image !== undefined) + allowedUpdates.avatar_image = avatar_image; + if (telegram_bot_token !== undefined) + allowedUpdates.telegram_bot_token = telegram_bot_token; + if (task_intelligence_enabled !== undefined) + allowedUpdates.task_intelligence_enabled = + task_intelligence_enabled; + if (task_summary_enabled !== undefined) + allowedUpdates.task_summary_enabled = task_summary_enabled; + if (task_summary_frequency !== undefined) + allowedUpdates.task_summary_frequency = task_summary_frequency; + if (auto_suggest_next_actions_enabled !== undefined) + allowedUpdates.auto_suggest_next_actions_enabled = + auto_suggest_next_actions_enabled; + if (pomodoro_enabled !== undefined) + allowedUpdates.pomodoro_enabled = pomodoro_enabled; + + // Handle password change if provided + if (currentPassword && newPassword) { + if (newPassword.length < 6) { + return res.status(400).json({ + field: 'newPassword', + error: 'Password must be at least 6 characters', + }); + } + + // Verify current password + const isValidPassword = await User.checkPassword( + currentPassword, + user.password_digest + ); + if (!isValidPassword) { + return res.status(400).json({ + field: 'currentPassword', + error: 'Current password is incorrect', + }); + } + + // Hash and include new password in updates + const hashedNewPassword = await User.hashPassword(newPassword); + allowedUpdates.password_digest = hashedNewPassword; + } + + await user.update(allowedUpdates); + + // Return updated user with limited fields + const updatedUser = await User.findByPk(user.id, { + attributes: [ + 'id', + 'email', + 'appearance', + 'language', + 'timezone', + 'avatar_image', + 'telegram_bot_token', + 'telegram_chat_id', + 'task_intelligence_enabled', + 'task_summary_enabled', + 'task_summary_frequency', + 'auto_suggest_next_actions_enabled', + 'pomodoro_enabled', + ], }); - } - // Verify current password - const isValidPassword = await User.checkPassword(currentPassword, user.password_digest); - if (!isValidPassword) { - return res.status(400).json({ - field: 'currentPassword', - error: 'Current password is incorrect' + res.json(updatedUser); + } catch (error) { + console.error('Error updating profile:', error); + res.status(400).json({ + error: 'Failed to update profile.', + details: error.errors + ? error.errors.map((e) => e.message) + : [error.message], }); - } - - // Hash and include new password in updates - const hashedNewPassword = await User.hashPassword(newPassword); - allowedUpdates.password_digest = hashedNewPassword; } - - await user.update(allowedUpdates); - - // Return updated user with limited fields - const updatedUser = await User.findByPk(user.id, { - attributes: ['id', 'email', 'appearance', 'language', 'timezone', 'avatar_image', 'telegram_bot_token', 'telegram_chat_id', 'task_intelligence_enabled', 'task_summary_enabled', 'task_summary_frequency', 'auto_suggest_next_actions_enabled', 'pomodoro_enabled'] - }); - - res.json(updatedUser); - } catch (error) { - console.error('Error updating profile:', error); - res.status(400).json({ - error: 'Failed to update profile.', - details: error.errors ? error.errors.map(e => e.message) : [error.message] - }); - } }); // POST /api/profile/change-password router.post('/profile/change-password', async (req, res) => { - try { - if (!req.session || !req.session.userId) { - return res.status(401).json({ error: 'Authentication required' }); + try { + if (!req.session || !req.session.userId) { + return res.status(401).json({ error: 'Authentication required' }); + } + + const { currentPassword, newPassword } = req.body; + + if (!currentPassword || !newPassword) { + return res.status(400).json({ + error: 'Current password and new password are required', + }); + } + + if (newPassword.length < 6) { + return res.status(400).json({ + field: 'newPassword', + error: 'Password must be at least 6 characters', + }); + } + + const user = await User.findByPk(req.session.userId); + if (!user) { + return res.status(404).json({ error: 'User not found' }); + } + + // Verify current password + const isValidPassword = await User.checkPassword( + currentPassword, + user.password_digest + ); + if (!isValidPassword) { + return res.status(400).json({ + field: 'currentPassword', + error: 'Current password is incorrect', + }); + } + + // Hash and update new password + const hashedNewPassword = await User.hashPassword(newPassword); + await user.update({ password_digest: hashedNewPassword }); + + res.json({ message: 'Password changed successfully' }); + } catch (error) { + console.error('Error changing password:', error); + res.status(500).json({ error: 'Internal server error' }); } - - const { currentPassword, newPassword } = req.body; - - if (!currentPassword || !newPassword) { - return res.status(400).json({ error: 'Current password and new password are required' }); - } - - if (newPassword.length < 6) { - return res.status(400).json({ - field: 'newPassword', - error: 'Password must be at least 6 characters' - }); - } - - const user = await User.findByPk(req.session.userId); - if (!user) { - return res.status(404).json({ error: 'User not found' }); - } - - // Verify current password - const isValidPassword = await User.checkPassword(currentPassword, user.password_digest); - if (!isValidPassword) { - return res.status(400).json({ - field: 'currentPassword', - error: 'Current password is incorrect' - }); - } - - // Hash and update new password - const hashedNewPassword = await User.hashPassword(newPassword); - await user.update({ password_digest: hashedNewPassword }); - - res.json({ message: 'Password changed successfully' }); - } catch (error) { - console.error('Error changing password:', error); - res.status(500).json({ error: 'Internal server error' }); - } }); // POST /api/profile/task-summary/toggle router.post('/profile/task-summary/toggle', async (req, res) => { - try { - if (!req.session || !req.session.userId) { - return res.status(401).json({ error: 'Authentication required' }); + try { + if (!req.session || !req.session.userId) { + return res.status(401).json({ error: 'Authentication required' }); + } + + const user = await User.findByPk(req.session.userId); + if (!user) { + return res.status(404).json({ error: 'User not found.' }); + } + + const enabled = !user.task_summary_enabled; + + await user.update({ task_summary_enabled: enabled }); + + // Note: Telegram integration would need to be implemented separately + const message = enabled + ? 'Task summary notifications have been enabled.' + : 'Task summary notifications have been disabled.'; + + res.json({ + success: true, + enabled: enabled, + message: message, + }); + } catch (error) { + console.error('Error toggling task summary:', error); + res.status(400).json({ + error: 'Failed to update task summary settings.', + details: error.errors + ? error.errors.map((e) => e.message) + : [error.message], + }); } - - const user = await User.findByPk(req.session.userId); - if (!user) { - return res.status(404).json({ error: 'User not found.' }); - } - - const enabled = !user.task_summary_enabled; - - await user.update({ task_summary_enabled: enabled }); - - // Note: Telegram integration would need to be implemented separately - const message = enabled - ? 'Task summary notifications have been enabled.' - : 'Task summary notifications have been disabled.'; - - res.json({ - success: true, - enabled: enabled, - message: message - }); - } catch (error) { - console.error('Error toggling task summary:', error); - res.status(400).json({ - error: 'Failed to update task summary settings.', - details: error.errors ? error.errors.map(e => e.message) : [error.message] - }); - } }); // POST /api/profile/task-summary/frequency router.post('/profile/task-summary/frequency', async (req, res) => { - try { - if (!req.session || !req.session.userId) { - return res.status(401).json({ error: 'Authentication required' }); + try { + if (!req.session || !req.session.userId) { + return res.status(401).json({ error: 'Authentication required' }); + } + + const { frequency } = req.body; + + if (!frequency) { + return res.status(400).json({ error: 'Frequency is required.' }); + } + + if (!VALID_FREQUENCIES.includes(frequency)) { + return res.status(400).json({ error: 'Invalid frequency value.' }); + } + + const user = await User.findByPk(req.session.userId); + if (!user) { + return res.status(404).json({ error: 'User not found.' }); + } + + await user.update({ task_summary_frequency: frequency }); + + res.json({ + success: true, + frequency: frequency, + message: `Task summary frequency has been set to ${frequency}.`, + }); + } catch (error) { + console.error('Error updating task summary frequency:', error); + res.status(400).json({ + error: 'Failed to update task summary frequency.', + details: error.errors + ? error.errors.map((e) => e.message) + : [error.message], + }); } - - const { frequency } = req.body; - - if (!frequency) { - return res.status(400).json({ error: 'Frequency is required.' }); - } - - if (!VALID_FREQUENCIES.includes(frequency)) { - return res.status(400).json({ error: 'Invalid frequency value.' }); - } - - const user = await User.findByPk(req.session.userId); - if (!user) { - return res.status(404).json({ error: 'User not found.' }); - } - - await user.update({ task_summary_frequency: frequency }); - - res.json({ - success: true, - frequency: frequency, - message: `Task summary frequency has been set to ${frequency}.` - }); - } catch (error) { - console.error('Error updating task summary frequency:', error); - res.status(400).json({ - error: 'Failed to update task summary frequency.', - details: error.errors ? error.errors.map(e => e.message) : [error.message] - }); - } }); // POST /api/profile/task-summary/send-now router.post('/profile/task-summary/send-now', async (req, res) => { - try { - if (!req.session || !req.session.userId) { - return res.status(401).json({ error: 'Authentication required' }); - } + try { + if (!req.session || !req.session.userId) { + return res.status(401).json({ error: 'Authentication required' }); + } - const user = await User.findByPk(req.session.userId); - if (!user) { - return res.status(404).json({ error: 'User not found.' }); - } + const user = await User.findByPk(req.session.userId); + if (!user) { + return res.status(404).json({ error: 'User not found.' }); + } - if (!user.telegram_bot_token || !user.telegram_chat_id) { - return res.status(400).json({ error: 'Telegram bot is not properly configured.' }); - } + if (!user.telegram_bot_token || !user.telegram_chat_id) { + return res + .status(400) + .json({ error: 'Telegram bot is not properly configured.' }); + } - // Send the task summary - const success = await taskSummaryService.sendSummaryToUser(user.id); - - if (success) { - res.json({ - success: true, - message: 'Task summary was sent to your Telegram.' - }); - } else { - res.status(400).json({ error: 'Failed to send message to Telegram.' }); + // Send the task summary + const success = await taskSummaryService.sendSummaryToUser(user.id); + + if (success) { + res.json({ + success: true, + message: 'Task summary was sent to your Telegram.', + }); + } else { + res.status(400).json({ + error: 'Failed to send message to Telegram.', + }); + } + } catch (error) { + console.error('Error sending task summary:', error); + res.status(400).json({ + error: 'Error sending message to Telegram.', + details: error.message, + }); } - } catch (error) { - console.error('Error sending task summary:', error); - res.status(400).json({ - error: 'Error sending message to Telegram.', - details: error.message - }); - } }); // GET /api/profile/task-summary/status router.get('/profile/task-summary/status', async (req, res) => { - try { - if (!req.session || !req.session.userId) { - return res.status(401).json({ error: 'Authentication required' }); - } + try { + if (!req.session || !req.session.userId) { + return res.status(401).json({ error: 'Authentication required' }); + } - const user = await User.findByPk(req.session.userId); - if (!user) { - return res.status(404).json({ error: 'User not found.' }); - } + const user = await User.findByPk(req.session.userId); + if (!user) { + return res.status(404).json({ error: 'User not found.' }); + } - res.json({ - success: true, - enabled: user.task_summary_enabled, - frequency: user.task_summary_frequency, - last_run: user.task_summary_last_run, - next_run: user.task_summary_next_run - }); - } catch (error) { - console.error('Error fetching task summary status:', error); - res.status(500).json({ error: 'Internal server error' }); - } + res.json({ + success: true, + enabled: user.task_summary_enabled, + frequency: user.task_summary_frequency, + last_run: user.task_summary_last_run, + next_run: user.task_summary_next_run, + }); + } catch (error) { + console.error('Error fetching task summary status:', error); + res.status(500).json({ error: 'Internal server error' }); + } }); // PUT /api/profile/today-settings router.put('/profile/today-settings', async (req, res) => { - try { - if (!req.session || !req.session.userId) { - return res.status(401).json({ error: 'Authentication required' }); + try { + if (!req.session || !req.session.userId) { + return res.status(401).json({ error: 'Authentication required' }); + } + + const user = await User.findByPk(req.session.userId); + if (!user) { + return res.status(404).json({ error: 'User not found.' }); + } + + const { + showMetrics, + showProductivity, + showIntelligence, + showDueToday, + showCompleted, + showProgressBar, + showDailyQuote, + } = req.body; + + const todaySettings = { + showMetrics: + showMetrics !== undefined + ? showMetrics + : user.today_settings?.showMetrics || false, + showProductivity: + showProductivity !== undefined + ? showProductivity + : user.today_settings?.showProductivity || false, + showIntelligence: + showIntelligence !== undefined + ? showIntelligence + : user.today_settings?.showIntelligence || false, + showDueToday: + showDueToday !== undefined + ? showDueToday + : user.today_settings?.showDueToday || true, + showCompleted: + showCompleted !== undefined + ? showCompleted + : user.today_settings?.showCompleted || true, + showProgressBar: true, // Always enabled - ignore any attempts to disable it + showDailyQuote: + showDailyQuote !== undefined + ? showDailyQuote + : user.today_settings?.showDailyQuote || true, + }; + + await user.update({ today_settings: todaySettings }); + + res.json({ + success: true, + today_settings: todaySettings, + }); + } catch (error) { + console.error('Error updating today settings:', error); + res.status(500).json({ error: 'Internal server error' }); } - - const user = await User.findByPk(req.session.userId); - if (!user) { - return res.status(404).json({ error: 'User not found.' }); - } - - const { - showMetrics, - showProductivity, - showIntelligence, - showDueToday, - showCompleted, - showProgressBar, - showDailyQuote - } = req.body; - - const todaySettings = { - showMetrics: showMetrics !== undefined ? showMetrics : user.today_settings?.showMetrics || false, - showProductivity: showProductivity !== undefined ? showProductivity : user.today_settings?.showProductivity || false, - showIntelligence: showIntelligence !== undefined ? showIntelligence : user.today_settings?.showIntelligence || false, - showDueToday: showDueToday !== undefined ? showDueToday : user.today_settings?.showDueToday || true, - showCompleted: showCompleted !== undefined ? showCompleted : user.today_settings?.showCompleted || true, - showProgressBar: true, // Always enabled - ignore any attempts to disable it - showDailyQuote: showDailyQuote !== undefined ? showDailyQuote : user.today_settings?.showDailyQuote || true - }; - - await user.update({ today_settings: todaySettings }); - - res.json({ - success: true, - today_settings: todaySettings - }); - } catch (error) { - console.error('Error updating today settings:', error); - res.status(500).json({ error: 'Internal server error' }); - } }); -module.exports = router; \ No newline at end of file +module.exports = router; diff --git a/backend/scripts/db-init.js b/backend/scripts/db-init.js index 734630c..72a82ab 100755 --- a/backend/scripts/db-init.js +++ b/backend/scripts/db-init.js @@ -9,19 +9,21 @@ require('dotenv').config(); const { sequelize } = require('../models'); async function initDatabase() { - try { - console.log('Initializing database...'); - console.log('WARNING: This will drop all existing data!'); - - await sequelize.sync({ force: true }); - - console.log('✅ Database initialized successfully'); - console.log('All tables have been created and existing data has been cleared'); - process.exit(0); - } catch (error) { - console.error('❌ Error initializing database:', error.message); - process.exit(1); - } + try { + console.log('Initializing database...'); + console.log('WARNING: This will drop all existing data!'); + + await sequelize.sync({ force: true }); + + console.log('✅ Database initialized successfully'); + console.log( + 'All tables have been created and existing data has been cleared' + ); + process.exit(0); + } catch (error) { + console.error('❌ Error initializing database:', error.message); + process.exit(1); + } } -initDatabase(); \ No newline at end of file +initDatabase(); diff --git a/backend/scripts/db-migrate.js b/backend/scripts/db-migrate.js index d23c5fc..ba1aa54 100755 --- a/backend/scripts/db-migrate.js +++ b/backend/scripts/db-migrate.js @@ -9,19 +9,19 @@ require('dotenv').config(); const { sequelize } = require('../models'); async function migrateDatabase() { - try { - console.log('Migrating database...'); - console.log('This will alter existing tables to match current models'); - - await sequelize.sync({ alter: true }); - - console.log('✅ Database migrated successfully'); - console.log('All tables have been updated to match current models'); - process.exit(0); - } catch (error) { - console.error('❌ Error migrating database:', error.message); - process.exit(1); - } + try { + console.log('Migrating database...'); + console.log('This will alter existing tables to match current models'); + + await sequelize.sync({ alter: true }); + + console.log('✅ Database migrated successfully'); + console.log('All tables have been updated to match current models'); + process.exit(0); + } catch (error) { + console.error('❌ Error migrating database:', error.message); + process.exit(1); + } } -migrateDatabase(); \ No newline at end of file +migrateDatabase(); diff --git a/backend/scripts/db-reset.js b/backend/scripts/db-reset.js index dddd7cf..5e1c822 100755 --- a/backend/scripts/db-reset.js +++ b/backend/scripts/db-reset.js @@ -9,19 +9,19 @@ require('dotenv').config(); const { sequelize } = require('../models'); async function resetDatabase() { - try { - console.log('Resetting database...'); - console.log('WARNING: This will permanently delete all data!'); - - await sequelize.sync({ force: true }); - - console.log('✅ Database reset successfully'); - console.log('All tables have been dropped and recreated'); - process.exit(0); - } catch (error) { - console.error('❌ Error resetting database:', error.message); - process.exit(1); - } + try { + console.log('Resetting database...'); + console.log('WARNING: This will permanently delete all data!'); + + await sequelize.sync({ force: true }); + + console.log('✅ Database reset successfully'); + console.log('All tables have been dropped and recreated'); + process.exit(0); + } catch (error) { + console.error('❌ Error resetting database:', error.message); + process.exit(1); + } } -resetDatabase(); \ No newline at end of file +resetDatabase(); diff --git a/backend/scripts/db-status.js b/backend/scripts/db-status.js index 131ca93..081e7ac 100755 --- a/backend/scripts/db-status.js +++ b/backend/scripts/db-status.js @@ -6,64 +6,75 @@ */ require('dotenv').config(); -const { sequelize, User, Task, Project, Area, Note, Tag, InboxItem } = require('../models'); +const { + sequelize, + User, + Task, + Project, + Area, + Note, + Tag, + InboxItem, +} = require('../models'); const fs = require('fs'); const path = require('path'); async function checkDatabaseStatus() { - try { - console.log('🔍 Checking database status...\n'); - - // Check database file - const dbConfig = sequelize.config || sequelize.options; - const dbPath = dbConfig.storage || sequelize.options.storage; - - console.log('📂 Database Configuration:'); - console.log(` Storage: ${dbPath}`); - console.log(` Dialect: ${dbConfig.dialect || sequelize.options.dialect || 'sqlite'}`); - console.log(` Environment: ${process.env.NODE_ENV || 'development'}`); - - // Check if database file exists - if (fs.existsSync(dbPath)) { - const stats = fs.statSync(dbPath); - console.log(` File size: ${(stats.size / 1024).toFixed(2)} KB`); - console.log(` Last modified: ${stats.mtime.toISOString()}`); - } else { - console.log(' ⚠️ Database file does not exist'); + try { + console.log('🔍 Checking database status...\n'); + + // Check database file + const dbConfig = sequelize.config || sequelize.options; + const dbPath = dbConfig.storage || sequelize.options.storage; + + console.log('📂 Database Configuration:'); + console.log(` Storage: ${dbPath}`); + console.log( + ` Dialect: ${dbConfig.dialect || sequelize.options.dialect || 'sqlite'}` + ); + console.log(` Environment: ${process.env.NODE_ENV || 'development'}`); + + // Check if database file exists + if (fs.existsSync(dbPath)) { + const stats = fs.statSync(dbPath); + console.log(` File size: ${(stats.size / 1024).toFixed(2)} KB`); + console.log(` Last modified: ${stats.mtime.toISOString()}`); + } else { + console.log(' ⚠️ Database file does not exist'); + } + + console.log('\n🔌 Testing database connection...'); + await sequelize.authenticate(); + console.log('✅ Database connection successful\n'); + + // Get table information + console.log('📊 Table Statistics:'); + const models = [ + { name: 'Users', model: User }, + { name: 'Areas', model: Area }, + { name: 'Projects', model: Project }, + { name: 'Tasks', model: Task }, + { name: 'Notes', model: Note }, + { name: 'Tags', model: Tag }, + { name: 'Inbox Items', model: InboxItem }, + ]; + + for (const { name, model } of models) { + try { + const count = await model.count(); + console.log(` ${name}: ${count} records`); + } catch (error) { + console.log(` ${name}: ❌ Error (${error.message})`); + } + } + + console.log('\n✅ Database status check completed'); + process.exit(0); + } catch (error) { + console.error('\n❌ Database connection failed:', error.message); + console.error('\n💡 Try running: npm run db:init'); + process.exit(1); } - - console.log('\n🔌 Testing database connection...'); - await sequelize.authenticate(); - console.log('✅ Database connection successful\n'); - - // Get table information - console.log('📊 Table Statistics:'); - const models = [ - { name: 'Users', model: User }, - { name: 'Areas', model: Area }, - { name: 'Projects', model: Project }, - { name: 'Tasks', model: Task }, - { name: 'Notes', model: Note }, - { name: 'Tags', model: Tag }, - { name: 'Inbox Items', model: InboxItem } - ]; - - for (const { name, model } of models) { - try { - const count = await model.count(); - console.log(` ${name}: ${count} records`); - } catch (error) { - console.log(` ${name}: ❌ Error (${error.message})`); - } - } - - console.log('\n✅ Database status check completed'); - process.exit(0); - } catch (error) { - console.error('\n❌ Database connection failed:', error.message); - console.error('\n💡 Try running: npm run db:init'); - process.exit(1); - } } -checkDatabaseStatus(); \ No newline at end of file +checkDatabaseStatus(); diff --git a/backend/scripts/db-sync.js b/backend/scripts/db-sync.js index 81ca901..3025771 100755 --- a/backend/scripts/db-sync.js +++ b/backend/scripts/db-sync.js @@ -9,18 +9,18 @@ require('dotenv').config(); const { sequelize } = require('../models'); async function syncDatabase() { - try { - console.log('Syncing database...'); - - await sequelize.sync(); - - console.log('✅ Database synchronized successfully'); - console.log('All tables have been created (existing data preserved)'); - process.exit(0); - } catch (error) { - console.error('❌ Error syncing database:', error.message); - process.exit(1); - } + try { + console.log('Syncing database...'); + + await sequelize.sync(); + + console.log('✅ Database synchronized successfully'); + console.log('All tables have been created (existing data preserved)'); + process.exit(0); + } catch (error) { + console.error('❌ Error syncing database:', error.message); + process.exit(1); + } } -syncDatabase(); \ No newline at end of file +syncDatabase(); diff --git a/backend/scripts/migration-create.js b/backend/scripts/migration-create.js index 64e4e9a..189d274 100755 --- a/backend/scripts/migration-create.js +++ b/backend/scripts/migration-create.js @@ -10,28 +10,31 @@ const fs = require('fs'); const path = require('path'); function createMigration() { - const migrationName = process.argv[2]; - - if (!migrationName) { - console.error('❌ Usage: npm run migration:create '); - console.error('Example: npm run migration:create add-description-to-tasks'); - process.exit(1); - } + const migrationName = process.argv[2]; - // Generate timestamp (YYYYMMDDHHMMSS format) - const now = new Date(); - const timestamp = now.getFullYear().toString() + - (now.getMonth() + 1).toString().padStart(2, '0') + - now.getDate().toString().padStart(2, '0') + - now.getHours().toString().padStart(2, '0') + - now.getMinutes().toString().padStart(2, '0') + - now.getSeconds().toString().padStart(2, '0'); + if (!migrationName) { + console.error('❌ Usage: npm run migration:create '); + console.error( + 'Example: npm run migration:create add-description-to-tasks' + ); + process.exit(1); + } - const fileName = `${timestamp}-${migrationName}.js`; - const filePath = path.join(__dirname, '..', 'migrations', fileName); + // Generate timestamp (YYYYMMDDHHMMSS format) + const now = new Date(); + const timestamp = + now.getFullYear().toString() + + (now.getMonth() + 1).toString().padStart(2, '0') + + now.getDate().toString().padStart(2, '0') + + now.getHours().toString().padStart(2, '0') + + now.getMinutes().toString().padStart(2, '0') + + now.getSeconds().toString().padStart(2, '0'); - // Migration template - const template = `'use strict'; + const fileName = `${timestamp}-${migrationName}.js`; + const filePath = path.join(__dirname, '..', 'migrations', fileName); + + // Migration template + const template = `'use strict'; module.exports = { async up(queryInterface, Sequelize) { @@ -87,22 +90,22 @@ module.exports = { } };`; - try { - fs.writeFileSync(filePath, template); - console.log('✅ Migration created successfully'); - console.log(`📁 File: ${fileName}`); - console.log(`📂 Path: ${filePath}`); - console.log(''); - console.log('📝 Next steps:'); - console.log('1. Edit the migration file to add your schema changes'); - console.log('2. Run: npm run migration:run'); - console.log('3. To rollback: npm run migration:undo'); - - process.exit(0); - } catch (error) { - console.error('❌ Error creating migration:', error.message); - process.exit(1); - } + try { + fs.writeFileSync(filePath, template); + console.log('✅ Migration created successfully'); + console.log(`📁 File: ${fileName}`); + console.log(`📂 Path: ${filePath}`); + console.log(''); + console.log('📝 Next steps:'); + console.log('1. Edit the migration file to add your schema changes'); + console.log('2. Run: npm run migration:run'); + console.log('3. To rollback: npm run migration:undo'); + + process.exit(0); + } catch (error) { + console.error('❌ Error creating migration:', error.message); + process.exit(1); + } } -createMigration(); \ No newline at end of file +createMigration(); diff --git a/backend/scripts/seed-dev-data.js b/backend/scripts/seed-dev-data.js index f8910f3..b3f09d1 100644 --- a/backend/scripts/seed-dev-data.js +++ b/backend/scripts/seed-dev-data.js @@ -8,11 +8,11 @@ process.env.NODE_ENV = process.env.NODE_ENV || 'development'; // Ensure we're using the correct database path if (!process.env.DATABASE_URL) { - process.env.DATABASE_URL = `sqlite:///${path.join(__dirname, '../db/development.sqlite3')}`; + process.env.DATABASE_URL = `sqlite:///${path.join(__dirname, '../db/development.sqlite3')}`; } console.log('🌱 Starting development data seeding...'); console.log(`📁 Database: ${process.env.DATABASE_URL}`); console.log(`🌍 Environment: ${process.env.NODE_ENV}`); -seedDatabase(); \ No newline at end of file +seedDatabase(); diff --git a/backend/scripts/user-create.js b/backend/scripts/user-create.js index 7e41d11..8314921 100755 --- a/backend/scripts/user-create.js +++ b/backend/scripts/user-create.js @@ -11,67 +11,71 @@ const { User } = require('../models'); const bcrypt = require('bcrypt'); async function createUser() { - const [email, password] = process.argv.slice(2); - - if (!email || password === undefined) { - console.error('❌ Usage: npm run user:create '); - console.error('Example: npm run user:create admin@example.com mypassword123'); - process.exit(1); - } + const [email, password] = process.argv.slice(2); - // Basic password validation (check for empty or short passwords) - if (!password || password.length < 6) { - console.error('❌ Password must be at least 6 characters long'); - process.exit(1); - } - - // Enhanced email validation - const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/; - - // Check for common invalid patterns - if (!email.includes('@') || - !email.includes('.') || - email.includes('@@') || - email.includes(' ') || - email.startsWith('@') || - email.endsWith('@') || - email.endsWith('.') || - email.includes('@.') || - email.includes('.@') || - !emailRegex.test(email)) { - console.error('❌ Invalid email format'); - process.exit(1); - } - - try { - console.log(`Creating user with email: ${email}`); - - // Check if user already exists - const existingUser = await User.findOne({ where: { email } }); - if (existingUser) { - console.error(`❌ User with email ${email} already exists`); - process.exit(1); + if (!email || password === undefined) { + console.error('❌ Usage: npm run user:create '); + console.error( + 'Example: npm run user:create admin@example.com mypassword123' + ); + process.exit(1); + } + + // Basic password validation (check for empty or short passwords) + if (!password || password.length < 6) { + console.error('❌ Password must be at least 6 characters long'); + process.exit(1); + } + + // Enhanced email validation + const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/; + + // Check for common invalid patterns + if ( + !email.includes('@') || + !email.includes('.') || + email.includes('@@') || + email.includes(' ') || + email.startsWith('@') || + email.endsWith('@') || + email.endsWith('.') || + email.includes('@.') || + email.includes('.@') || + !emailRegex.test(email) + ) { + console.error('❌ Invalid email format'); + process.exit(1); + } + + try { + console.log(`Creating user with email: ${email}`); + + // Check if user already exists + const existingUser = await User.findOne({ where: { email } }); + if (existingUser) { + console.error(`❌ User with email ${email} already exists`); + process.exit(1); + } + + // Hash the password manually since the hook might not be working in this context + const hashedPassword = await bcrypt.hash(password, 10); + + // Create the user + const user = await User.create({ + email, + password_digest: hashedPassword, + }); + + console.log('✅ User created successfully'); + console.log(`📧 Email: ${user.email}`); + console.log(`🆔 User ID: ${user.id}`); + console.log(`📅 Created: ${user.created_at}`); + + process.exit(0); + } catch (error) { + console.error('❌ Error creating user:', error.message); + process.exit(1); } - - // Hash the password manually since the hook might not be working in this context - const hashedPassword = await bcrypt.hash(password, 10); - - // Create the user - const user = await User.create({ - email, - password_digest: hashedPassword - }); - - console.log('✅ User created successfully'); - console.log(`📧 Email: ${user.email}`); - console.log(`🆔 User ID: ${user.id}`); - console.log(`📅 Created: ${user.created_at}`); - - process.exit(0); - } catch (error) { - console.error('❌ Error creating user:', error.message); - process.exit(1); - } } -createUser(); \ No newline at end of file +createUser(); diff --git a/backend/seeders/dev-seeder.js b/backend/seeders/dev-seeder.js index 3aac596..beae8dc 100644 --- a/backend/seeders/dev-seeder.js +++ b/backend/seeders/dev-seeder.js @@ -1,652 +1,809 @@ -const { User, Area, Project, Task, Tag, Note, InboxItem } = require('../models'); +const { + User, + Area, + Project, + Task, + Tag, + Note, + InboxItem, +} = require('../models'); const bcrypt = require('bcrypt'); const { createMassiveTaskData } = require('./massive-tasks'); async function seedDatabase() { - try { - console.log('🌱 Starting database seeding...'); + try { + console.log('🌱 Starting database seeding...'); - // Create SEPARATE test user for seeding (never overwrite existing users!) - console.log('👤 Creating separate test user for testing...'); - const testEmail = 'test@tududi.com'; - - let testUser = await User.findOne({ where: { email: testEmail } }); - - if (!testUser) { - testUser = await User.create({ - name: 'Test User', - email: testEmail, - password_digest: await bcrypt.hash('password123', 10), - appearance: 'light', - language: 'en', - timezone: 'Europe/Athens' - }); - console.log('✅ Created new test user with ID:', testUser.id); - } else { - console.log('✅ Found existing test user with ID:', testUser.id); - // Clear ONLY the test user's data to refresh it - console.log('🧹 Clearing test user data for fresh seeding...'); - await Task.destroy({ where: { user_id: testUser.id } }); - await Project.destroy({ where: { user_id: testUser.id } }); - await Area.destroy({ where: { user_id: testUser.id } }); - await Tag.destroy({ where: { user_id: testUser.id } }); - await Note.destroy({ where: { user_id: testUser.id } }); - await InboxItem.destroy({ where: { user_id: testUser.id } }); - } + // Create SEPARATE test user for seeding (never overwrite existing users!) + console.log('👤 Creating separate test user for testing...'); + const testEmail = 'test@tududi.com'; - // Create areas - console.log('📁 Creating areas...'); - const areas = await Area.bulkCreate([ - { name: 'Personal', user_id: testUser.id }, - { name: 'Work', user_id: testUser.id }, - { name: 'Health & Fitness', user_id: testUser.id }, - { name: 'Learning', user_id: testUser.id }, - { name: 'Home & Family', user_id: testUser.id }, - { name: 'Finance', user_id: testUser.id }, - { name: 'Travel', user_id: testUser.id }, - { name: 'Hobbies', user_id: testUser.id }, - { name: 'Social', user_id: testUser.id }, - { name: 'Career', user_id: testUser.id } - ]); + let testUser = await User.findOne({ where: { email: testEmail } }); - // Create projects - console.log('📂 Creating projects...'); - const projects = await Project.bulkCreate([ - { - name: 'Website Redesign', - description: 'Complete overhaul of company website', - user_id: testUser.id, - area_id: areas[1].id, - active: true, - due_date_at: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000) // 30 days from now - }, - { - name: 'Learn React Native', - description: 'Master mobile app development', - user_id: testUser.id, - area_id: areas[3].id, - active: true - }, - { - name: 'Home Renovation', - description: 'Kitchen and bathroom updates', - user_id: testUser.id, - area_id: areas[4].id, - active: true, - due_date_at: new Date(Date.now() + 60 * 24 * 60 * 60 * 1000) // 60 days from now - }, - { - name: 'Fitness Challenge', - description: '90-day fitness transformation', - user_id: testUser.id, - area_id: areas[2].id, - active: true, - due_date_at: new Date(Date.now() + 90 * 24 * 60 * 60 * 1000) // 90 days from now - }, - { - name: 'Side Business', - description: 'Launch online consulting service', - user_id: testUser.id, - area_id: areas[1].id, - active: true - }, - { - name: 'Investment Portfolio', - description: 'Build diversified investment portfolio', - user_id: testUser.id, - area_id: areas[5].id, - active: true, - due_date_at: new Date(Date.now() + 120 * 24 * 60 * 60 * 1000) // 120 days from now - }, - { - name: 'Europe Trip 2024', - description: 'Plan and execute 3-week European vacation', - user_id: testUser.id, - area_id: areas[6].id, - active: true, - due_date_at: new Date(Date.now() + 180 * 24 * 60 * 60 * 1000) // 180 days from now - }, - { - name: 'Photography Mastery', - description: 'Learn advanced photography techniques', - user_id: testUser.id, - area_id: areas[7].id, - active: true - }, - { - name: 'Professional Certification', - description: 'Get AWS Solutions Architect certification', - user_id: testUser.id, - area_id: areas[9].id, - active: true, - due_date_at: new Date(Date.now() + 150 * 24 * 60 * 60 * 1000) // 150 days from now - }, - { - name: 'Garden Makeover', - description: 'Transform backyard into productive garden', - user_id: testUser.id, - area_id: areas[4].id, - active: true, - due_date_at: new Date(Date.now() + 45 * 24 * 60 * 60 * 1000) // 45 days from now - }, - { - name: 'Blog Launch', - description: 'Start personal tech blog', - user_id: testUser.id, - area_id: areas[0].id, - active: true - }, - { - name: 'Language Learning Spanish', - description: 'Become conversational in Spanish', - user_id: testUser.id, - area_id: areas[3].id, - active: false // Paused project - }, - { - name: 'Wedding Planning', - description: 'Plan and organize wedding ceremony', - user_id: testUser.id, - area_id: areas[8].id, - active: true, - due_date_at: new Date(Date.now() + 365 * 24 * 60 * 60 * 1000) // 1 year from now - }, - { - name: 'Meal Prep System', - description: 'Establish weekly meal preparation routine', - user_id: testUser.id, - area_id: areas[2].id, - active: true - }, - { - name: 'Smart Home Setup', - description: 'Install and configure smart home devices', - user_id: testUser.id, - area_id: areas[4].id, - active: true, - due_date_at: new Date(Date.now() + 21 * 24 * 60 * 60 * 1000) // 21 days from now - } - ]); + if (!testUser) { + testUser = await User.create({ + name: 'Test User', + email: testEmail, + password_digest: await bcrypt.hash('password123', 10), + appearance: 'light', + language: 'en', + timezone: 'Europe/Athens', + }); + console.log('✅ Created new test user with ID:', testUser.id); + } else { + console.log('✅ Found existing test user with ID:', testUser.id); + // Clear ONLY the test user's data to refresh it + console.log('🧹 Clearing test user data for fresh seeding...'); + await Task.destroy({ where: { user_id: testUser.id } }); + await Project.destroy({ where: { user_id: testUser.id } }); + await Area.destroy({ where: { user_id: testUser.id } }); + await Tag.destroy({ where: { user_id: testUser.id } }); + await Note.destroy({ where: { user_id: testUser.id } }); + await InboxItem.destroy({ where: { user_id: testUser.id } }); + } - // Create tags - console.log('🏷️ Creating tags...'); - const tags = await Tag.bulkCreate([ - { name: 'urgent', user_id: testUser.id }, - { name: 'quick-win', user_id: testUser.id }, - { name: 'research', user_id: testUser.id }, - { name: 'meeting', user_id: testUser.id }, - { name: 'creative', user_id: testUser.id }, - { name: 'phone-call', user_id: testUser.id }, - { name: 'online', user_id: testUser.id }, - { name: 'weekend', user_id: testUser.id }, - { name: 'shopping', user_id: testUser.id }, - { name: 'admin', user_id: testUser.id }, - { name: 'waiting-for', user_id: testUser.id }, - { name: 'someday-maybe', user_id: testUser.id }, - { name: 'high-energy', user_id: testUser.id }, - { name: 'low-energy', user_id: testUser.id }, - { name: 'collaboration', user_id: testUser.id }, - { name: 'learning', user_id: testUser.id }, - { name: 'maintenance', user_id: testUser.id }, - { name: 'financial', user_id: testUser.id }, - { name: 'health', user_id: testUser.id }, - { name: 'outdoor', user_id: testUser.id }, - { name: 'planning', user_id: testUser.id }, - { name: 'review', user_id: testUser.id }, - { name: 'automation', user_id: testUser.id }, - { name: 'documentation', user_id: testUser.id }, - { name: 'bug-fix', user_id: testUser.id } - ]); + // Create areas + console.log('📁 Creating areas...'); + const areas = await Area.bulkCreate([ + { name: 'Personal', user_id: testUser.id }, + { name: 'Work', user_id: testUser.id }, + { name: 'Health & Fitness', user_id: testUser.id }, + { name: 'Learning', user_id: testUser.id }, + { name: 'Home & Family', user_id: testUser.id }, + { name: 'Finance', user_id: testUser.id }, + { name: 'Travel', user_id: testUser.id }, + { name: 'Hobbies', user_id: testUser.id }, + { name: 'Social', user_id: testUser.id }, + { name: 'Career', user_id: testUser.id }, + ]); - // Helper function to get random date - const getRandomDate = (daysFromNow) => { - const randomDays = Math.floor(Math.random() * daysFromNow); - return new Date(Date.now() + randomDays * 24 * 60 * 60 * 1000); - }; + // Create projects + console.log('📂 Creating projects...'); + const projects = await Project.bulkCreate([ + { + name: 'Website Redesign', + description: 'Complete overhaul of company website', + user_id: testUser.id, + area_id: areas[1].id, + active: true, + due_date_at: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000), // 30 days from now + }, + { + name: 'Learn React Native', + description: 'Master mobile app development', + user_id: testUser.id, + area_id: areas[3].id, + active: true, + }, + { + name: 'Home Renovation', + description: 'Kitchen and bathroom updates', + user_id: testUser.id, + area_id: areas[4].id, + active: true, + due_date_at: new Date(Date.now() + 60 * 24 * 60 * 60 * 1000), // 60 days from now + }, + { + name: 'Fitness Challenge', + description: '90-day fitness transformation', + user_id: testUser.id, + area_id: areas[2].id, + active: true, + due_date_at: new Date(Date.now() + 90 * 24 * 60 * 60 * 1000), // 90 days from now + }, + { + name: 'Side Business', + description: 'Launch online consulting service', + user_id: testUser.id, + area_id: areas[1].id, + active: true, + }, + { + name: 'Investment Portfolio', + description: 'Build diversified investment portfolio', + user_id: testUser.id, + area_id: areas[5].id, + active: true, + due_date_at: new Date(Date.now() + 120 * 24 * 60 * 60 * 1000), // 120 days from now + }, + { + name: 'Europe Trip 2024', + description: 'Plan and execute 3-week European vacation', + user_id: testUser.id, + area_id: areas[6].id, + active: true, + due_date_at: new Date(Date.now() + 180 * 24 * 60 * 60 * 1000), // 180 days from now + }, + { + name: 'Photography Mastery', + description: 'Learn advanced photography techniques', + user_id: testUser.id, + area_id: areas[7].id, + active: true, + }, + { + name: 'Professional Certification', + description: 'Get AWS Solutions Architect certification', + user_id: testUser.id, + area_id: areas[9].id, + active: true, + due_date_at: new Date(Date.now() + 150 * 24 * 60 * 60 * 1000), // 150 days from now + }, + { + name: 'Garden Makeover', + description: 'Transform backyard into productive garden', + user_id: testUser.id, + area_id: areas[4].id, + active: true, + due_date_at: new Date(Date.now() + 45 * 24 * 60 * 60 * 1000), // 45 days from now + }, + { + name: 'Blog Launch', + description: 'Start personal tech blog', + user_id: testUser.id, + area_id: areas[0].id, + active: true, + }, + { + name: 'Language Learning Spanish', + description: 'Become conversational in Spanish', + user_id: testUser.id, + area_id: areas[3].id, + active: false, // Paused project + }, + { + name: 'Wedding Planning', + description: 'Plan and organize wedding ceremony', + user_id: testUser.id, + area_id: areas[8].id, + active: true, + due_date_at: new Date(Date.now() + 365 * 24 * 60 * 60 * 1000), // 1 year from now + }, + { + name: 'Meal Prep System', + description: 'Establish weekly meal preparation routine', + user_id: testUser.id, + area_id: areas[2].id, + active: true, + }, + { + name: 'Smart Home Setup', + description: 'Install and configure smart home devices', + user_id: testUser.id, + area_id: areas[4].id, + active: true, + due_date_at: new Date(Date.now() + 21 * 24 * 60 * 60 * 1000), // 21 days from now + }, + ]); - const getPastDate = (daysAgo) => { - const randomDays = Math.floor(Math.random() * daysAgo); - return new Date(Date.now() - randomDays * 24 * 60 * 60 * 1000); - }; + // Create tags + console.log('🏷️ Creating tags...'); + const tags = await Tag.bulkCreate([ + { name: 'urgent', user_id: testUser.id }, + { name: 'quick-win', user_id: testUser.id }, + { name: 'research', user_id: testUser.id }, + { name: 'meeting', user_id: testUser.id }, + { name: 'creative', user_id: testUser.id }, + { name: 'phone-call', user_id: testUser.id }, + { name: 'online', user_id: testUser.id }, + { name: 'weekend', user_id: testUser.id }, + { name: 'shopping', user_id: testUser.id }, + { name: 'admin', user_id: testUser.id }, + { name: 'waiting-for', user_id: testUser.id }, + { name: 'someday-maybe', user_id: testUser.id }, + { name: 'high-energy', user_id: testUser.id }, + { name: 'low-energy', user_id: testUser.id }, + { name: 'collaboration', user_id: testUser.id }, + { name: 'learning', user_id: testUser.id }, + { name: 'maintenance', user_id: testUser.id }, + { name: 'financial', user_id: testUser.id }, + { name: 'health', user_id: testUser.id }, + { name: 'outdoor', user_id: testUser.id }, + { name: 'planning', user_id: testUser.id }, + { name: 'review', user_id: testUser.id }, + { name: 'automation', user_id: testUser.id }, + { name: 'documentation', user_id: testUser.id }, + { name: 'bug-fix', user_id: testUser.id }, + ]); - // Create tasks - console.log('✅ Creating massive task dataset...'); - const taskData = createMassiveTaskData(projects, getRandomDate, getPastDate); + // Helper function to get random date + const getRandomDate = (daysFromNow) => { + const randomDays = Math.floor(Math.random() * daysFromNow); + return new Date(Date.now() + randomDays * 24 * 60 * 60 * 1000); + }; - const tasks = []; - for (const taskInfo of taskData) { - const task = await Task.create({ - ...taskInfo, - user_id: testUser.id, - note: taskInfo.note || null - }); - tasks.push(task); - } + const getPastDate = (daysAgo) => { + const randomDays = Math.floor(Math.random() * daysAgo); + return new Date(Date.now() - randomDays * 24 * 60 * 60 * 1000); + }; - // Create additional backlog tasks with old creation dates for realistic metrics - console.log('📅 Creating backlog tasks with older dates...'); - const backlogTaskNames = [ - 'Organize old photo albums', - 'Learn French language basics', - 'Research retirement planning options', - 'Clean out basement storage', - 'Update professional portfolio', - 'Plan career development goals', - 'Research home security systems', - 'Organize digital file system', - 'Plan vacation for next year', - 'Research new investment strategies', - 'Update emergency preparedness kit', - 'Learn new cooking techniques', - 'Research sustainable living options', - 'Plan home office reorganization', - 'Update professional headshots', - 'Research online course options', - 'Plan garden landscaping project', - 'Research new technology trends', - 'Update financial planning documents', - 'Plan family history research', - 'Research health and wellness programs', - 'Plan hobby room organization', - 'Research eco-friendly home improvements', - 'Update professional networking', - 'Plan creative writing project', - 'Research mindfulness practices', - 'Plan workshop or shed organization', - 'Research travel planning tools', - 'Update subscription management', - 'Plan digital decluttering project' - ]; + // Create tasks + console.log('✅ Creating massive task dataset...'); + const taskData = createMassiveTaskData( + projects, + getRandomDate, + getPastDate + ); - for (let i = 0; i < backlogTaskNames.length; i++) { - const daysAgo = Math.floor(Math.random() * 120) + 31; // 31-150 days ago - const oldDate = new Date(Date.now() - daysAgo * 24 * 60 * 60 * 1000); - - const backlogTask = await Task.create({ - name: backlogTaskNames[i], - priority: Math.floor(Math.random() * 3), - status: Math.random() < 0.9 ? 0 : 1, // 90% not started, 10% in progress - user_id: testUser.id, - project_id: Math.random() < 0.3 ? projects[Math.floor(Math.random() * projects.length)].id : null, - due_date: Math.random() < 0.2 ? getRandomDate(30) : null, - created_at: oldDate, - updated_at: oldDate - }); - tasks.push(backlogTask); - } + const tasks = []; + for (const taskInfo of taskData) { + const task = await Task.create({ + ...taskInfo, + user_id: testUser.id, + note: taskInfo.note || null, + }); + tasks.push(task); + } - // Create tasks due today for realistic "Due Today" section - console.log('📅 Creating tasks due today...'); - const todayTaskNames = [ - 'Submit weekly status report', - 'Call insurance company about claim', - 'Pick up prescription medication', - 'Schedule appointment with accountant', - 'Review and approve team proposals', - 'Prepare presentation slides for Monday', - 'Complete expense report submission', - 'Follow up on pending client emails', - 'Review contract terms and conditions', - 'Update project timeline document' - ]; + // Create additional backlog tasks with old creation dates for realistic metrics + console.log('📅 Creating backlog tasks with older dates...'); + const backlogTaskNames = [ + 'Organize old photo albums', + 'Learn French language basics', + 'Research retirement planning options', + 'Clean out basement storage', + 'Update professional portfolio', + 'Plan career development goals', + 'Research home security systems', + 'Organize digital file system', + 'Plan vacation for next year', + 'Research new investment strategies', + 'Update emergency preparedness kit', + 'Learn new cooking techniques', + 'Research sustainable living options', + 'Plan home office reorganization', + 'Update professional headshots', + 'Research online course options', + 'Plan garden landscaping project', + 'Research new technology trends', + 'Update financial planning documents', + 'Plan family history research', + 'Research health and wellness programs', + 'Plan hobby room organization', + 'Research eco-friendly home improvements', + 'Update professional networking', + 'Plan creative writing project', + 'Research mindfulness practices', + 'Plan workshop or shed organization', + 'Research travel planning tools', + 'Update subscription management', + 'Plan digital decluttering project', + ]; - const today = new Date(); - today.setHours(0, 0, 0, 0); // Set to start of today + for (let i = 0; i < backlogTaskNames.length; i++) { + const daysAgo = Math.floor(Math.random() * 120) + 31; // 31-150 days ago + const oldDate = new Date( + Date.now() - daysAgo * 24 * 60 * 60 * 1000 + ); - for (let i = 0; i < todayTaskNames.length; i++) { - const todayTask = await Task.create({ - name: todayTaskNames[i], - priority: Math.floor(Math.random() * 3), - status: Math.random() < 0.8 ? 0 : 1, // 80% not started, 20% in progress - user_id: testUser.id, - project_id: Math.random() < 0.4 ? projects[Math.floor(Math.random() * projects.length)].id : null, - due_date: today, - created_at: getPastDate(7), // Created within last week - updated_at: getPastDate(7) - }); - tasks.push(todayTask); - } + const backlogTask = await Task.create({ + name: backlogTaskNames[i], + priority: Math.floor(Math.random() * 3), + status: Math.random() < 0.9 ? 0 : 1, // 90% not started, 10% in progress + user_id: testUser.id, + project_id: + Math.random() < 0.3 + ? projects[Math.floor(Math.random() * projects.length)] + .id + : null, + due_date: Math.random() < 0.2 ? getRandomDate(30) : null, + created_at: oldDate, + updated_at: oldDate, + }); + tasks.push(backlogTask); + } - // Create intelligent task-tag associations - console.log('🔗 Creating intelligent task-tag associations...'); - - // Helper function to tag tasks based on keywords and patterns - const addIntelligentTags = async () => { - for (let i = 0; i < tasks.length; i++) { - const task = tasks[i]; - const taskName = task.name.toLowerCase(); - const taskTags = []; - - // Pattern-based tagging for AI trigger recognition - if (taskName.includes('urgent') || taskName.includes('asap') || task.due_date && new Date(task.due_date) < new Date()) { - taskTags.push(tags[0]); // urgent - } - - if (taskName.includes('call') || taskName.includes('phone')) { - taskTags.push(tags[5]); // phone-call - } - - if (taskName.includes('meeting') || taskName.includes('standup') || taskName.includes('conference')) { - taskTags.push(tags[3]); // meeting - } - - if (taskName.includes('research') || taskName.includes('study') || taskName.includes('learn')) { - taskTags.push(tags[2]); // research - taskTags.push(tags[15]); // learning - } - - if (taskName.includes('buy') || taskName.includes('purchase') || taskName.includes('shop')) { - taskTags.push(tags[8]); // shopping - } - - if (taskName.includes('design') || taskName.includes('create') || taskName.includes('write') || taskName.includes('paint')) { - taskTags.push(tags[4]); // creative - } - - if (taskName.includes('health') || taskName.includes('doctor') || taskName.includes('medical') || taskName.includes('fitness') || taskName.includes('workout')) { - taskTags.push(tags[18]); // health - } - - if (taskName.includes('financial') || taskName.includes('budget') || taskName.includes('invest') || taskName.includes('money') || taskName.includes('pay')) { - taskTags.push(tags[17]); // financial - } - - if (taskName.includes('outdoor') || taskName.includes('garden') || taskName.includes('hiking') || taskName.includes('park')) { - taskTags.push(tags[19]); // outdoor - } - - if (taskName.includes('plan') || taskName.includes('schedule') || taskName.includes('organize')) { - taskTags.push(tags[20]); // planning - } - - if (taskName.includes('review') || taskName.includes('check') || taskName.includes('audit')) { - taskTags.push(tags[21]); // review - } - - if (taskName.includes('fix') || taskName.includes('repair') || taskName.includes('maintain') || taskName.includes('clean')) { - taskTags.push(tags[16]); // maintenance - } - - if (taskName.includes('weekend') || task.due_date && [0, 6].includes(new Date(task.due_date).getDay())) { - taskTags.push(tags[7]); // weekend - } - - if (taskName.includes('online') || taskName.includes('website') || taskName.includes('digital') || taskName.includes('app')) { - taskTags.push(tags[6]); // online - } - - if (task.status === 4) { // waiting status - taskTags.push(tags[10]); // waiting-for - } - - if (task.priority === 0 && !task.due_date) { - taskTags.push(tags[11]); // someday-maybe - } - - if (taskName.includes('team') || taskName.includes('group') || taskName.includes('collaborate')) { - taskTags.push(tags[14]); // collaboration - } - - if (taskName.includes('quick') || taskName.includes('fast') || taskName.includes('simple')) { - taskTags.push(tags[1]); // quick-win - } - - if (taskName.includes('energy') || taskName.includes('intensive') || taskName.includes('focus')) { - taskTags.push(tags[12]); // high-energy - } - - if (taskName.includes('relax') || taskName.includes('easy') || taskName.includes('light')) { - taskTags.push(tags[13]); // low-energy - } - - if (taskName.includes('automate') || taskName.includes('script') || taskName.includes('automation')) { - taskTags.push(tags[22]); // automation - } - - if (taskName.includes('document') || taskName.includes('write') || taskName.includes('manual')) { - taskTags.push(tags[23]); // documentation - } - - if (taskName.includes('bug') || taskName.includes('fix') || taskName.includes('error')) { - taskTags.push(tags[24]); // bug-fix - } - - // Apply tags if any were identified - if (taskTags.length > 0) { - await task.setTags(taskTags); - } - } - }; - - await addIntelligentTags(); + // Create tasks due today for realistic "Due Today" section + console.log('📅 Creating tasks due today...'); + const todayTaskNames = [ + 'Submit weekly status report', + 'Call insurance company about claim', + 'Pick up prescription medication', + 'Schedule appointment with accountant', + 'Review and approve team proposals', + 'Prepare presentation slides for Monday', + 'Complete expense report submission', + 'Follow up on pending client emails', + 'Review contract terms and conditions', + 'Update project timeline document', + ]; - // Create task events for AI pattern learning - console.log('📊 Creating task events for AI pattern recognition...'); - const TaskEventService = require('../services/taskEventService'); - - // Create events for completed tasks to show user patterns - const completedTasks = tasks.filter(t => t.status === 2); - for (const task of completedTasks.slice(0, 20)) { // Just first 20 to avoid too much data - try { - // Create task creation event - await TaskEventService.logTaskCreated(task.id, testUser.id, { - name: task.name, - status: 0, - priority: task.priority, - project_id: task.project_id - }, { source: 'web' }); - - // Create status change to in_progress - if (Math.random() < 0.7) { // 70% had in_progress phase - await TaskEventService.logStatusChange(task.id, testUser.id, 0, 1, { source: 'web' }); + const today = new Date(); + today.setHours(0, 0, 0, 0); // Set to start of today + + for (let i = 0; i < todayTaskNames.length; i++) { + const todayTask = await Task.create({ + name: todayTaskNames[i], + priority: Math.floor(Math.random() * 3), + status: Math.random() < 0.8 ? 0 : 1, // 80% not started, 20% in progress + user_id: testUser.id, + project_id: + Math.random() < 0.4 + ? projects[Math.floor(Math.random() * projects.length)] + .id + : null, + due_date: today, + created_at: getPastDate(7), // Created within last week + updated_at: getPastDate(7), + }); + tasks.push(todayTask); } - - // Create completion event - await TaskEventService.logStatusChange(task.id, testUser.id, 1, 2, { source: 'web' }); - } catch (eventError) { - console.log(`Skipping event creation for task ${task.id}: ${eventError.message}`); - } - } - - // Create events for some in-progress tasks - const inProgressTasks = tasks.filter(t => t.status === 1); - for (const task of inProgressTasks.slice(0, 10)) { - try { - await TaskEventService.logTaskCreated(task.id, testUser.id, { - name: task.name, - status: 0, - priority: task.priority, - project_id: task.project_id - }, { source: 'web' }); - - await TaskEventService.logStatusChange(task.id, testUser.id, 0, 1, { source: 'web' }); - } catch (eventError) { - console.log(`Skipping event creation for task ${task.id}: ${eventError.message}`); - } - } - // Create notes - console.log('📝 Creating notes...'); - await Note.bulkCreate([ - { - title: 'Meeting Notes - Website Redesign', - content: 'Key decisions:\n- Use blue and white color scheme\n- Include customer testimonials\n- Mobile-first approach\n- Launch date: End of next month\n\nAction items:\n- Get approval from stakeholders\n- Create prototype by Friday\n- Schedule user testing session', - user_id: testUser.id, - project_id: projects[0].id - }, - { - title: 'React Native Learning Resources', - content: 'Useful links:\n- Official documentation\n- Expo.dev for quick prototyping\n- React Navigation library\n- AsyncStorage for local data\n\nTutorials to check out:\n- React Native School\n- The Net Ninja series\n- React Native Express', - user_id: testUser.id, - project_id: projects[1].id - }, - { - title: 'Home Renovation Budget', - content: 'Budget breakdown:\n- Kitchen: $15,000\n- Bathroom: $8,000\n- Contingency: $3,000\n- Total: $26,000\n\nContractors to contact:\n- ABC Construction: 555-0123\n- Quality Home Builders: 555-0456\n- Elite Renovations: 555-0789', - user_id: testUser.id, - project_id: projects[2].id - }, - { - title: 'Investment Strategy Notes', - content: 'Portfolio allocation goals:\n- 60% Stock index funds\n- 30% Bond index funds\n- 10% International funds\n\nPlatforms to consider:\n- Vanguard\n- Fidelity\n- Charles Schwab\n\nMonthly investment: $1,000', - user_id: testUser.id, - project_id: projects[5].id - }, - { - title: 'Europe Trip Planning', - content: 'Destinations:\n1. Paris, France (5 days)\n2. Rome, Italy (4 days)\n3. Barcelona, Spain (4 days)\n4. Amsterdam, Netherlands (3 days)\n5. Prague, Czech Republic (3 days)\n\nEstimated costs:\n- Flights: $1,200\n- Hotels: $2,500\n- Food: $1,000\n- Activities: $800\n- Total: $5,500', - user_id: testUser.id, - project_id: projects[6].id - }, - { - title: 'Photography Equipment Wishlist', - content: 'Camera gear to consider:\n- Canon EOS R6 Mark II\n- 24-70mm f/2.8 lens\n- 85mm f/1.8 portrait lens\n- Tripod: Manfrotto MT055CXPRO4\n- Editing software: Lightroom + Photoshop\n\nLearning resources:\n- Sean Tucker YouTube channel\n- Peter McKinnon tutorials\n- Local photography meetups', - user_id: testUser.id, - project_id: projects[7].id - }, - { - title: 'Book Recommendations', - content: 'To read:\n- "Deep Work" by Cal Newport\n- "The Lean Startup" by Eric Ries\n- "Atomic Habits" by James Clear\n- "Clean Code" by Robert Martin\n- "The Psychology of Money" by Morgan Housel\n- "Educated" by Tara Westover\n- "Sapiens" by Yuval Noah Harari', - user_id: testUser.id - }, - { - title: 'Recipe Ideas', - content: 'Meals to try:\n- Mediterranean quinoa bowl\n- Thai green curry\n- Homemade pizza\n- Greek lemon chicken\n- Mushroom risotto\n- Korean bulgogi\n- Mexican street corn salad\n- Indian butter chicken\n- Japanese ramen', - user_id: testUser.id - }, - { - title: 'Business Ideas', - content: 'Potential side businesses:\n- Web development consulting\n- Online course creation\n- Photography services\n- Productivity coaching\n- Technical writing\n\nRevenue streams to explore:\n- Subscription services\n- One-time consulting\n- Product sales\n- Affiliate marketing', - user_id: testUser.id, - project_id: projects[4].id - }, - { - title: 'Fitness Goals & Progress', - content: 'Current stats:\n- Weight: 180 lbs\n- Body fat: 18%\n- Bench press: 185 lbs\n- Squat: 225 lbs\n- Deadlift: 275 lbs\n\nGoals (90 days):\n- Weight: 175 lbs\n- Body fat: 15%\n- Bench press: 205 lbs\n- Squat: 255 lbs\n- Deadlift: 315 lbs', - user_id: testUser.id, - project_id: projects[3].id - }, - { - title: 'Weekly Meal Prep Ideas', - content: 'Prep schedule:\nSunday: Protein prep (chicken, fish, tofu)\nMonday: Vegetable chopping\nWednesday: Mid-week refresh\n\nMeal rotation:\n- Breakfast: Overnight oats, egg muffins\n- Lunch: Buddha bowls, salads\n- Dinner: Stir-fries, sheet pan meals\n- Snacks: Greek yogurt, nuts, fruit', - user_id: testUser.id, - project_id: projects[13].id - }, - { - title: 'Smart Home Device List', - content: 'Devices to install:\n- Smart thermostat (Nest)\n- Smart doorbell (Ring)\n- Smart locks (August)\n- Smart lights (Philips Hue)\n- Smart speakers (Echo Dot)\n- Security cameras (Arlo)\n- Smart switches (TP-Link Kasa)\n\nEstimated cost: $2,500\nInstallation timeline: 3 weeks', - user_id: testUser.id, - project_id: projects[14].id - } - ]); + // Create intelligent task-tag associations + console.log('🔗 Creating intelligent task-tag associations...'); - // Create inbox items - console.log('📥 Creating inbox items...'); - await InboxItem.bulkCreate([ - { - content: 'Research new project management tools', - user_id: testUser.id, - processed: false - }, - { - content: 'Plan team building activity for Q4', - user_id: testUser.id, - processed: false - }, - { - content: 'Look into cloud storage solutions', - user_id: testUser.id, - processed: false - }, - { - content: 'Consider learning TypeScript', - user_id: testUser.id, - processed: true - }, - { - content: 'Update emergency contact information', - user_id: testUser.id, - processed: false - }, - { - content: 'Research sustainable investing options', - user_id: testUser.id, - processed: false - }, - { - content: 'Look into ergonomic desk setup', - user_id: testUser.id, - processed: false - }, - { - content: 'Consider getting a pet', - user_id: testUser.id, - processed: false - }, - { - content: 'Research meditation retreats', - user_id: testUser.id, - processed: false - }, - { - content: 'Look into renewable energy for home', - user_id: testUser.id, - processed: false - }, - { - content: 'Consider starting a podcast', - user_id: testUser.id, - processed: true - }, - { - content: 'Research local volunteer opportunities', - user_id: testUser.id, - processed: false - }, - { - content: 'Look into professional coaching', - user_id: testUser.id, - processed: false - }, - { - content: 'Consider learning a musical instrument', - user_id: testUser.id, - processed: false - }, - { - content: 'Research minimalism lifestyle', - user_id: testUser.id, - processed: true - }, - { - content: 'Look into starting a garden', - user_id: testUser.id, - processed: false - }, - { - content: 'Consider learning sign language', - user_id: testUser.id, - processed: false - }, - { - content: 'Research passive income strategies', - user_id: testUser.id, - processed: false - }, - { - content: 'Look into digital nomad lifestyle', - user_id: testUser.id, - processed: false - }, - { - content: 'Consider getting professional headshots', - user_id: testUser.id, - processed: false - } - ]); + // Helper function to tag tasks based on keywords and patterns + const addIntelligentTags = async () => { + for (let i = 0; i < tasks.length; i++) { + const task = tasks[i]; + const taskName = task.name.toLowerCase(); + const taskTags = []; - console.log('✨ Database seeding completed successfully!'); - console.log(`📊 Created test data for SEPARATE test user: + // Pattern-based tagging for AI trigger recognition + if ( + taskName.includes('urgent') || + taskName.includes('asap') || + (task.due_date && new Date(task.due_date) < new Date()) + ) { + taskTags.push(tags[0]); // urgent + } + + if (taskName.includes('call') || taskName.includes('phone')) { + taskTags.push(tags[5]); // phone-call + } + + if ( + taskName.includes('meeting') || + taskName.includes('standup') || + taskName.includes('conference') + ) { + taskTags.push(tags[3]); // meeting + } + + if ( + taskName.includes('research') || + taskName.includes('study') || + taskName.includes('learn') + ) { + taskTags.push(tags[2]); // research + taskTags.push(tags[15]); // learning + } + + if ( + taskName.includes('buy') || + taskName.includes('purchase') || + taskName.includes('shop') + ) { + taskTags.push(tags[8]); // shopping + } + + if ( + taskName.includes('design') || + taskName.includes('create') || + taskName.includes('write') || + taskName.includes('paint') + ) { + taskTags.push(tags[4]); // creative + } + + if ( + taskName.includes('health') || + taskName.includes('doctor') || + taskName.includes('medical') || + taskName.includes('fitness') || + taskName.includes('workout') + ) { + taskTags.push(tags[18]); // health + } + + if ( + taskName.includes('financial') || + taskName.includes('budget') || + taskName.includes('invest') || + taskName.includes('money') || + taskName.includes('pay') + ) { + taskTags.push(tags[17]); // financial + } + + if ( + taskName.includes('outdoor') || + taskName.includes('garden') || + taskName.includes('hiking') || + taskName.includes('park') + ) { + taskTags.push(tags[19]); // outdoor + } + + if ( + taskName.includes('plan') || + taskName.includes('schedule') || + taskName.includes('organize') + ) { + taskTags.push(tags[20]); // planning + } + + if ( + taskName.includes('review') || + taskName.includes('check') || + taskName.includes('audit') + ) { + taskTags.push(tags[21]); // review + } + + if ( + taskName.includes('fix') || + taskName.includes('repair') || + taskName.includes('maintain') || + taskName.includes('clean') + ) { + taskTags.push(tags[16]); // maintenance + } + + if ( + taskName.includes('weekend') || + (task.due_date && + [0, 6].includes(new Date(task.due_date).getDay())) + ) { + taskTags.push(tags[7]); // weekend + } + + if ( + taskName.includes('online') || + taskName.includes('website') || + taskName.includes('digital') || + taskName.includes('app') + ) { + taskTags.push(tags[6]); // online + } + + if (task.status === 4) { + // waiting status + taskTags.push(tags[10]); // waiting-for + } + + if (task.priority === 0 && !task.due_date) { + taskTags.push(tags[11]); // someday-maybe + } + + if ( + taskName.includes('team') || + taskName.includes('group') || + taskName.includes('collaborate') + ) { + taskTags.push(tags[14]); // collaboration + } + + if ( + taskName.includes('quick') || + taskName.includes('fast') || + taskName.includes('simple') + ) { + taskTags.push(tags[1]); // quick-win + } + + if ( + taskName.includes('energy') || + taskName.includes('intensive') || + taskName.includes('focus') + ) { + taskTags.push(tags[12]); // high-energy + } + + if ( + taskName.includes('relax') || + taskName.includes('easy') || + taskName.includes('light') + ) { + taskTags.push(tags[13]); // low-energy + } + + if ( + taskName.includes('automate') || + taskName.includes('script') || + taskName.includes('automation') + ) { + taskTags.push(tags[22]); // automation + } + + if ( + taskName.includes('document') || + taskName.includes('write') || + taskName.includes('manual') + ) { + taskTags.push(tags[23]); // documentation + } + + if ( + taskName.includes('bug') || + taskName.includes('fix') || + taskName.includes('error') + ) { + taskTags.push(tags[24]); // bug-fix + } + + // Apply tags if any were identified + if (taskTags.length > 0) { + await task.setTags(taskTags); + } + } + }; + + await addIntelligentTags(); + + // Create task events for AI pattern learning + console.log('📊 Creating task events for AI pattern recognition...'); + const TaskEventService = require('../services/taskEventService'); + + // Create events for completed tasks to show user patterns + const completedTasks = tasks.filter((t) => t.status === 2); + for (const task of completedTasks.slice(0, 20)) { + // Just first 20 to avoid too much data + try { + // Create task creation event + await TaskEventService.logTaskCreated( + task.id, + testUser.id, + { + name: task.name, + status: 0, + priority: task.priority, + project_id: task.project_id, + }, + { source: 'web' } + ); + + // Create status change to in_progress + if (Math.random() < 0.7) { + // 70% had in_progress phase + await TaskEventService.logStatusChange( + task.id, + testUser.id, + 0, + 1, + { source: 'web' } + ); + } + + // Create completion event + await TaskEventService.logStatusChange( + task.id, + testUser.id, + 1, + 2, + { source: 'web' } + ); + } catch (eventError) { + console.log( + `Skipping event creation for task ${task.id}: ${eventError.message}` + ); + } + } + + // Create events for some in-progress tasks + const inProgressTasks = tasks.filter((t) => t.status === 1); + for (const task of inProgressTasks.slice(0, 10)) { + try { + await TaskEventService.logTaskCreated( + task.id, + testUser.id, + { + name: task.name, + status: 0, + priority: task.priority, + project_id: task.project_id, + }, + { source: 'web' } + ); + + await TaskEventService.logStatusChange( + task.id, + testUser.id, + 0, + 1, + { source: 'web' } + ); + } catch (eventError) { + console.log( + `Skipping event creation for task ${task.id}: ${eventError.message}` + ); + } + } + + // Create notes + console.log('📝 Creating notes...'); + await Note.bulkCreate([ + { + title: 'Meeting Notes - Website Redesign', + content: + 'Key decisions:\n- Use blue and white color scheme\n- Include customer testimonials\n- Mobile-first approach\n- Launch date: End of next month\n\nAction items:\n- Get approval from stakeholders\n- Create prototype by Friday\n- Schedule user testing session', + user_id: testUser.id, + project_id: projects[0].id, + }, + { + title: 'React Native Learning Resources', + content: + 'Useful links:\n- Official documentation\n- Expo.dev for quick prototyping\n- React Navigation library\n- AsyncStorage for local data\n\nTutorials to check out:\n- React Native School\n- The Net Ninja series\n- React Native Express', + user_id: testUser.id, + project_id: projects[1].id, + }, + { + title: 'Home Renovation Budget', + content: + 'Budget breakdown:\n- Kitchen: $15,000\n- Bathroom: $8,000\n- Contingency: $3,000\n- Total: $26,000\n\nContractors to contact:\n- ABC Construction: 555-0123\n- Quality Home Builders: 555-0456\n- Elite Renovations: 555-0789', + user_id: testUser.id, + project_id: projects[2].id, + }, + { + title: 'Investment Strategy Notes', + content: + 'Portfolio allocation goals:\n- 60% Stock index funds\n- 30% Bond index funds\n- 10% International funds\n\nPlatforms to consider:\n- Vanguard\n- Fidelity\n- Charles Schwab\n\nMonthly investment: $1,000', + user_id: testUser.id, + project_id: projects[5].id, + }, + { + title: 'Europe Trip Planning', + content: + 'Destinations:\n1. Paris, France (5 days)\n2. Rome, Italy (4 days)\n3. Barcelona, Spain (4 days)\n4. Amsterdam, Netherlands (3 days)\n5. Prague, Czech Republic (3 days)\n\nEstimated costs:\n- Flights: $1,200\n- Hotels: $2,500\n- Food: $1,000\n- Activities: $800\n- Total: $5,500', + user_id: testUser.id, + project_id: projects[6].id, + }, + { + title: 'Photography Equipment Wishlist', + content: + 'Camera gear to consider:\n- Canon EOS R6 Mark II\n- 24-70mm f/2.8 lens\n- 85mm f/1.8 portrait lens\n- Tripod: Manfrotto MT055CXPRO4\n- Editing software: Lightroom + Photoshop\n\nLearning resources:\n- Sean Tucker YouTube channel\n- Peter McKinnon tutorials\n- Local photography meetups', + user_id: testUser.id, + project_id: projects[7].id, + }, + { + title: 'Book Recommendations', + content: + 'To read:\n- "Deep Work" by Cal Newport\n- "The Lean Startup" by Eric Ries\n- "Atomic Habits" by James Clear\n- "Clean Code" by Robert Martin\n- "The Psychology of Money" by Morgan Housel\n- "Educated" by Tara Westover\n- "Sapiens" by Yuval Noah Harari', + user_id: testUser.id, + }, + { + title: 'Recipe Ideas', + content: + 'Meals to try:\n- Mediterranean quinoa bowl\n- Thai green curry\n- Homemade pizza\n- Greek lemon chicken\n- Mushroom risotto\n- Korean bulgogi\n- Mexican street corn salad\n- Indian butter chicken\n- Japanese ramen', + user_id: testUser.id, + }, + { + title: 'Business Ideas', + content: + 'Potential side businesses:\n- Web development consulting\n- Online course creation\n- Photography services\n- Productivity coaching\n- Technical writing\n\nRevenue streams to explore:\n- Subscription services\n- One-time consulting\n- Product sales\n- Affiliate marketing', + user_id: testUser.id, + project_id: projects[4].id, + }, + { + title: 'Fitness Goals & Progress', + content: + 'Current stats:\n- Weight: 180 lbs\n- Body fat: 18%\n- Bench press: 185 lbs\n- Squat: 225 lbs\n- Deadlift: 275 lbs\n\nGoals (90 days):\n- Weight: 175 lbs\n- Body fat: 15%\n- Bench press: 205 lbs\n- Squat: 255 lbs\n- Deadlift: 315 lbs', + user_id: testUser.id, + project_id: projects[3].id, + }, + { + title: 'Weekly Meal Prep Ideas', + content: + 'Prep schedule:\nSunday: Protein prep (chicken, fish, tofu)\nMonday: Vegetable chopping\nWednesday: Mid-week refresh\n\nMeal rotation:\n- Breakfast: Overnight oats, egg muffins\n- Lunch: Buddha bowls, salads\n- Dinner: Stir-fries, sheet pan meals\n- Snacks: Greek yogurt, nuts, fruit', + user_id: testUser.id, + project_id: projects[13].id, + }, + { + title: 'Smart Home Device List', + content: + 'Devices to install:\n- Smart thermostat (Nest)\n- Smart doorbell (Ring)\n- Smart locks (August)\n- Smart lights (Philips Hue)\n- Smart speakers (Echo Dot)\n- Security cameras (Arlo)\n- Smart switches (TP-Link Kasa)\n\nEstimated cost: $2,500\nInstallation timeline: 3 weeks', + user_id: testUser.id, + project_id: projects[14].id, + }, + ]); + + // Create inbox items + console.log('📥 Creating inbox items...'); + await InboxItem.bulkCreate([ + { + content: 'Research new project management tools', + user_id: testUser.id, + processed: false, + }, + { + content: 'Plan team building activity for Q4', + user_id: testUser.id, + processed: false, + }, + { + content: 'Look into cloud storage solutions', + user_id: testUser.id, + processed: false, + }, + { + content: 'Consider learning TypeScript', + user_id: testUser.id, + processed: true, + }, + { + content: 'Update emergency contact information', + user_id: testUser.id, + processed: false, + }, + { + content: 'Research sustainable investing options', + user_id: testUser.id, + processed: false, + }, + { + content: 'Look into ergonomic desk setup', + user_id: testUser.id, + processed: false, + }, + { + content: 'Consider getting a pet', + user_id: testUser.id, + processed: false, + }, + { + content: 'Research meditation retreats', + user_id: testUser.id, + processed: false, + }, + { + content: 'Look into renewable energy for home', + user_id: testUser.id, + processed: false, + }, + { + content: 'Consider starting a podcast', + user_id: testUser.id, + processed: true, + }, + { + content: 'Research local volunteer opportunities', + user_id: testUser.id, + processed: false, + }, + { + content: 'Look into professional coaching', + user_id: testUser.id, + processed: false, + }, + { + content: 'Consider learning a musical instrument', + user_id: testUser.id, + processed: false, + }, + { + content: 'Research minimalism lifestyle', + user_id: testUser.id, + processed: true, + }, + { + content: 'Look into starting a garden', + user_id: testUser.id, + processed: false, + }, + { + content: 'Consider learning sign language', + user_id: testUser.id, + processed: false, + }, + { + content: 'Research passive income strategies', + user_id: testUser.id, + processed: false, + }, + { + content: 'Look into digital nomad lifestyle', + user_id: testUser.id, + processed: false, + }, + { + content: 'Consider getting professional headshots', + user_id: testUser.id, + processed: false, + }, + ]); + + console.log('✨ Database seeding completed successfully!'); + console.log(`📊 Created test data for SEPARATE test user: - 1 test user (test@tududi.com / password123) - ${areas.length} areas - ${projects.length} projects @@ -655,28 +812,31 @@ async function seedDatabase() { - 12 notes - 20 inbox items`); - console.log('\n🚀 You can now:'); - console.log('- Login with test@tududi.com / password123 to see test data'); - console.log('- Your original account data is preserved and untouched'); - console.log('- Explore the Today view with various task statuses'); - console.log('- Test task editing, priority changes, etc.'); - console.log('- View projects with different completion states'); - console.log('- Test the task timeline feature'); - - } catch (error) { - console.error('❌ Error seeding database:', error); - } + console.log('\n🚀 You can now:'); + console.log( + '- Login with test@tududi.com / password123 to see test data' + ); + console.log('- Your original account data is preserved and untouched'); + console.log('- Explore the Today view with various task statuses'); + console.log('- Test task editing, priority changes, etc.'); + console.log('- View projects with different completion states'); + console.log('- Test the task timeline feature'); + } catch (error) { + console.error('❌ Error seeding database:', error); + } } module.exports = { seedDatabase }; // Allow running directly if (require.main === module) { - seedDatabase().then(() => { - console.log('🏁 Seeding finished'); - process.exit(0); - }).catch(error => { - console.error('💥 Seeding failed:', error); - process.exit(1); - }); -} \ No newline at end of file + seedDatabase() + .then(() => { + console.log('🏁 Seeding finished'); + process.exit(0); + }) + .catch((error) => { + console.error('💥 Seeding failed:', error); + process.exit(1); + }); +} diff --git a/backend/seeders/expanded-tasks.js b/backend/seeders/expanded-tasks.js index 6bf4989..16c5834 100644 --- a/backend/seeders/expanded-tasks.js +++ b/backend/seeders/expanded-tasks.js @@ -1,252 +1,849 @@ // Helper function to create expanded task data function createExpandedTaskData(projects, getRandomDate, getPastDate) { - return [ - // Website Redesign Project Tasks (Project 0) - { name: 'Research competitor websites', project_id: projects[0].id, priority: 1, status: 2, completed_at: getPastDate(5) }, - { name: 'Create wireframes for homepage', project_id: projects[0].id, priority: 2, status: 1 }, - { name: 'Design new color palette', project_id: projects[0].id, priority: 1, status: 0 }, - { name: 'Write content for About page', project_id: projects[0].id, priority: 1, status: 0 }, - { name: 'Set up staging environment', project_id: projects[0].id, priority: 2, status: 0, due_date: getRandomDate(7) }, - { name: 'Optimize images for web', project_id: projects[0].id, priority: 1, status: 0 }, - { name: 'Implement responsive design', project_id: projects[0].id, priority: 2, status: 0 }, - { name: 'Test cross-browser compatibility', project_id: projects[0].id, priority: 1, status: 0 }, - { name: 'Setup Google Analytics', project_id: projects[0].id, priority: 1, status: 0 }, - { name: 'Create contact form', project_id: projects[0].id, priority: 1, status: 0 }, - { name: 'Write SEO meta descriptions', project_id: projects[0].id, priority: 1, status: 0 }, - { name: 'Design mobile navigation', project_id: projects[0].id, priority: 2, status: 0 }, - { name: 'Create footer section', project_id: projects[0].id, priority: 0, status: 0 }, - { name: 'Add social media icons', project_id: projects[0].id, priority: 0, status: 0 }, - { name: 'Setup SSL certificate', project_id: projects[0].id, priority: 2, status: 0, due_date: getRandomDate(5) }, + return [ + // Website Redesign Project Tasks (Project 0) + { + name: 'Research competitor websites', + project_id: projects[0].id, + priority: 1, + status: 2, + completed_at: getPastDate(5), + }, + { + name: 'Create wireframes for homepage', + project_id: projects[0].id, + priority: 2, + status: 1, + }, + { + name: 'Design new color palette', + project_id: projects[0].id, + priority: 1, + status: 0, + }, + { + name: 'Write content for About page', + project_id: projects[0].id, + priority: 1, + status: 0, + }, + { + name: 'Set up staging environment', + project_id: projects[0].id, + priority: 2, + status: 0, + due_date: getRandomDate(7), + }, + { + name: 'Optimize images for web', + project_id: projects[0].id, + priority: 1, + status: 0, + }, + { + name: 'Implement responsive design', + project_id: projects[0].id, + priority: 2, + status: 0, + }, + { + name: 'Test cross-browser compatibility', + project_id: projects[0].id, + priority: 1, + status: 0, + }, + { + name: 'Setup Google Analytics', + project_id: projects[0].id, + priority: 1, + status: 0, + }, + { + name: 'Create contact form', + project_id: projects[0].id, + priority: 1, + status: 0, + }, + { + name: 'Write SEO meta descriptions', + project_id: projects[0].id, + priority: 1, + status: 0, + }, + { + name: 'Design mobile navigation', + project_id: projects[0].id, + priority: 2, + status: 0, + }, + { + name: 'Create footer section', + project_id: projects[0].id, + priority: 0, + status: 0, + }, + { + name: 'Add social media icons', + project_id: projects[0].id, + priority: 0, + status: 0, + }, + { + name: 'Setup SSL certificate', + project_id: projects[0].id, + priority: 2, + status: 0, + due_date: getRandomDate(5), + }, - // Learn React Native Project Tasks (Project 1) - { name: 'Complete React Native tutorial', project_id: projects[1].id, priority: 2, status: 1 }, - { name: 'Build first mobile app', project_id: projects[1].id, priority: 2, status: 0 }, - { name: 'Learn about navigation', project_id: projects[1].id, priority: 1, status: 0 }, - { name: 'Study state management', project_id: projects[1].id, priority: 1, status: 0 }, - { name: 'Practice with APIs', project_id: projects[1].id, priority: 1, status: 0 }, - { name: 'Setup development environment', project_id: projects[1].id, priority: 2, status: 2, completed_at: getPastDate(10) }, - { name: 'Learn about debugging tools', project_id: projects[1].id, priority: 1, status: 0 }, - { name: 'Study push notifications', project_id: projects[1].id, priority: 1, status: 0 }, - { name: 'Learn about app deployment', project_id: projects[1].id, priority: 1, status: 0 }, - { name: 'Practice with AsyncStorage', project_id: projects[1].id, priority: 1, status: 0 }, + // Learn React Native Project Tasks (Project 1) + { + name: 'Complete React Native tutorial', + project_id: projects[1].id, + priority: 2, + status: 1, + }, + { + name: 'Build first mobile app', + project_id: projects[1].id, + priority: 2, + status: 0, + }, + { + name: 'Learn about navigation', + project_id: projects[1].id, + priority: 1, + status: 0, + }, + { + name: 'Study state management', + project_id: projects[1].id, + priority: 1, + status: 0, + }, + { + name: 'Practice with APIs', + project_id: projects[1].id, + priority: 1, + status: 0, + }, + { + name: 'Setup development environment', + project_id: projects[1].id, + priority: 2, + status: 2, + completed_at: getPastDate(10), + }, + { + name: 'Learn about debugging tools', + project_id: projects[1].id, + priority: 1, + status: 0, + }, + { + name: 'Study push notifications', + project_id: projects[1].id, + priority: 1, + status: 0, + }, + { + name: 'Learn about app deployment', + project_id: projects[1].id, + priority: 1, + status: 0, + }, + { + name: 'Practice with AsyncStorage', + project_id: projects[1].id, + priority: 1, + status: 0, + }, - // Home Renovation Project Tasks (Project 2) - { name: 'Get quotes from contractors', project_id: projects[2].id, priority: 2, status: 0, due_date: getRandomDate(14) }, - { name: 'Choose kitchen tiles', project_id: projects[2].id, priority: 1, status: 0 }, - { name: 'Order new appliances', project_id: projects[2].id, priority: 2, status: 0, due_date: getRandomDate(21) }, - { name: 'Plan bathroom layout', project_id: projects[2].id, priority: 1, status: 0 }, - { name: 'Select paint colors', project_id: projects[2].id, priority: 1, status: 1 }, - { name: 'Research flooring options', project_id: projects[2].id, priority: 1, status: 2, completed_at: getPastDate(3) }, - { name: 'Schedule plumbing inspection', project_id: projects[2].id, priority: 2, status: 0, due_date: getRandomDate(10) }, - { name: 'Order cabinet hardware', project_id: projects[2].id, priority: 0, status: 0 }, - { name: 'Plan electrical upgrades', project_id: projects[2].id, priority: 2, status: 0 }, - { name: 'Choose lighting fixtures', project_id: projects[2].id, priority: 1, status: 0 }, + // Home Renovation Project Tasks (Project 2) + { + name: 'Get quotes from contractors', + project_id: projects[2].id, + priority: 2, + status: 0, + due_date: getRandomDate(14), + }, + { + name: 'Choose kitchen tiles', + project_id: projects[2].id, + priority: 1, + status: 0, + }, + { + name: 'Order new appliances', + project_id: projects[2].id, + priority: 2, + status: 0, + due_date: getRandomDate(21), + }, + { + name: 'Plan bathroom layout', + project_id: projects[2].id, + priority: 1, + status: 0, + }, + { + name: 'Select paint colors', + project_id: projects[2].id, + priority: 1, + status: 1, + }, + { + name: 'Research flooring options', + project_id: projects[2].id, + priority: 1, + status: 2, + completed_at: getPastDate(3), + }, + { + name: 'Schedule plumbing inspection', + project_id: projects[2].id, + priority: 2, + status: 0, + due_date: getRandomDate(10), + }, + { + name: 'Order cabinet hardware', + project_id: projects[2].id, + priority: 0, + status: 0, + }, + { + name: 'Plan electrical upgrades', + project_id: projects[2].id, + priority: 2, + status: 0, + }, + { + name: 'Choose lighting fixtures', + project_id: projects[2].id, + priority: 1, + status: 0, + }, - // Fitness Challenge Project Tasks (Project 3) - { name: 'Create workout schedule', project_id: projects[3].id, priority: 2, status: 2, completed_at: getPastDate(10) }, - { name: 'Track daily calories', project_id: projects[3].id, priority: 1, status: 1 }, - { name: 'Join gym membership', project_id: projects[3].id, priority: 2, status: 2, completed_at: getPastDate(15) }, - { name: 'Buy workout equipment', project_id: projects[3].id, priority: 1, status: 0 }, - { name: 'Plan meal prep schedule', project_id: projects[3].id, priority: 1, status: 1 }, - { name: 'Find workout buddy', project_id: projects[3].id, priority: 0, status: 0 }, - { name: 'Set up fitness tracking app', project_id: projects[3].id, priority: 1, status: 2, completed_at: getPastDate(8) }, - { name: 'Schedule body composition test', project_id: projects[3].id, priority: 1, status: 0, due_date: getRandomDate(7) }, - { name: 'Research supplements', project_id: projects[3].id, priority: 0, status: 0 }, - { name: 'Plan recovery routine', project_id: projects[3].id, priority: 1, status: 0 }, + // Fitness Challenge Project Tasks (Project 3) + { + name: 'Create workout schedule', + project_id: projects[3].id, + priority: 2, + status: 2, + completed_at: getPastDate(10), + }, + { + name: 'Track daily calories', + project_id: projects[3].id, + priority: 1, + status: 1, + }, + { + name: 'Join gym membership', + project_id: projects[3].id, + priority: 2, + status: 2, + completed_at: getPastDate(15), + }, + { + name: 'Buy workout equipment', + project_id: projects[3].id, + priority: 1, + status: 0, + }, + { + name: 'Plan meal prep schedule', + project_id: projects[3].id, + priority: 1, + status: 1, + }, + { + name: 'Find workout buddy', + project_id: projects[3].id, + priority: 0, + status: 0, + }, + { + name: 'Set up fitness tracking app', + project_id: projects[3].id, + priority: 1, + status: 2, + completed_at: getPastDate(8), + }, + { + name: 'Schedule body composition test', + project_id: projects[3].id, + priority: 1, + status: 0, + due_date: getRandomDate(7), + }, + { + name: 'Research supplements', + project_id: projects[3].id, + priority: 0, + status: 0, + }, + { + name: 'Plan recovery routine', + project_id: projects[3].id, + priority: 1, + status: 0, + }, - // Side Business Project Tasks (Project 4) - { name: 'Define service offerings', project_id: projects[4].id, priority: 2, status: 1 }, - { name: 'Create business website', project_id: projects[4].id, priority: 2, status: 0 }, - { name: 'Set up payment processing', project_id: projects[4].id, priority: 1, status: 0 }, - { name: 'Network with potential clients', project_id: projects[4].id, priority: 1, status: 0 }, - { name: 'Register business name', project_id: projects[4].id, priority: 2, status: 2, completed_at: getPastDate(12) }, - { name: 'Open business bank account', project_id: projects[4].id, priority: 2, status: 0, due_date: getRandomDate(5) }, - { name: 'Create marketing materials', project_id: projects[4].id, priority: 1, status: 0 }, - { name: 'Set up accounting system', project_id: projects[4].id, priority: 1, status: 0 }, - { name: 'Research competitors', project_id: projects[4].id, priority: 1, status: 1 }, - { name: 'Create pricing strategy', project_id: projects[4].id, priority: 2, status: 0 }, + // Side Business Project Tasks (Project 4) + { + name: 'Define service offerings', + project_id: projects[4].id, + priority: 2, + status: 1, + }, + { + name: 'Create business website', + project_id: projects[4].id, + priority: 2, + status: 0, + }, + { + name: 'Set up payment processing', + project_id: projects[4].id, + priority: 1, + status: 0, + }, + { + name: 'Network with potential clients', + project_id: projects[4].id, + priority: 1, + status: 0, + }, + { + name: 'Register business name', + project_id: projects[4].id, + priority: 2, + status: 2, + completed_at: getPastDate(12), + }, + { + name: 'Open business bank account', + project_id: projects[4].id, + priority: 2, + status: 0, + due_date: getRandomDate(5), + }, + { + name: 'Create marketing materials', + project_id: projects[4].id, + priority: 1, + status: 0, + }, + { + name: 'Set up accounting system', + project_id: projects[4].id, + priority: 1, + status: 0, + }, + { + name: 'Research competitors', + project_id: projects[4].id, + priority: 1, + status: 1, + }, + { + name: 'Create pricing strategy', + project_id: projects[4].id, + priority: 2, + status: 0, + }, - // Investment Portfolio Project Tasks (Project 5) - { name: 'Research investment platforms', project_id: projects[5].id, priority: 2, status: 1 }, - { name: 'Open brokerage account', project_id: projects[5].id, priority: 2, status: 0, due_date: getRandomDate(10) }, - { name: 'Study different asset classes', project_id: projects[5].id, priority: 1, status: 0 }, - { name: 'Set investment goals', project_id: projects[5].id, priority: 2, status: 2, completed_at: getPastDate(7) }, - { name: 'Create risk assessment', project_id: projects[5].id, priority: 1, status: 1 }, - { name: 'Research ETFs and mutual funds', project_id: projects[5].id, priority: 1, status: 0 }, - { name: 'Set up automatic investing', project_id: projects[5].id, priority: 1, status: 0 }, - { name: 'Learn about tax implications', project_id: projects[5].id, priority: 1, status: 0 }, - { name: 'Create emergency fund', project_id: projects[5].id, priority: 2, status: 1 }, - { name: 'Review portfolio monthly', project_id: projects[5].id, priority: 1, status: 0 }, + // Investment Portfolio Project Tasks (Project 5) + { + name: 'Research investment platforms', + project_id: projects[5].id, + priority: 2, + status: 1, + }, + { + name: 'Open brokerage account', + project_id: projects[5].id, + priority: 2, + status: 0, + due_date: getRandomDate(10), + }, + { + name: 'Study different asset classes', + project_id: projects[5].id, + priority: 1, + status: 0, + }, + { + name: 'Set investment goals', + project_id: projects[5].id, + priority: 2, + status: 2, + completed_at: getPastDate(7), + }, + { + name: 'Create risk assessment', + project_id: projects[5].id, + priority: 1, + status: 1, + }, + { + name: 'Research ETFs and mutual funds', + project_id: projects[5].id, + priority: 1, + status: 0, + }, + { + name: 'Set up automatic investing', + project_id: projects[5].id, + priority: 1, + status: 0, + }, + { + name: 'Learn about tax implications', + project_id: projects[5].id, + priority: 1, + status: 0, + }, + { + name: 'Create emergency fund', + project_id: projects[5].id, + priority: 2, + status: 1, + }, + { + name: 'Review portfolio monthly', + project_id: projects[5].id, + priority: 1, + status: 0, + }, - // Europe Trip 2024 Project Tasks (Project 6) - { name: 'Research destinations', project_id: projects[6].id, priority: 2, status: 1 }, - { name: 'Book flights', project_id: projects[6].id, priority: 2, status: 0, due_date: getRandomDate(30) }, - { name: 'Reserve accommodations', project_id: projects[6].id, priority: 2, status: 0, due_date: getRandomDate(45) }, - { name: 'Apply for passport renewal', project_id: projects[6].id, priority: 2, status: 0, due_date: getRandomDate(60) }, - { name: 'Plan itinerary', project_id: projects[6].id, priority: 1, status: 0 }, - { name: 'Research local customs', project_id: projects[6].id, priority: 0, status: 0 }, - { name: 'Learn basic phrases', project_id: projects[6].id, priority: 0, status: 0 }, - { name: 'Check visa requirements', project_id: projects[6].id, priority: 2, status: 0, due_date: getRandomDate(90) }, - { name: 'Get travel insurance', project_id: projects[6].id, priority: 1, status: 0, due_date: getRandomDate(21) }, - { name: 'Plan budget', project_id: projects[6].id, priority: 1, status: 1 }, + // Europe Trip 2024 Project Tasks (Project 6) + { + name: 'Research destinations', + project_id: projects[6].id, + priority: 2, + status: 1, + }, + { + name: 'Book flights', + project_id: projects[6].id, + priority: 2, + status: 0, + due_date: getRandomDate(30), + }, + { + name: 'Reserve accommodations', + project_id: projects[6].id, + priority: 2, + status: 0, + due_date: getRandomDate(45), + }, + { + name: 'Apply for passport renewal', + project_id: projects[6].id, + priority: 2, + status: 0, + due_date: getRandomDate(60), + }, + { + name: 'Plan itinerary', + project_id: projects[6].id, + priority: 1, + status: 0, + }, + { + name: 'Research local customs', + project_id: projects[6].id, + priority: 0, + status: 0, + }, + { + name: 'Learn basic phrases', + project_id: projects[6].id, + priority: 0, + status: 0, + }, + { + name: 'Check visa requirements', + project_id: projects[6].id, + priority: 2, + status: 0, + due_date: getRandomDate(90), + }, + { + name: 'Get travel insurance', + project_id: projects[6].id, + priority: 1, + status: 0, + due_date: getRandomDate(21), + }, + { + name: 'Plan budget', + project_id: projects[6].id, + priority: 1, + status: 1, + }, - // Photography Mastery Project Tasks (Project 7) - { name: 'Learn camera basics', project_id: projects[7].id, priority: 2, status: 2, completed_at: getPastDate(14) }, - { name: 'Practice composition rules', project_id: projects[7].id, priority: 1, status: 1 }, - { name: 'Study lighting techniques', project_id: projects[7].id, priority: 1, status: 0 }, - { name: 'Learn photo editing', project_id: projects[7].id, priority: 1, status: 0 }, - { name: 'Build portfolio', project_id: projects[7].id, priority: 1, status: 0 }, - { name: 'Join photography community', project_id: projects[7].id, priority: 0, status: 0 }, - { name: 'Experiment with different styles', project_id: projects[7].id, priority: 1, status: 0 }, - { name: 'Learn about gear', project_id: projects[7].id, priority: 0, status: 0 }, - { name: 'Practice street photography', project_id: projects[7].id, priority: 1, status: 0 }, - { name: 'Study famous photographers', project_id: projects[7].id, priority: 0, status: 0 }, + // Photography Mastery Project Tasks (Project 7) + { + name: 'Learn camera basics', + project_id: projects[7].id, + priority: 2, + status: 2, + completed_at: getPastDate(14), + }, + { + name: 'Practice composition rules', + project_id: projects[7].id, + priority: 1, + status: 1, + }, + { + name: 'Study lighting techniques', + project_id: projects[7].id, + priority: 1, + status: 0, + }, + { + name: 'Learn photo editing', + project_id: projects[7].id, + priority: 1, + status: 0, + }, + { + name: 'Build portfolio', + project_id: projects[7].id, + priority: 1, + status: 0, + }, + { + name: 'Join photography community', + project_id: projects[7].id, + priority: 0, + status: 0, + }, + { + name: 'Experiment with different styles', + project_id: projects[7].id, + priority: 1, + status: 0, + }, + { + name: 'Learn about gear', + project_id: projects[7].id, + priority: 0, + status: 0, + }, + { + name: 'Practice street photography', + project_id: projects[7].id, + priority: 1, + status: 0, + }, + { + name: 'Study famous photographers', + project_id: projects[7].id, + priority: 0, + status: 0, + }, - // Non-project tasks - Personal productivity and life management - { name: 'Call dentist for appointment', priority: 1, status: 0, due_date: getRandomDate(3) }, - { name: 'Buy groceries for the week', priority: 0, status: 0 }, - { name: 'Clean garage', priority: 0, status: 0 }, - { name: 'Update resume', priority: 1, status: 0 }, - { name: 'Read "Atomic Habits" book', priority: 0, status: 0 }, - { name: 'Organize digital photos', priority: 0, status: 0 }, - { name: 'Schedule car maintenance', priority: 1, status: 0, due_date: getRandomDate(7) }, - { name: 'Plan weekend trip', priority: 0, status: 0 }, - { name: 'Learn basic Spanish', priority: 0, status: 0 }, - { name: 'Backup computer files', priority: 1, status: 0 }, - { name: 'Donate old clothes', priority: 0, status: 0 }, - { name: 'Research investment options', priority: 1, status: 0 }, - { name: 'Call mom and dad', priority: 1, status: 0, due_date: getRandomDate(2) }, - { name: 'Fix leaky faucet', priority: 0, status: 0 }, - { name: 'Try new restaurant', priority: 0, status: 0 }, - { name: 'Update LinkedIn profile', priority: 1, status: 0 }, - { name: 'Review monthly expenses', priority: 1, status: 0, due_date: getRandomDate(5) }, - { name: 'Organize desk workspace', priority: 0, status: 0 }, - { name: 'Plan birthday party', priority: 1, status: 0 }, - { name: 'Research new phone', priority: 0, status: 0 }, - { name: 'Schedule eye exam', priority: 1, status: 0, due_date: getRandomDate(14) }, - { name: 'Update emergency contacts', priority: 1, status: 0 }, - { name: 'Clean out email inbox', priority: 0, status: 0 }, - { name: 'Research vacation destinations', priority: 0, status: 0 }, - { name: 'Update computer software', priority: 1, status: 0 }, - { name: 'Plan meal prep for week', priority: 1, status: 0 }, - { name: 'Research online courses', priority: 0, status: 0 }, - { name: 'Update password manager', priority: 1, status: 0 }, - { name: 'Organize physical documents', priority: 0, status: 0 }, - { name: 'Research new coffee maker', priority: 0, status: 0 }, - { name: 'Schedule oil change', priority: 1, status: 0, due_date: getRandomDate(10) }, - { name: 'Plan gift for anniversary', priority: 1, status: 0 }, - { name: 'Research home security system', priority: 0, status: 0 }, - { name: 'Update will and testament', priority: 2, status: 0, due_date: getRandomDate(30) }, - { name: 'Learn keyboard shortcuts', priority: 0, status: 0 }, - { name: 'Research meditation apps', priority: 0, status: 0 }, - { name: 'Plan date night', priority: 1, status: 0 }, - { name: 'Research side income ideas', priority: 0, status: 0 }, - { name: 'Update insurance policies', priority: 1, status: 0, due_date: getRandomDate(21) }, - { name: 'Learn new cooking recipe', priority: 0, status: 0 }, - { name: 'Research productivity tools', priority: 0, status: 0 }, - { name: 'Plan garden for spring', priority: 0, status: 0 }, - { name: 'Research home improvement ideas', priority: 0, status: 0 }, - { name: 'Update social media profiles', priority: 0, status: 0 }, - { name: 'Plan weekend activities', priority: 0, status: 0 }, - { name: 'Research new podcast', priority: 0, status: 0 }, - { name: 'Schedule annual checkup', priority: 1, status: 0, due_date: getRandomDate(45) }, - { name: 'Learn new Excel functions', priority: 0, status: 0 }, - { name: 'Research retirement planning', priority: 1, status: 0 }, - { name: 'Plan family reunion', priority: 1, status: 0 }, - { name: 'Research new book to read', priority: 0, status: 0 }, - { name: 'Update contact information', priority: 0, status: 0 }, - { name: 'Plan workout routine', priority: 1, status: 0 }, + // Non-project tasks - Personal productivity and life management + { + name: 'Call dentist for appointment', + priority: 1, + status: 0, + due_date: getRandomDate(3), + }, + { name: 'Buy groceries for the week', priority: 0, status: 0 }, + { name: 'Clean garage', priority: 0, status: 0 }, + { name: 'Update resume', priority: 1, status: 0 }, + { name: 'Read "Atomic Habits" book', priority: 0, status: 0 }, + { name: 'Organize digital photos', priority: 0, status: 0 }, + { + name: 'Schedule car maintenance', + priority: 1, + status: 0, + due_date: getRandomDate(7), + }, + { name: 'Plan weekend trip', priority: 0, status: 0 }, + { name: 'Learn basic Spanish', priority: 0, status: 0 }, + { name: 'Backup computer files', priority: 1, status: 0 }, + { name: 'Donate old clothes', priority: 0, status: 0 }, + { name: 'Research investment options', priority: 1, status: 0 }, + { + name: 'Call mom and dad', + priority: 1, + status: 0, + due_date: getRandomDate(2), + }, + { name: 'Fix leaky faucet', priority: 0, status: 0 }, + { name: 'Try new restaurant', priority: 0, status: 0 }, + { name: 'Update LinkedIn profile', priority: 1, status: 0 }, + { + name: 'Review monthly expenses', + priority: 1, + status: 0, + due_date: getRandomDate(5), + }, + { name: 'Organize desk workspace', priority: 0, status: 0 }, + { name: 'Plan birthday party', priority: 1, status: 0 }, + { name: 'Research new phone', priority: 0, status: 0 }, + { + name: 'Schedule eye exam', + priority: 1, + status: 0, + due_date: getRandomDate(14), + }, + { name: 'Update emergency contacts', priority: 1, status: 0 }, + { name: 'Clean out email inbox', priority: 0, status: 0 }, + { name: 'Research vacation destinations', priority: 0, status: 0 }, + { name: 'Update computer software', priority: 1, status: 0 }, + { name: 'Plan meal prep for week', priority: 1, status: 0 }, + { name: 'Research online courses', priority: 0, status: 0 }, + { name: 'Update password manager', priority: 1, status: 0 }, + { name: 'Organize physical documents', priority: 0, status: 0 }, + { name: 'Research new coffee maker', priority: 0, status: 0 }, + { + name: 'Schedule oil change', + priority: 1, + status: 0, + due_date: getRandomDate(10), + }, + { name: 'Plan gift for anniversary', priority: 1, status: 0 }, + { name: 'Research home security system', priority: 0, status: 0 }, + { + name: 'Update will and testament', + priority: 2, + status: 0, + due_date: getRandomDate(30), + }, + { name: 'Learn keyboard shortcuts', priority: 0, status: 0 }, + { name: 'Research meditation apps', priority: 0, status: 0 }, + { name: 'Plan date night', priority: 1, status: 0 }, + { name: 'Research side income ideas', priority: 0, status: 0 }, + { + name: 'Update insurance policies', + priority: 1, + status: 0, + due_date: getRandomDate(21), + }, + { name: 'Learn new cooking recipe', priority: 0, status: 0 }, + { name: 'Research productivity tools', priority: 0, status: 0 }, + { name: 'Plan garden for spring', priority: 0, status: 0 }, + { name: 'Research home improvement ideas', priority: 0, status: 0 }, + { name: 'Update social media profiles', priority: 0, status: 0 }, + { name: 'Plan weekend activities', priority: 0, status: 0 }, + { name: 'Research new podcast', priority: 0, status: 0 }, + { + name: 'Schedule annual checkup', + priority: 1, + status: 0, + due_date: getRandomDate(45), + }, + { name: 'Learn new Excel functions', priority: 0, status: 0 }, + { name: 'Research retirement planning', priority: 1, status: 0 }, + { name: 'Plan family reunion', priority: 1, status: 0 }, + { name: 'Research new book to read', priority: 0, status: 0 }, + { name: 'Update contact information', priority: 0, status: 0 }, + { name: 'Plan workout routine', priority: 1, status: 0 }, - // Completed tasks for metrics - spread across different dates - { name: 'Pay monthly bills', priority: 1, status: 2, completed_at: getPastDate(1) }, - { name: 'Submit expense reports', priority: 1, status: 2, completed_at: getPastDate(1) }, - { name: 'Weekly team meeting', priority: 1, status: 2, completed_at: getPastDate(2) }, - { name: 'Review project proposal', priority: 2, status: 2, completed_at: getPastDate(3) }, - { name: 'Update LinkedIn profile', priority: 0, status: 2, completed_at: getPastDate(4) }, - { name: 'Clean kitchen', priority: 0, status: 2, completed_at: getPastDate(5) }, - { name: 'Water plants', priority: 0, status: 2, completed_at: getPastDate(6) }, - { name: 'Grocery shopping', priority: 1, status: 2, completed_at: getPastDate(1) }, - { name: 'Call insurance company', priority: 1, status: 2, completed_at: getPastDate(2) }, - { name: 'Send birthday card', priority: 0, status: 2, completed_at: getPastDate(3) }, - { name: 'Fix printer issue', priority: 1, status: 2, completed_at: getPastDate(1) }, - { name: 'Review budget', priority: 1, status: 2, completed_at: getPastDate(4) }, - { name: 'Attend networking event', priority: 1, status: 2, completed_at: getPastDate(5) }, - { name: 'Complete online training', priority: 1, status: 2, completed_at: getPastDate(6) }, - { name: 'Schedule vet appointment', priority: 1, status: 2, completed_at: getPastDate(2) }, - { name: 'Buy gift for colleague', priority: 0, status: 2, completed_at: getPastDate(3) }, - { name: 'Update calendar', priority: 0, status: 2, completed_at: getPastDate(1) }, - { name: 'Research vacation spots', priority: 0, status: 2, completed_at: getPastDate(4) }, - { name: 'Backup important files', priority: 1, status: 2, completed_at: getPastDate(5) }, - { name: 'Clean bathroom', priority: 0, status: 2, completed_at: getPastDate(1) }, + // Completed tasks for metrics - spread across different dates + { + name: 'Pay monthly bills', + priority: 1, + status: 2, + completed_at: getPastDate(1), + }, + { + name: 'Submit expense reports', + priority: 1, + status: 2, + completed_at: getPastDate(1), + }, + { + name: 'Weekly team meeting', + priority: 1, + status: 2, + completed_at: getPastDate(2), + }, + { + name: 'Review project proposal', + priority: 2, + status: 2, + completed_at: getPastDate(3), + }, + { + name: 'Update LinkedIn profile', + priority: 0, + status: 2, + completed_at: getPastDate(4), + }, + { + name: 'Clean kitchen', + priority: 0, + status: 2, + completed_at: getPastDate(5), + }, + { + name: 'Water plants', + priority: 0, + status: 2, + completed_at: getPastDate(6), + }, + { + name: 'Grocery shopping', + priority: 1, + status: 2, + completed_at: getPastDate(1), + }, + { + name: 'Call insurance company', + priority: 1, + status: 2, + completed_at: getPastDate(2), + }, + { + name: 'Send birthday card', + priority: 0, + status: 2, + completed_at: getPastDate(3), + }, + { + name: 'Fix printer issue', + priority: 1, + status: 2, + completed_at: getPastDate(1), + }, + { + name: 'Review budget', + priority: 1, + status: 2, + completed_at: getPastDate(4), + }, + { + name: 'Attend networking event', + priority: 1, + status: 2, + completed_at: getPastDate(5), + }, + { + name: 'Complete online training', + priority: 1, + status: 2, + completed_at: getPastDate(6), + }, + { + name: 'Schedule vet appointment', + priority: 1, + status: 2, + completed_at: getPastDate(2), + }, + { + name: 'Buy gift for colleague', + priority: 0, + status: 2, + completed_at: getPastDate(3), + }, + { + name: 'Update calendar', + priority: 0, + status: 2, + completed_at: getPastDate(1), + }, + { + name: 'Research vacation spots', + priority: 0, + status: 2, + completed_at: getPastDate(4), + }, + { + name: 'Backup important files', + priority: 1, + status: 2, + completed_at: getPastDate(5), + }, + { + name: 'Clean bathroom', + priority: 0, + status: 2, + completed_at: getPastDate(1), + }, - // Recurring tasks - { - name: 'Daily standup meeting', - priority: 1, - status: 0, - recurrence_type: 'daily', - recurrence_interval: 1, - due_date: new Date(), - project_id: projects[0].id - }, - { - name: 'Weekly grocery shopping', - priority: 1, - status: 0, - recurrence_type: 'weekly', - recurrence_interval: 1, - recurrence_weekday: 6, // Saturday - due_date: getRandomDate(7) - }, - { - name: 'Monthly budget review', - priority: 2, - status: 0, - recurrence_type: 'monthly', - recurrence_interval: 1, - recurrence_month_day: 1, - due_date: getRandomDate(30) - }, - { - name: 'Weekly meal prep', - priority: 1, - status: 0, - recurrence_type: 'weekly', - recurrence_interval: 1, - recurrence_weekday: 0, // Sunday - due_date: getRandomDate(7) - }, - { - name: 'Daily workout', - priority: 1, - status: 0, - recurrence_type: 'daily', - recurrence_interval: 1, - due_date: new Date(), - project_id: projects[3].id - }, - { - name: 'Weekly house cleaning', - priority: 1, - status: 0, - recurrence_type: 'weekly', - recurrence_interval: 1, - recurrence_weekday: 6, // Saturday - due_date: getRandomDate(7) - }, + // Recurring tasks + { + name: 'Daily standup meeting', + priority: 1, + status: 0, + recurrence_type: 'daily', + recurrence_interval: 1, + due_date: new Date(), + project_id: projects[0].id, + }, + { + name: 'Weekly grocery shopping', + priority: 1, + status: 0, + recurrence_type: 'weekly', + recurrence_interval: 1, + recurrence_weekday: 6, // Saturday + due_date: getRandomDate(7), + }, + { + name: 'Monthly budget review', + priority: 2, + status: 0, + recurrence_type: 'monthly', + recurrence_interval: 1, + recurrence_month_day: 1, + due_date: getRandomDate(30), + }, + { + name: 'Weekly meal prep', + priority: 1, + status: 0, + recurrence_type: 'weekly', + recurrence_interval: 1, + recurrence_weekday: 0, // Sunday + due_date: getRandomDate(7), + }, + { + name: 'Daily workout', + priority: 1, + status: 0, + recurrence_type: 'daily', + recurrence_interval: 1, + due_date: new Date(), + project_id: projects[3].id, + }, + { + name: 'Weekly house cleaning', + priority: 1, + status: 0, + recurrence_type: 'weekly', + recurrence_interval: 1, + recurrence_weekday: 6, // Saturday + due_date: getRandomDate(7), + }, - // Waiting and someday tasks - { name: 'Wait for contractor estimate', priority: 1, status: 4, project_id: projects[2].id }, - { name: 'Learn advanced photography', priority: 0, status: 0 }, - { name: 'Write a book', priority: 0, status: 0 }, - { name: 'Learn to play guitar', priority: 0, status: 0 }, - { name: 'Take pottery class', priority: 0, status: 0 }, - { name: 'Visit Japan', priority: 0, status: 0 }, - { name: 'Learn rock climbing', priority: 0, status: 0 }, - { name: 'Start a podcast', priority: 0, status: 0 }, - { name: 'Learn wine tasting', priority: 0, status: 0 }, - { name: 'Take dance lessons', priority: 0, status: 0 } - ]; + // Waiting and someday tasks + { + name: 'Wait for contractor estimate', + priority: 1, + status: 4, + project_id: projects[2].id, + }, + { name: 'Learn advanced photography', priority: 0, status: 0 }, + { name: 'Write a book', priority: 0, status: 0 }, + { name: 'Learn to play guitar', priority: 0, status: 0 }, + { name: 'Take pottery class', priority: 0, status: 0 }, + { name: 'Visit Japan', priority: 0, status: 0 }, + { name: 'Learn rock climbing', priority: 0, status: 0 }, + { name: 'Start a podcast', priority: 0, status: 0 }, + { name: 'Learn wine tasting', priority: 0, status: 0 }, + { name: 'Take dance lessons', priority: 0, status: 0 }, + ]; } -module.exports = { createExpandedTaskData }; \ No newline at end of file +module.exports = { createExpandedTaskData }; diff --git a/backend/seeders/massive-tasks.js b/backend/seeders/massive-tasks.js index 8d5caf8..6e77e20 100644 --- a/backend/seeders/massive-tasks.js +++ b/backend/seeders/massive-tasks.js @@ -1,631 +1,1183 @@ // Helper function to create massive task data with AI feature triggers function createMassiveTaskData(projects, getRandomDate, getPastDate) { - // Helper to get random items from array - const getRandomItems = (arr, count) => { - const shuffled = [...arr].sort(() => 0.5 - Math.random()); - return shuffled.slice(0, count); - }; - - // Helper to get random priority - const getRandomPriority = () => Math.floor(Math.random() * 3); // 0, 1, or 2 - - // Helper to get random status - const getRandomStatus = () => { - const statuses = [0, 0, 0, 0, 0, 0, 1, 1, 1, 2, 2, 3, 4]; // More weighted towards active tasks - return statuses[Math.floor(Math.random() * statuses.length)]; - }; - - // Productivity and work tasks - const workTasks = [ - 'Review quarterly performance metrics', - 'Update project documentation', - 'Prepare presentation for board meeting', - 'Conduct code review for new feature', - 'Write technical specification document', - 'Schedule one-on-one meetings with team', - 'Update project timeline and milestones', - 'Research new development tools', - 'Optimize database queries', - 'Create automated testing suite', - 'Refactor legacy code modules', - 'Implement security audit recommendations', - 'Design API documentation', - 'Setup continuous integration pipeline', - 'Create user acceptance testing plan', - 'Migrate data to new system', - 'Setup monitoring and alerting', - 'Write deployment procedures', - 'Create backup and recovery plan', - 'Conduct performance testing', - 'Update coding standards documentation', - 'Setup development environment', - 'Create onboarding documentation', - 'Review and update dependencies', - 'Implement feature toggles', - 'Setup load balancing', - 'Create disaster recovery plan', - 'Conduct security penetration testing', - 'Setup SSL certificates', - 'Implement caching strategy', - 'Create analytics dashboard', - 'Setup error tracking', - 'Implement rate limiting', - 'Create API versioning strategy', - 'Setup database replication', - 'Implement search functionality', - 'Create notification system', - 'Setup file upload handling', - 'Implement user authentication', - 'Create password reset functionality', - 'Setup email templates', - 'Implement data validation', - 'Create audit logging', - 'Setup health checks', - 'Implement graceful shutdowns' - ]; - - // Personal development and learning tasks - const learningTasks = [ - 'Complete online course on machine learning', - 'Read "Clean Architecture" book', - 'Practice coding challenges on LeetCode', - 'Learn advanced Git techniques', - 'Study microservices architecture', - 'Complete Docker certification', - 'Learn Kubernetes fundamentals', - 'Study system design patterns', - 'Practice algorithm problems', - 'Learn about database optimization', - 'Study network security principles', - 'Complete AWS certification', - 'Learn about blockchain technology', - 'Study DevOps best practices', - 'Learn advanced JavaScript features', - 'Study React performance optimization', - 'Learn about GraphQL', - 'Study mobile app development', - 'Learn about AI and neural networks', - 'Study cloud computing concepts', - 'Learn about containerization', - 'Study API design principles', - 'Learn about testing strategies', - 'Study agile methodologies', - 'Learn about project management', - 'Study user experience design', - 'Learn about data visualization', - 'Study cybersecurity fundamentals', - 'Learn about scalability patterns', - 'Study database design principles' - ]; - - // Health and fitness tasks - const healthTasks = [ - 'Schedule annual physical exam', - 'Book dental cleaning appointment', - 'Schedule eye exam', - 'Get blood work done', - 'Schedule dermatologist appointment', - 'Book massage therapy session', - 'Schedule physical therapy session', - 'Get flu vaccination', - 'Schedule mammogram', - 'Book nutrition consultation', - 'Schedule mental health counseling', - 'Get hearing test', - 'Schedule chiropractor appointment', - 'Book acupuncture session', - 'Schedule sleep study', - 'Get allergy testing done', - 'Schedule colonoscopy', - 'Book podiatrist appointment', - 'Schedule orthopedic consultation', - 'Get heart health screening', - 'Complete 30-minute cardio workout', - 'Do strength training session', - 'Practice yoga for 45 minutes', - 'Go for 5-mile run', - 'Complete HIIT workout', - 'Do pilates session', - 'Practice meditation for 20 minutes', - 'Track daily water intake', - 'Meal prep for the week', - 'Plan healthy breakfast options', - 'Research new workout routines', - 'Update fitness goals', - 'Track daily steps (10,000 goal)', - 'Practice breathing exercises', - 'Do stretching routine', - 'Plan weekly workout schedule', - 'Research healthy recipes', - 'Update meal planning app', - 'Schedule workout with trainer', - 'Join new fitness class' - ]; - - // Home and family tasks - const homeTasks = [ - 'Deep clean living room', - 'Organize bedroom closet', - 'Clean out garage', - 'Wash and fold laundry', - 'Vacuum all carpets', - 'Mop kitchen and bathroom floors', - 'Clean windows inside and out', - 'Organize pantry and kitchen cabinets', - 'Clean out refrigerator', - 'Wash bedsheets and pillowcases', - 'Dust all furniture', - 'Clean bathroom thoroughly', - 'Organize home office', - 'Sort through old documents', - 'Clean out car interior', - 'Wash car exterior', - 'Organize basement storage', - 'Clean air conditioning filters', - 'Test smoke detector batteries', - 'Check and clean gutters', - 'Trim bushes and hedges', - 'Water indoor plants', - 'Plant vegetables in garden', - 'Mow lawn and edge walkways', - 'Repair leaky faucet', - 'Fix squeaky door hinges', - 'Replace burnt out light bulbs', - 'Caulk bathroom tiles', - 'Touch up paint on walls', - 'Clean grout in shower', - 'Organize tool shed', - 'Season cast iron cookware', - 'Clean oven and stovetop', - 'Descale coffee maker', - 'Clean dishwasher filter', - 'Replace water filter', - 'Clean dryer vent', - 'Organize medicine cabinet', - 'Check expiration dates on medications', - 'Update emergency contact list' - ]; - - // Financial and administrative tasks - const financialTasks = [ - 'Review monthly budget', - 'Pay credit card bills', - 'Transfer money to savings', - 'Update investment portfolio', - 'Review insurance policies', - 'File tax documents', - 'Update will and testament', - 'Review retirement contributions', - 'Check credit report', - 'Update beneficiary information', - 'Review bank statements', - 'Cancel unused subscriptions', - 'Negotiate lower cable bill', - 'Shop for better car insurance', - 'Review cell phone plan', - 'Update emergency fund', - 'Research investment options', - 'Meet with financial advisor', - 'Review mortgage rates', - 'Update home insurance', - 'File warranty claims', - 'Organize receipts for taxes', - 'Update accounting software', - 'Review business expenses', - 'Pay quarterly taxes', - 'Update PayPal account', - 'Review online banking security', - 'Setup automatic bill pay', - 'Research high-yield savings', - 'Update direct deposit info' - ]; - - // Social and relationship tasks - const socialTasks = [ - 'Call parents to check in', - 'Send birthday card to friend', - 'Plan date night with partner', - 'Schedule coffee with colleague', - 'Write thank you note', - 'Plan family reunion', - 'Organize game night with friends', - 'Send holiday cards', - 'Plan surprise party', - 'Schedule lunch with mentor', - 'Join local community group', - 'Volunteer at local charity', - 'Attend networking event', - 'Plan weekend getaway', - 'Organize book club meeting', - 'Schedule video call with family', - 'Plan group hiking trip', - 'Organize potluck dinner', - 'Plan movie night', - 'Schedule catch-up with old friend', - 'Write recommendation letter', - 'Plan anniversary celebration', - 'Organize children\'s playdate', - 'Schedule babysitter', - 'Plan family photo session', - 'Organize neighborhood BBQ', - 'Plan holiday gathering', - 'Schedule couple\'s therapy', - 'Plan birthday celebration', - 'Organize team building activity' - ]; - - // Creative and hobby tasks - const creativeeTasks = [ - 'Practice guitar for 30 minutes', - 'Work on oil painting', - 'Write in journal', - 'Take photography workshop', - 'Learn new recipe', - 'Practice calligraphy', - 'Work on knitting project', - 'Write short story', - 'Learn new song on piano', - 'Practice drawing portraits', - 'Work on pottery project', - 'Edit video footage', - 'Write blog post', - 'Practice singing', - 'Work on woodworking project', - 'Learn new dance moves', - 'Practice photography techniques', - 'Work on scrapbook', - 'Write poetry', - 'Learn origami', - 'Practice sketching', - 'Work on embroidery', - 'Learn new cooking technique', - 'Practice watercolor painting', - 'Work on jewelry making', - 'Learn magic tricks', - 'Practice stand-up comedy', - 'Work on graphic design', - 'Learn new language phrases', - 'Practice mindful writing' - ]; - - // Travel and adventure tasks - const travelTasks = [ - 'Research vacation destinations', - 'Book flight tickets', - 'Reserve hotel accommodation', - 'Plan daily itinerary', - 'Apply for passport renewal', - 'Get travel insurance', - 'Exchange currency', - 'Pack suitcase', - 'Check visa requirements', - 'Update travel emergency contacts', - 'Download offline maps', - 'Research local customs', - 'Learn basic phrases', - 'Book airport parking', - 'Arrange pet sitting', - 'Stop mail delivery', - 'Set house security system', - 'Pack travel first aid kit', - 'Research local restaurants', - 'Book tours and activities', - 'Print boarding passes', - 'Check weather forecast', - 'Pack travel documents', - 'Arrange airport transportation', - 'Update travel blog' - ]; - - // All task categories combined - const allTaskCategories = [ - ...workTasks, - ...learningTasks, - ...healthTasks, - ...homeTasks, - ...financialTasks, - ...socialTasks, - ...creativeeTasks, - ...travelTasks - ]; - - // Create base task data with existing project tasks - const baseTaskData = [ - // Website Redesign Project (triggers collaboration, urgent deadlines) - { name: 'Research competitor websites', project_id: projects[0].id, priority: 1, status: 2, completed_at: getPastDate(5) }, - { name: 'Create wireframes for homepage', project_id: projects[0].id, priority: 2, status: 1 }, - { name: 'Design new color palette', project_id: projects[0].id, priority: 1, status: 0 }, - { name: 'Write content for About page', project_id: projects[0].id, priority: 1, status: 0 }, - { name: 'Set up staging environment', project_id: projects[0].id, priority: 2, status: 0, due_date: getRandomDate(3) }, // Urgent deadline - { name: 'Optimize images for web', project_id: projects[0].id, priority: 1, status: 0 }, - { name: 'Implement responsive design', project_id: projects[0].id, priority: 2, status: 0, due_date: getRandomDate(7) }, - { name: 'Test cross-browser compatibility', project_id: projects[0].id, priority: 1, status: 0 }, - { name: 'Setup Google Analytics', project_id: projects[0].id, priority: 1, status: 0 }, - { name: 'Create contact form', project_id: projects[0].id, priority: 1, status: 0 }, - { name: 'Write SEO meta descriptions', project_id: projects[0].id, priority: 1, status: 0 }, - { name: 'Design mobile navigation', project_id: projects[0].id, priority: 2, status: 0 }, - { name: 'Create footer section', project_id: projects[0].id, priority: 0, status: 0 }, - { name: 'Add social media icons', project_id: projects[0].id, priority: 0, status: 0 }, - { name: 'Setup SSL certificate', project_id: projects[0].id, priority: 2, status: 0, due_date: getRandomDate(2) }, // Very urgent - - // Europe Trip 2024 - triggers travel planning AI features - { name: 'Research flight options to Paris', project_id: projects[6].id, priority: 2, status: 1 }, - { name: 'Book hotel in Rome', project_id: projects[6].id, priority: 2, status: 0, due_date: getRandomDate(14) }, - { name: 'Apply for European travel insurance', project_id: projects[6].id, priority: 2, status: 0, due_date: getRandomDate(30) }, - { name: 'Learn basic Italian phrases', project_id: projects[6].id, priority: 1, status: 0 }, - { name: 'Research train routes between cities', project_id: projects[6].id, priority: 1, status: 0 }, - { name: 'Plan museum visits in Paris', project_id: projects[6].id, priority: 1, status: 0 }, - { name: 'Book restaurant reservations', project_id: projects[6].id, priority: 1, status: 0 }, - { name: 'Pack European travel adapter', project_id: projects[6].id, priority: 0, status: 0 }, - - // Fitness Challenge - triggers health/wellness AI features - { name: 'Track daily protein intake', project_id: projects[3].id, priority: 1, status: 1 }, - { name: 'Complete morning cardio workout', project_id: projects[3].id, priority: 1, status: 2, completed_at: getPastDate(1) }, - { name: 'Plan weekly meal prep', project_id: projects[3].id, priority: 1, status: 0 }, - { name: 'Schedule body composition scan', project_id: projects[3].id, priority: 1, status: 0, due_date: getRandomDate(7) }, - { name: 'Research new workout routines', project_id: projects[3].id, priority: 0, status: 0 }, - { name: 'Update fitness tracker goals', project_id: projects[3].id, priority: 1, status: 0 }, - - // Investment Portfolio - triggers financial AI features - { name: 'Research ESG investment options', project_id: projects[5].id, priority: 1, status: 0 }, - { name: 'Rebalance portfolio allocation', project_id: projects[5].id, priority: 2, status: 0, due_date: getRandomDate(5) }, - { name: 'Review quarterly performance', project_id: projects[5].id, priority: 1, status: 1 }, - { name: 'Set up automatic dividend reinvestment', project_id: projects[5].id, priority: 1, status: 0 }, - { name: 'Research international market exposure', project_id: projects[5].id, priority: 0, status: 0 }, - - // Side Business - triggers entrepreneurship AI features - { name: 'Create business plan document', project_id: projects[4].id, priority: 2, status: 1 }, - { name: 'Research target market demographics', project_id: projects[4].id, priority: 2, status: 0 }, - { name: 'Design logo and branding', project_id: projects[4].id, priority: 1, status: 0 }, - { name: 'Setup business social media accounts', project_id: projects[4].id, priority: 1, status: 0 }, - { name: 'Register domain name', project_id: projects[4].id, priority: 2, status: 2, completed_at: getPastDate(3) }, - { name: 'Create pricing strategy', project_id: projects[4].id, priority: 2, status: 0 }, - { name: 'Draft service agreements', project_id: projects[4].id, priority: 1, status: 0 }, - - // Home Renovation - triggers home improvement AI features - { name: 'Get electrical work permit', project_id: projects[2].id, priority: 2, status: 0, due_date: getRandomDate(10) }, - { name: 'Choose bathroom tile pattern', project_id: projects[2].id, priority: 1, status: 1 }, - { name: 'Schedule plumbing inspection', project_id: projects[2].id, priority: 2, status: 0, due_date: getRandomDate(14) }, - { name: 'Order kitchen countertops', project_id: projects[2].id, priority: 2, status: 0, due_date: getRandomDate(21) }, - { name: 'Research energy-efficient appliances', project_id: projects[2].id, priority: 1, status: 0 }, - { name: 'Plan kitchen lighting layout', project_id: projects[2].id, priority: 1, status: 0 }, - - // Photography Mastery - triggers creative learning AI features - { name: 'Practice portrait lighting techniques', project_id: projects[7].id, priority: 1, status: 1 }, - { name: 'Edit last weekend\'s photo shoot', project_id: projects[7].id, priority: 1, status: 0 }, - { name: 'Research local photography groups', project_id: projects[7].id, priority: 0, status: 0 }, - { name: 'Plan golden hour photo session', project_id: projects[7].id, priority: 1, status: 0 }, - { name: 'Learn advanced Lightroom techniques', project_id: projects[7].id, priority: 1, status: 0 }, - - // Smart Home Setup - triggers technology AI features - { name: 'Install smart thermostat', project_id: projects[14].id, priority: 2, status: 1 }, - { name: 'Configure home security system', project_id: projects[14].id, priority: 2, status: 0, due_date: getRandomDate(7) }, - { name: 'Setup voice assistant routines', project_id: projects[14].id, priority: 1, status: 0 }, - { name: 'Install smart door locks', project_id: projects[14].id, priority: 2, status: 0 }, - { name: 'Configure automated lighting', project_id: projects[14].id, priority: 1, status: 0 }, - - // Blog Launch - triggers content creation AI features - { name: 'Write first blog post about productivity', project_id: projects[10].id, priority: 2, status: 1 }, - { name: 'Design blog layout and theme', project_id: projects[10].id, priority: 1, status: 0 }, - { name: 'Setup email newsletter signup', project_id: projects[10].id, priority: 1, status: 0 }, - { name: 'Research SEO keywords for niche', project_id: projects[10].id, priority: 1, status: 0 }, - { name: 'Create content calendar for 3 months', project_id: projects[10].id, priority: 2, status: 0 }, - - // Professional Certification - triggers career development AI features - { name: 'Complete AWS practice exams', project_id: projects[8].id, priority: 2, status: 1 }, - { name: 'Schedule certification exam', project_id: projects[8].id, priority: 2, status: 0, due_date: getRandomDate(30) }, - { name: 'Review cloud architecture patterns', project_id: projects[8].id, priority: 1, status: 0 }, - { name: 'Practice hands-on labs', project_id: projects[8].id, priority: 1, status: 1 }, - { name: 'Join AWS study group', project_id: projects[8].id, priority: 0, status: 0 }, - - // Meal Prep System - triggers nutrition AI features - { name: 'Plan balanced weekly menu', project_id: projects[13].id, priority: 1, status: 1 }, - { name: 'Prep vegetables for the week', project_id: projects[13].id, priority: 1, status: 0 }, - { name: 'Cook batch of protein sources', project_id: projects[13].id, priority: 1, status: 0 }, - { name: 'Calculate macronutrient ratios', project_id: projects[13].id, priority: 1, status: 0 }, - { name: 'Research meal prep containers', project_id: projects[13].id, priority: 0, status: 0 }, - - // Wedding Planning - triggers event planning AI features - { name: 'Book wedding venue', project_id: projects[12].id, priority: 2, status: 2, completed_at: getPastDate(30) }, - { name: 'Send save the date cards', project_id: projects[12].id, priority: 2, status: 0, due_date: getRandomDate(60) }, - { name: 'Book wedding photographer', project_id: projects[12].id, priority: 2, status: 0, due_date: getRandomDate(45) }, - { name: 'Choose wedding cake flavors', project_id: projects[12].id, priority: 1, status: 0 }, - { name: 'Plan seating arrangement', project_id: projects[12].id, priority: 1, status: 0 }, - { name: 'Book honeymoon flights', project_id: projects[12].id, priority: 1, status: 0 }, - - // Garden Makeover - triggers gardening/sustainability AI features - { name: 'Plan vegetable garden layout', project_id: projects[9].id, priority: 1, status: 1 }, - { name: 'Order seeds for spring planting', project_id: projects[9].id, priority: 2, status: 0, due_date: getRandomDate(14) }, - { name: 'Install drip irrigation system', project_id: projects[9].id, priority: 1, status: 0 }, - { name: 'Build raised garden beds', project_id: projects[9].id, priority: 2, status: 0 }, - { name: 'Research companion planting', project_id: projects[9].id, priority: 0, status: 0 } - ]; - - // Generate massive additional tasks - const massiveTasks = []; - - // Add random tasks from all categories (including old tasks for backlog) - for (let i = 0; i < 150; i++) { - const taskName = allTaskCategories[Math.floor(Math.random() * allTaskCategories.length)]; - const hasProject = Math.random() < 0.4; // 40% chance of having a project - const hasDueDate = Math.random() < 0.3; // 30% chance of having a due date - const isCompleted = Math.random() < 0.08; // 8% chance of being completed - - const task = { - name: taskName, - priority: getRandomPriority(), - status: isCompleted ? 2 : getRandomStatus(), - note: Math.random() < 0.1 ? 'Added some notes during planning phase' : null + // Helper to get random items from array + const getRandomItems = (arr, count) => { + const shuffled = [...arr].sort(() => 0.5 - Math.random()); + return shuffled.slice(0, count); }; - - if (hasProject) { - task.project_id = projects[Math.floor(Math.random() * projects.length)].id; - } - - if (hasDueDate) { - if (Math.random() < 0.2) { - // 20% chance of overdue task (AI should flag these) - task.due_date = getPastDate(Math.floor(Math.random() * 30) + 1); - } else { - // Future due date - task.due_date = getRandomDate(Math.floor(Math.random() * 60) + 1); - } - } - - if (isCompleted) { - task.completed_at = getPastDate(Math.floor(Math.random() * 30) + 1); - } - - massiveTasks.push(task); - } - // Add specific AI trigger tasks (tasks that should trigger intelligent suggestions) - const aiTriggerTasks = [ - // Overdue tasks (AI should suggest prioritizing) - { name: 'Submit tax documents', priority: 2, status: 0, due_date: getPastDate(5) }, - { name: 'Renew car registration', priority: 2, status: 0, due_date: getPastDate(3) }, - { name: 'Pay property taxes', priority: 2, status: 0, due_date: getPastDate(10) }, - { name: 'Submit insurance claim', priority: 2, status: 0, due_date: getPastDate(7) }, - - // High-priority tasks with near deadlines (AI should suggest immediate action) - { name: 'Prepare presentation for CEO', priority: 2, status: 0, due_date: getRandomDate(1) }, - { name: 'Submit project proposal', priority: 2, status: 0, due_date: getRandomDate(2) }, - { name: 'Complete performance review', priority: 2, status: 0, due_date: getRandomDate(3) }, - - // Health-related tasks (AI should suggest wellness patterns) - { name: 'Schedule annual checkup', priority: 1, status: 0 }, - { name: 'Get eye exam', priority: 1, status: 0 }, - { name: 'Book dental cleaning', priority: 1, status: 0 }, - { name: 'Update prescription medications', priority: 1, status: 0 }, - - // Financial tasks (AI should suggest money management) - { name: 'Review investment portfolio', priority: 1, status: 0 }, - { name: 'Update budget spreadsheet', priority: 1, status: 0 }, - { name: 'Research high-yield savings accounts', priority: 0, status: 0 }, - { name: 'Review insurance coverage', priority: 1, status: 0 }, - - // Learning tasks (AI should suggest skill development) - { name: 'Complete Python course', priority: 1, status: 1 }, - { name: 'Read industry publication', priority: 0, status: 0 }, - { name: 'Attend professional conference', priority: 1, status: 0 }, - { name: 'Update professional certifications', priority: 1, status: 0 }, - - // Maintenance tasks (AI should suggest regular upkeep) - { name: 'Change air filter in HVAC', priority: 0, status: 0 }, - { name: 'Test smoke detector batteries', priority: 1, status: 0 }, - { name: 'Backup computer files', priority: 1, status: 0 }, - { name: 'Update software and security patches', priority: 1, status: 0 }, - - // Social/relationship tasks (AI should suggest work-life balance) - { name: 'Plan anniversary dinner', priority: 1, status: 0 }, - { name: 'Call grandparents', priority: 1, status: 0 }, - { name: 'Schedule date night', priority: 0, status: 0 }, - { name: 'Organize family gathering', priority: 1, status: 0 }, - - // Creative/hobby tasks (AI should suggest personal fulfillment) - { name: 'Practice guitar daily', priority: 0, status: 0 }, - { name: 'Work on painting project', priority: 0, status: 0 }, - { name: 'Write in journal', priority: 0, status: 0 }, - { name: 'Learn new recipe', priority: 0, status: 0 }, - - // Recurring daily tasks (AI should recognize patterns) - { - name: 'Daily meditation practice', - priority: 1, - status: 0, - recurrence_type: 'daily', - recurrence_interval: 1, - due_date: new Date() - }, - { - name: 'Review daily priorities', - priority: 1, - status: 0, - recurrence_type: 'daily', - recurrence_interval: 1, - due_date: new Date() - }, - { - name: 'Log daily expenses', - priority: 0, - status: 0, - recurrence_type: 'daily', - recurrence_interval: 1, - due_date: new Date() - }, - - // Weekly recurring tasks - { - name: 'Weekly meal planning', - priority: 1, - status: 0, - recurrence_type: 'weekly', - recurrence_interval: 1, - recurrence_weekday: 0, // Sunday - due_date: getRandomDate(7) - }, - { - name: 'Weekly house cleaning', - priority: 1, - status: 0, - recurrence_type: 'weekly', - recurrence_interval: 1, - recurrence_weekday: 6, // Saturday - due_date: getRandomDate(7) - }, - { - name: 'Weekly team standup', - priority: 1, - status: 0, - recurrence_type: 'weekly', - recurrence_interval: 1, - recurrence_weekday: 1, // Monday - due_date: getRandomDate(7), - project_id: projects[0].id - }, - - // Monthly recurring tasks - { - name: 'Monthly budget review', - priority: 2, - status: 0, - recurrence_type: 'monthly', - recurrence_interval: 1, - recurrence_month_day: 1, - due_date: getRandomDate(30) - }, - { - name: 'Monthly backup verification', - priority: 1, - status: 0, - recurrence_type: 'monthly', - recurrence_interval: 1, - recurrence_month_day: 15, - due_date: getRandomDate(30) - }, - - // Waiting status tasks (AI should suggest follow-up actions) - { name: 'Wait for contractor estimate', priority: 1, status: 4, project_id: projects[2].id }, - { name: 'Wait for insurance approval', priority: 2, status: 4 }, - { name: 'Wait for vendor response', priority: 1, status: 4, project_id: projects[0].id }, - { name: 'Wait for medical test results', priority: 1, status: 4 }, - { name: 'Wait for loan approval', priority: 2, status: 4 }, - - // Recently completed tasks for learning patterns - { name: 'Complete weekly workout goal', priority: 1, status: 2, completed_at: getPastDate(1), project_id: projects[3].id }, - { name: 'Finish reading productivity book', priority: 0, status: 2, completed_at: getPastDate(2) }, - { name: 'Complete online course module', priority: 1, status: 2, completed_at: getPastDate(1) }, - { name: 'Submit weekly report', priority: 1, status: 2, completed_at: getPastDate(1), project_id: projects[0].id }, - { name: 'Complete meal prep for week', priority: 1, status: 2, completed_at: getPastDate(1), project_id: projects[13].id }, - { name: 'Finish monthly budget', priority: 1, status: 2, completed_at: getPastDate(3) }, - { name: 'Complete photography assignment', priority: 1, status: 2, completed_at: getPastDate(2), project_id: projects[7].id }, - { name: 'Finish home organization project', priority: 0, status: 2, completed_at: getPastDate(4) }, - { name: 'Complete investment research', priority: 1, status: 2, completed_at: getPastDate(5), project_id: projects[5].id }, - { name: 'Finish blog post draft', priority: 1, status: 2, completed_at: getPastDate(2), project_id: projects[10].id } - ]; + // Helper to get random priority + const getRandomPriority = () => Math.floor(Math.random() * 3); // 0, 1, or 2 - // Combine all tasks - return [...baseTaskData, ...massiveTasks, ...aiTriggerTasks]; + // Helper to get random status + const getRandomStatus = () => { + const statuses = [0, 0, 0, 0, 0, 0, 1, 1, 1, 2, 2, 3, 4]; // More weighted towards active tasks + return statuses[Math.floor(Math.random() * statuses.length)]; + }; + + // Productivity and work tasks + const workTasks = [ + 'Review quarterly performance metrics', + 'Update project documentation', + 'Prepare presentation for board meeting', + 'Conduct code review for new feature', + 'Write technical specification document', + 'Schedule one-on-one meetings with team', + 'Update project timeline and milestones', + 'Research new development tools', + 'Optimize database queries', + 'Create automated testing suite', + 'Refactor legacy code modules', + 'Implement security audit recommendations', + 'Design API documentation', + 'Setup continuous integration pipeline', + 'Create user acceptance testing plan', + 'Migrate data to new system', + 'Setup monitoring and alerting', + 'Write deployment procedures', + 'Create backup and recovery plan', + 'Conduct performance testing', + 'Update coding standards documentation', + 'Setup development environment', + 'Create onboarding documentation', + 'Review and update dependencies', + 'Implement feature toggles', + 'Setup load balancing', + 'Create disaster recovery plan', + 'Conduct security penetration testing', + 'Setup SSL certificates', + 'Implement caching strategy', + 'Create analytics dashboard', + 'Setup error tracking', + 'Implement rate limiting', + 'Create API versioning strategy', + 'Setup database replication', + 'Implement search functionality', + 'Create notification system', + 'Setup file upload handling', + 'Implement user authentication', + 'Create password reset functionality', + 'Setup email templates', + 'Implement data validation', + 'Create audit logging', + 'Setup health checks', + 'Implement graceful shutdowns', + ]; + + // Personal development and learning tasks + const learningTasks = [ + 'Complete online course on machine learning', + 'Read "Clean Architecture" book', + 'Practice coding challenges on LeetCode', + 'Learn advanced Git techniques', + 'Study microservices architecture', + 'Complete Docker certification', + 'Learn Kubernetes fundamentals', + 'Study system design patterns', + 'Practice algorithm problems', + 'Learn about database optimization', + 'Study network security principles', + 'Complete AWS certification', + 'Learn about blockchain technology', + 'Study DevOps best practices', + 'Learn advanced JavaScript features', + 'Study React performance optimization', + 'Learn about GraphQL', + 'Study mobile app development', + 'Learn about AI and neural networks', + 'Study cloud computing concepts', + 'Learn about containerization', + 'Study API design principles', + 'Learn about testing strategies', + 'Study agile methodologies', + 'Learn about project management', + 'Study user experience design', + 'Learn about data visualization', + 'Study cybersecurity fundamentals', + 'Learn about scalability patterns', + 'Study database design principles', + ]; + + // Health and fitness tasks + const healthTasks = [ + 'Schedule annual physical exam', + 'Book dental cleaning appointment', + 'Schedule eye exam', + 'Get blood work done', + 'Schedule dermatologist appointment', + 'Book massage therapy session', + 'Schedule physical therapy session', + 'Get flu vaccination', + 'Schedule mammogram', + 'Book nutrition consultation', + 'Schedule mental health counseling', + 'Get hearing test', + 'Schedule chiropractor appointment', + 'Book acupuncture session', + 'Schedule sleep study', + 'Get allergy testing done', + 'Schedule colonoscopy', + 'Book podiatrist appointment', + 'Schedule orthopedic consultation', + 'Get heart health screening', + 'Complete 30-minute cardio workout', + 'Do strength training session', + 'Practice yoga for 45 minutes', + 'Go for 5-mile run', + 'Complete HIIT workout', + 'Do pilates session', + 'Practice meditation for 20 minutes', + 'Track daily water intake', + 'Meal prep for the week', + 'Plan healthy breakfast options', + 'Research new workout routines', + 'Update fitness goals', + 'Track daily steps (10,000 goal)', + 'Practice breathing exercises', + 'Do stretching routine', + 'Plan weekly workout schedule', + 'Research healthy recipes', + 'Update meal planning app', + 'Schedule workout with trainer', + 'Join new fitness class', + ]; + + // Home and family tasks + const homeTasks = [ + 'Deep clean living room', + 'Organize bedroom closet', + 'Clean out garage', + 'Wash and fold laundry', + 'Vacuum all carpets', + 'Mop kitchen and bathroom floors', + 'Clean windows inside and out', + 'Organize pantry and kitchen cabinets', + 'Clean out refrigerator', + 'Wash bedsheets and pillowcases', + 'Dust all furniture', + 'Clean bathroom thoroughly', + 'Organize home office', + 'Sort through old documents', + 'Clean out car interior', + 'Wash car exterior', + 'Organize basement storage', + 'Clean air conditioning filters', + 'Test smoke detector batteries', + 'Check and clean gutters', + 'Trim bushes and hedges', + 'Water indoor plants', + 'Plant vegetables in garden', + 'Mow lawn and edge walkways', + 'Repair leaky faucet', + 'Fix squeaky door hinges', + 'Replace burnt out light bulbs', + 'Caulk bathroom tiles', + 'Touch up paint on walls', + 'Clean grout in shower', + 'Organize tool shed', + 'Season cast iron cookware', + 'Clean oven and stovetop', + 'Descale coffee maker', + 'Clean dishwasher filter', + 'Replace water filter', + 'Clean dryer vent', + 'Organize medicine cabinet', + 'Check expiration dates on medications', + 'Update emergency contact list', + ]; + + // Financial and administrative tasks + const financialTasks = [ + 'Review monthly budget', + 'Pay credit card bills', + 'Transfer money to savings', + 'Update investment portfolio', + 'Review insurance policies', + 'File tax documents', + 'Update will and testament', + 'Review retirement contributions', + 'Check credit report', + 'Update beneficiary information', + 'Review bank statements', + 'Cancel unused subscriptions', + 'Negotiate lower cable bill', + 'Shop for better car insurance', + 'Review cell phone plan', + 'Update emergency fund', + 'Research investment options', + 'Meet with financial advisor', + 'Review mortgage rates', + 'Update home insurance', + 'File warranty claims', + 'Organize receipts for taxes', + 'Update accounting software', + 'Review business expenses', + 'Pay quarterly taxes', + 'Update PayPal account', + 'Review online banking security', + 'Setup automatic bill pay', + 'Research high-yield savings', + 'Update direct deposit info', + ]; + + // Social and relationship tasks + const socialTasks = [ + 'Call parents to check in', + 'Send birthday card to friend', + 'Plan date night with partner', + 'Schedule coffee with colleague', + 'Write thank you note', + 'Plan family reunion', + 'Organize game night with friends', + 'Send holiday cards', + 'Plan surprise party', + 'Schedule lunch with mentor', + 'Join local community group', + 'Volunteer at local charity', + 'Attend networking event', + 'Plan weekend getaway', + 'Organize book club meeting', + 'Schedule video call with family', + 'Plan group hiking trip', + 'Organize potluck dinner', + 'Plan movie night', + 'Schedule catch-up with old friend', + 'Write recommendation letter', + 'Plan anniversary celebration', + "Organize children's playdate", + 'Schedule babysitter', + 'Plan family photo session', + 'Organize neighborhood BBQ', + 'Plan holiday gathering', + "Schedule couple's therapy", + 'Plan birthday celebration', + 'Organize team building activity', + ]; + + // Creative and hobby tasks + const creativeeTasks = [ + 'Practice guitar for 30 minutes', + 'Work on oil painting', + 'Write in journal', + 'Take photography workshop', + 'Learn new recipe', + 'Practice calligraphy', + 'Work on knitting project', + 'Write short story', + 'Learn new song on piano', + 'Practice drawing portraits', + 'Work on pottery project', + 'Edit video footage', + 'Write blog post', + 'Practice singing', + 'Work on woodworking project', + 'Learn new dance moves', + 'Practice photography techniques', + 'Work on scrapbook', + 'Write poetry', + 'Learn origami', + 'Practice sketching', + 'Work on embroidery', + 'Learn new cooking technique', + 'Practice watercolor painting', + 'Work on jewelry making', + 'Learn magic tricks', + 'Practice stand-up comedy', + 'Work on graphic design', + 'Learn new language phrases', + 'Practice mindful writing', + ]; + + // Travel and adventure tasks + const travelTasks = [ + 'Research vacation destinations', + 'Book flight tickets', + 'Reserve hotel accommodation', + 'Plan daily itinerary', + 'Apply for passport renewal', + 'Get travel insurance', + 'Exchange currency', + 'Pack suitcase', + 'Check visa requirements', + 'Update travel emergency contacts', + 'Download offline maps', + 'Research local customs', + 'Learn basic phrases', + 'Book airport parking', + 'Arrange pet sitting', + 'Stop mail delivery', + 'Set house security system', + 'Pack travel first aid kit', + 'Research local restaurants', + 'Book tours and activities', + 'Print boarding passes', + 'Check weather forecast', + 'Pack travel documents', + 'Arrange airport transportation', + 'Update travel blog', + ]; + + // All task categories combined + const allTaskCategories = [ + ...workTasks, + ...learningTasks, + ...healthTasks, + ...homeTasks, + ...financialTasks, + ...socialTasks, + ...creativeeTasks, + ...travelTasks, + ]; + + // Create base task data with existing project tasks + const baseTaskData = [ + // Website Redesign Project (triggers collaboration, urgent deadlines) + { + name: 'Research competitor websites', + project_id: projects[0].id, + priority: 1, + status: 2, + completed_at: getPastDate(5), + }, + { + name: 'Create wireframes for homepage', + project_id: projects[0].id, + priority: 2, + status: 1, + }, + { + name: 'Design new color palette', + project_id: projects[0].id, + priority: 1, + status: 0, + }, + { + name: 'Write content for About page', + project_id: projects[0].id, + priority: 1, + status: 0, + }, + { + name: 'Set up staging environment', + project_id: projects[0].id, + priority: 2, + status: 0, + due_date: getRandomDate(3), + }, // Urgent deadline + { + name: 'Optimize images for web', + project_id: projects[0].id, + priority: 1, + status: 0, + }, + { + name: 'Implement responsive design', + project_id: projects[0].id, + priority: 2, + status: 0, + due_date: getRandomDate(7), + }, + { + name: 'Test cross-browser compatibility', + project_id: projects[0].id, + priority: 1, + status: 0, + }, + { + name: 'Setup Google Analytics', + project_id: projects[0].id, + priority: 1, + status: 0, + }, + { + name: 'Create contact form', + project_id: projects[0].id, + priority: 1, + status: 0, + }, + { + name: 'Write SEO meta descriptions', + project_id: projects[0].id, + priority: 1, + status: 0, + }, + { + name: 'Design mobile navigation', + project_id: projects[0].id, + priority: 2, + status: 0, + }, + { + name: 'Create footer section', + project_id: projects[0].id, + priority: 0, + status: 0, + }, + { + name: 'Add social media icons', + project_id: projects[0].id, + priority: 0, + status: 0, + }, + { + name: 'Setup SSL certificate', + project_id: projects[0].id, + priority: 2, + status: 0, + due_date: getRandomDate(2), + }, // Very urgent + + // Europe Trip 2024 - triggers travel planning AI features + { + name: 'Research flight options to Paris', + project_id: projects[6].id, + priority: 2, + status: 1, + }, + { + name: 'Book hotel in Rome', + project_id: projects[6].id, + priority: 2, + status: 0, + due_date: getRandomDate(14), + }, + { + name: 'Apply for European travel insurance', + project_id: projects[6].id, + priority: 2, + status: 0, + due_date: getRandomDate(30), + }, + { + name: 'Learn basic Italian phrases', + project_id: projects[6].id, + priority: 1, + status: 0, + }, + { + name: 'Research train routes between cities', + project_id: projects[6].id, + priority: 1, + status: 0, + }, + { + name: 'Plan museum visits in Paris', + project_id: projects[6].id, + priority: 1, + status: 0, + }, + { + name: 'Book restaurant reservations', + project_id: projects[6].id, + priority: 1, + status: 0, + }, + { + name: 'Pack European travel adapter', + project_id: projects[6].id, + priority: 0, + status: 0, + }, + + // Fitness Challenge - triggers health/wellness AI features + { + name: 'Track daily protein intake', + project_id: projects[3].id, + priority: 1, + status: 1, + }, + { + name: 'Complete morning cardio workout', + project_id: projects[3].id, + priority: 1, + status: 2, + completed_at: getPastDate(1), + }, + { + name: 'Plan weekly meal prep', + project_id: projects[3].id, + priority: 1, + status: 0, + }, + { + name: 'Schedule body composition scan', + project_id: projects[3].id, + priority: 1, + status: 0, + due_date: getRandomDate(7), + }, + { + name: 'Research new workout routines', + project_id: projects[3].id, + priority: 0, + status: 0, + }, + { + name: 'Update fitness tracker goals', + project_id: projects[3].id, + priority: 1, + status: 0, + }, + + // Investment Portfolio - triggers financial AI features + { + name: 'Research ESG investment options', + project_id: projects[5].id, + priority: 1, + status: 0, + }, + { + name: 'Rebalance portfolio allocation', + project_id: projects[5].id, + priority: 2, + status: 0, + due_date: getRandomDate(5), + }, + { + name: 'Review quarterly performance', + project_id: projects[5].id, + priority: 1, + status: 1, + }, + { + name: 'Set up automatic dividend reinvestment', + project_id: projects[5].id, + priority: 1, + status: 0, + }, + { + name: 'Research international market exposure', + project_id: projects[5].id, + priority: 0, + status: 0, + }, + + // Side Business - triggers entrepreneurship AI features + { + name: 'Create business plan document', + project_id: projects[4].id, + priority: 2, + status: 1, + }, + { + name: 'Research target market demographics', + project_id: projects[4].id, + priority: 2, + status: 0, + }, + { + name: 'Design logo and branding', + project_id: projects[4].id, + priority: 1, + status: 0, + }, + { + name: 'Setup business social media accounts', + project_id: projects[4].id, + priority: 1, + status: 0, + }, + { + name: 'Register domain name', + project_id: projects[4].id, + priority: 2, + status: 2, + completed_at: getPastDate(3), + }, + { + name: 'Create pricing strategy', + project_id: projects[4].id, + priority: 2, + status: 0, + }, + { + name: 'Draft service agreements', + project_id: projects[4].id, + priority: 1, + status: 0, + }, + + // Home Renovation - triggers home improvement AI features + { + name: 'Get electrical work permit', + project_id: projects[2].id, + priority: 2, + status: 0, + due_date: getRandomDate(10), + }, + { + name: 'Choose bathroom tile pattern', + project_id: projects[2].id, + priority: 1, + status: 1, + }, + { + name: 'Schedule plumbing inspection', + project_id: projects[2].id, + priority: 2, + status: 0, + due_date: getRandomDate(14), + }, + { + name: 'Order kitchen countertops', + project_id: projects[2].id, + priority: 2, + status: 0, + due_date: getRandomDate(21), + }, + { + name: 'Research energy-efficient appliances', + project_id: projects[2].id, + priority: 1, + status: 0, + }, + { + name: 'Plan kitchen lighting layout', + project_id: projects[2].id, + priority: 1, + status: 0, + }, + + // Photography Mastery - triggers creative learning AI features + { + name: 'Practice portrait lighting techniques', + project_id: projects[7].id, + priority: 1, + status: 1, + }, + { + name: "Edit last weekend's photo shoot", + project_id: projects[7].id, + priority: 1, + status: 0, + }, + { + name: 'Research local photography groups', + project_id: projects[7].id, + priority: 0, + status: 0, + }, + { + name: 'Plan golden hour photo session', + project_id: projects[7].id, + priority: 1, + status: 0, + }, + { + name: 'Learn advanced Lightroom techniques', + project_id: projects[7].id, + priority: 1, + status: 0, + }, + + // Smart Home Setup - triggers technology AI features + { + name: 'Install smart thermostat', + project_id: projects[14].id, + priority: 2, + status: 1, + }, + { + name: 'Configure home security system', + project_id: projects[14].id, + priority: 2, + status: 0, + due_date: getRandomDate(7), + }, + { + name: 'Setup voice assistant routines', + project_id: projects[14].id, + priority: 1, + status: 0, + }, + { + name: 'Install smart door locks', + project_id: projects[14].id, + priority: 2, + status: 0, + }, + { + name: 'Configure automated lighting', + project_id: projects[14].id, + priority: 1, + status: 0, + }, + + // Blog Launch - triggers content creation AI features + { + name: 'Write first blog post about productivity', + project_id: projects[10].id, + priority: 2, + status: 1, + }, + { + name: 'Design blog layout and theme', + project_id: projects[10].id, + priority: 1, + status: 0, + }, + { + name: 'Setup email newsletter signup', + project_id: projects[10].id, + priority: 1, + status: 0, + }, + { + name: 'Research SEO keywords for niche', + project_id: projects[10].id, + priority: 1, + status: 0, + }, + { + name: 'Create content calendar for 3 months', + project_id: projects[10].id, + priority: 2, + status: 0, + }, + + // Professional Certification - triggers career development AI features + { + name: 'Complete AWS practice exams', + project_id: projects[8].id, + priority: 2, + status: 1, + }, + { + name: 'Schedule certification exam', + project_id: projects[8].id, + priority: 2, + status: 0, + due_date: getRandomDate(30), + }, + { + name: 'Review cloud architecture patterns', + project_id: projects[8].id, + priority: 1, + status: 0, + }, + { + name: 'Practice hands-on labs', + project_id: projects[8].id, + priority: 1, + status: 1, + }, + { + name: 'Join AWS study group', + project_id: projects[8].id, + priority: 0, + status: 0, + }, + + // Meal Prep System - triggers nutrition AI features + { + name: 'Plan balanced weekly menu', + project_id: projects[13].id, + priority: 1, + status: 1, + }, + { + name: 'Prep vegetables for the week', + project_id: projects[13].id, + priority: 1, + status: 0, + }, + { + name: 'Cook batch of protein sources', + project_id: projects[13].id, + priority: 1, + status: 0, + }, + { + name: 'Calculate macronutrient ratios', + project_id: projects[13].id, + priority: 1, + status: 0, + }, + { + name: 'Research meal prep containers', + project_id: projects[13].id, + priority: 0, + status: 0, + }, + + // Wedding Planning - triggers event planning AI features + { + name: 'Book wedding venue', + project_id: projects[12].id, + priority: 2, + status: 2, + completed_at: getPastDate(30), + }, + { + name: 'Send save the date cards', + project_id: projects[12].id, + priority: 2, + status: 0, + due_date: getRandomDate(60), + }, + { + name: 'Book wedding photographer', + project_id: projects[12].id, + priority: 2, + status: 0, + due_date: getRandomDate(45), + }, + { + name: 'Choose wedding cake flavors', + project_id: projects[12].id, + priority: 1, + status: 0, + }, + { + name: 'Plan seating arrangement', + project_id: projects[12].id, + priority: 1, + status: 0, + }, + { + name: 'Book honeymoon flights', + project_id: projects[12].id, + priority: 1, + status: 0, + }, + + // Garden Makeover - triggers gardening/sustainability AI features + { + name: 'Plan vegetable garden layout', + project_id: projects[9].id, + priority: 1, + status: 1, + }, + { + name: 'Order seeds for spring planting', + project_id: projects[9].id, + priority: 2, + status: 0, + due_date: getRandomDate(14), + }, + { + name: 'Install drip irrigation system', + project_id: projects[9].id, + priority: 1, + status: 0, + }, + { + name: 'Build raised garden beds', + project_id: projects[9].id, + priority: 2, + status: 0, + }, + { + name: 'Research companion planting', + project_id: projects[9].id, + priority: 0, + status: 0, + }, + ]; + + // Generate massive additional tasks + const massiveTasks = []; + + // Add random tasks from all categories (including old tasks for backlog) + for (let i = 0; i < 150; i++) { + const taskName = + allTaskCategories[ + Math.floor(Math.random() * allTaskCategories.length) + ]; + const hasProject = Math.random() < 0.4; // 40% chance of having a project + const hasDueDate = Math.random() < 0.3; // 30% chance of having a due date + const isCompleted = Math.random() < 0.08; // 8% chance of being completed + + const task = { + name: taskName, + priority: getRandomPriority(), + status: isCompleted ? 2 : getRandomStatus(), + note: + Math.random() < 0.1 + ? 'Added some notes during planning phase' + : null, + }; + + if (hasProject) { + task.project_id = + projects[Math.floor(Math.random() * projects.length)].id; + } + + if (hasDueDate) { + if (Math.random() < 0.2) { + // 20% chance of overdue task (AI should flag these) + task.due_date = getPastDate(Math.floor(Math.random() * 30) + 1); + } else { + // Future due date + task.due_date = getRandomDate( + Math.floor(Math.random() * 60) + 1 + ); + } + } + + if (isCompleted) { + task.completed_at = getPastDate(Math.floor(Math.random() * 30) + 1); + } + + massiveTasks.push(task); + } + + // Add specific AI trigger tasks (tasks that should trigger intelligent suggestions) + const aiTriggerTasks = [ + // Overdue tasks (AI should suggest prioritizing) + { + name: 'Submit tax documents', + priority: 2, + status: 0, + due_date: getPastDate(5), + }, + { + name: 'Renew car registration', + priority: 2, + status: 0, + due_date: getPastDate(3), + }, + { + name: 'Pay property taxes', + priority: 2, + status: 0, + due_date: getPastDate(10), + }, + { + name: 'Submit insurance claim', + priority: 2, + status: 0, + due_date: getPastDate(7), + }, + + // High-priority tasks with near deadlines (AI should suggest immediate action) + { + name: 'Prepare presentation for CEO', + priority: 2, + status: 0, + due_date: getRandomDate(1), + }, + { + name: 'Submit project proposal', + priority: 2, + status: 0, + due_date: getRandomDate(2), + }, + { + name: 'Complete performance review', + priority: 2, + status: 0, + due_date: getRandomDate(3), + }, + + // Health-related tasks (AI should suggest wellness patterns) + { name: 'Schedule annual checkup', priority: 1, status: 0 }, + { name: 'Get eye exam', priority: 1, status: 0 }, + { name: 'Book dental cleaning', priority: 1, status: 0 }, + { name: 'Update prescription medications', priority: 1, status: 0 }, + + // Financial tasks (AI should suggest money management) + { name: 'Review investment portfolio', priority: 1, status: 0 }, + { name: 'Update budget spreadsheet', priority: 1, status: 0 }, + { + name: 'Research high-yield savings accounts', + priority: 0, + status: 0, + }, + { name: 'Review insurance coverage', priority: 1, status: 0 }, + + // Learning tasks (AI should suggest skill development) + { name: 'Complete Python course', priority: 1, status: 1 }, + { name: 'Read industry publication', priority: 0, status: 0 }, + { name: 'Attend professional conference', priority: 1, status: 0 }, + { name: 'Update professional certifications', priority: 1, status: 0 }, + + // Maintenance tasks (AI should suggest regular upkeep) + { name: 'Change air filter in HVAC', priority: 0, status: 0 }, + { name: 'Test smoke detector batteries', priority: 1, status: 0 }, + { name: 'Backup computer files', priority: 1, status: 0 }, + { + name: 'Update software and security patches', + priority: 1, + status: 0, + }, + + // Social/relationship tasks (AI should suggest work-life balance) + { name: 'Plan anniversary dinner', priority: 1, status: 0 }, + { name: 'Call grandparents', priority: 1, status: 0 }, + { name: 'Schedule date night', priority: 0, status: 0 }, + { name: 'Organize family gathering', priority: 1, status: 0 }, + + // Creative/hobby tasks (AI should suggest personal fulfillment) + { name: 'Practice guitar daily', priority: 0, status: 0 }, + { name: 'Work on painting project', priority: 0, status: 0 }, + { name: 'Write in journal', priority: 0, status: 0 }, + { name: 'Learn new recipe', priority: 0, status: 0 }, + + // Recurring daily tasks (AI should recognize patterns) + { + name: 'Daily meditation practice', + priority: 1, + status: 0, + recurrence_type: 'daily', + recurrence_interval: 1, + due_date: new Date(), + }, + { + name: 'Review daily priorities', + priority: 1, + status: 0, + recurrence_type: 'daily', + recurrence_interval: 1, + due_date: new Date(), + }, + { + name: 'Log daily expenses', + priority: 0, + status: 0, + recurrence_type: 'daily', + recurrence_interval: 1, + due_date: new Date(), + }, + + // Weekly recurring tasks + { + name: 'Weekly meal planning', + priority: 1, + status: 0, + recurrence_type: 'weekly', + recurrence_interval: 1, + recurrence_weekday: 0, // Sunday + due_date: getRandomDate(7), + }, + { + name: 'Weekly house cleaning', + priority: 1, + status: 0, + recurrence_type: 'weekly', + recurrence_interval: 1, + recurrence_weekday: 6, // Saturday + due_date: getRandomDate(7), + }, + { + name: 'Weekly team standup', + priority: 1, + status: 0, + recurrence_type: 'weekly', + recurrence_interval: 1, + recurrence_weekday: 1, // Monday + due_date: getRandomDate(7), + project_id: projects[0].id, + }, + + // Monthly recurring tasks + { + name: 'Monthly budget review', + priority: 2, + status: 0, + recurrence_type: 'monthly', + recurrence_interval: 1, + recurrence_month_day: 1, + due_date: getRandomDate(30), + }, + { + name: 'Monthly backup verification', + priority: 1, + status: 0, + recurrence_type: 'monthly', + recurrence_interval: 1, + recurrence_month_day: 15, + due_date: getRandomDate(30), + }, + + // Waiting status tasks (AI should suggest follow-up actions) + { + name: 'Wait for contractor estimate', + priority: 1, + status: 4, + project_id: projects[2].id, + }, + { name: 'Wait for insurance approval', priority: 2, status: 4 }, + { + name: 'Wait for vendor response', + priority: 1, + status: 4, + project_id: projects[0].id, + }, + { name: 'Wait for medical test results', priority: 1, status: 4 }, + { name: 'Wait for loan approval', priority: 2, status: 4 }, + + // Recently completed tasks for learning patterns + { + name: 'Complete weekly workout goal', + priority: 1, + status: 2, + completed_at: getPastDate(1), + project_id: projects[3].id, + }, + { + name: 'Finish reading productivity book', + priority: 0, + status: 2, + completed_at: getPastDate(2), + }, + { + name: 'Complete online course module', + priority: 1, + status: 2, + completed_at: getPastDate(1), + }, + { + name: 'Submit weekly report', + priority: 1, + status: 2, + completed_at: getPastDate(1), + project_id: projects[0].id, + }, + { + name: 'Complete meal prep for week', + priority: 1, + status: 2, + completed_at: getPastDate(1), + project_id: projects[13].id, + }, + { + name: 'Finish monthly budget', + priority: 1, + status: 2, + completed_at: getPastDate(3), + }, + { + name: 'Complete photography assignment', + priority: 1, + status: 2, + completed_at: getPastDate(2), + project_id: projects[7].id, + }, + { + name: 'Finish home organization project', + priority: 0, + status: 2, + completed_at: getPastDate(4), + }, + { + name: 'Complete investment research', + priority: 1, + status: 2, + completed_at: getPastDate(5), + project_id: projects[5].id, + }, + { + name: 'Finish blog post draft', + priority: 1, + status: 2, + completed_at: getPastDate(2), + project_id: projects[10].id, + }, + ]; + + // Combine all tasks + return [...baseTaskData, ...massiveTasks, ...aiTriggerTasks]; } -module.exports = { createMassiveTaskData }; \ No newline at end of file +module.exports = { createMassiveTaskData }; diff --git a/backend/services/quotesService.js b/backend/services/quotesService.js index 3bd26d5..af0a980 100644 --- a/backend/services/quotesService.js +++ b/backend/services/quotesService.js @@ -4,128 +4,119 @@ const yaml = require('js-yaml'); // create default quotes const createDefaultQuotes = () => [ - "Believe you can and you're halfway there.", - "The only way to do great work is to love what you do.", - "It always seems impossible until it's done.", - "Focus on progress, not perfection.", - "One task at a time leads to great accomplishments." + "Believe you can and you're halfway there.", + 'The only way to do great work is to love what you do.', + "It always seems impossible until it's done.", + 'Focus on progress, not perfection.', + 'One task at a time leads to great accomplishments.', ]; // get quotes file path -const getQuotesFilePath = () => - path.join(__dirname, '../config/quotes.yml'); +const getQuotesFilePath = () => path.join(__dirname, '../config/quotes.yml'); // Side effect function to check if file exists -const fileExists = (filePath) => - fs.existsSync(filePath); +const fileExists = (filePath) => fs.existsSync(filePath); // Side effect function to read file contents -const readFileContents = (filePath) => - fs.readFileSync(filePath, 'utf8'); +const readFileContents = (filePath) => fs.readFileSync(filePath, 'utf8'); // parse YAML content const parseYamlContent = (content) => { - try { - return yaml.load(content); - } catch (error) { - throw new Error(`Failed to parse YAML: ${error.message}`); - } + try { + return yaml.load(content); + } catch (error) { + throw new Error(`Failed to parse YAML: ${error.message}`); + } }; // validate quotes data structure -const validateQuotesData = (data) => - !!(data && data.quotes && Array.isArray(data.quotes)); +const validateQuotesData = (data) => + !!(data && data.quotes && Array.isArray(data.quotes)); // extract quotes from data const extractQuotes = (data) => { - if (validateQuotesData(data)) { - return data.quotes; - } - return null; + if (validateQuotesData(data)) { + return data.quotes; + } + return null; }; // Side effect function to load quotes from file const loadQuotesFromFile = () => { - try { - const quotesPath = getQuotesFilePath(); - - if (!fileExists(quotesPath)) { - console.warn('Quotes configuration file not found, using defaults'); - return createDefaultQuotes(); - } + try { + const quotesPath = getQuotesFilePath(); - const fileContents = readFileContents(quotesPath); - const data = parseYamlContent(fileContents); - const quotes = extractQuotes(data); - - if (quotes) { - console.log(`Loaded ${quotes.length} quotes from configuration`); - return quotes; - } else { - console.warn('No quotes found in configuration file'); - return createDefaultQuotes(); + if (!fileExists(quotesPath)) { + console.warn('Quotes configuration file not found, using defaults'); + return createDefaultQuotes(); + } + + const fileContents = readFileContents(quotesPath); + const data = parseYamlContent(fileContents); + const quotes = extractQuotes(data); + + if (quotes) { + console.log(`Loaded ${quotes.length} quotes from configuration`); + return quotes; + } else { + console.warn('No quotes found in configuration file'); + return createDefaultQuotes(); + } + } catch (error) { + console.error('Error loading quotes:', error.message); + return createDefaultQuotes(); } - } catch (error) { - console.error('Error loading quotes:', error.message); - return createDefaultQuotes(); - } }; // get random index -const getRandomIndex = (arrayLength) => - Math.floor(Math.random() * arrayLength); +const getRandomIndex = (arrayLength) => Math.floor(Math.random() * arrayLength); // get random quote from array const getRandomQuoteFromArray = (quotes) => { - if (quotes.length === 0) { - return "Stay focused and keep going!"; - } - - const randomIndex = getRandomIndex(quotes.length); - return quotes[randomIndex]; + if (quotes.length === 0) { + return 'Stay focused and keep going!'; + } + + const randomIndex = getRandomIndex(quotes.length); + return quotes[randomIndex]; }; // get all quotes -const getAllQuotesFromArray = (quotes) => - [...quotes]; // Return copy to maintain immutability +const getAllQuotesFromArray = (quotes) => [...quotes]; // Return copy to maintain immutability // get quotes count -const getQuotesCount = (quotes) => - quotes.length; +const getQuotesCount = (quotes) => quotes.length; // Initialize quotes on module load let quotes = loadQuotesFromFile(); // Function to reload quotes (contains side effects) const reloadQuotes = () => { - quotes = loadQuotesFromFile(); - return quotes; + quotes = loadQuotesFromFile(); + return quotes; }; // get random quote -const getRandomQuote = () => - getRandomQuoteFromArray(quotes); +const getRandomQuote = () => getRandomQuoteFromArray(quotes); // get all quotes -const getAllQuotes = () => - getAllQuotesFromArray(quotes); +const getAllQuotes = () => getAllQuotesFromArray(quotes); // get count -const getCount = () => - getQuotesCount(quotes); +const getCount = () => getQuotesCount(quotes); // Export functional interface module.exports = { - getRandomQuote, - getAllQuotes, - getQuotesCount: getCount, - reloadQuotes, - // For testing - _createDefaultQuotes: createDefaultQuotes, - _getQuotesFilePath: getQuotesFilePath, - _parseYamlContent: parseYamlContent, - _validateQuotesData: validateQuotesData, - _extractQuotes: extractQuotes, - _getRandomIndex: getRandomIndex, - _getRandomQuoteFromArray: getRandomQuoteFromArray -}; \ No newline at end of file + getRandomQuote, + getAllQuotes, + getQuotesCount: getCount, + reloadQuotes, + // For testing + _createDefaultQuotes: createDefaultQuotes, + _getQuotesFilePath: getQuotesFilePath, + _parseYamlContent: parseYamlContent, + _validateQuotesData: validateQuotesData, + _extractQuotes: extractQuotes, + _getRandomIndex: getRandomIndex, + _getRandomQuoteFromArray: getRandomQuoteFromArray, +}; diff --git a/backend/services/recurringTaskService.js b/backend/services/recurringTaskService.js index 5e96875..48a16fe 100644 --- a/backend/services/recurringTaskService.js +++ b/backend/services/recurringTaskService.js @@ -5,411 +5,463 @@ const { Op } = require('sequelize'); * Service for managing recurring tasks */ class RecurringTaskService { - - /** - * Generate new tasks from recurring task templates - * @param {number} userId - Optional user ID to limit processing - * @returns {Promise} Array of newly created tasks - */ - static async generateRecurringTasks(userId = null) { - try { - const whereClause = { - recurrence_type: { [Op.ne]: 'none' }, - status: { [Op.ne]: Task.STATUS.ARCHIVED } - }; + /** + * Generate new tasks from recurring task templates + * @param {number} userId - Optional user ID to limit processing + * @returns {Promise} Array of newly created tasks + */ + static async generateRecurringTasks(userId = null) { + try { + const whereClause = { + recurrence_type: { [Op.ne]: 'none' }, + status: { [Op.ne]: Task.STATUS.ARCHIVED }, + }; - if (userId) { - whereClause.user_id = userId; - } + if (userId) { + whereClause.user_id = userId; + } - // Find all recurring tasks that need processing - const recurringTasks = await Task.findAll({ - where: whereClause, - order: [['last_generated_date', 'ASC']] - }); + // Find all recurring tasks that need processing + const recurringTasks = await Task.findAll({ + where: whereClause, + order: [['last_generated_date', 'ASC']], + }); - const newTasks = []; - const now = new Date(); + const newTasks = []; + const now = new Date(); - for (const task of recurringTasks) { - const generatedTasks = await this.processRecurringTask(task, now); - newTasks.push(...generatedTasks); - } + for (const task of recurringTasks) { + const generatedTasks = await this.processRecurringTask( + task, + now + ); + newTasks.push(...generatedTasks); + } - return newTasks; - } catch (error) { - console.error('Error generating recurring tasks:', error); - throw error; - } - } - - /** - * Process a single recurring task and generate new instances if needed - * @param {Object} task - The recurring task template - * @param {Date} now - Current timestamp - * @returns {Promise} Array of newly created task instances - */ - static async processRecurringTask(task, now) { - const newTasks = []; - - // Skip if recurrence has ended - if (task.recurrence_end_date && now > task.recurrence_end_date) { - return newTasks; - } - - let nextDueDate = this.calculateNextDueDate(task, now); - - // Generate tasks up to current date - while (nextDueDate && nextDueDate <= now) { - // Check if this due date already has a task instance - const existingTask = await Task.findOne({ - where: { - user_id: task.user_id, - name: task.name, - due_date: nextDueDate, - project_id: task.project_id + return newTasks; + } catch (error) { + console.error('Error generating recurring tasks:', error); + throw error; } - }); - - if (!existingTask) { - const newTask = await this.createTaskInstance(task, nextDueDate); - newTasks.push(newTask); - } - - // Update last generated date - task.last_generated_date = nextDueDate; - await task.save(); - - // Calculate next due date - nextDueDate = this.calculateNextDueDate(task, nextDueDate); - - // Safety check to prevent infinite loops - if (newTasks.length > 100) { - console.warn(`Generated 100+ tasks for recurring task ${task.id}, stopping to prevent overflow`); - break; - } } - return newTasks; - } + /** + * Process a single recurring task and generate new instances if needed + * @param {Object} task - The recurring task template + * @param {Date} now - Current timestamp + * @returns {Promise} Array of newly created task instances + */ + static async processRecurringTask(task, now) { + const newTasks = []; - /** - * Create a new task instance from a recurring task template - * @param {Object} template - The recurring task template - * @param {Date} dueDate - Due date for the new task instance - * @returns {Promise} The newly created task - */ - static async createTaskInstance(template, dueDate) { - const taskData = { - name: template.name, - description: template.description, - due_date: dueDate, - today: false, - priority: template.priority, - status: Task.STATUS.NOT_STARTED, - note: template.note, - user_id: template.user_id, - project_id: template.project_id, - recurrence_type: 'none', // Instances are not recurring themselves - recurring_parent_id: template.id // Link to the original recurring task - }; + // Skip if recurrence has ended + if (task.recurrence_end_date && now > task.recurrence_end_date) { + return newTasks; + } - return await Task.create(taskData); - } + let nextDueDate = this.calculateNextDueDate(task, now); - /** - * Calculate the next due date for a recurring task - * @param {Object} task - The recurring task - * @param {Date} fromDate - Date to calculate from - * @returns {Date|null} Next due date or null if no more recurrences - */ - static calculateNextDueDate(task, fromDate) { - // Handle invalid inputs - if (!task || !task.recurrence_type || !fromDate || isNaN(fromDate.getTime())) { - return null; + // Generate tasks up to current date + while (nextDueDate && nextDueDate <= now) { + // Check if this due date already has a task instance + const existingTask = await Task.findOne({ + where: { + user_id: task.user_id, + name: task.name, + due_date: nextDueDate, + project_id: task.project_id, + }, + }); + + if (!existingTask) { + const newTask = await this.createTaskInstance( + task, + nextDueDate + ); + newTasks.push(newTask); + } + + // Update last generated date + task.last_generated_date = nextDueDate; + await task.save(); + + // Calculate next due date + nextDueDate = this.calculateNextDueDate(task, nextDueDate); + + // Safety check to prevent infinite loops + if (newTasks.length > 100) { + console.warn( + `Generated 100+ tasks for recurring task ${task.id}, stopping to prevent overflow` + ); + break; + } + } + + return newTasks; } - const baseDate = task.completion_based ? - (task.last_generated_date || task.created_at) : - (task.due_date || task.created_at); - - // If no base date is available, use fromDate - const startDate = baseDate ? - new Date(Math.max(fromDate.getTime(), baseDate.getTime())) : - new Date(fromDate.getTime()); - - switch (task.recurrence_type) { - case 'daily': - return this.calculateDailyRecurrence(startDate, task.recurrence_interval || 1); - - case 'weekly': - return this.calculateWeeklyRecurrence(startDate, task.recurrence_interval || 1, task.recurrence_weekday); - - case 'monthly': - return this.calculateMonthlyRecurrence(startDate, task.recurrence_interval || 1, task.recurrence_month_day); - - case 'monthly_weekday': - return this.calculateMonthlyWeekdayRecurrence( - startDate, - task.recurrence_interval || 1, - task.recurrence_weekday, - task.recurrence_week_of_month + /** + * Create a new task instance from a recurring task template + * @param {Object} template - The recurring task template + * @param {Date} dueDate - Due date for the new task instance + * @returns {Promise} The newly created task + */ + static async createTaskInstance(template, dueDate) { + const taskData = { + name: template.name, + description: template.description, + due_date: dueDate, + today: false, + priority: template.priority, + status: Task.STATUS.NOT_STARTED, + note: template.note, + user_id: template.user_id, + project_id: template.project_id, + recurrence_type: 'none', // Instances are not recurring themselves + recurring_parent_id: template.id, // Link to the original recurring task + }; + + return await Task.create(taskData); + } + + /** + * Calculate the next due date for a recurring task + * @param {Object} task - The recurring task + * @param {Date} fromDate - Date to calculate from + * @returns {Date|null} Next due date or null if no more recurrences + */ + static calculateNextDueDate(task, fromDate) { + // Handle invalid inputs + if ( + !task || + !task.recurrence_type || + !fromDate || + isNaN(fromDate.getTime()) + ) { + return null; + } + + const baseDate = task.completion_based + ? task.last_generated_date || task.created_at + : task.due_date || task.created_at; + + // If no base date is available, use fromDate + const startDate = baseDate + ? new Date(Math.max(fromDate.getTime(), baseDate.getTime())) + : new Date(fromDate.getTime()); + + switch (task.recurrence_type) { + case 'daily': + return this.calculateDailyRecurrence( + startDate, + task.recurrence_interval || 1 + ); + + case 'weekly': + return this.calculateWeeklyRecurrence( + startDate, + task.recurrence_interval || 1, + task.recurrence_weekday + ); + + case 'monthly': + return this.calculateMonthlyRecurrence( + startDate, + task.recurrence_interval || 1, + task.recurrence_month_day + ); + + case 'monthly_weekday': + return this.calculateMonthlyWeekdayRecurrence( + startDate, + task.recurrence_interval || 1, + task.recurrence_weekday, + task.recurrence_week_of_month + ); + + case 'monthly_last_day': + return this.calculateMonthlyLastDayRecurrence( + startDate, + task.recurrence_interval || 1 + ); + + default: + return null; + } + } + + /** + * Calculate next daily recurrence + * @param {Date} fromDate - Starting date + * @param {number} interval - Days between recurrences + * @returns {Date} Next due date + */ + static calculateDailyRecurrence(fromDate, interval) { + const nextDate = new Date(fromDate); + nextDate.setDate(nextDate.getDate() + interval); + return nextDate; + } + + /** + * Calculate next weekly recurrence + * @param {Date} fromDate - Starting date + * @param {number} interval - Weeks between recurrences + * @param {number} weekday - Target day of week (0=Sunday, 6=Saturday) + * @returns {Date} Next due date + */ + static calculateWeeklyRecurrence(fromDate, interval, weekday) { + const nextDate = new Date(fromDate); + + if (weekday !== null && weekday !== undefined) { + // Find next occurrence of the specified weekday + const currentWeekday = nextDate.getDay(); + const daysUntilTarget = (weekday - currentWeekday + 7) % 7; + + if ( + daysUntilTarget === 0 && + nextDate.getTime() === fromDate.getTime() + ) { + // If today is the target weekday and we're calculating from today, add interval weeks + nextDate.setDate(nextDate.getDate() + interval * 7); + } else { + nextDate.setDate(nextDate.getDate() + daysUntilTarget); + if (nextDate <= fromDate) { + nextDate.setDate(nextDate.getDate() + interval * 7); + } + } + } else { + // No specific weekday, just add interval weeks + nextDate.setDate(nextDate.getDate() + interval * 7); + } + + return nextDate; + } + + /** + * Calculate next monthly recurrence on specific day + * @param {Date} fromDate - Starting date + * @param {number} interval - Months between recurrences + * @param {number} dayOfMonth - Target day of month (1-31) + * @returns {Date} Next due date + */ + static calculateMonthlyRecurrence(fromDate, interval, dayOfMonth) { + const nextDate = new Date(fromDate); + const targetDay = dayOfMonth || fromDate.getUTCDate(); + + // Move to target month + const targetMonth = nextDate.getUTCMonth() + interval; + const targetYear = + nextDate.getUTCFullYear() + Math.floor(targetMonth / 12); + const finalMonth = targetMonth % 12; + + // Get the max day for the target month + const maxDay = new Date( + Date.UTC(targetYear, finalMonth + 1, 0) + ).getUTCDate(); + const finalDay = Math.min(targetDay, maxDay); + + // Create the new date + const result = new Date( + Date.UTC( + targetYear, + finalMonth, + finalDay, + fromDate.getUTCHours(), + fromDate.getUTCMinutes(), + fromDate.getUTCSeconds(), + fromDate.getUTCMilliseconds() + ) ); - - case 'monthly_last_day': - return this.calculateMonthlyLastDayRecurrence(startDate, task.recurrence_interval || 1); - - default: - return null; + + return result; } - } - /** - * Calculate next daily recurrence - * @param {Date} fromDate - Starting date - * @param {number} interval - Days between recurrences - * @returns {Date} Next due date - */ - static calculateDailyRecurrence(fromDate, interval) { - const nextDate = new Date(fromDate); - nextDate.setDate(nextDate.getDate() + interval); - return nextDate; - } + /** + * Calculate next monthly recurrence on specific weekday of month + * @param {Date} fromDate - Starting date + * @param {number} interval - Months between recurrences + * @param {number} weekday - Target weekday (0=Sunday, 6=Saturday) + * @param {number} weekOfMonth - Which occurrence in month (1-5) + * @returns {Date} Next due date + */ + static calculateMonthlyWeekdayRecurrence( + fromDate, + interval, + weekday, + weekOfMonth + ) { + const nextDate = new Date(fromDate); + nextDate.setUTCMonth(nextDate.getUTCMonth() + interval); - /** - * Calculate next weekly recurrence - * @param {Date} fromDate - Starting date - * @param {number} interval - Weeks between recurrences - * @param {number} weekday - Target day of week (0=Sunday, 6=Saturday) - * @returns {Date} Next due date - */ - static calculateWeeklyRecurrence(fromDate, interval, weekday) { - const nextDate = new Date(fromDate); - - if (weekday !== null && weekday !== undefined) { - // Find next occurrence of the specified weekday - const currentWeekday = nextDate.getDay(); - const daysUntilTarget = (weekday - currentWeekday + 7) % 7; - - if (daysUntilTarget === 0 && nextDate.getTime() === fromDate.getTime()) { - // If today is the target weekday and we're calculating from today, add interval weeks - nextDate.setDate(nextDate.getDate() + (interval * 7)); - } else { - nextDate.setDate(nextDate.getDate() + daysUntilTarget); - if (nextDate <= fromDate) { - nextDate.setDate(nextDate.getDate() + (interval * 7)); + // Find the first day of the month + const firstOfMonth = new Date( + Date.UTC(nextDate.getUTCFullYear(), nextDate.getUTCMonth(), 1) + ); + const firstWeekday = firstOfMonth.getUTCDay(); + + // Calculate the first occurrence of the target weekday + const daysToAdd = (weekday - firstWeekday + 7) % 7; + const firstOccurrence = new Date(firstOfMonth); + firstOccurrence.setUTCDate(1 + daysToAdd); + + // Add weeks to get to the target week of month + const targetDate = new Date(firstOccurrence); + targetDate.setUTCDate( + firstOccurrence.getUTCDate() + (weekOfMonth - 1) * 7 + ); + + // Make sure we're still in the same month + if (targetDate.getUTCMonth() !== nextDate.getUTCMonth()) { + // Week doesn't exist in this month, use last occurrence + targetDate.setUTCDate(targetDate.getUTCDate() - 7); } - } - } else { - // No specific weekday, just add interval weeks - nextDate.setDate(nextDate.getDate() + (interval * 7)); - } - - return nextDate; - } - /** - * Calculate next monthly recurrence on specific day - * @param {Date} fromDate - Starting date - * @param {number} interval - Months between recurrences - * @param {number} dayOfMonth - Target day of month (1-31) - * @returns {Date} Next due date - */ - static calculateMonthlyRecurrence(fromDate, interval, dayOfMonth) { - const nextDate = new Date(fromDate); - const targetDay = dayOfMonth || fromDate.getUTCDate(); - - // Move to target month - const targetMonth = nextDate.getUTCMonth() + interval; - const targetYear = nextDate.getUTCFullYear() + Math.floor(targetMonth / 12); - const finalMonth = targetMonth % 12; - - // Get the max day for the target month - const maxDay = new Date(Date.UTC(targetYear, finalMonth + 1, 0)).getUTCDate(); - const finalDay = Math.min(targetDay, maxDay); - - // Create the new date - const result = new Date(Date.UTC( - targetYear, - finalMonth, - finalDay, - fromDate.getUTCHours(), - fromDate.getUTCMinutes(), - fromDate.getUTCSeconds(), - fromDate.getUTCMilliseconds() - )); - - return result; - } + // Preserve the original time + targetDate.setUTCHours( + fromDate.getUTCHours(), + fromDate.getUTCMinutes(), + fromDate.getUTCSeconds(), + fromDate.getUTCMilliseconds() + ); - /** - * Calculate next monthly recurrence on specific weekday of month - * @param {Date} fromDate - Starting date - * @param {number} interval - Months between recurrences - * @param {number} weekday - Target weekday (0=Sunday, 6=Saturday) - * @param {number} weekOfMonth - Which occurrence in month (1-5) - * @returns {Date} Next due date - */ - static calculateMonthlyWeekdayRecurrence(fromDate, interval, weekday, weekOfMonth) { - const nextDate = new Date(fromDate); - nextDate.setUTCMonth(nextDate.getUTCMonth() + interval); - - // Find the first day of the month - const firstOfMonth = new Date(Date.UTC(nextDate.getUTCFullYear(), nextDate.getUTCMonth(), 1)); - const firstWeekday = firstOfMonth.getUTCDay(); - - // Calculate the first occurrence of the target weekday - const daysToAdd = (weekday - firstWeekday + 7) % 7; - const firstOccurrence = new Date(firstOfMonth); - firstOccurrence.setUTCDate(1 + daysToAdd); - - // Add weeks to get to the target week of month - const targetDate = new Date(firstOccurrence); - targetDate.setUTCDate(firstOccurrence.getUTCDate() + ((weekOfMonth - 1) * 7)); - - // Make sure we're still in the same month - if (targetDate.getUTCMonth() !== nextDate.getUTCMonth()) { - // Week doesn't exist in this month, use last occurrence - targetDate.setUTCDate(targetDate.getUTCDate() - 7); - } - - // Preserve the original time - targetDate.setUTCHours(fromDate.getUTCHours(), fromDate.getUTCMinutes(), fromDate.getUTCSeconds(), fromDate.getUTCMilliseconds()); - - return targetDate; - } - - /** - * Calculate next monthly recurrence on last day of month - * @param {Date} fromDate - Starting date - * @param {number} interval - Months between recurrences - * @returns {Date} Next due date - */ - static calculateMonthlyLastDayRecurrence(fromDate, interval) { - const nextDate = new Date(fromDate); - nextDate.setUTCMonth(nextDate.getUTCMonth() + interval); - - // Set to last day of month - nextDate.setUTCMonth(nextDate.getUTCMonth() + 1, 0); - - return nextDate; - } - - /** - * Helper function to get first weekday of month - * @param {number} year - Year - * @param {number} month - Month (0-11) - * @param {number} weekday - Weekday (0=Sunday, 6=Saturday) - * @returns {Date} First occurrence of weekday in month - */ - static _getFirstWeekdayOfMonth(year, month, weekday) { - const firstOfMonth = new Date(year, month, 1); - const firstWeekday = firstOfMonth.getDay(); - const daysToAdd = (weekday - firstWeekday + 7) % 7; - return new Date(year, month, 1 + daysToAdd); - } - - /** - * Helper function to get last weekday of month - * @param {number} year - Year - * @param {number} month - Month (0-11) - * @param {number} weekday - Weekday (0=Sunday, 6=Saturday) - * @returns {Date} Last occurrence of weekday in month - */ - static _getLastWeekdayOfMonth(year, month, weekday) { - const lastOfMonth = new Date(year, month + 1, 0); - const lastWeekday = lastOfMonth.getDay(); - const daysToSubtract = (lastWeekday - weekday + 7) % 7; - return new Date(year, month, lastOfMonth.getDate() - daysToSubtract); - } - - /** - * Helper function to get nth weekday of month - * @param {number} year - Year - * @param {number} month - Month (0-11) - * @param {number} weekday - Weekday (0=Sunday, 6=Saturday) - * @param {number} n - Which occurrence (1-5) - * @returns {Date} Nth occurrence of weekday in month - */ - static _getNthWeekdayOfMonth(year, month, weekday, n) { - const firstOccurrence = this._getFirstWeekdayOfMonth(year, month, weekday); - const targetDate = new Date(firstOccurrence); - targetDate.setDate(firstOccurrence.getDate() + ((n - 1) * 7)); - - // If target date is in next month, return null - if (targetDate.getMonth() !== month) { - return null; - } - - return targetDate; - } - - /** - * Helper function to check if next task should be generated - * @param {Object} task - The recurring task - * @param {Date} nextDate - Next due date - * @returns {boolean} Whether to generate next task - */ - static _shouldGenerateNextTask(task, nextDate) { - if (!task.recurrence_end_date) { - return true; - } - return nextDate < task.recurrence_end_date; - } - - /** - * Handle task completion for recurring tasks - * @param {Object} task - The completed task - * @returns {Promise} Next task instance if applicable - */ - static async handleTaskCompletion(task) { - // Check if the completed task itself is a recurring task - if (!task.recurrence_type || task.recurrence_type === 'none') { - return null; + return targetDate; } - // Only generate next task if completion_based is true - if (!task.completion_based) { - return null; + /** + * Calculate next monthly recurrence on last day of month + * @param {Date} fromDate - Starting date + * @param {number} interval - Months between recurrences + * @returns {Date} Next due date + */ + static calculateMonthlyLastDayRecurrence(fromDate, interval) { + const nextDate = new Date(fromDate); + nextDate.setUTCMonth(nextDate.getUTCMonth() + interval); + + // Set to last day of month + nextDate.setUTCMonth(nextDate.getUTCMonth() + 1, 0); + + return nextDate; } - // Update the task's last generated date to completion date - task.last_generated_date = new Date(); - await task.save(); - - // For completion-based tasks, create the next instance immediately - const nextDueDate = this.calculateNextDueDate(task, new Date()); - - if (!nextDueDate) { - return null; + /** + * Helper function to get first weekday of month + * @param {number} year - Year + * @param {number} month - Month (0-11) + * @param {number} weekday - Weekday (0=Sunday, 6=Saturday) + * @returns {Date} First occurrence of weekday in month + */ + static _getFirstWeekdayOfMonth(year, month, weekday) { + const firstOfMonth = new Date(year, month, 1); + const firstWeekday = firstOfMonth.getDay(); + const daysToAdd = (weekday - firstWeekday + 7) % 7; + return new Date(year, month, 1 + daysToAdd); } - // Check if this due date already has a task instance - const whereClause = { - user_id: task.user_id, - name: task.name, - due_date: nextDueDate - }; - - // Only add project_id to where clause if it's not null/undefined - if (task.project_id !== null && task.project_id !== undefined) { - whereClause.project_id = task.project_id; - } else { - whereClause.project_id = null; - } - - const existingTask = await Task.findOne({ - where: whereClause - }); - - if (existingTask) { - return null; // Task already exists for this date + /** + * Helper function to get last weekday of month + * @param {number} year - Year + * @param {number} month - Month (0-11) + * @param {number} weekday - Weekday (0=Sunday, 6=Saturday) + * @returns {Date} Last occurrence of weekday in month + */ + static _getLastWeekdayOfMonth(year, month, weekday) { + const lastOfMonth = new Date(year, month + 1, 0); + const lastWeekday = lastOfMonth.getDay(); + const daysToSubtract = (lastWeekday - weekday + 7) % 7; + return new Date(year, month, lastOfMonth.getDate() - daysToSubtract); } - // Create the next task instance - const nextTask = await this.createTaskInstance(task, nextDueDate); - return nextTask; - } + /** + * Helper function to get nth weekday of month + * @param {number} year - Year + * @param {number} month - Month (0-11) + * @param {number} weekday - Weekday (0=Sunday, 6=Saturday) + * @param {number} n - Which occurrence (1-5) + * @returns {Date} Nth occurrence of weekday in month + */ + static _getNthWeekdayOfMonth(year, month, weekday, n) { + const firstOccurrence = this._getFirstWeekdayOfMonth( + year, + month, + weekday + ); + const targetDate = new Date(firstOccurrence); + targetDate.setDate(firstOccurrence.getDate() + (n - 1) * 7); + + // If target date is in next month, return null + if (targetDate.getMonth() !== month) { + return null; + } + + return targetDate; + } + + /** + * Helper function to check if next task should be generated + * @param {Object} task - The recurring task + * @param {Date} nextDate - Next due date + * @returns {boolean} Whether to generate next task + */ + static _shouldGenerateNextTask(task, nextDate) { + if (!task.recurrence_end_date) { + return true; + } + return nextDate < task.recurrence_end_date; + } + + /** + * Handle task completion for recurring tasks + * @param {Object} task - The completed task + * @returns {Promise} Next task instance if applicable + */ + static async handleTaskCompletion(task) { + // Check if the completed task itself is a recurring task + if (!task.recurrence_type || task.recurrence_type === 'none') { + return null; + } + + // Only generate next task if completion_based is true + if (!task.completion_based) { + return null; + } + + // Update the task's last generated date to completion date + task.last_generated_date = new Date(); + await task.save(); + + // For completion-based tasks, create the next instance immediately + const nextDueDate = this.calculateNextDueDate(task, new Date()); + + if (!nextDueDate) { + return null; + } + + // Check if this due date already has a task instance + const whereClause = { + user_id: task.user_id, + name: task.name, + due_date: nextDueDate, + }; + + // Only add project_id to where clause if it's not null/undefined + if (task.project_id !== null && task.project_id !== undefined) { + whereClause.project_id = task.project_id; + } else { + whereClause.project_id = null; + } + + const existingTask = await Task.findOne({ + where: whereClause, + }); + + if (existingTask) { + return null; // Task already exists for this date + } + + // Create the next task instance + const nextTask = await this.createTaskInstance(task, nextDueDate); + return nextTask; + } } -module.exports = RecurringTaskService; \ No newline at end of file +module.exports = RecurringTaskService; diff --git a/backend/services/taskEventService.js b/backend/services/taskEventService.js index 8d85067..ab5e269 100644 --- a/backend/services/taskEventService.js +++ b/backend/services/taskEventService.js @@ -1,329 +1,417 @@ const { TaskEvent } = require('../models'); class TaskEventService { - /** - * Log a task event - * @param {Object} eventData - Event data - * @param {number} eventData.taskId - Task ID - * @param {number} eventData.userId - User ID - * @param {string} eventData.eventType - Type of event - * @param {string} eventData.fieldName - Field that changed (optional) - * @param {any} eventData.oldValue - Old value (optional) - * @param {any} eventData.newValue - New value (optional) - * @param {Object} eventData.metadata - Additional metadata (optional) - */ - static async logEvent({ taskId, userId, eventType, fieldName = null, oldValue = null, newValue = null, metadata = {} }) { - try { - // Add source to metadata if not provided - if (!metadata.source) { - metadata.source = 'web'; - } - - const event = await TaskEvent.create({ - task_id: taskId, - user_id: userId, - event_type: eventType, - field_name: fieldName, - old_value: oldValue ? { [fieldName || 'value']: oldValue } : null, - new_value: newValue ? { [fieldName || 'value']: newValue } : null, - metadata: metadata - }); - - return event; - } catch (error) { - console.error('Error logging task event:', error); - throw error; - } - } - - /** - * Log task creation event - */ - static async logTaskCreated(taskId, userId, taskData, metadata = {}) { - return await this.logEvent({ - taskId, - userId, - eventType: 'created', - newValue: taskData, - metadata: { ...metadata, action: 'task_created' } - }); - } - - /** - * Log status change event - */ - static async logStatusChange(taskId, userId, oldStatus, newStatus, metadata = {}) { - const eventType = newStatus === 2 ? 'completed' : - newStatus === 3 ? 'archived' : - 'status_changed'; - - return await this.logEvent({ - taskId, - userId, - eventType, - fieldName: 'status', - oldValue: oldStatus, - newValue: newStatus, - metadata: { ...metadata, action: 'status_change' } - }); - } - - /** - * Log priority change event - */ - static async logPriorityChange(taskId, userId, oldPriority, newPriority, metadata = {}) { - return await this.logEvent({ - taskId, - userId, - eventType: 'priority_changed', - fieldName: 'priority', - oldValue: oldPriority, - newValue: newPriority, - metadata: { ...metadata, action: 'priority_change' } - }); - } - - /** - * Log due date change event - */ - static async logDueDateChange(taskId, userId, oldDueDate, newDueDate, metadata = {}) { - return await this.logEvent({ - taskId, - userId, - eventType: 'due_date_changed', - fieldName: 'due_date', - oldValue: oldDueDate, - newValue: newDueDate, - metadata: { ...metadata, action: 'due_date_change' } - }); - } - - /** - * Log project change event - */ - static async logProjectChange(taskId, userId, oldProjectId, newProjectId, metadata = {}) { - return await this.logEvent({ - taskId, - userId, - eventType: 'project_changed', - fieldName: 'project_id', - oldValue: oldProjectId, - newValue: newProjectId, - metadata: { ...metadata, action: 'project_change' } - }); - } - - /** - * Log task name change event - */ - static async logNameChange(taskId, userId, oldName, newName, metadata = {}) { - return await this.logEvent({ - taskId, - userId, - eventType: 'name_changed', - fieldName: 'name', - oldValue: oldName, - newValue: newName, - metadata: { ...metadata, action: 'name_change' } - }); - } - - /** - * Log description change event - */ - static async logDescriptionChange(taskId, userId, oldDescription, newDescription, metadata = {}) { - return await this.logEvent({ - taskId, - userId, - eventType: 'description_changed', - fieldName: 'description', - oldValue: oldDescription, - newValue: newDescription, - metadata: { ...metadata, action: 'description_change' } - }); - } - - /** - * Log multiple field changes at once - */ - static async logTaskUpdate(taskId, userId, changes, metadata = {}) { - const events = []; - - for (const [fieldName, { oldValue, newValue }] of Object.entries(changes)) { - // Skip if values are the same - if (oldValue === newValue) continue; - - let eventType; - switch (fieldName) { - case 'status': - eventType = newValue === 2 ? 'completed' : - newValue === 3 ? 'archived' : - 'status_changed'; - break; - default: - eventType = `${fieldName}_changed`; - } - - const event = await this.logEvent({ + /** + * Log a task event + * @param {Object} eventData - Event data + * @param {number} eventData.taskId - Task ID + * @param {number} eventData.userId - User ID + * @param {string} eventData.eventType - Type of event + * @param {string} eventData.fieldName - Field that changed (optional) + * @param {any} eventData.oldValue - Old value (optional) + * @param {any} eventData.newValue - New value (optional) + * @param {Object} eventData.metadata - Additional metadata (optional) + */ + static async logEvent({ taskId, userId, eventType, - fieldName, - oldValue, - newValue, - metadata: { ...metadata, action: 'bulk_update' } - }); + fieldName = null, + oldValue = null, + newValue = null, + metadata = {}, + }) { + try { + // Add source to metadata if not provided + if (!metadata.source) { + metadata.source = 'web'; + } - events.push(event); - } + const event = await TaskEvent.create({ + task_id: taskId, + user_id: userId, + event_type: eventType, + field_name: fieldName, + old_value: oldValue + ? { [fieldName || 'value']: oldValue } + : null, + new_value: newValue + ? { [fieldName || 'value']: newValue } + : null, + metadata: metadata, + }); - return events; - } - - /** - * Get task timeline (all events for a task) - */ - static async getTaskTimeline(taskId) { - return await TaskEvent.findAll({ - where: { task_id: taskId }, - order: [['created_at', 'ASC']], - include: [{ - model: require('../models').User, - as: 'User', - attributes: ['id', 'name', 'email'] - }] - }); - } - - /** - * Get task completion metrics - */ - static async getTaskCompletionTime(taskId) { - const events = await TaskEvent.findAll({ - where: { - task_id: taskId, - event_type: ['status_changed', 'created', 'completed'] - }, - order: [['created_at', 'ASC']] - }); - - if (events.length === 0) return null; - - // Find when task was started (moved to in_progress or created) - const startEvent = events.find(e => - e.event_type === 'created' || - (e.event_type === 'status_changed' && e.new_value?.status === 1) // in_progress - ); - - // Find when task was completed - const completedEvent = events.find(e => - e.event_type === 'completed' || - (e.event_type === 'status_changed' && e.new_value?.status === 2) // done - ); - - if (!startEvent || !completedEvent) return null; - - const startTime = new Date(startEvent.created_at); - const endTime = new Date(completedEvent.created_at); - - return { - task_id: taskId, - started_at: startTime, - completed_at: endTime, - duration_ms: endTime - startTime, - duration_hours: (endTime - startTime) / (1000 * 60 * 60), - duration_days: (endTime - startTime) / (1000 * 60 * 60 * 24) - }; - } - - /** - * Get user productivity metrics - */ - static async getUserProductivityMetrics(userId, startDate = null, endDate = null) { - const whereClause = { user_id: userId }; - - if (startDate && endDate) { - whereClause.created_at = { - [require('sequelize').Op.between]: [startDate, endDate] - }; - } - - const events = await TaskEvent.findAll({ - where: whereClause, - order: [['created_at', 'ASC']] - }); - - // Calculate metrics - const metrics = { - total_events: events.length, - tasks_created: events.filter(e => e.event_type === 'created').length, - tasks_completed: events.filter(e => e.event_type === 'completed').length, - status_changes: events.filter(e => e.event_type === 'status_changed').length, - average_completion_time: null, - completion_times: [] - }; - - // Calculate completion times for all completed tasks - const completedTasks = events.filter(e => e.event_type === 'completed'); - const completionTimes = []; - - for (const completedEvent of completedTasks) { - const taskCompletion = await this.getTaskCompletionTime(completedEvent.task_id); - if (taskCompletion) { - completionTimes.push(taskCompletion); - } - } - - if (completionTimes.length > 0) { - const totalHours = completionTimes.reduce((sum, ct) => sum + ct.duration_hours, 0); - metrics.average_completion_time = totalHours / completionTimes.length; - metrics.completion_times = completionTimes; - } - - return metrics; - } - - /** - * Get task activity summary for a date range - */ - static async getTaskActivitySummary(userId, startDate, endDate) { - const events = await TaskEvent.findAll({ - where: { - user_id: userId, - created_at: { - [require('sequelize').Op.between]: [startDate, endDate] + return event; + } catch (error) { + console.error('Error logging task event:', error); + throw error; } - }, - attributes: [ - 'event_type', - [require('sequelize').fn('COUNT', require('sequelize').col('id')), 'count'], - [require('sequelize').fn('DATE', require('sequelize').col('created_at')), 'date'] - ], - group: ['event_type', 'date'], - order: [['date', 'ASC']] - }); + } - return events; - } + /** + * Log task creation event + */ + static async logTaskCreated(taskId, userId, taskData, metadata = {}) { + return await this.logEvent({ + taskId, + userId, + eventType: 'created', + newValue: taskData, + metadata: { ...metadata, action: 'task_created' }, + }); + } - /** - * Get count of how many times a task has been moved to today - */ - static async getTaskTodayMoveCount(taskId) { - const { Op } = require('sequelize'); - - const count = await TaskEvent.count({ - where: { - task_id: taskId, - event_type: 'today_changed', - new_value: { - [Op.like]: '%"today":true%' + /** + * Log status change event + */ + static async logStatusChange( + taskId, + userId, + oldStatus, + newStatus, + metadata = {} + ) { + const eventType = + newStatus === 2 + ? 'completed' + : newStatus === 3 + ? 'archived' + : 'status_changed'; + + return await this.logEvent({ + taskId, + userId, + eventType, + fieldName: 'status', + oldValue: oldStatus, + newValue: newStatus, + metadata: { ...metadata, action: 'status_change' }, + }); + } + + /** + * Log priority change event + */ + static async logPriorityChange( + taskId, + userId, + oldPriority, + newPriority, + metadata = {} + ) { + return await this.logEvent({ + taskId, + userId, + eventType: 'priority_changed', + fieldName: 'priority', + oldValue: oldPriority, + newValue: newPriority, + metadata: { ...metadata, action: 'priority_change' }, + }); + } + + /** + * Log due date change event + */ + static async logDueDateChange( + taskId, + userId, + oldDueDate, + newDueDate, + metadata = {} + ) { + return await this.logEvent({ + taskId, + userId, + eventType: 'due_date_changed', + fieldName: 'due_date', + oldValue: oldDueDate, + newValue: newDueDate, + metadata: { ...metadata, action: 'due_date_change' }, + }); + } + + /** + * Log project change event + */ + static async logProjectChange( + taskId, + userId, + oldProjectId, + newProjectId, + metadata = {} + ) { + return await this.logEvent({ + taskId, + userId, + eventType: 'project_changed', + fieldName: 'project_id', + oldValue: oldProjectId, + newValue: newProjectId, + metadata: { ...metadata, action: 'project_change' }, + }); + } + + /** + * Log task name change event + */ + static async logNameChange( + taskId, + userId, + oldName, + newName, + metadata = {} + ) { + return await this.logEvent({ + taskId, + userId, + eventType: 'name_changed', + fieldName: 'name', + oldValue: oldName, + newValue: newName, + metadata: { ...metadata, action: 'name_change' }, + }); + } + + /** + * Log description change event + */ + static async logDescriptionChange( + taskId, + userId, + oldDescription, + newDescription, + metadata = {} + ) { + return await this.logEvent({ + taskId, + userId, + eventType: 'description_changed', + fieldName: 'description', + oldValue: oldDescription, + newValue: newDescription, + metadata: { ...metadata, action: 'description_change' }, + }); + } + + /** + * Log multiple field changes at once + */ + static async logTaskUpdate(taskId, userId, changes, metadata = {}) { + const events = []; + + for (const [fieldName, { oldValue, newValue }] of Object.entries( + changes + )) { + // Skip if values are the same + if (oldValue === newValue) continue; + + let eventType; + switch (fieldName) { + case 'status': + eventType = + newValue === 2 + ? 'completed' + : newValue === 3 + ? 'archived' + : 'status_changed'; + break; + default: + eventType = `${fieldName}_changed`; + } + + const event = await this.logEvent({ + taskId, + userId, + eventType, + fieldName, + oldValue, + newValue, + metadata: { ...metadata, action: 'bulk_update' }, + }); + + events.push(event); } - } - }); - return count; - } + return events; + } + + /** + * Get task timeline (all events for a task) + */ + static async getTaskTimeline(taskId) { + return await TaskEvent.findAll({ + where: { task_id: taskId }, + order: [['created_at', 'ASC']], + include: [ + { + model: require('../models').User, + as: 'User', + attributes: ['id', 'name', 'email'], + }, + ], + }); + } + + /** + * Get task completion metrics + */ + static async getTaskCompletionTime(taskId) { + const events = await TaskEvent.findAll({ + where: { + task_id: taskId, + event_type: ['status_changed', 'created', 'completed'], + }, + order: [['created_at', 'ASC']], + }); + + if (events.length === 0) return null; + + // Find when task was started (moved to in_progress or created) + const startEvent = events.find( + (e) => + e.event_type === 'created' || + (e.event_type === 'status_changed' && e.new_value?.status === 1) // in_progress + ); + + // Find when task was completed + const completedEvent = events.find( + (e) => + e.event_type === 'completed' || + (e.event_type === 'status_changed' && e.new_value?.status === 2) // done + ); + + if (!startEvent || !completedEvent) return null; + + const startTime = new Date(startEvent.created_at); + const endTime = new Date(completedEvent.created_at); + + return { + task_id: taskId, + started_at: startTime, + completed_at: endTime, + duration_ms: endTime - startTime, + duration_hours: (endTime - startTime) / (1000 * 60 * 60), + duration_days: (endTime - startTime) / (1000 * 60 * 60 * 24), + }; + } + + /** + * Get user productivity metrics + */ + static async getUserProductivityMetrics( + userId, + startDate = null, + endDate = null + ) { + const whereClause = { user_id: userId }; + + if (startDate && endDate) { + whereClause.created_at = { + [require('sequelize').Op.between]: [startDate, endDate], + }; + } + + const events = await TaskEvent.findAll({ + where: whereClause, + order: [['created_at', 'ASC']], + }); + + // Calculate metrics + const metrics = { + total_events: events.length, + tasks_created: events.filter((e) => e.event_type === 'created') + .length, + tasks_completed: events.filter((e) => e.event_type === 'completed') + .length, + status_changes: events.filter( + (e) => e.event_type === 'status_changed' + ).length, + average_completion_time: null, + completion_times: [], + }; + + // Calculate completion times for all completed tasks + const completedTasks = events.filter( + (e) => e.event_type === 'completed' + ); + const completionTimes = []; + + for (const completedEvent of completedTasks) { + const taskCompletion = await this.getTaskCompletionTime( + completedEvent.task_id + ); + if (taskCompletion) { + completionTimes.push(taskCompletion); + } + } + + if (completionTimes.length > 0) { + const totalHours = completionTimes.reduce( + (sum, ct) => sum + ct.duration_hours, + 0 + ); + metrics.average_completion_time = + totalHours / completionTimes.length; + metrics.completion_times = completionTimes; + } + + return metrics; + } + + /** + * Get task activity summary for a date range + */ + static async getTaskActivitySummary(userId, startDate, endDate) { + const events = await TaskEvent.findAll({ + where: { + user_id: userId, + created_at: { + [require('sequelize').Op.between]: [startDate, endDate], + }, + }, + attributes: [ + 'event_type', + [ + require('sequelize').fn( + 'COUNT', + require('sequelize').col('id') + ), + 'count', + ], + [ + require('sequelize').fn( + 'DATE', + require('sequelize').col('created_at') + ), + 'date', + ], + ], + group: ['event_type', 'date'], + order: [['date', 'ASC']], + }); + + return events; + } + + /** + * Get count of how many times a task has been moved to today + */ + static async getTaskTodayMoveCount(taskId) { + const { Op } = require('sequelize'); + + const count = await TaskEvent.count({ + where: { + task_id: taskId, + event_type: 'today_changed', + new_value: { + [Op.like]: '%"today":true%', + }, + }, + }); + + return count; + } } -module.exports = TaskEventService; \ No newline at end of file +module.exports = TaskEventService; diff --git a/backend/services/taskScheduler.js b/backend/services/taskScheduler.js index b9dc7ff..5881549 100644 --- a/backend/services/taskScheduler.js +++ b/backend/services/taskScheduler.js @@ -5,187 +5,197 @@ const RecurringTaskService = require('./recurringTaskService'); // Create scheduler state const createSchedulerState = () => ({ - jobs: new Map(), - isInitialized: false + jobs: new Map(), + isInitialized: false, }); // Global mutable state (will be managed functionally) let schedulerState = createSchedulerState(); // Check if scheduler should be disabled -const shouldDisableScheduler = () => - process.env.NODE_ENV === 'test' || process.env.DISABLE_SCHEDULER === 'true'; +const shouldDisableScheduler = () => + process.env.NODE_ENV === 'test' || process.env.DISABLE_SCHEDULER === 'true'; // Create job configuration const createJobConfig = () => ({ - scheduled: false, - timezone: 'UTC' + scheduled: false, + timezone: 'UTC', }); // Create cron expressions const getCronExpression = (frequency) => { - const expressions = { - daily: '0 7 * * *', - weekdays: '0 7 * * 1-5', - weekly: '0 7 * * 1', - '1h': '0 * * * *', - '2h': '0 */2 * * *', - '4h': '0 */4 * * *', - '8h': '0 */8 * * *', - '12h': '0 */12 * * *', - recurring_tasks: '0 6 * * *' // Daily at 6 AM for recurring task generation - }; - return expressions[frequency]; + const expressions = { + daily: '0 7 * * *', + weekdays: '0 7 * * 1-5', + weekly: '0 7 * * 1', + '1h': '0 * * * *', + '2h': '0 */2 * * *', + '4h': '0 */4 * * *', + '8h': '0 */8 * * *', + '12h': '0 */12 * * *', + recurring_tasks: '0 6 * * *', // Daily at 6 AM for recurring task generation + }; + return expressions[frequency]; }; // Create job handler const createJobHandler = (frequency) => async () => { - if (frequency === 'recurring_tasks') { - await processRecurringTasks(); - } else { - await processSummariesForFrequency(frequency); - } + if (frequency === 'recurring_tasks') { + await processRecurringTasks(); + } else { + await processSummariesForFrequency(frequency); + } }; // Create job entries const createJobEntries = () => { - const frequencies = ['daily', 'weekdays', 'weekly', '1h', '2h', '4h', '8h', '12h', 'recurring_tasks']; - - return frequencies.map(frequency => { - const cronExpression = getCronExpression(frequency); - const jobHandler = createJobHandler(frequency); - const jobConfig = createJobConfig(); - const job = cron.schedule(cronExpression, jobHandler, jobConfig); - - return [frequency, job]; - }); + const frequencies = [ + 'daily', + 'weekdays', + 'weekly', + '1h', + '2h', + '4h', + '8h', + '12h', + 'recurring_tasks', + ]; + + return frequencies.map((frequency) => { + const cronExpression = getCronExpression(frequency); + const jobHandler = createJobHandler(frequency); + const jobConfig = createJobConfig(); + const job = cron.schedule(cronExpression, jobHandler, jobConfig); + + return [frequency, job]; + }); }; // Start all jobs const startJobs = (jobs) => { - jobs.forEach((job, frequency) => { - job.start(); - }); + jobs.forEach((job, frequency) => { + job.start(); + }); }; // Stop all jobs const stopJobs = (jobs) => { - jobs.forEach((job, frequency) => { - job.stop(); - }); + jobs.forEach((job, frequency) => { + job.stop(); + }); }; // Side effect function to fetch users for frequency const fetchUsersForFrequency = async (frequency) => { - return await User.findAll({ - where: { - telegram_bot_token: { [require('sequelize').Op.ne]: null }, - telegram_chat_id: { [require('sequelize').Op.ne]: null }, - task_summary_enabled: true, - task_summary_frequency: frequency - } - }); + return await User.findAll({ + where: { + telegram_bot_token: { [require('sequelize').Op.ne]: null }, + telegram_chat_id: { [require('sequelize').Op.ne]: null }, + task_summary_enabled: true, + task_summary_frequency: frequency, + }, + }); }; // Side effect function to send summary to user const sendSummaryToUser = async (userId, frequency) => { - try { - const success = await TaskSummaryService.sendSummaryToUser(userId); - return success; - } catch (error) { - return false; - } + try { + const success = await TaskSummaryService.sendSummaryToUser(userId); + return success; + } catch (error) { + return false; + } }; // Function to process summaries for frequency (contains side effects) const processSummariesForFrequency = async (frequency) => { - try { - const users = await fetchUsersForFrequency(frequency); + try { + const users = await fetchUsersForFrequency(frequency); - const results = await Promise.allSettled( - users.map(user => sendSummaryToUser(user.id, frequency)) - ); + const results = await Promise.allSettled( + users.map((user) => sendSummaryToUser(user.id, frequency)) + ); - return results; - } catch (error) { - throw error; - } + return results; + } catch (error) { + throw error; + } }; // Function to process recurring tasks (contains side effects) const processRecurringTasks = async () => { - try { - const newTasks = await RecurringTaskService.generateRecurringTasks(); - return newTasks; - } catch (error) { - throw error; - } + try { + const newTasks = await RecurringTaskService.generateRecurringTasks(); + return newTasks; + } catch (error) { + throw error; + } }; // Function to initialize scheduler (contains side effects) const initialize = async () => { - if (schedulerState.isInitialized) { + if (schedulerState.isInitialized) { + return schedulerState; + } + + if (shouldDisableScheduler()) { + return schedulerState; + } + + // Create job entries + const jobEntries = createJobEntries(); + const jobs = new Map(jobEntries); + + // Start all jobs + startJobs(jobs); + + // Update state immutably + schedulerState = { + jobs, + isInitialized: true, + }; + return schedulerState; - } - - if (shouldDisableScheduler()) { - return schedulerState; - } - - // Create job entries - const jobEntries = createJobEntries(); - const jobs = new Map(jobEntries); - - // Start all jobs - startJobs(jobs); - - // Update state immutably - schedulerState = { - jobs, - isInitialized: true - }; - - return schedulerState; }; // Function to stop scheduler (contains side effects) const stop = async () => { - if (!schedulerState.isInitialized) { - return schedulerState; - } - - // Stop all jobs - stopJobs(schedulerState.jobs); + if (!schedulerState.isInitialized) { + return schedulerState; + } - // Reset state immutably - schedulerState = createSchedulerState(); - - return schedulerState; + // Stop all jobs + stopJobs(schedulerState.jobs); + + // Reset state immutably + schedulerState = createSchedulerState(); + + return schedulerState; }; // Function to restart scheduler const restart = async () => { - await stop(); - return await initialize(); + await stop(); + return await initialize(); }; // Get scheduler status const getStatus = () => ({ - initialized: schedulerState.isInitialized, - jobCount: schedulerState.jobs.size, - jobs: Array.from(schedulerState.jobs.keys()) + initialized: schedulerState.isInitialized, + jobCount: schedulerState.jobs.size, + jobs: Array.from(schedulerState.jobs.keys()), }); // Export functional interface module.exports = { - initialize, - stop, - restart, - getStatus, - processSummariesForFrequency, - processRecurringTasks, - // For testing - _createSchedulerState: createSchedulerState, - _shouldDisableScheduler: shouldDisableScheduler, - _getCronExpression: getCronExpression -}; \ No newline at end of file + initialize, + stop, + restart, + getStatus, + processSummariesForFrequency, + processRecurringTasks, + // For testing + _createSchedulerState: createSchedulerState, + _shouldDisableScheduler: shouldDisableScheduler, + _getCronExpression: getCronExpression, +}; diff --git a/backend/services/taskSummaryService.js b/backend/services/taskSummaryService.js index 5499f36..dc405e3 100644 --- a/backend/services/taskSummaryService.js +++ b/backend/services/taskSummaryService.js @@ -4,283 +4,302 @@ const TelegramPoller = require('./telegramPoller'); // escape markdown special characters const escapeMarkdown = (text) => { - if (!text) return ''; - // Characters that need to be escaped in MarkdownV2: _*[]()~`>#+-=|{}.! - return text.toString().replace(/([_*\[\]()~`>#+\-=|{}.!])/g, '\\$1'); + if (!text) return ''; + // Characters that need to be escaped in MarkdownV2: _*[]()~`>#+-=|{}.! + return text.toString().replace(/([_*\[\]()~`>#+\-=|{}.!])/g, '\\$1'); }; // get priority emoji const getPriorityEmoji = (priority) => { - const emojiMap = { - 2: '🔴', // high - 1: '🟠', // medium - 0: '🟢' // low - }; - return emojiMap[priority] || '⚪'; + const emojiMap = { + 2: '🔴', // high + 1: '🟠', // medium + 0: '🟢', // low + }; + return emojiMap[priority] || '⚪'; }; // create date range for today const createTodayDateRange = () => { - const today = new Date(); - today.setHours(0, 0, 0, 0); - const tomorrow = new Date(today); - tomorrow.setDate(tomorrow.getDate() + 1); - return { today, tomorrow }; + const today = new Date(); + today.setHours(0, 0, 0, 0); + const tomorrow = new Date(today); + tomorrow.setDate(tomorrow.getDate() + 1); + return { today, tomorrow }; }; // format task for display const formatTaskForDisplay = (task, index, includeStatus = false) => { - const priorityEmoji = getPriorityEmoji(task.priority); - const statusEmoji = includeStatus ? '✅ ' : ''; - const taskName = escapeMarkdown(task.name); - const projectInfo = task.Project ? ` \\[${escapeMarkdown(task.Project.name)}\\]` : ''; - return `${index + 1}\\. ${statusEmoji}${priorityEmoji} ${taskName}${projectInfo}\n`; + const priorityEmoji = getPriorityEmoji(task.priority); + const statusEmoji = includeStatus ? '✅ ' : ''; + const taskName = escapeMarkdown(task.name); + const projectInfo = task.Project + ? ` \\[${escapeMarkdown(task.Project.name)}\\]` + : ''; + return `${index + 1}\\. ${statusEmoji}${priorityEmoji} ${taskName}${projectInfo}\n`; }; // build task section const buildTaskSection = (tasks, title, includeStatus = false) => { - if (tasks.length === 0) return ''; - - let section = `${title}\n`; - section += tasks.map((task, index) => - formatTaskForDisplay(task, index, includeStatus) - ).join(''); - section += '\n'; - - return section; + if (tasks.length === 0) return ''; + + let section = `${title}\n`; + section += tasks + .map((task, index) => formatTaskForDisplay(task, index, includeStatus)) + .join(''); + section += '\n'; + + return section; }; // build summary message const buildSummaryMessage = (taskSections) => { - let message = "📋 *Today's Task Summary*\n\n"; - message += "━━━━━━━━━━━━━━━━━━━━━━━━\n\n"; - message += "✏️ *Today's Plan*\n\n"; - - message += taskSections.dueToday; - message += taskSections.inProgress; - message += taskSections.suggested; - message += taskSections.completed; - - message += "━━━━━━━━━━━━━━━━━━━━━━━━\n"; - message += "🎯 *Stay focused and make it happen\\!*"; - - return message; + let message = "📋 *Today's Task Summary*\n\n"; + message += '━━━━━━━━━━━━━━━━━━━━━━━━\n\n'; + message += "✏️ *Today's Plan*\n\n"; + + message += taskSections.dueToday; + message += taskSections.inProgress; + message += taskSections.suggested; + message += taskSections.completed; + + message += '━━━━━━━━━━━━━━━━━━━━━━━━\n'; + message += '🎯 *Stay focused and make it happen\\!*'; + + return message; }; // calculate next run time const calculateNextRunTime = (user, fromTime = new Date()) => { - const frequency = user.task_summary_frequency; - const from = new Date(fromTime); + const frequency = user.task_summary_frequency; + const from = new Date(fromTime); - const calculations = { - daily: () => { - const nextDay = new Date(from); - nextDay.setDate(nextDay.getDate() + 1); - nextDay.setHours(7, 0, 0, 0); - return nextDay; - }, - - weekdays: () => { - const currentDay = from.getDay(); // 0 = Sunday, 1 = Monday, ..., 6 = Saturday - let daysToAdd = 1; - if (currentDay === 5) { // Friday - daysToAdd = 3; // Skip to Monday - } else if (currentDay === 6) { // Saturday - daysToAdd = 2; // Skip to Monday - } - const nextWeekday = new Date(from); - nextWeekday.setDate(nextWeekday.getDate() + daysToAdd); - nextWeekday.setHours(7, 0, 0, 0); - return nextWeekday; - }, - - weekly: () => { - const nextWeek = new Date(from); - nextWeek.setDate(nextWeek.getDate() + 7); - nextWeek.setHours(7, 0, 0, 0); - return nextWeek; - }, - - '1h': () => { - const nextHour = new Date(from); - nextHour.setHours(nextHour.getHours() + 1); - return nextHour; - }, - - '2h': () => { - const next = new Date(from); - next.setHours(next.getHours() + 2); - return next; - }, - - '4h': () => { - const next = new Date(from); - next.setHours(next.getHours() + 4); - return next; - }, - - '8h': () => { - const next = new Date(from); - next.setHours(next.getHours() + 8); - return next; - }, - - '12h': () => { - const next = new Date(from); - next.setHours(next.getHours() + 12); - return next; - } - }; + const calculations = { + daily: () => { + const nextDay = new Date(from); + nextDay.setDate(nextDay.getDate() + 1); + nextDay.setHours(7, 0, 0, 0); + return nextDay; + }, - const calculator = calculations[frequency]; - return calculator ? calculator() : calculations.daily(); + weekdays: () => { + const currentDay = from.getDay(); // 0 = Sunday, 1 = Monday, ..., 6 = Saturday + let daysToAdd = 1; + if (currentDay === 5) { + // Friday + daysToAdd = 3; // Skip to Monday + } else if (currentDay === 6) { + // Saturday + daysToAdd = 2; // Skip to Monday + } + const nextWeekday = new Date(from); + nextWeekday.setDate(nextWeekday.getDate() + daysToAdd); + nextWeekday.setHours(7, 0, 0, 0); + return nextWeekday; + }, + + weekly: () => { + const nextWeek = new Date(from); + nextWeek.setDate(nextWeek.getDate() + 7); + nextWeek.setHours(7, 0, 0, 0); + return nextWeek; + }, + + '1h': () => { + const nextHour = new Date(from); + nextHour.setHours(nextHour.getHours() + 1); + return nextHour; + }, + + '2h': () => { + const next = new Date(from); + next.setHours(next.getHours() + 2); + return next; + }, + + '4h': () => { + const next = new Date(from); + next.setHours(next.getHours() + 4); + return next; + }, + + '8h': () => { + const next = new Date(from); + next.setHours(next.getHours() + 8); + return next; + }, + + '12h': () => { + const next = new Date(from); + next.setHours(next.getHours() + 12); + return next; + }, + }; + + const calculator = calculations[frequency]; + return calculator ? calculator() : calculations.daily(); }; // Side effect function to fetch user by ID -const fetchUser = async (userId) => - await User.findByPk(userId); +const fetchUser = async (userId) => await User.findByPk(userId); // Side effect function to fetch due today tasks -const fetchDueTodayTasks = async (userId, today, tomorrow) => - await Task.findAll({ - where: { - user_id: userId, - due_date: { - [Op.gte]: today, - [Op.lt]: tomorrow - }, - status: { [Op.ne]: 2 } // not done - }, - include: [{ model: Project, attributes: ['name'] }], - order: [['name', 'ASC']] - }); +const fetchDueTodayTasks = async (userId, today, tomorrow) => + await Task.findAll({ + where: { + user_id: userId, + due_date: { + [Op.gte]: today, + [Op.lt]: tomorrow, + }, + status: { [Op.ne]: 2 }, // not done + }, + include: [{ model: Project, attributes: ['name'] }], + order: [['name', 'ASC']], + }); // Side effect function to fetch in progress tasks -const fetchInProgressTasks = async (userId) => - await Task.findAll({ - where: { - user_id: userId, - status: 1 // in_progress - }, - include: [{ model: Project, attributes: ['name'] }], - order: [['name', 'ASC']] - }); +const fetchInProgressTasks = async (userId) => + await Task.findAll({ + where: { + user_id: userId, + status: 1, // in_progress + }, + include: [{ model: Project, attributes: ['name'] }], + order: [['name', 'ASC']], + }); // Side effect function to fetch completed today tasks -const fetchCompletedTodayTasks = async (userId, today, tomorrow) => - await Task.findAll({ - where: { - user_id: userId, - status: 2, // done - updated_at: { - [Op.gte]: today, - [Op.lt]: tomorrow - } - }, - include: [{ model: Project, attributes: ['name'] }], - order: [['name', 'ASC']] - }); +const fetchCompletedTodayTasks = async (userId, today, tomorrow) => + await Task.findAll({ + where: { + user_id: userId, + status: 2, // done + updated_at: { + [Op.gte]: today, + [Op.lt]: tomorrow, + }, + }, + include: [{ model: Project, attributes: ['name'] }], + order: [['name', 'ASC']], + }); // Side effect function to fetch suggested tasks -const fetchSuggestedTasks = async (userId, excludedIds) => - await Task.findAll({ - where: { - user_id: userId, - status: { [Op.ne]: 2 }, // not done - id: { [Op.notIn]: excludedIds } - }, - include: [{ model: Project, attributes: ['name'] }], - order: [['priority', 'DESC'], ['name', 'ASC']], - limit: 5 - }); +const fetchSuggestedTasks = async (userId, excludedIds) => + await Task.findAll({ + where: { + user_id: userId, + status: { [Op.ne]: 2 }, // not done + id: { [Op.notIn]: excludedIds }, + }, + include: [{ model: Project, attributes: ['name'] }], + order: [ + ['priority', 'DESC'], + ['name', 'ASC'], + ], + limit: 5, + }); // Side effect function to send telegram message const sendTelegramMessage = async (token, chatId, message) => { - const poller = TelegramPoller; - return await poller.sendTelegramMessage(token, chatId, message); + const poller = TelegramPoller; + return await poller.sendTelegramMessage(token, chatId, message); }; // Side effect function to update user tracking fields -const updateUserTracking = async (user, lastRun, nextRun) => - await user.update({ - task_summary_last_run: lastRun, - task_summary_next_run: nextRun - }); +const updateUserTracking = async (user, lastRun, nextRun) => + await user.update({ + task_summary_last_run: lastRun, + task_summary_next_run: nextRun, + }); // Function to generate summary for user (contains side effects) const generateSummaryForUser = async (userId) => { - try { - const user = await fetchUser(userId); - if (!user) return null; + try { + const user = await fetchUser(userId); + if (!user) return null; - const { today, tomorrow } = createTodayDateRange(); + const { today, tomorrow } = createTodayDateRange(); - // Fetch all task data in parallel - const [dueToday, inProgress, completedToday] = await Promise.all([ - fetchDueTodayTasks(userId, today, tomorrow), - fetchInProgressTasks(userId), - fetchCompletedTodayTasks(userId, today, tomorrow) - ]); + // Fetch all task data in parallel + const [dueToday, inProgress, completedToday] = await Promise.all([ + fetchDueTodayTasks(userId, today, tomorrow), + fetchInProgressTasks(userId), + fetchCompletedTodayTasks(userId, today, tomorrow), + ]); - // Get suggested tasks (excluding already fetched ones) - const excludedIds = [...dueToday.map(t => t.id), ...inProgress.map(t => t.id)]; - const suggestedTasks = await fetchSuggestedTasks(userId, excludedIds); + // Get suggested tasks (excluding already fetched ones) + const excludedIds = [ + ...dueToday.map((t) => t.id), + ...inProgress.map((t) => t.id), + ]; + const suggestedTasks = await fetchSuggestedTasks(userId, excludedIds); - // Build task sections - const taskSections = { - dueToday: buildTaskSection(dueToday, "🚀 *Tasks Due Today:*"), - inProgress: buildTaskSection(inProgress, "⚙️ *In Progress Tasks:*"), - suggested: buildTaskSection(suggestedTasks, "💡 *Suggested Tasks:*"), - completed: buildTaskSection(completedToday, "✅ *Completed Today:*", true) - }; + // Build task sections + const taskSections = { + dueToday: buildTaskSection(dueToday, '🚀 *Tasks Due Today:*'), + inProgress: buildTaskSection(inProgress, '⚙️ *In Progress Tasks:*'), + suggested: buildTaskSection( + suggestedTasks, + '💡 *Suggested Tasks:*' + ), + completed: buildTaskSection( + completedToday, + '✅ *Completed Today:*', + true + ), + }; - return buildSummaryMessage(taskSections); - } catch (error) { - console.error('Error generating task summary:', error); - return null; - } + return buildSummaryMessage(taskSections); + } catch (error) { + console.error('Error generating task summary:', error); + return null; + } }; // Function to send summary to user (contains side effects) const sendSummaryToUser = async (userId) => { - try { - const user = await fetchUser(userId); - if (!user || !user.telegram_bot_token || !user.telegram_chat_id) { - return false; + try { + const user = await fetchUser(userId); + if (!user || !user.telegram_bot_token || !user.telegram_chat_id) { + return false; + } + + const summary = await generateSummaryForUser(userId); + if (!summary) return false; + + // Send the message via Telegram + await sendTelegramMessage( + user.telegram_bot_token, + user.telegram_chat_id, + summary + ); + + // Update tracking fields + const now = new Date(); + const nextRun = calculateNextRunTime(user, now); + await updateUserTracking(user, now, nextRun); + + return true; + } catch (error) { + console.error( + `Error sending task summary to user ${userId}:`, + error.message + ); + return false; } - - const summary = await generateSummaryForUser(userId); - if (!summary) return false; - - // Send the message via Telegram - await sendTelegramMessage( - user.telegram_bot_token, - user.telegram_chat_id, - summary - ); - - // Update tracking fields - const now = new Date(); - const nextRun = calculateNextRunTime(user, now); - await updateUserTracking(user, now, nextRun); - - return true; - } catch (error) { - console.error(`Error sending task summary to user ${userId}:`, error.message); - return false; - } }; // Export functional interface module.exports = { - generateSummaryForUser, - sendSummaryToUser, - calculateNextRunTime, - // For testing - _escapeMarkdown: escapeMarkdown, - _getPriorityEmoji: getPriorityEmoji, - _createTodayDateRange: createTodayDateRange, - _formatTaskForDisplay: formatTaskForDisplay, - _buildTaskSection: buildTaskSection, - _buildSummaryMessage: buildSummaryMessage -}; \ No newline at end of file + generateSummaryForUser, + sendSummaryToUser, + calculateNextRunTime, + // For testing + _escapeMarkdown: escapeMarkdown, + _getPriorityEmoji: getPriorityEmoji, + _createTodayDateRange: createTodayDateRange, + _formatTaskForDisplay: formatTaskForDisplay, + _buildTaskSection: buildTaskSection, + _buildSummaryMessage: buildSummaryMessage, +}; diff --git a/backend/services/telegramInitializer.js b/backend/services/telegramInitializer.js index 71c9ed0..a5e7e7a 100644 --- a/backend/services/telegramInitializer.js +++ b/backend/services/telegramInitializer.js @@ -2,29 +2,32 @@ const telegramPoller = require('./telegramPoller'); const { User } = require('../models'); async function initializeTelegramPolling() { - if (process.env.NODE_ENV === 'test' || process.env.DISABLE_TELEGRAM === 'true') { - return; - } - - try { - // Find users with configured Telegram tokens - const usersWithTelegram = await User.findAll({ - where: { - telegram_bot_token: { - [require('sequelize').Op.ne]: null - } - } - }); - - if (usersWithTelegram.length > 0) { - // Add each user to the polling list - for (const user of usersWithTelegram) { - await telegramPoller.addUser(user); - } + if ( + process.env.NODE_ENV === 'test' || + process.env.DISABLE_TELEGRAM === 'true' + ) { + return; + } + + try { + // Find users with configured Telegram tokens + const usersWithTelegram = await User.findAll({ + where: { + telegram_bot_token: { + [require('sequelize').Op.ne]: null, + }, + }, + }); + + if (usersWithTelegram.length > 0) { + // Add each user to the polling list + for (const user of usersWithTelegram) { + await telegramPoller.addUser(user); + } + } + } catch (error) { + // Telegram polling will be initialized later when the database is available } - } catch (error) { - // Telegram polling will be initialized later when the database is available - } } -module.exports = { initializeTelegramPolling }; \ No newline at end of file +module.exports = { initializeTelegramPolling }; diff --git a/backend/services/telegramPoller.js b/backend/services/telegramPoller.js index f79bb43..8dc387d 100644 --- a/backend/services/telegramPoller.js +++ b/backend/services/telegramPoller.js @@ -3,400 +3,422 @@ const { User, InboxItem } = require('../models'); // Create poller state const createPollerState = () => ({ - running: false, - interval: null, - pollInterval: 5000, // 5 seconds - usersToPool: [], - userStatus: {}, - processedUpdates: new Set() // Track processed update IDs to prevent duplicates + running: false, + interval: null, + pollInterval: 5000, // 5 seconds + usersToPool: [], + userStatus: {}, + processedUpdates: new Set(), // Track processed update IDs to prevent duplicates }); // Global mutable state (managed functionally) let pollerState = createPollerState(); // Check if user exists in list -const userExistsInList = (users, userId) => - users.some(u => u.id === userId); +const userExistsInList = (users, userId) => users.some((u) => u.id === userId); // Add user to list const addUserToList = (users, user) => { - if (userExistsInList(users, user.id)) { - return users; - } - return [...users, user]; + if (userExistsInList(users, user.id)) { + return users; + } + return [...users, user]; }; // Remove user from list -const removeUserFromList = (users, userId) => - users.filter(u => u.id !== userId); +const removeUserFromList = (users, userId) => + users.filter((u) => u.id !== userId); // Remove user status const removeUserStatus = (userStatus, userId) => { - const { [userId]: removed, ...rest } = userStatus; - return rest; + const { [userId]: removed, ...rest } = userStatus; + return rest; }; // Update user status const updateUserStatus = (userStatus, userId, updates) => ({ - ...userStatus, - [userId]: { - ...userStatus[userId], - ...updates - } + ...userStatus, + [userId]: { + ...userStatus[userId], + ...updates, + }, }); // Get highest update ID from updates const getHighestUpdateId = (updates) => { - if (!updates.length) return 0; - return Math.max(...updates.map(u => u.update_id)); + if (!updates.length) return 0; + return Math.max(...updates.map((u) => u.update_id)); }; // Create message parameters const createMessageParams = (chatId, text, replyToMessageId = null) => { - const params = { chat_id: chatId, text: text }; - if (replyToMessageId) { - params.reply_to_message_id = replyToMessageId; - } - return params; + const params = { chat_id: chatId, text: text }; + if (replyToMessageId) { + params.reply_to_message_id = replyToMessageId; + } + return params; }; // Create Telegram API URL const createTelegramUrl = (token, endpoint, params = {}) => { - const baseUrl = `https://api.telegram.org/bot${token}/${endpoint}`; - if (Object.keys(params).length === 0) return baseUrl; - - const searchParams = new URLSearchParams(params); - return `${baseUrl}?${searchParams}`; + const baseUrl = `https://api.telegram.org/bot${token}/${endpoint}`; + if (Object.keys(params).length === 0) return baseUrl; + + const searchParams = new URLSearchParams(params); + return `${baseUrl}?${searchParams}`; }; // Side effect function to make HTTP GET request const makeHttpGetRequest = (url, timeout = 5000) => { - return new Promise((resolve, reject) => { - https.get(url, { timeout }, (res) => { - let data = ''; - - res.on('data', (chunk) => { - data += chunk; - }); - - res.on('end', () => { - try { - const response = JSON.parse(data); - resolve(response); - } catch (error) { - reject(error); - } - }); - }).on('error', (error) => { - reject(error); - }).on('timeout', () => { - reject(new Error('Request timeout')); + return new Promise((resolve, reject) => { + https + .get(url, { timeout }, (res) => { + let data = ''; + + res.on('data', (chunk) => { + data += chunk; + }); + + res.on('end', () => { + try { + const response = JSON.parse(data); + resolve(response); + } catch (error) { + reject(error); + } + }); + }) + .on('error', (error) => { + reject(error); + }) + .on('timeout', () => { + reject(new Error('Request timeout')); + }); }); - }); }; // Side effect function to make HTTP POST request const makeHttpPostRequest = (url, postData, options) => { - return new Promise((resolve, reject) => { - const req = https.request(url, options, (res) => { - let data = ''; - res.on('data', (chunk) => data += chunk); - res.on('end', () => { - try { - const response = JSON.parse(data); - resolve(response); - } catch (error) { - reject(error); - } - }); - }); + return new Promise((resolve, reject) => { + const req = https.request(url, options, (res) => { + let data = ''; + res.on('data', (chunk) => (data += chunk)); + res.on('end', () => { + try { + const response = JSON.parse(data); + resolve(response); + } catch (error) { + reject(error); + } + }); + }); - req.on('error', reject); - req.write(postData); - req.end(); - }); + req.on('error', reject); + req.write(postData); + req.end(); + }); }; // Side effect function to get Telegram updates const getTelegramUpdates = async (token, offset) => { - try { - const url = createTelegramUrl(token, 'getUpdates', { - offset: offset.toString(), - timeout: '1' - }); - - const response = await makeHttpGetRequest(url, 5000); - - if (response.ok && Array.isArray(response.result)) { - return response.result; - } else { - return []; + try { + const url = createTelegramUrl(token, 'getUpdates', { + offset: offset.toString(), + timeout: '1', + }); + + const response = await makeHttpGetRequest(url, 5000); + + if (response.ok && Array.isArray(response.result)) { + return response.result; + } else { + return []; + } + } catch (error) { + throw error; } - } catch (error) { - throw error; - } }; // Side effect function to send Telegram message -const sendTelegramMessage = async (token, chatId, text, replyToMessageId = null) => { - try { - const messageParams = createMessageParams(chatId, text, replyToMessageId); - const postData = JSON.stringify(messageParams); - const url = createTelegramUrl(token, 'sendMessage'); - - const options = { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'Content-Length': Buffer.byteLength(postData) - } - }; +const sendTelegramMessage = async ( + token, + chatId, + text, + replyToMessageId = null +) => { + try { + const messageParams = createMessageParams( + chatId, + text, + replyToMessageId + ); + const postData = JSON.stringify(messageParams); + const url = createTelegramUrl(token, 'sendMessage'); - return await makeHttpPostRequest(url, postData, options); - } catch (error) { - throw error; - } + const options = { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Content-Length': Buffer.byteLength(postData), + }, + }; + + return await makeHttpPostRequest(url, postData, options); + } catch (error) { + throw error; + } }; // Side effect function to update user chat ID const updateUserChatId = async (userId, chatId) => { - await User.update( - { telegram_chat_id: chatId }, - { where: { id: userId } } - ); + await User.update({ telegram_chat_id: chatId }, { where: { id: userId } }); }; // Side effect function to create inbox item const createInboxItem = async (content, userId, messageId) => { - // Check if a similar item was created recently (within last 30 seconds) - // to prevent duplicates from network issues or multiple processing - const recentCutoff = new Date(Date.now() - 30000); // 30 seconds ago - - const existingItem = await InboxItem.findOne({ - where: { - content: content, - user_id: userId, - source: 'telegram', - created_at: { - [require('sequelize').Op.gte]: recentCutoff - } + // Check if a similar item was created recently (within last 30 seconds) + // to prevent duplicates from network issues or multiple processing + const recentCutoff = new Date(Date.now() - 30000); // 30 seconds ago + + const existingItem = await InboxItem.findOne({ + where: { + content: content, + user_id: userId, + source: 'telegram', + created_at: { + [require('sequelize').Op.gte]: recentCutoff, + }, + }, + }); + + if (existingItem) { + console.log( + `Duplicate inbox item detected for user ${userId}, content: "${content}". Skipping creation.` + ); + return existingItem; } - }); - - if (existingItem) { - console.log(`Duplicate inbox item detected for user ${userId}, content: "${content}". Skipping creation.`); - return existingItem; - } - - return await InboxItem.create({ - content: content, - source: 'telegram', - user_id: userId, - metadata: { telegram_message_id: messageId } // Store message ID for reference - }); + + return await InboxItem.create({ + content: content, + source: 'telegram', + user_id: userId, + metadata: { telegram_message_id: messageId }, // Store message ID for reference + }); }; // Function to process a single message (contains side effects) const processMessage = async (user, update) => { - const message = update.message; - const text = message.text; - const chatId = message.chat.id.toString(); - const messageId = message.message_id; + const message = update.message; + const text = message.text; + const chatId = message.chat.id.toString(); + const messageId = message.message_id; - // Update chat ID if needed - if (!user.telegram_chat_id) { - await updateUserChatId(user.id, chatId); - user.telegram_chat_id = chatId; // Update local object - } + // Update chat ID if needed + if (!user.telegram_chat_id) { + await updateUserChatId(user.id, chatId); + user.telegram_chat_id = chatId; // Update local object + } - try { - // Create inbox item (with duplicate check) - const inboxItem = await createInboxItem(text, user.id, messageId); + try { + // Create inbox item (with duplicate check) + const inboxItem = await createInboxItem(text, user.id, messageId); - // Send confirmation - await sendTelegramMessage( - user.telegram_bot_token, - chatId, - `✅ Added to Tududi inbox: "${text}"`, - messageId - ); - - console.log(`Successfully processed message ${messageId} for user ${user.id}: "${text}"`); - } catch (error) { - // Send error message - await sendTelegramMessage( - user.telegram_bot_token, - chatId, - `❌ Failed to add to inbox: ${error.message}`, - messageId - ); - } + // Send confirmation + await sendTelegramMessage( + user.telegram_bot_token, + chatId, + `✅ Added to Tududi inbox: "${text}"`, + messageId + ); + + console.log( + `Successfully processed message ${messageId} for user ${user.id}: "${text}"` + ); + } catch (error) { + // Send error message + await sendTelegramMessage( + user.telegram_bot_token, + chatId, + `❌ Failed to add to inbox: ${error.message}`, + messageId + ); + } }; // Function to process updates (contains side effects) const processUpdates = async (user, updates) => { - if (!updates.length) return; + if (!updates.length) return; - // Filter out already processed updates - const newUpdates = updates.filter(update => { - const updateKey = `${user.id}-${update.update_id}`; - return !pollerState.processedUpdates.has(updateKey); - }); + // Filter out already processed updates + const newUpdates = updates.filter((update) => { + const updateKey = `${user.id}-${update.update_id}`; + return !pollerState.processedUpdates.has(updateKey); + }); - if (!newUpdates.length) return; + if (!newUpdates.length) return; - // Get highest update ID from new updates - const highestUpdateId = getHighestUpdateId(newUpdates); - - // Update user status - pollerState = { - ...pollerState, - userStatus: updateUserStatus(pollerState.userStatus, user.id, { - lastUpdateId: highestUpdateId - }) - }; + // Get highest update ID from new updates + const highestUpdateId = getHighestUpdateId(newUpdates); - // Process each new update - for (const update of newUpdates) { - try { - const updateKey = `${user.id}-${update.update_id}`; - - if (update.message && update.message.text) { - await processMessage(user, update); - - // Mark update as processed - pollerState.processedUpdates.add(updateKey); - - // Clean up old processed updates (keep only last 1000 to prevent memory leak) - if (pollerState.processedUpdates.size > 1000) { - const oldestEntries = Array.from(pollerState.processedUpdates).slice(0, 100); - oldestEntries.forEach(entry => pollerState.processedUpdates.delete(entry)); + // Update user status + pollerState = { + ...pollerState, + userStatus: updateUserStatus(pollerState.userStatus, user.id, { + lastUpdateId: highestUpdateId, + }), + }; + + // Process each new update + for (const update of newUpdates) { + try { + const updateKey = `${user.id}-${update.update_id}`; + + if (update.message && update.message.text) { + await processMessage(user, update); + + // Mark update as processed + pollerState.processedUpdates.add(updateKey); + + // Clean up old processed updates (keep only last 1000 to prevent memory leak) + if (pollerState.processedUpdates.size > 1000) { + const oldestEntries = Array.from( + pollerState.processedUpdates + ).slice(0, 100); + oldestEntries.forEach((entry) => + pollerState.processedUpdates.delete(entry) + ); + } + } + } catch (error) { + console.error( + `Error processing update ${update.update_id} for user ${user.id}:`, + error + ); } - } - } catch (error) { - console.error(`Error processing update ${update.update_id} for user ${user.id}:`, error); } - } }; // Function to poll updates for all users (contains side effects) const pollUpdates = async () => { - for (const user of pollerState.usersToPool) { - const token = user.telegram_bot_token; - if (!token) continue; + for (const user of pollerState.usersToPool) { + const token = user.telegram_bot_token; + if (!token) continue; - try { - const lastUpdateId = pollerState.userStatus[user.id]?.lastUpdateId || 0; - const updates = await getTelegramUpdates(token, lastUpdateId + 1); - - if (updates && updates.length > 0) { - console.log(`Processing ${updates.length} updates for user ${user.id}, starting from update ID ${lastUpdateId + 1}`); - await processUpdates(user, updates); - } - } catch (error) { - console.error(`Error getting updates for user ${user.id}:`, error); + try { + const lastUpdateId = + pollerState.userStatus[user.id]?.lastUpdateId || 0; + const updates = await getTelegramUpdates(token, lastUpdateId + 1); + + if (updates && updates.length > 0) { + console.log( + `Processing ${updates.length} updates for user ${user.id}, starting from update ID ${lastUpdateId + 1}` + ); + await processUpdates(user, updates); + } + } catch (error) { + console.error(`Error getting updates for user ${user.id}:`, error); + } } - } }; // Function to start polling (contains side effects) const startPolling = () => { - if (pollerState.running) return; - - const interval = setInterval(async () => { - try { - await pollUpdates(); - } catch (error) { - // Error polling Telegram - } - }, pollerState.pollInterval); + if (pollerState.running) return; - pollerState = { - ...pollerState, - running: true, - interval - }; + const interval = setInterval(async () => { + try { + await pollUpdates(); + } catch (error) { + // Error polling Telegram + } + }, pollerState.pollInterval); + + pollerState = { + ...pollerState, + running: true, + interval, + }; }; // Function to stop polling (contains side effects) const stopPolling = () => { - if (!pollerState.running) return; - - if (pollerState.interval) { - clearInterval(pollerState.interval); - } + if (!pollerState.running) return; - pollerState = { - ...pollerState, - running: false, - interval: null - }; + if (pollerState.interval) { + clearInterval(pollerState.interval); + } + + pollerState = { + ...pollerState, + running: false, + interval: null, + }; }; // Function to add user (contains side effects) const addUser = async (user) => { - if (!user || !user.telegram_bot_token) { - return false; - } + if (!user || !user.telegram_bot_token) { + return false; + } - // Add user to list - const newUsersList = addUserToList(pollerState.usersToPool, user); - - pollerState = { - ...pollerState, - usersToPool: newUsersList - }; + // Add user to list + const newUsersList = addUserToList(pollerState.usersToPool, user); - // Start polling if not already running and we have users - if (pollerState.usersToPool.length > 0 && !pollerState.running) { - startPolling(); - } + pollerState = { + ...pollerState, + usersToPool: newUsersList, + }; - return true; + // Start polling if not already running and we have users + if (pollerState.usersToPool.length > 0 && !pollerState.running) { + startPolling(); + } + + return true; }; // Function to remove user (contains side effects) const removeUser = (userId) => { - // Remove user from list and status - const newUsersList = removeUserFromList(pollerState.usersToPool, userId); - const newUserStatus = removeUserStatus(pollerState.userStatus, userId); + // Remove user from list and status + const newUsersList = removeUserFromList(pollerState.usersToPool, userId); + const newUserStatus = removeUserStatus(pollerState.userStatus, userId); - pollerState = { - ...pollerState, - usersToPool: newUsersList, - userStatus: newUserStatus - }; + pollerState = { + ...pollerState, + usersToPool: newUsersList, + userStatus: newUserStatus, + }; - // Stop polling if no users left - if (pollerState.usersToPool.length === 0 && pollerState.running) { - stopPolling(); - } + // Stop polling if no users left + if (pollerState.usersToPool.length === 0 && pollerState.running) { + stopPolling(); + } - return true; + return true; }; // Get poller status const getStatus = () => ({ - running: pollerState.running, - usersCount: pollerState.usersToPool.length, - pollInterval: pollerState.pollInterval, - userStatus: pollerState.userStatus + running: pollerState.running, + usersCount: pollerState.usersToPool.length, + pollInterval: pollerState.pollInterval, + userStatus: pollerState.userStatus, }); // Export functional interface module.exports = { - addUser, - removeUser, - startPolling, - stopPolling, - getStatus, - sendTelegramMessage, - // For testing - _createPollerState: createPollerState, - _userExistsInList: userExistsInList, - _addUserToList: addUserToList, - _removeUserFromList: removeUserFromList, - _getHighestUpdateId: getHighestUpdateId, - _createMessageParams: createMessageParams, - _createTelegramUrl: createTelegramUrl -}; \ No newline at end of file + addUser, + removeUser, + startPolling, + stopPolling, + getStatus, + sendTelegramMessage, + // For testing + _createPollerState: createPollerState, + _userExistsInList: userExistsInList, + _addUserToList: addUserToList, + _removeUserFromList: removeUserFromList, + _getHighestUpdateId: getHighestUpdateId, + _createMessageParams: createMessageParams, + _createTelegramUrl: createTelegramUrl, +}; diff --git a/backend/tests/README.md b/backend/tests/README.md index e76f1fb..9dde6c3 100644 --- a/backend/tests/README.md +++ b/backend/tests/README.md @@ -18,26 +18,31 @@ tests/ ## Running Tests ### All Tests + ```bash npm test ``` ### Unit Tests Only + ```bash npm run test:unit ``` ### Integration Tests Only + ```bash npm run test:integration ``` ### Watch Mode (for development) + ```bash npm run test:watch ``` ### Coverage Report + ```bash npm run test:coverage ``` @@ -45,6 +50,7 @@ npm run test:coverage ## Test Environment Tests run in a separate test environment with: + - In-memory SQLite database (isolated from development data) - Test-specific configuration from `.env.test` - Automatic database cleanup between tests @@ -52,17 +58,20 @@ Tests run in a separate test environment with: ## Writing Tests ### Unit Tests + - Test individual functions, models, or middleware in isolation - Mock external dependencies - Focus on business logic and edge cases ### Integration Tests + - Test complete API endpoints - Use authenticated requests where needed - Test real database interactions - Verify response formats and status codes ### Test Utilities + - `tests/helpers/testUtils.js` provides utilities for creating test data - `tests/helpers/setup.js` handles database setup and cleanup - Use `createTestUser()` for creating authenticated test users @@ -79,4 +88,4 @@ Tests run in a separate test environment with: - **Jest**: Test framework - **Supertest**: HTTP testing library for integration tests -- **cross-env**: Cross-platform environment variable setting \ No newline at end of file +- **cross-env**: Cross-platform environment variable setting diff --git a/backend/tests/helpers/setup.js b/backend/tests/helpers/setup.js index 4344ab8..fd6432f 100644 --- a/backend/tests/helpers/setup.js +++ b/backend/tests/helpers/setup.js @@ -6,37 +6,43 @@ const fs = require('fs'); const path = require('path'); beforeAll(async () => { - // Ensure test database is clean and created - await sequelize.sync({ force: true }); + // Ensure test database is clean and created + await sequelize.sync({ force: true }); }, 30000); beforeEach(async () => { - // Clean all tables except Sessions to avoid conflicts - try { - const models = Object.values(sequelize.models); - const nonSessionModels = models.filter(model => model.name !== 'Session'); - await Promise.all(nonSessionModels.map(model => model.destroy({ truncate: true, cascade: true }))); - } catch (error) { - // Ignore errors during cleanup - } + // Clean all tables except Sessions to avoid conflicts + try { + const models = Object.values(sequelize.models); + const nonSessionModels = models.filter( + (model) => model.name !== 'Session' + ); + await Promise.all( + nonSessionModels.map((model) => + model.destroy({ truncate: true, cascade: true }) + ) + ); + } catch (error) { + // Ignore errors during cleanup + } }); afterEach(async () => { - // Clean up sessions after each test - try { - const Session = sequelize.models.Session; - if (Session) { - await Session.destroy({ truncate: true }); + // Clean up sessions after each test + try { + const Session = sequelize.models.Session; + if (Session) { + await Session.destroy({ truncate: true }); + } + } catch (error) { + // Ignore errors during session cleanup } - } catch (error) { - // Ignore errors during session cleanup - } }); afterAll(async () => { - try { - await sequelize.close(); - } catch (error) { - // Database may already be closed - } -}, 30000); \ No newline at end of file + try { + await sequelize.close(); + } catch (error) { + // Database may already be closed + } +}, 30000); diff --git a/backend/tests/helpers/testUtils.js b/backend/tests/helpers/testUtils.js index c80c391..4d48316 100644 --- a/backend/tests/helpers/testUtils.js +++ b/backend/tests/helpers/testUtils.js @@ -2,27 +2,25 @@ const bcrypt = require('bcrypt'); const { User } = require('../../models'); const createTestUser = async (userData = {}) => { - const defaultUser = { - email: 'test@example.com', - password: 'password123', // Use password field to trigger model hook - ...userData - }; - - return await User.create(defaultUser); + const defaultUser = { + email: 'test@example.com', + password: 'password123', // Use password field to trigger model hook + ...userData, + }; + + return await User.create(defaultUser); }; const authenticateUser = async (request, user) => { - const response = await request - .post('/api/login') - .send({ - email: user.email, - password: 'password123' + const response = await request.post('/api/login').send({ + email: user.email, + password: 'password123', }); - - return response.headers['set-cookie']; + + return response.headers['set-cookie']; }; module.exports = { - createTestUser, - authenticateUser -}; \ No newline at end of file + createTestUser, + authenticateUser, +}; diff --git a/backend/tests/integration/areas.test.js b/backend/tests/integration/areas.test.js index 99140e5..4741124 100644 --- a/backend/tests/integration/areas.test.js +++ b/backend/tests/integration/areas.test.js @@ -4,277 +4,275 @@ const { Area, User } = require('../../models'); const { createTestUser } = require('../helpers/testUtils'); describe('Areas Routes', () => { - let user, agent; - - beforeEach(async () => { - user = await createTestUser({ - email: 'test@example.com' - }); - - // Create authenticated agent - agent = request.agent(app); - await agent - .post('/api/login') - .send({ - email: 'test@example.com', - password: 'password123' - }); - }); - - describe('POST /api/areas', () => { - it('should create a new area', async () => { - const areaData = { - name: 'Work', - description: 'Work related projects' - }; - - const response = await agent - .post('/api/areas') - .send(areaData); - - expect(response.status).toBe(201); - expect(response.body.name).toBe(areaData.name); - expect(response.body.description).toBe(areaData.description); - expect(response.body.user_id).toBe(user.id); - }); - - it('should require authentication', async () => { - const areaData = { - name: 'Work' - }; - - const response = await request(app) - .post('/api/areas') - .send(areaData); - - expect(response.status).toBe(401); - expect(response.body.error).toBe('Authentication required'); - }); - - it('should require area name', async () => { - const areaData = { - description: 'Area without name' - }; - - const response = await agent - .post('/api/areas') - .send(areaData); - - expect(response.status).toBe(400); - expect(response.body.error).toBe('Area name is required.'); - }); - }); - - describe('GET /api/areas', () => { - let area1, area2; + let user, agent; beforeEach(async () => { - area1 = await Area.create({ - name: 'Work', - description: 'Work projects', - user_id: user.id - }); + user = await createTestUser({ + email: 'test@example.com', + }); - area2 = await Area.create({ - name: 'Personal', - description: 'Personal projects', - user_id: user.id - }); + // Create authenticated agent + agent = request.agent(app); + await agent.post('/api/login').send({ + email: 'test@example.com', + password: 'password123', + }); }); - it('should get all user areas', async () => { - const response = await agent.get('/api/areas'); + describe('POST /api/areas', () => { + it('should create a new area', async () => { + const areaData = { + name: 'Work', + description: 'Work related projects', + }; - expect(response.status).toBe(200); - expect(response.body).toHaveLength(2); - expect(response.body.map(a => a.id)).toContain(area1.id); - expect(response.body.map(a => a.id)).toContain(area2.id); + const response = await agent.post('/api/areas').send(areaData); + + expect(response.status).toBe(201); + expect(response.body.name).toBe(areaData.name); + expect(response.body.description).toBe(areaData.description); + expect(response.body.user_id).toBe(user.id); + }); + + it('should require authentication', async () => { + const areaData = { + name: 'Work', + }; + + const response = await request(app) + .post('/api/areas') + .send(areaData); + + expect(response.status).toBe(401); + expect(response.body.error).toBe('Authentication required'); + }); + + it('should require area name', async () => { + const areaData = { + description: 'Area without name', + }; + + const response = await agent.post('/api/areas').send(areaData); + + expect(response.status).toBe(400); + expect(response.body.error).toBe('Area name is required.'); + }); }); - it('should order areas by name', async () => { - const response = await agent.get('/api/areas'); + describe('GET /api/areas', () => { + let area1, area2; - expect(response.status).toBe(200); - expect(response.body[0].name).toBe('Personal'); // P comes before W - expect(response.body[1].name).toBe('Work'); + beforeEach(async () => { + area1 = await Area.create({ + name: 'Work', + description: 'Work projects', + user_id: user.id, + }); + + area2 = await Area.create({ + name: 'Personal', + description: 'Personal projects', + user_id: user.id, + }); + }); + + it('should get all user areas', async () => { + const response = await agent.get('/api/areas'); + + expect(response.status).toBe(200); + expect(response.body).toHaveLength(2); + expect(response.body.map((a) => a.id)).toContain(area1.id); + expect(response.body.map((a) => a.id)).toContain(area2.id); + }); + + it('should order areas by name', async () => { + const response = await agent.get('/api/areas'); + + expect(response.status).toBe(200); + expect(response.body[0].name).toBe('Personal'); // P comes before W + expect(response.body[1].name).toBe('Work'); + }); + + it('should require authentication', async () => { + const response = await request(app).get('/api/areas'); + + expect(response.status).toBe(401); + expect(response.body.error).toBe('Authentication required'); + }); }); - it('should require authentication', async () => { - const response = await request(app).get('/api/areas'); + describe('GET /api/areas/:id', () => { + let area; - expect(response.status).toBe(401); - expect(response.body.error).toBe('Authentication required'); - }); - }); + beforeEach(async () => { + area = await Area.create({ + name: 'Work', + description: 'Work projects', + user_id: user.id, + }); + }); - describe('GET /api/areas/:id', () => { - let area; + it('should get area by id', async () => { + const response = await agent.get(`/api/areas/${area.id}`); - beforeEach(async () => { - area = await Area.create({ - name: 'Work', - description: 'Work projects', - user_id: user.id - }); + expect(response.status).toBe(200); + expect(response.body.id).toBe(area.id); + expect(response.body.name).toBe(area.name); + expect(response.body.description).toBe(area.description); + }); + + it('should return 404 for non-existent area', async () => { + const response = await agent.get('/api/areas/999999'); + + expect(response.status).toBe(404); + expect(response.body.error).toBe( + "Area not found or doesn't belong to the current user." + ); + }); + + it("should not allow access to other user's areas", async () => { + const bcrypt = require('bcrypt'); + const otherUser = await User.create({ + email: 'other@example.com', + password_digest: await bcrypt.hash('password123', 10), + }); + + const otherArea = await Area.create({ + name: 'Other Area', + user_id: otherUser.id, + }); + + const response = await agent.get(`/api/areas/${otherArea.id}`); + + expect(response.status).toBe(404); + expect(response.body.error).toBe( + "Area not found or doesn't belong to the current user." + ); + }); + + it('should require authentication', async () => { + const response = await request(app).get(`/api/areas/${area.id}`); + + expect(response.status).toBe(401); + expect(response.body.error).toBe('Authentication required'); + }); }); - it('should get area by id', async () => { - const response = await agent.get(`/api/areas/${area.id}`); + describe('PATCH /api/areas/:id', () => { + let area; - expect(response.status).toBe(200); - expect(response.body.id).toBe(area.id); - expect(response.body.name).toBe(area.name); - expect(response.body.description).toBe(area.description); + beforeEach(async () => { + area = await Area.create({ + name: 'Work', + description: 'Work projects', + user_id: user.id, + }); + }); + + it('should update area', async () => { + const updateData = { + name: 'Updated Work', + description: 'Updated description', + }; + + const response = await agent + .patch(`/api/areas/${area.id}`) + .send(updateData); + + expect(response.status).toBe(200); + expect(response.body.name).toBe(updateData.name); + expect(response.body.description).toBe(updateData.description); + }); + + it('should return 404 for non-existent area', async () => { + const response = await agent + .patch('/api/areas/999999') + .send({ name: 'Updated' }); + + expect(response.status).toBe(404); + expect(response.body.error).toBe('Area not found.'); + }); + + it("should not allow updating other user's areas", async () => { + const bcrypt = require('bcrypt'); + const otherUser = await User.create({ + email: 'other@example.com', + password_digest: await bcrypt.hash('password123', 10), + }); + + const otherArea = await Area.create({ + name: 'Other Area', + user_id: otherUser.id, + }); + + const response = await agent + .patch(`/api/areas/${otherArea.id}`) + .send({ name: 'Updated' }); + + expect(response.status).toBe(404); + expect(response.body.error).toBe('Area not found.'); + }); + + it('should require authentication', async () => { + const response = await request(app) + .patch(`/api/areas/${area.id}`) + .send({ name: 'Updated' }); + + expect(response.status).toBe(401); + expect(response.body.error).toBe('Authentication required'); + }); }); - it('should return 404 for non-existent area', async () => { - const response = await agent.get('/api/areas/999999'); + describe('DELETE /api/areas/:id', () => { + let area; - expect(response.status).toBe(404); - expect(response.body.error).toBe("Area not found or doesn't belong to the current user."); + beforeEach(async () => { + area = await Area.create({ + name: 'Work', + user_id: user.id, + }); + }); + + it('should delete area', async () => { + const response = await agent.delete(`/api/areas/${area.id}`); + + expect(response.status).toBe(204); + + // Verify area is deleted + const deletedArea = await Area.findByPk(area.id); + expect(deletedArea).toBeNull(); + }); + + it('should return 404 for non-existent area', async () => { + const response = await agent.delete('/api/areas/999999'); + + expect(response.status).toBe(404); + expect(response.body.error).toBe('Area not found.'); + }); + + it("should not allow deleting other user's areas", async () => { + const bcrypt = require('bcrypt'); + const otherUser = await User.create({ + email: 'other@example.com', + password_digest: await bcrypt.hash('password123', 10), + }); + + const otherArea = await Area.create({ + name: 'Other Area', + user_id: otherUser.id, + }); + + const response = await agent.delete(`/api/areas/${otherArea.id}`); + + expect(response.status).toBe(404); + expect(response.body.error).toBe('Area not found.'); + }); + + it('should require authentication', async () => { + const response = await request(app).delete(`/api/areas/${area.id}`); + + expect(response.status).toBe(401); + expect(response.body.error).toBe('Authentication required'); + }); }); - - it('should not allow access to other user\'s areas', async () => { - const bcrypt = require('bcrypt'); - const otherUser = await User.create({ - email: 'other@example.com', - password_digest: await bcrypt.hash('password123', 10) - }); - - const otherArea = await Area.create({ - name: 'Other Area', - user_id: otherUser.id - }); - - const response = await agent.get(`/api/areas/${otherArea.id}`); - - expect(response.status).toBe(404); - expect(response.body.error).toBe("Area not found or doesn't belong to the current user."); - }); - - it('should require authentication', async () => { - const response = await request(app).get(`/api/areas/${area.id}`); - - expect(response.status).toBe(401); - expect(response.body.error).toBe('Authentication required'); - }); - }); - - describe('PATCH /api/areas/:id', () => { - let area; - - beforeEach(async () => { - area = await Area.create({ - name: 'Work', - description: 'Work projects', - user_id: user.id - }); - }); - - it('should update area', async () => { - const updateData = { - name: 'Updated Work', - description: 'Updated description' - }; - - const response = await agent - .patch(`/api/areas/${area.id}`) - .send(updateData); - - expect(response.status).toBe(200); - expect(response.body.name).toBe(updateData.name); - expect(response.body.description).toBe(updateData.description); - }); - - it('should return 404 for non-existent area', async () => { - const response = await agent - .patch('/api/areas/999999') - .send({ name: 'Updated' }); - - expect(response.status).toBe(404); - expect(response.body.error).toBe('Area not found.'); - }); - - it('should not allow updating other user\'s areas', async () => { - const bcrypt = require('bcrypt'); - const otherUser = await User.create({ - email: 'other@example.com', - password_digest: await bcrypt.hash('password123', 10) - }); - - const otherArea = await Area.create({ - name: 'Other Area', - user_id: otherUser.id - }); - - const response = await agent - .patch(`/api/areas/${otherArea.id}`) - .send({ name: 'Updated' }); - - expect(response.status).toBe(404); - expect(response.body.error).toBe('Area not found.'); - }); - - it('should require authentication', async () => { - const response = await request(app) - .patch(`/api/areas/${area.id}`) - .send({ name: 'Updated' }); - - expect(response.status).toBe(401); - expect(response.body.error).toBe('Authentication required'); - }); - }); - - describe('DELETE /api/areas/:id', () => { - let area; - - beforeEach(async () => { - area = await Area.create({ - name: 'Work', - user_id: user.id - }); - }); - - it('should delete area', async () => { - const response = await agent.delete(`/api/areas/${area.id}`); - - expect(response.status).toBe(204); - - // Verify area is deleted - const deletedArea = await Area.findByPk(area.id); - expect(deletedArea).toBeNull(); - }); - - it('should return 404 for non-existent area', async () => { - const response = await agent.delete('/api/areas/999999'); - - expect(response.status).toBe(404); - expect(response.body.error).toBe('Area not found.'); - }); - - it('should not allow deleting other user\'s areas', async () => { - const bcrypt = require('bcrypt'); - const otherUser = await User.create({ - email: 'other@example.com', - password_digest: await bcrypt.hash('password123', 10) - }); - - const otherArea = await Area.create({ - name: 'Other Area', - user_id: otherUser.id - }); - - const response = await agent.delete(`/api/areas/${otherArea.id}`); - - expect(response.status).toBe(404); - expect(response.body.error).toBe('Area not found.'); - }); - - it('should require authentication', async () => { - const response = await request(app).delete(`/api/areas/${area.id}`); - - expect(response.status).toBe(401); - expect(response.body.error).toBe('Authentication required'); - }); - }); -}); \ No newline at end of file +}); diff --git a/backend/tests/integration/auth.test.js b/backend/tests/integration/auth.test.js index 81e61f9..709a734 100644 --- a/backend/tests/integration/auth.test.js +++ b/backend/tests/integration/auth.test.js @@ -4,152 +4,138 @@ const { User } = require('../../models'); const { createTestUser } = require('../helpers/testUtils'); describe('Auth Routes', () => { - describe('POST /api/login', () => { - let user; + describe('POST /api/login', () => { + let user; - beforeEach(async () => { - user = await createTestUser({ - email: 'test@example.com' - }); - }); - - it('should login with valid credentials', async () => { - const response = await request(app) - .post('/api/login') - .send({ - email: 'test@example.com', - password: 'password123' + beforeEach(async () => { + user = await createTestUser({ + email: 'test@example.com', + }); }); - expect(response.status).toBe(200); - expect(response.body.user).toBeDefined(); - expect(response.body.user.email).toBe('test@example.com'); - expect(response.body.user.id).toBe(user.id); - expect(response.body.user.language).toBe('en'); - expect(response.body.user.appearance).toBe('light'); - expect(response.body.user.timezone).toBe('UTC'); - }); + it('should login with valid credentials', async () => { + const response = await request(app).post('/api/login').send({ + email: 'test@example.com', + password: 'password123', + }); - it('should return 400 for missing email', async () => { - const response = await request(app) - .post('/api/login') - .send({ - password: 'password123' + expect(response.status).toBe(200); + expect(response.body.user).toBeDefined(); + expect(response.body.user.email).toBe('test@example.com'); + expect(response.body.user.id).toBe(user.id); + expect(response.body.user.language).toBe('en'); + expect(response.body.user.appearance).toBe('light'); + expect(response.body.user.timezone).toBe('UTC'); }); - expect(response.status).toBe(400); - expect(response.body.error).toBe('Invalid login parameters.'); - }); + it('should return 400 for missing email', async () => { + const response = await request(app).post('/api/login').send({ + password: 'password123', + }); - it('should return 400 for missing password', async () => { - const response = await request(app) - .post('/api/login') - .send({ - email: 'test@example.com' + expect(response.status).toBe(400); + expect(response.body.error).toBe('Invalid login parameters.'); }); - expect(response.status).toBe(400); - expect(response.body.error).toBe('Invalid login parameters.'); - }); + it('should return 400 for missing password', async () => { + const response = await request(app).post('/api/login').send({ + email: 'test@example.com', + }); - it('should return 401 for non-existent user', async () => { - const response = await request(app) - .post('/api/login') - .send({ - email: 'nonexistent@example.com', - password: 'password123' + expect(response.status).toBe(400); + expect(response.body.error).toBe('Invalid login parameters.'); }); - expect(response.status).toBe(401); - expect(response.body.errors).toEqual(['Invalid credentials']); - }); + it('should return 401 for non-existent user', async () => { + const response = await request(app).post('/api/login').send({ + email: 'nonexistent@example.com', + password: 'password123', + }); - it('should return 401 for invalid password', async () => { - const response = await request(app) - .post('/api/login') - .send({ - email: 'test@example.com', - password: 'wrongpassword' + expect(response.status).toBe(401); + expect(response.body.errors).toEqual(['Invalid credentials']); }); - expect(response.status).toBe(401); - expect(response.body.errors).toEqual(['Invalid credentials']); - }); - }); + it('should return 401 for invalid password', async () => { + const response = await request(app).post('/api/login').send({ + email: 'test@example.com', + password: 'wrongpassword', + }); - describe('GET /api/current_user', () => { - let user; - - beforeEach(async () => { - user = await createTestUser({ - email: 'test@example.com' - }); + expect(response.status).toBe(401); + expect(response.body.errors).toEqual(['Invalid credentials']); + }); }); - it('should return current user when logged in', async () => { - const agent = request.agent(app); - - // Login first - await agent - .post('/api/login') - .send({ - email: 'test@example.com', - password: 'password123' + describe('GET /api/current_user', () => { + let user; + + beforeEach(async () => { + user = await createTestUser({ + email: 'test@example.com', + }); }); - // Check current user - const response = await agent.get('/api/current_user'); + it('should return current user when logged in', async () => { + const agent = request.agent(app); - expect(response.status).toBe(200); - expect(response.body.user).toBeDefined(); - expect(response.body.user.email).toBe('test@example.com'); - expect(response.body.user.id).toBe(user.id); - }); + // Login first + await agent.post('/api/login').send({ + email: 'test@example.com', + password: 'password123', + }); - it('should return null user when not logged in', async () => { - const response = await request(app).get('/api/current_user'); + // Check current user + const response = await agent.get('/api/current_user'); - expect(response.status).toBe(200); - expect(response.body.user).toBeNull(); - }); - }); - - describe('GET /api/logout', () => { - let user; - - beforeEach(async () => { - user = await createTestUser({ - email: 'test@example.com' - }); - }); - - it('should logout successfully', async () => { - const agent = request.agent(app); - - // Login first - await agent - .post('/api/login') - .send({ - email: 'test@example.com', - password: 'password123' + expect(response.status).toBe(200); + expect(response.body.user).toBeDefined(); + expect(response.body.user.email).toBe('test@example.com'); + expect(response.body.user.id).toBe(user.id); }); - // Logout - const response = await agent.get('/api/logout'); + it('should return null user when not logged in', async () => { + const response = await request(app).get('/api/current_user'); - expect(response.status).toBe(200); - expect(response.body.message).toBe('Logged out successfully'); - - // Verify user is logged out - const currentUserResponse = await agent.get('/api/current_user'); - expect(currentUserResponse.body.user).toBeNull(); + expect(response.status).toBe(200); + expect(response.body.user).toBeNull(); + }); }); - it('should handle logout when not logged in', async () => { - const response = await request(app).get('/api/logout'); + describe('GET /api/logout', () => { + let user; - expect(response.status).toBe(200); - expect(response.body.message).toBe('Logged out successfully'); + beforeEach(async () => { + user = await createTestUser({ + email: 'test@example.com', + }); + }); + + it('should logout successfully', async () => { + const agent = request.agent(app); + + // Login first + await agent.post('/api/login').send({ + email: 'test@example.com', + password: 'password123', + }); + + // Logout + const response = await agent.get('/api/logout'); + + expect(response.status).toBe(200); + expect(response.body.message).toBe('Logged out successfully'); + + // Verify user is logged out + const currentUserResponse = await agent.get('/api/current_user'); + expect(currentUserResponse.body.user).toBeNull(); + }); + + it('should handle logout when not logged in', async () => { + const response = await request(app).get('/api/logout'); + + expect(response.status).toBe(200); + expect(response.body.message).toBe('Logged out successfully'); + }); }); - }); -}); \ No newline at end of file +}); diff --git a/backend/tests/integration/inbox-no-tags.test.js b/backend/tests/integration/inbox-no-tags.test.js index f0d0b07..a517d25 100644 --- a/backend/tests/integration/inbox-no-tags.test.js +++ b/backend/tests/integration/inbox-no-tags.test.js @@ -4,345 +4,349 @@ const { InboxItem, Tag } = require('../../models'); const { createTestUser } = require('../helpers/testUtils'); describe('Inbox Routes - No Tags Scenario', () => { - let user, agent; - - beforeEach(async () => { - user = await createTestUser({ - email: 'test@example.com' - }); - - // Create authenticated agent - agent = request.agent(app); - await agent - .post('/api/login') - .send({ - email: 'test@example.com', - password: 'password123' - }); - - // Ensure no tags exist for this user (clean slate) - await Tag.destroy({ where: { user_id: user.id } }); - }); - - describe('GET /api/inbox - No Tags Scenario', () => { - it('should return empty inbox when no items exist and no tags exist', async () => { - const response = await agent.get('/api/inbox'); - - expect(response.status).toBe(200); - expect(Array.isArray(response.body)).toBe(true); - expect(response.body.length).toBe(0); - }); - - it('should return inbox items even when no tags exist in system', async () => { - // Create inbox items without any tags in the system - const inboxItem1 = await InboxItem.create({ - content: 'Test item without tags', - status: 'added', - user_id: user.id - }); - - const inboxItem2 = await InboxItem.create({ - content: 'Another item without tags', - status: 'added', - user_id: user.id - }); - - const response = await agent.get('/api/inbox'); - - expect(response.status).toBe(200); - expect(Array.isArray(response.body)).toBe(true); - expect(response.body.length).toBe(2); - expect(response.body.map(item => item.id)).toContain(inboxItem1.id); - expect(response.body.map(item => item.id)).toContain(inboxItem2.id); - expect(response.body[0].content).toBeDefined(); - expect(response.body[0].status).toBe('added'); - expect(response.body[0].user_id).toBe(user.id); - }); - - it('should handle mixed inbox items when no tags exist', async () => { - // Create inbox items with different statuses - const addedItem = await InboxItem.create({ - content: 'Added item', - status: 'added', - user_id: user.id - }); - - await InboxItem.create({ - content: 'Processed item', - status: 'processed', - user_id: user.id - }); - - await InboxItem.create({ - content: 'Deleted item', - status: 'deleted', - user_id: user.id - }); - - const response = await agent.get('/api/inbox'); - - expect(response.status).toBe(200); - expect(response.body.length).toBe(1); // Only 'added' items should be returned - expect(response.body[0].id).toBe(addedItem.id); - expect(response.body[0].status).toBe('added'); - }); - }); - - describe('GET /api/tags - No Tags Scenario', () => { - it('should return empty array when no tags exist', async () => { - const response = await agent.get('/api/tags'); - - expect(response.status).toBe(200); - expect(Array.isArray(response.body)).toBe(true); - expect(response.body.length).toBe(0); - }); - - it('should not affect inbox functionality when tags endpoint returns empty', async () => { - // Create inbox item - await InboxItem.create({ - content: 'Test item', - status: 'added', - user_id: user.id - }); - - // Verify tags endpoint returns empty - const tagsResponse = await agent.get('/api/tags'); - expect(tagsResponse.status).toBe(200); - expect(tagsResponse.body.length).toBe(0); - - // Verify inbox still works - const inboxResponse = await agent.get('/api/inbox'); - expect(inboxResponse.status).toBe(200); - expect(inboxResponse.body.length).toBe(1); - expect(inboxResponse.body[0].content).toBe('Test item'); - }); - }); - - describe('POST /api/inbox - No Tags Scenario', () => { - it('should create inbox items successfully when no tags exist', async () => { - const inboxData = { - content: 'New inbox item without tags', - source: 'web' - }; - - const response = await agent - .post('/api/inbox') - .send(inboxData); - - expect(response.status).toBe(201); - expect(response.body.content).toBe(inboxData.content); - expect(response.body.source).toBe(inboxData.source); - expect(response.body.status).toBe('added'); - expect(response.body.user_id).toBe(user.id); - }); - - it('should handle multiple inbox items creation when no tags exist', async () => { - const items = [ - { content: 'First item', source: 'web' }, - { content: 'Second item', source: 'telegram' }, - { content: 'Third item', source: 'api' } - ]; - - for (const item of items) { - const response = await agent - .post('/api/inbox') - .send(item); - - expect(response.status).toBe(201); - expect(response.body.content).toBe(item.content); - expect(response.body.source).toBe(item.source); - } - - // Verify all items are retrievable - const getResponse = await agent.get('/api/inbox'); - expect(getResponse.status).toBe(200); - expect(getResponse.body.length).toBe(3); - }); - }); - - describe('PATCH /api/inbox/:id - No Tags Scenario', () => { - let inboxItem; + let user, agent; beforeEach(async () => { - inboxItem = await InboxItem.create({ - content: 'Original content', - status: 'added', - user_id: user.id - }); + user = await createTestUser({ + email: 'test@example.com', + }); + + // Create authenticated agent + agent = request.agent(app); + await agent.post('/api/login').send({ + email: 'test@example.com', + password: 'password123', + }); + + // Ensure no tags exist for this user (clean slate) + await Tag.destroy({ where: { user_id: user.id } }); }); - it('should update inbox items when no tags exist', async () => { - const updateData = { - content: 'Updated content without tags', - status: 'processed' - }; + describe('GET /api/inbox - No Tags Scenario', () => { + it('should return empty inbox when no items exist and no tags exist', async () => { + const response = await agent.get('/api/inbox'); - const response = await agent - .patch(`/api/inbox/${inboxItem.id}`) - .send(updateData); + expect(response.status).toBe(200); + expect(Array.isArray(response.body)).toBe(true); + expect(response.body.length).toBe(0); + }); - expect(response.status).toBe(200); - expect(response.body.content).toBe(updateData.content); - expect(response.body.status).toBe(updateData.status); - }); - }); + it('should return inbox items even when no tags exist in system', async () => { + // Create inbox items without any tags in the system + const inboxItem1 = await InboxItem.create({ + content: 'Test item without tags', + status: 'added', + user_id: user.id, + }); - describe('PATCH /api/inbox/:id/process - No Tags Scenario', () => { - let inboxItem; + const inboxItem2 = await InboxItem.create({ + content: 'Another item without tags', + status: 'added', + user_id: user.id, + }); - beforeEach(async () => { - inboxItem = await InboxItem.create({ - content: 'Item to process', - status: 'added', - user_id: user.id - }); + const response = await agent.get('/api/inbox'); + + expect(response.status).toBe(200); + expect(Array.isArray(response.body)).toBe(true); + expect(response.body.length).toBe(2); + expect(response.body.map((item) => item.id)).toContain( + inboxItem1.id + ); + expect(response.body.map((item) => item.id)).toContain( + inboxItem2.id + ); + expect(response.body[0].content).toBeDefined(); + expect(response.body[0].status).toBe('added'); + expect(response.body[0].user_id).toBe(user.id); + }); + + it('should handle mixed inbox items when no tags exist', async () => { + // Create inbox items with different statuses + const addedItem = await InboxItem.create({ + content: 'Added item', + status: 'added', + user_id: user.id, + }); + + await InboxItem.create({ + content: 'Processed item', + status: 'processed', + user_id: user.id, + }); + + await InboxItem.create({ + content: 'Deleted item', + status: 'deleted', + user_id: user.id, + }); + + const response = await agent.get('/api/inbox'); + + expect(response.status).toBe(200); + expect(response.body.length).toBe(1); // Only 'added' items should be returned + expect(response.body[0].id).toBe(addedItem.id); + expect(response.body[0].status).toBe('added'); + }); }); - it('should process inbox items when no tags exist', async () => { - const response = await agent.patch(`/api/inbox/${inboxItem.id}/process`); + describe('GET /api/tags - No Tags Scenario', () => { + it('should return empty array when no tags exist', async () => { + const response = await agent.get('/api/tags'); - expect(response.status).toBe(200); - expect(response.body.status).toBe('processed'); - }); - }); + expect(response.status).toBe(200); + expect(Array.isArray(response.body)).toBe(true); + expect(response.body.length).toBe(0); + }); - describe('DELETE /api/inbox/:id - No Tags Scenario', () => { - let inboxItem; + it('should not affect inbox functionality when tags endpoint returns empty', async () => { + // Create inbox item + await InboxItem.create({ + content: 'Test item', + status: 'added', + user_id: user.id, + }); - beforeEach(async () => { - inboxItem = await InboxItem.create({ - content: 'Item to delete', - status: 'added', - user_id: user.id - }); + // Verify tags endpoint returns empty + const tagsResponse = await agent.get('/api/tags'); + expect(tagsResponse.status).toBe(200); + expect(tagsResponse.body.length).toBe(0); + + // Verify inbox still works + const inboxResponse = await agent.get('/api/inbox'); + expect(inboxResponse.status).toBe(200); + expect(inboxResponse.body.length).toBe(1); + expect(inboxResponse.body[0].content).toBe('Test item'); + }); }); - it('should delete inbox items when no tags exist', async () => { - const response = await agent.delete(`/api/inbox/${inboxItem.id}`); + describe('POST /api/inbox - No Tags Scenario', () => { + it('should create inbox items successfully when no tags exist', async () => { + const inboxData = { + content: 'New inbox item without tags', + source: 'web', + }; - expect(response.status).toBe(200); - expect(response.body.message).toBe('Inbox item successfully deleted'); + const response = await agent.post('/api/inbox').send(inboxData); - // Verify item status is updated to deleted - const deletedItem = await InboxItem.findByPk(inboxItem.id); - expect(deletedItem).not.toBeNull(); - expect(deletedItem.status).toBe('deleted'); - }); - }); + expect(response.status).toBe(201); + expect(response.body.content).toBe(inboxData.content); + expect(response.body.source).toBe(inboxData.source); + expect(response.body.status).toBe('added'); + expect(response.body.user_id).toBe(user.id); + }); - describe('Full Workflow - No Tags Scenario', () => { - it('should support complete inbox workflow without any tags in system', async () => { - // Step 1: Verify no tags exist - const tagsResponse = await agent.get('/api/tags'); - expect(tagsResponse.status).toBe(200); - expect(tagsResponse.body.length).toBe(0); + it('should handle multiple inbox items creation when no tags exist', async () => { + const items = [ + { content: 'First item', source: 'web' }, + { content: 'Second item', source: 'telegram' }, + { content: 'Third item', source: 'api' }, + ]; - // Step 2: Create inbox item - const createResponse = await agent - .post('/api/inbox') - .send({ content: 'Complete workflow test', source: 'web' }); - expect(createResponse.status).toBe(201); - const itemId = createResponse.body.id; + for (const item of items) { + const response = await agent.post('/api/inbox').send(item); - // Step 3: Retrieve inbox items - const getResponse = await agent.get('/api/inbox'); - expect(getResponse.status).toBe(200); - expect(getResponse.body.length).toBe(1); - expect(getResponse.body[0].id).toBe(itemId); + expect(response.status).toBe(201); + expect(response.body.content).toBe(item.content); + expect(response.body.source).toBe(item.source); + } - // Step 4: Update inbox item - const updateResponse = await agent - .patch(`/api/inbox/${itemId}`) - .send({ content: 'Updated workflow test' }); - expect(updateResponse.status).toBe(200); - expect(updateResponse.body.content).toBe('Updated workflow test'); - - // Step 5: Process inbox item - const processResponse = await agent.patch(`/api/inbox/${itemId}/process`); - expect(processResponse.status).toBe(200); - expect(processResponse.body.status).toBe('processed'); - - // Step 6: Verify processed item is not in main inbox list - const finalGetResponse = await agent.get('/api/inbox'); - expect(finalGetResponse.status).toBe(200); - expect(finalGetResponse.body.length).toBe(0); // Processed items don't appear in inbox + // Verify all items are retrievable + const getResponse = await agent.get('/api/inbox'); + expect(getResponse.status).toBe(200); + expect(getResponse.body.length).toBe(3); + }); }); - it('should handle concurrent operations when no tags exist', async () => { - // Create multiple items concurrently - const createPromises = Array.from({ length: 5 }, (_, i) => - agent.post('/api/inbox').send({ - content: `Concurrent item ${i + 1}`, - source: 'test' - }) - ); + describe('PATCH /api/inbox/:id - No Tags Scenario', () => { + let inboxItem; - const createResponses = await Promise.all(createPromises); - - // All should succeed - createResponses.forEach(response => { - expect(response.status).toBe(201); - }); + beforeEach(async () => { + inboxItem = await InboxItem.create({ + content: 'Original content', + status: 'added', + user_id: user.id, + }); + }); - // Verify all items are retrievable - const getResponse = await agent.get('/api/inbox'); - expect(getResponse.status).toBe(200); - expect(getResponse.body.length).toBe(5); + it('should update inbox items when no tags exist', async () => { + const updateData = { + content: 'Updated content without tags', + status: 'processed', + }; - // Process all items concurrently - const itemIds = createResponses.map(response => response.body.id); - const processPromises = itemIds.map(id => - agent.patch(`/api/inbox/${id}/process`) - ); + const response = await agent + .patch(`/api/inbox/${inboxItem.id}`) + .send(updateData); - const processResponses = await Promise.all(processPromises); - - // All should succeed - processResponses.forEach(response => { - expect(response.status).toBe(200); - expect(response.body.status).toBe('processed'); - }); - - // Verify no items remain in inbox - const finalGetResponse = await agent.get('/api/inbox'); - expect(finalGetResponse.status).toBe(200); - expect(finalGetResponse.body.length).toBe(0); - }); - }); - - describe('Error Handling - No Tags Scenario', () => { - it('should handle invalid inbox item operations gracefully when no tags exist', async () => { - // Try to get non-existent item - const getResponse = await agent.get('/api/inbox/999999'); - expect(getResponse.status).toBe(404); - expect(getResponse.body.error).toBe('Inbox item not found.'); - - // Try to update non-existent item - const updateResponse = await agent - .patch('/api/inbox/999999') - .send({ content: 'Updated' }); - expect(updateResponse.status).toBe(404); - expect(updateResponse.body.error).toBe('Inbox item not found.'); - - // Try to process non-existent item - const processResponse = await agent.patch('/api/inbox/999999/process'); - expect(processResponse.status).toBe(404); - expect(processResponse.body.error).toBe('Inbox item not found.'); - - // Try to delete non-existent item - const deleteResponse = await agent.delete('/api/inbox/999999'); - expect(deleteResponse.status).toBe(404); - expect(deleteResponse.body.error).toBe('Inbox item not found.'); + expect(response.status).toBe(200); + expect(response.body.content).toBe(updateData.content); + expect(response.body.status).toBe(updateData.status); + }); }); - it('should validate required fields when creating inbox items (no tags scenario)', async () => { - // Try to create item without content - const response = await agent - .post('/api/inbox') - .send({}); + describe('PATCH /api/inbox/:id/process - No Tags Scenario', () => { + let inboxItem; - expect(response.status).toBe(400); - expect(response.body.error).toBe('Content is required'); + beforeEach(async () => { + inboxItem = await InboxItem.create({ + content: 'Item to process', + status: 'added', + user_id: user.id, + }); + }); + + it('should process inbox items when no tags exist', async () => { + const response = await agent.patch( + `/api/inbox/${inboxItem.id}/process` + ); + + expect(response.status).toBe(200); + expect(response.body.status).toBe('processed'); + }); }); - }); -}); \ No newline at end of file + + describe('DELETE /api/inbox/:id - No Tags Scenario', () => { + let inboxItem; + + beforeEach(async () => { + inboxItem = await InboxItem.create({ + content: 'Item to delete', + status: 'added', + user_id: user.id, + }); + }); + + it('should delete inbox items when no tags exist', async () => { + const response = await agent.delete(`/api/inbox/${inboxItem.id}`); + + expect(response.status).toBe(200); + expect(response.body.message).toBe( + 'Inbox item successfully deleted' + ); + + // Verify item status is updated to deleted + const deletedItem = await InboxItem.findByPk(inboxItem.id); + expect(deletedItem).not.toBeNull(); + expect(deletedItem.status).toBe('deleted'); + }); + }); + + describe('Full Workflow - No Tags Scenario', () => { + it('should support complete inbox workflow without any tags in system', async () => { + // Step 1: Verify no tags exist + const tagsResponse = await agent.get('/api/tags'); + expect(tagsResponse.status).toBe(200); + expect(tagsResponse.body.length).toBe(0); + + // Step 2: Create inbox item + const createResponse = await agent + .post('/api/inbox') + .send({ content: 'Complete workflow test', source: 'web' }); + expect(createResponse.status).toBe(201); + const itemId = createResponse.body.id; + + // Step 3: Retrieve inbox items + const getResponse = await agent.get('/api/inbox'); + expect(getResponse.status).toBe(200); + expect(getResponse.body.length).toBe(1); + expect(getResponse.body[0].id).toBe(itemId); + + // Step 4: Update inbox item + const updateResponse = await agent + .patch(`/api/inbox/${itemId}`) + .send({ content: 'Updated workflow test' }); + expect(updateResponse.status).toBe(200); + expect(updateResponse.body.content).toBe('Updated workflow test'); + + // Step 5: Process inbox item + const processResponse = await agent.patch( + `/api/inbox/${itemId}/process` + ); + expect(processResponse.status).toBe(200); + expect(processResponse.body.status).toBe('processed'); + + // Step 6: Verify processed item is not in main inbox list + const finalGetResponse = await agent.get('/api/inbox'); + expect(finalGetResponse.status).toBe(200); + expect(finalGetResponse.body.length).toBe(0); // Processed items don't appear in inbox + }); + + it('should handle concurrent operations when no tags exist', async () => { + // Create multiple items concurrently + const createPromises = Array.from({ length: 5 }, (_, i) => + agent.post('/api/inbox').send({ + content: `Concurrent item ${i + 1}`, + source: 'test', + }) + ); + + const createResponses = await Promise.all(createPromises); + + // All should succeed + createResponses.forEach((response) => { + expect(response.status).toBe(201); + }); + + // Verify all items are retrievable + const getResponse = await agent.get('/api/inbox'); + expect(getResponse.status).toBe(200); + expect(getResponse.body.length).toBe(5); + + // Process all items concurrently + const itemIds = createResponses.map((response) => response.body.id); + const processPromises = itemIds.map((id) => + agent.patch(`/api/inbox/${id}/process`) + ); + + const processResponses = await Promise.all(processPromises); + + // All should succeed + processResponses.forEach((response) => { + expect(response.status).toBe(200); + expect(response.body.status).toBe('processed'); + }); + + // Verify no items remain in inbox + const finalGetResponse = await agent.get('/api/inbox'); + expect(finalGetResponse.status).toBe(200); + expect(finalGetResponse.body.length).toBe(0); + }); + }); + + describe('Error Handling - No Tags Scenario', () => { + it('should handle invalid inbox item operations gracefully when no tags exist', async () => { + // Try to get non-existent item + const getResponse = await agent.get('/api/inbox/999999'); + expect(getResponse.status).toBe(404); + expect(getResponse.body.error).toBe('Inbox item not found.'); + + // Try to update non-existent item + const updateResponse = await agent + .patch('/api/inbox/999999') + .send({ content: 'Updated' }); + expect(updateResponse.status).toBe(404); + expect(updateResponse.body.error).toBe('Inbox item not found.'); + + // Try to process non-existent item + const processResponse = await agent.patch( + '/api/inbox/999999/process' + ); + expect(processResponse.status).toBe(404); + expect(processResponse.body.error).toBe('Inbox item not found.'); + + // Try to delete non-existent item + const deleteResponse = await agent.delete('/api/inbox/999999'); + expect(deleteResponse.status).toBe(404); + expect(deleteResponse.body.error).toBe('Inbox item not found.'); + }); + + it('should validate required fields when creating inbox items (no tags scenario)', async () => { + // Try to create item without content + const response = await agent.post('/api/inbox').send({}); + + expect(response.status).toBe(400); + expect(response.body.error).toBe('Content is required'); + }); + }); +}); diff --git a/backend/tests/integration/inbox.test.js b/backend/tests/integration/inbox.test.js index cc7d6b5..0e8d52e 100644 --- a/backend/tests/integration/inbox.test.js +++ b/backend/tests/integration/inbox.test.js @@ -4,272 +4,276 @@ const { InboxItem, User } = require('../../models'); const { createTestUser } = require('../helpers/testUtils'); describe('Inbox Routes', () => { - let user, agent; - - beforeEach(async () => { - user = await createTestUser({ - email: 'test@example.com' - }); - - // Create authenticated agent - agent = request.agent(app); - await agent - .post('/api/login') - .send({ - email: 'test@example.com', - password: 'password123' - }); - }); - - describe('POST /api/inbox', () => { - it('should create a new inbox item', async () => { - const inboxData = { - content: 'Remember to buy groceries', - source: 'web' - }; - - const response = await agent - .post('/api/inbox') - .send(inboxData); - - expect(response.status).toBe(201); - expect(response.body.content).toBe(inboxData.content); - expect(response.body.source).toBe(inboxData.source); - expect(response.body.status).toBe('added'); - expect(response.body.user_id).toBe(user.id); - }); - - it('should require authentication', async () => { - const inboxData = { - content: 'Test content' - }; - - const response = await request(app) - .post('/api/inbox') - .send(inboxData); - - expect(response.status).toBe(401); - expect(response.body.error).toBe('Authentication required'); - }); - - it('should require content', async () => { - const inboxData = {}; - - const response = await agent - .post('/api/inbox') - .send(inboxData); - - expect(response.status).toBe(400); - expect(response.body.error).toBe('Content is required'); - }); - }); - - describe('GET /api/inbox', () => { - let inboxItem1, inboxItem2; + let user, agent; beforeEach(async () => { - inboxItem1 = await InboxItem.create({ - content: 'First item', - status: 'added', - user_id: user.id - }); + user = await createTestUser({ + email: 'test@example.com', + }); - inboxItem2 = await InboxItem.create({ - content: 'Second item', - status: 'processed', - user_id: user.id - }); + // Create authenticated agent + agent = request.agent(app); + await agent.post('/api/login').send({ + email: 'test@example.com', + password: 'password123', + }); }); - it('should get all user inbox items', async () => { - const response = await agent.get('/api/inbox'); + describe('POST /api/inbox', () => { + it('should create a new inbox item', async () => { + const inboxData = { + content: 'Remember to buy groceries', + source: 'web', + }; - expect(response.status).toBe(200); - expect(Array.isArray(response.body)).toBe(true); - expect(response.body.length).toBe(1); // Only items with status 'added' are returned - expect(response.body.map(i => i.id)).toContain(inboxItem1.id); + const response = await agent.post('/api/inbox').send(inboxData); + + expect(response.status).toBe(201); + expect(response.body.content).toBe(inboxData.content); + expect(response.body.source).toBe(inboxData.source); + expect(response.body.status).toBe('added'); + expect(response.body.user_id).toBe(user.id); + }); + + it('should require authentication', async () => { + const inboxData = { + content: 'Test content', + }; + + const response = await request(app) + .post('/api/inbox') + .send(inboxData); + + expect(response.status).toBe(401); + expect(response.body.error).toBe('Authentication required'); + }); + + it('should require content', async () => { + const inboxData = {}; + + const response = await agent.post('/api/inbox').send(inboxData); + + expect(response.status).toBe(400); + expect(response.body.error).toBe('Content is required'); + }); }); - it('should only return items with added status', async () => { - const response = await agent.get('/api/inbox'); + describe('GET /api/inbox', () => { + let inboxItem1, inboxItem2; - expect(response.status).toBe(200); - expect(response.body.length).toBe(1); - expect(response.body[0].id).toBe(inboxItem1.id); - expect(response.body[0].status).toBe('added'); + beforeEach(async () => { + inboxItem1 = await InboxItem.create({ + content: 'First item', + status: 'added', + user_id: user.id, + }); + + inboxItem2 = await InboxItem.create({ + content: 'Second item', + status: 'processed', + user_id: user.id, + }); + }); + + it('should get all user inbox items', async () => { + const response = await agent.get('/api/inbox'); + + expect(response.status).toBe(200); + expect(Array.isArray(response.body)).toBe(true); + expect(response.body.length).toBe(1); // Only items with status 'added' are returned + expect(response.body.map((i) => i.id)).toContain(inboxItem1.id); + }); + + it('should only return items with added status', async () => { + const response = await agent.get('/api/inbox'); + + expect(response.status).toBe(200); + expect(response.body.length).toBe(1); + expect(response.body[0].id).toBe(inboxItem1.id); + expect(response.body[0].status).toBe('added'); + }); + + it('should require authentication', async () => { + const response = await request(app).get('/api/inbox'); + + expect(response.status).toBe(401); + expect(response.body.error).toBe('Authentication required'); + }); }); - it('should require authentication', async () => { - const response = await request(app).get('/api/inbox'); + describe('GET /api/inbox/:id', () => { + let inboxItem; - expect(response.status).toBe(401); - expect(response.body.error).toBe('Authentication required'); - }); - }); + beforeEach(async () => { + inboxItem = await InboxItem.create({ + content: 'Test content', + user_id: user.id, + }); + }); - describe('GET /api/inbox/:id', () => { - let inboxItem; + it('should get inbox item by id', async () => { + const response = await agent.get(`/api/inbox/${inboxItem.id}`); - beforeEach(async () => { - inboxItem = await InboxItem.create({ - content: 'Test content', - user_id: user.id - }); + expect(response.status).toBe(200); + expect(response.body.id).toBe(inboxItem.id); + expect(response.body.content).toBe(inboxItem.content); + }); + + it('should return 404 for non-existent inbox item', async () => { + const response = await agent.get('/api/inbox/999999'); + + expect(response.status).toBe(404); + expect(response.body.error).toBe('Inbox item not found.'); + }); + + it("should not allow access to other user's inbox items", async () => { + const bcrypt = require('bcrypt'); + const otherUser = await User.create({ + email: 'other@example.com', + password_digest: await bcrypt.hash('password123', 10), + }); + + const otherInboxItem = await InboxItem.create({ + content: 'Other content', + user_id: otherUser.id, + }); + + const response = await agent.get(`/api/inbox/${otherInboxItem.id}`); + + expect(response.status).toBe(404); + expect(response.body.error).toBe('Inbox item not found.'); + }); + + it('should require authentication', async () => { + const response = await request(app).get( + `/api/inbox/${inboxItem.id}` + ); + + expect(response.status).toBe(401); + expect(response.body.error).toBe('Authentication required'); + }); }); - it('should get inbox item by id', async () => { - const response = await agent.get(`/api/inbox/${inboxItem.id}`); + describe('PATCH /api/inbox/:id', () => { + let inboxItem; - expect(response.status).toBe(200); - expect(response.body.id).toBe(inboxItem.id); - expect(response.body.content).toBe(inboxItem.content); + beforeEach(async () => { + inboxItem = await InboxItem.create({ + content: 'Test content', + status: 'added', + user_id: user.id, + }); + }); + + it('should update inbox item', async () => { + const updateData = { + content: 'Updated content', + status: 'processed', + }; + + const response = await agent + .patch(`/api/inbox/${inboxItem.id}`) + .send(updateData); + + expect(response.status).toBe(200); + expect(response.body.content).toBe(updateData.content); + expect(response.body.status).toBe(updateData.status); + }); + + it('should return 404 for non-existent inbox item', async () => { + const response = await agent + .patch('/api/inbox/999999') + .send({ content: 'Updated' }); + + expect(response.status).toBe(404); + expect(response.body.error).toBe('Inbox item not found.'); + }); + + it('should require authentication', async () => { + const response = await request(app) + .patch(`/api/inbox/${inboxItem.id}`) + .send({ content: 'Updated' }); + + expect(response.status).toBe(401); + expect(response.body.error).toBe('Authentication required'); + }); }); - it('should return 404 for non-existent inbox item', async () => { - const response = await agent.get('/api/inbox/999999'); + describe('DELETE /api/inbox/:id', () => { + let inboxItem; - expect(response.status).toBe(404); - expect(response.body.error).toBe('Inbox item not found.'); + beforeEach(async () => { + inboxItem = await InboxItem.create({ + content: 'Test content', + user_id: user.id, + }); + }); + + it('should delete inbox item', async () => { + const response = await agent.delete(`/api/inbox/${inboxItem.id}`); + + expect(response.status).toBe(200); + expect(response.body.message).toBe( + 'Inbox item successfully deleted' + ); + + // Verify inbox item status is updated to deleted + const deletedItem = await InboxItem.findByPk(inboxItem.id); + expect(deletedItem).not.toBeNull(); + expect(deletedItem.status).toBe('deleted'); + }); + + it('should return 404 for non-existent inbox item', async () => { + const response = await agent.delete('/api/inbox/999999'); + + expect(response.status).toBe(404); + expect(response.body.error).toBe('Inbox item not found.'); + }); + + it('should require authentication', async () => { + const response = await request(app).delete( + `/api/inbox/${inboxItem.id}` + ); + + expect(response.status).toBe(401); + expect(response.body.error).toBe('Authentication required'); + }); }); - it('should not allow access to other user\'s inbox items', async () => { - const bcrypt = require('bcrypt'); - const otherUser = await User.create({ - email: 'other@example.com', - password_digest: await bcrypt.hash('password123', 10) - }); + describe('PATCH /api/inbox/:id/process', () => { + let inboxItem; - const otherInboxItem = await InboxItem.create({ - content: 'Other content', - user_id: otherUser.id - }); + beforeEach(async () => { + inboxItem = await InboxItem.create({ + content: 'Test content', + status: 'added', + user_id: user.id, + }); + }); - const response = await agent.get(`/api/inbox/${otherInboxItem.id}`); + it('should process inbox item', async () => { + const response = await agent.patch( + `/api/inbox/${inboxItem.id}/process` + ); - expect(response.status).toBe(404); - expect(response.body.error).toBe('Inbox item not found.'); + expect(response.status).toBe(200); + expect(response.body.status).toBe('processed'); + }); + + it('should return 404 for non-existent inbox item', async () => { + const response = await agent.patch('/api/inbox/999999/process'); + + expect(response.status).toBe(404); + expect(response.body.error).toBe('Inbox item not found.'); + }); + + it('should require authentication', async () => { + const response = await request(app).patch( + `/api/inbox/${inboxItem.id}/process` + ); + + expect(response.status).toBe(401); + expect(response.body.error).toBe('Authentication required'); + }); }); - - it('should require authentication', async () => { - const response = await request(app).get(`/api/inbox/${inboxItem.id}`); - - expect(response.status).toBe(401); - expect(response.body.error).toBe('Authentication required'); - }); - }); - - describe('PATCH /api/inbox/:id', () => { - let inboxItem; - - beforeEach(async () => { - inboxItem = await InboxItem.create({ - content: 'Test content', - status: 'added', - user_id: user.id - }); - }); - - it('should update inbox item', async () => { - const updateData = { - content: 'Updated content', - status: 'processed' - }; - - const response = await agent - .patch(`/api/inbox/${inboxItem.id}`) - .send(updateData); - - expect(response.status).toBe(200); - expect(response.body.content).toBe(updateData.content); - expect(response.body.status).toBe(updateData.status); - }); - - it('should return 404 for non-existent inbox item', async () => { - const response = await agent - .patch('/api/inbox/999999') - .send({ content: 'Updated' }); - - expect(response.status).toBe(404); - expect(response.body.error).toBe('Inbox item not found.'); - }); - - it('should require authentication', async () => { - const response = await request(app) - .patch(`/api/inbox/${inboxItem.id}`) - .send({ content: 'Updated' }); - - expect(response.status).toBe(401); - expect(response.body.error).toBe('Authentication required'); - }); - }); - - describe('DELETE /api/inbox/:id', () => { - let inboxItem; - - beforeEach(async () => { - inboxItem = await InboxItem.create({ - content: 'Test content', - user_id: user.id - }); - }); - - it('should delete inbox item', async () => { - const response = await agent.delete(`/api/inbox/${inboxItem.id}`); - - expect(response.status).toBe(200); - expect(response.body.message).toBe('Inbox item successfully deleted'); - - // Verify inbox item status is updated to deleted - const deletedItem = await InboxItem.findByPk(inboxItem.id); - expect(deletedItem).not.toBeNull(); - expect(deletedItem.status).toBe('deleted'); - }); - - it('should return 404 for non-existent inbox item', async () => { - const response = await agent.delete('/api/inbox/999999'); - - expect(response.status).toBe(404); - expect(response.body.error).toBe('Inbox item not found.'); - }); - - it('should require authentication', async () => { - const response = await request(app).delete(`/api/inbox/${inboxItem.id}`); - - expect(response.status).toBe(401); - expect(response.body.error).toBe('Authentication required'); - }); - }); - - describe('PATCH /api/inbox/:id/process', () => { - let inboxItem; - - beforeEach(async () => { - inboxItem = await InboxItem.create({ - content: 'Test content', - status: 'added', - user_id: user.id - }); - }); - - it('should process inbox item', async () => { - const response = await agent.patch(`/api/inbox/${inboxItem.id}/process`); - - expect(response.status).toBe(200); - expect(response.body.status).toBe('processed'); - }); - - it('should return 404 for non-existent inbox item', async () => { - const response = await agent.patch('/api/inbox/999999/process'); - - expect(response.status).toBe(404); - expect(response.body.error).toBe('Inbox item not found.'); - }); - - it('should require authentication', async () => { - const response = await request(app).patch(`/api/inbox/${inboxItem.id}/process`); - - expect(response.status).toBe(401); - expect(response.body.error).toBe('Authentication required'); - }); - }); -}); \ No newline at end of file +}); diff --git a/backend/tests/integration/notes.test.js b/backend/tests/integration/notes.test.js index 7ac001b..a3896f0 100644 --- a/backend/tests/integration/notes.test.js +++ b/backend/tests/integration/notes.test.js @@ -4,305 +4,301 @@ const { Note, User, Project } = require('../../models'); const { createTestUser } = require('../helpers/testUtils'); describe('Notes Routes', () => { - let user, project, agent; - - beforeEach(async () => { - user = await createTestUser({ - email: 'test@example.com' - }); - - project = await Project.create({ - name: 'Test Project', - user_id: user.id - }); - - // Create authenticated agent - agent = request.agent(app); - await agent - .post('/api/login') - .send({ - email: 'test@example.com', - password: 'password123' - }); - }); - - describe('POST /api/note', () => { - it('should create a new note', async () => { - const noteData = { - title: 'Test Note', - content: 'This is a test note content', - project_id: project.id - }; - - const response = await agent - .post('/api/note') - .send(noteData); - - expect(response.status).toBe(201); - expect(response.body.title).toBe(noteData.title); - expect(response.body.content).toBe(noteData.content); - expect(response.body.project_id).toBe(project.id); - expect(response.body.user_id).toBe(user.id); - }); - - it('should create note without project', async () => { - const noteData = { - title: 'Test Note', - content: 'This is a test note content' - }; - - const response = await agent - .post('/api/note') - .send(noteData); - - expect(response.status).toBe(201); - expect(response.body.title).toBe(noteData.title); - expect(response.body.content).toBe(noteData.content); - expect(response.body.project_id).toBeNull(); - expect(response.body.user_id).toBe(user.id); - }); - - it('should require authentication', async () => { - const noteData = { - title: 'Test Note', - content: 'Test content' - }; - - const response = await request(app) - .post('/api/note') - .send(noteData); - - expect(response.status).toBe(401); - expect(response.body.error).toBe('Authentication required'); - }); - }); - - describe('GET /api/notes', () => { - let note1, note2; + let user, project, agent; beforeEach(async () => { - note1 = await Note.create({ - title: 'Note 1', - content: 'First note content', - user_id: user.id, - project_id: project.id - }); + user = await createTestUser({ + email: 'test@example.com', + }); - note2 = await Note.create({ - title: 'Note 2', - content: 'Second note content', - user_id: user.id - }); + project = await Project.create({ + name: 'Test Project', + user_id: user.id, + }); + + // Create authenticated agent + agent = request.agent(app); + await agent.post('/api/login').send({ + email: 'test@example.com', + password: 'password123', + }); }); - it('should get all user notes', async () => { - const response = await agent.get('/api/notes'); + describe('POST /api/note', () => { + it('should create a new note', async () => { + const noteData = { + title: 'Test Note', + content: 'This is a test note content', + project_id: project.id, + }; - expect(response.status).toBe(200); - expect(Array.isArray(response.body)).toBe(true); - expect(response.body.length).toBe(2); - expect(response.body.map(n => n.id)).toContain(note1.id); - expect(response.body.map(n => n.id)).toContain(note2.id); + const response = await agent.post('/api/note').send(noteData); + + expect(response.status).toBe(201); + expect(response.body.title).toBe(noteData.title); + expect(response.body.content).toBe(noteData.content); + expect(response.body.project_id).toBe(project.id); + expect(response.body.user_id).toBe(user.id); + }); + + it('should create note without project', async () => { + const noteData = { + title: 'Test Note', + content: 'This is a test note content', + }; + + const response = await agent.post('/api/note').send(noteData); + + expect(response.status).toBe(201); + expect(response.body.title).toBe(noteData.title); + expect(response.body.content).toBe(noteData.content); + expect(response.body.project_id).toBeNull(); + expect(response.body.user_id).toBe(user.id); + }); + + it('should require authentication', async () => { + const noteData = { + title: 'Test Note', + content: 'Test content', + }; + + const response = await request(app) + .post('/api/note') + .send(noteData); + + expect(response.status).toBe(401); + expect(response.body.error).toBe('Authentication required'); + }); }); - it('should include project information', async () => { - const response = await agent.get('/api/notes'); + describe('GET /api/notes', () => { + let note1, note2; - expect(response.status).toBe(200); - const noteWithProject = response.body.find(n => n.id === note1.id); - expect(noteWithProject.Project).toBeDefined(); - expect(noteWithProject.Project.name).toBe(project.name); + beforeEach(async () => { + note1 = await Note.create({ + title: 'Note 1', + content: 'First note content', + user_id: user.id, + project_id: project.id, + }); + + note2 = await Note.create({ + title: 'Note 2', + content: 'Second note content', + user_id: user.id, + }); + }); + + it('should get all user notes', async () => { + const response = await agent.get('/api/notes'); + + expect(response.status).toBe(200); + expect(Array.isArray(response.body)).toBe(true); + expect(response.body.length).toBe(2); + expect(response.body.map((n) => n.id)).toContain(note1.id); + expect(response.body.map((n) => n.id)).toContain(note2.id); + }); + + it('should include project information', async () => { + const response = await agent.get('/api/notes'); + + expect(response.status).toBe(200); + const noteWithProject = response.body.find( + (n) => n.id === note1.id + ); + expect(noteWithProject.Project).toBeDefined(); + expect(noteWithProject.Project.name).toBe(project.name); + }); + + it('should return all notes when no filter is applied', async () => { + const response = await agent.get('/api/notes'); + + expect(response.status).toBe(200); + expect(response.body.length).toBe(2); + expect(response.body.map((n) => n.id)).toContain(note1.id); + expect(response.body.map((n) => n.id)).toContain(note2.id); + }); + + it('should require authentication', async () => { + const response = await request(app).get('/api/notes'); + + expect(response.status).toBe(401); + expect(response.body.error).toBe('Authentication required'); + }); }); - it('should return all notes when no filter is applied', async () => { - const response = await agent.get('/api/notes'); + describe('GET /api/note/:id', () => { + let note; - expect(response.status).toBe(200); - expect(response.body.length).toBe(2); - expect(response.body.map(n => n.id)).toContain(note1.id); - expect(response.body.map(n => n.id)).toContain(note2.id); + beforeEach(async () => { + note = await Note.create({ + title: 'Test Note', + content: 'Test content', + user_id: user.id, + project_id: project.id, + }); + }); + + it('should get note by id', async () => { + const response = await agent.get(`/api/note/${note.id}`); + + expect(response.status).toBe(200); + expect(response.body.id).toBe(note.id); + expect(response.body.title).toBe(note.title); + expect(response.body.content).toBe(note.content); + }); + + it('should return 404 for non-existent note', async () => { + const response = await agent.get('/api/note/999999'); + + expect(response.status).toBe(404); + expect(response.body.error).toBe('Note not found.'); + }); + + it("should not allow access to other user's notes", async () => { + const bcrypt = require('bcrypt'); + const otherUser = await User.create({ + email: 'other@example.com', + password_digest: await bcrypt.hash('password123', 10), + }); + + const otherNote = await Note.create({ + title: 'Other Note', + user_id: otherUser.id, + }); + + const response = await agent.get(`/api/note/${otherNote.id}`); + + expect(response.status).toBe(404); + expect(response.body.error).toBe('Note not found.'); + }); + + it('should require authentication', async () => { + const response = await request(app).get(`/api/note/${note.id}`); + + expect(response.status).toBe(401); + expect(response.body.error).toBe('Authentication required'); + }); }); - it('should require authentication', async () => { - const response = await request(app).get('/api/notes'); + describe('PATCH /api/note/:id', () => { + let note; - expect(response.status).toBe(401); - expect(response.body.error).toBe('Authentication required'); - }); - }); + beforeEach(async () => { + note = await Note.create({ + title: 'Test Note', + content: 'Test content', + user_id: user.id, + }); + }); - describe('GET /api/note/:id', () => { - let note; + it('should update note', async () => { + const updateData = { + title: 'Updated Note', + content: 'Updated content', + project_id: project.id, + }; - beforeEach(async () => { - note = await Note.create({ - title: 'Test Note', - content: 'Test content', - user_id: user.id, - project_id: project.id - }); + const response = await agent + .patch(`/api/note/${note.id}`) + .send(updateData); + + expect(response.status).toBe(200); + expect(response.body.title).toBe(updateData.title); + expect(response.body.content).toBe(updateData.content); + expect(response.body.project_id).toBe(project.id); + }); + + it('should return 404 for non-existent note', async () => { + const response = await agent + .patch('/api/note/999999') + .send({ title: 'Updated' }); + + expect(response.status).toBe(404); + expect(response.body.error).toBe('Note not found.'); + }); + + it("should not allow updating other user's notes", async () => { + const bcrypt = require('bcrypt'); + const otherUser = await User.create({ + email: 'other@example.com', + password_digest: await bcrypt.hash('password123', 10), + }); + + const otherNote = await Note.create({ + title: 'Other Note', + user_id: otherUser.id, + }); + + const response = await agent + .patch(`/api/note/${otherNote.id}`) + .send({ title: 'Updated' }); + + expect(response.status).toBe(404); + expect(response.body.error).toBe('Note not found.'); + }); + + it('should require authentication', async () => { + const response = await request(app) + .patch(`/api/note/${note.id}`) + .send({ title: 'Updated' }); + + expect(response.status).toBe(401); + expect(response.body.error).toBe('Authentication required'); + }); }); - it('should get note by id', async () => { - const response = await agent.get(`/api/note/${note.id}`); + describe('DELETE /api/note/:id', () => { + let note; - expect(response.status).toBe(200); - expect(response.body.id).toBe(note.id); - expect(response.body.title).toBe(note.title); - expect(response.body.content).toBe(note.content); + beforeEach(async () => { + note = await Note.create({ + title: 'Test Note', + user_id: user.id, + }); + }); + + it('should delete note', async () => { + const response = await agent.delete(`/api/note/${note.id}`); + + expect(response.status).toBe(200); + expect(response.body.message).toBe('Note deleted successfully.'); + + // Verify note is deleted + const deletedNote = await Note.findByPk(note.id); + expect(deletedNote).toBeNull(); + }); + + it('should return 404 for non-existent note', async () => { + const response = await agent.delete('/api/note/999999'); + + expect(response.status).toBe(404); + expect(response.body.error).toBe('Note not found.'); + }); + + it("should not allow deleting other user's notes", async () => { + const bcrypt = require('bcrypt'); + const otherUser = await User.create({ + email: 'other@example.com', + password_digest: await bcrypt.hash('password123', 10), + }); + + const otherNote = await Note.create({ + title: 'Other Note', + user_id: otherUser.id, + }); + + const response = await agent.delete(`/api/note/${otherNote.id}`); + + expect(response.status).toBe(404); + expect(response.body.error).toBe('Note not found.'); + }); + + it('should require authentication', async () => { + const response = await request(app).delete(`/api/note/${note.id}`); + + expect(response.status).toBe(401); + expect(response.body.error).toBe('Authentication required'); + }); }); - - it('should return 404 for non-existent note', async () => { - const response = await agent.get('/api/note/999999'); - - expect(response.status).toBe(404); - expect(response.body.error).toBe('Note not found.'); - }); - - it('should not allow access to other user\'s notes', async () => { - const bcrypt = require('bcrypt'); - const otherUser = await User.create({ - email: 'other@example.com', - password_digest: await bcrypt.hash('password123', 10) - }); - - const otherNote = await Note.create({ - title: 'Other Note', - user_id: otherUser.id - }); - - const response = await agent.get(`/api/note/${otherNote.id}`); - - expect(response.status).toBe(404); - expect(response.body.error).toBe('Note not found.'); - }); - - it('should require authentication', async () => { - const response = await request(app).get(`/api/note/${note.id}`); - - expect(response.status).toBe(401); - expect(response.body.error).toBe('Authentication required'); - }); - }); - - describe('PATCH /api/note/:id', () => { - let note; - - beforeEach(async () => { - note = await Note.create({ - title: 'Test Note', - content: 'Test content', - user_id: user.id - }); - }); - - it('should update note', async () => { - const updateData = { - title: 'Updated Note', - content: 'Updated content', - project_id: project.id - }; - - const response = await agent - .patch(`/api/note/${note.id}`) - .send(updateData); - - expect(response.status).toBe(200); - expect(response.body.title).toBe(updateData.title); - expect(response.body.content).toBe(updateData.content); - expect(response.body.project_id).toBe(project.id); - }); - - it('should return 404 for non-existent note', async () => { - const response = await agent - .patch('/api/note/999999') - .send({ title: 'Updated' }); - - expect(response.status).toBe(404); - expect(response.body.error).toBe('Note not found.'); - }); - - it('should not allow updating other user\'s notes', async () => { - const bcrypt = require('bcrypt'); - const otherUser = await User.create({ - email: 'other@example.com', - password_digest: await bcrypt.hash('password123', 10) - }); - - const otherNote = await Note.create({ - title: 'Other Note', - user_id: otherUser.id - }); - - const response = await agent - .patch(`/api/note/${otherNote.id}`) - .send({ title: 'Updated' }); - - expect(response.status).toBe(404); - expect(response.body.error).toBe('Note not found.'); - }); - - it('should require authentication', async () => { - const response = await request(app) - .patch(`/api/note/${note.id}`) - .send({ title: 'Updated' }); - - expect(response.status).toBe(401); - expect(response.body.error).toBe('Authentication required'); - }); - }); - - describe('DELETE /api/note/:id', () => { - let note; - - beforeEach(async () => { - note = await Note.create({ - title: 'Test Note', - user_id: user.id - }); - }); - - it('should delete note', async () => { - const response = await agent.delete(`/api/note/${note.id}`); - - expect(response.status).toBe(200); - expect(response.body.message).toBe('Note deleted successfully.'); - - // Verify note is deleted - const deletedNote = await Note.findByPk(note.id); - expect(deletedNote).toBeNull(); - }); - - it('should return 404 for non-existent note', async () => { - const response = await agent.delete('/api/note/999999'); - - expect(response.status).toBe(404); - expect(response.body.error).toBe('Note not found.'); - }); - - it('should not allow deleting other user\'s notes', async () => { - const bcrypt = require('bcrypt'); - const otherUser = await User.create({ - email: 'other@example.com', - password_digest: await bcrypt.hash('password123', 10) - }); - - const otherNote = await Note.create({ - title: 'Other Note', - user_id: otherUser.id - }); - - const response = await agent.delete(`/api/note/${otherNote.id}`); - - expect(response.status).toBe(404); - expect(response.body.error).toBe('Note not found.'); - }); - - it('should require authentication', async () => { - const response = await request(app).delete(`/api/note/${note.id}`); - - expect(response.status).toBe(401); - expect(response.body.error).toBe('Authentication required'); - }); - }); -}); \ No newline at end of file +}); diff --git a/backend/tests/integration/projects.test.js b/backend/tests/integration/projects.test.js index 016064a..6a7b666 100644 --- a/backend/tests/integration/projects.test.js +++ b/backend/tests/integration/projects.test.js @@ -4,300 +4,308 @@ const { Project, User, Area } = require('../../models'); const { createTestUser } = require('../helpers/testUtils'); describe('Projects Routes', () => { - let user, area, agent; - - beforeEach(async () => { - user = await createTestUser({ - email: 'test@example.com' - }); - - area = await Area.create({ - name: 'Work', - user_id: user.id - }); - - // Create authenticated agent - agent = request.agent(app); - await agent - .post('/api/login') - .send({ - email: 'test@example.com', - password: 'password123' - }); - }); - - describe('POST /api/project', () => { - it('should create a new project', async () => { - const projectData = { - name: 'Test Project', - description: 'Test Description', - active: true, - pin_to_sidebar: false, - priority: 1, - area_id: area.id - }; - - const response = await agent - .post('/api/project') - .send(projectData); - - expect(response.status).toBe(201); - expect(response.body.name).toBe(projectData.name); - expect(response.body.description).toBe(projectData.description); - expect(response.body.active).toBe(projectData.active); - expect(response.body.pin_to_sidebar).toBe(projectData.pin_to_sidebar); - expect(response.body.priority).toBe(projectData.priority); - expect(response.body.area_id).toBe(area.id); - expect(response.body.user_id).toBe(user.id); - }); - - it('should require authentication', async () => { - const projectData = { - name: 'Test Project' - }; - - const response = await request(app) - .post('/api/project') - .send(projectData); - - expect(response.status).toBe(401); - expect(response.body.error).toBe('Authentication required'); - }); - - it('should require project name', async () => { - const projectData = { - description: 'Project without name' - }; - - const response = await agent - .post('/api/project') - .send(projectData); - - expect(response.status).toBe(400); - }); - }); - - describe('GET /api/projects', () => { - let project1, project2; + let user, area, agent; beforeEach(async () => { - project1 = await Project.create({ - name: 'Project 1', - description: 'First project', - user_id: user.id, - area_id: area.id - }); + user = await createTestUser({ + email: 'test@example.com', + }); - project2 = await Project.create({ - name: 'Project 2', - description: 'Second project', - user_id: user.id - }); + area = await Area.create({ + name: 'Work', + user_id: user.id, + }); + + // Create authenticated agent + agent = request.agent(app); + await agent.post('/api/login').send({ + email: 'test@example.com', + password: 'password123', + }); }); - it('should get all user projects', async () => { - const response = await agent.get('/api/projects'); + describe('POST /api/project', () => { + it('should create a new project', async () => { + const projectData = { + name: 'Test Project', + description: 'Test Description', + active: true, + pin_to_sidebar: false, + priority: 1, + area_id: area.id, + }; - expect(response.status).toBe(200); - expect(response.body.projects).toBeDefined(); - expect(response.body.projects.length).toBe(2); - expect(response.body.projects.map(p => p.id)).toContain(project1.id); - expect(response.body.projects.map(p => p.id)).toContain(project2.id); + const response = await agent.post('/api/project').send(projectData); + + expect(response.status).toBe(201); + expect(response.body.name).toBe(projectData.name); + expect(response.body.description).toBe(projectData.description); + expect(response.body.active).toBe(projectData.active); + expect(response.body.pin_to_sidebar).toBe( + projectData.pin_to_sidebar + ); + expect(response.body.priority).toBe(projectData.priority); + expect(response.body.area_id).toBe(area.id); + expect(response.body.user_id).toBe(user.id); + }); + + it('should require authentication', async () => { + const projectData = { + name: 'Test Project', + }; + + const response = await request(app) + .post('/api/project') + .send(projectData); + + expect(response.status).toBe(401); + expect(response.body.error).toBe('Authentication required'); + }); + + it('should require project name', async () => { + const projectData = { + description: 'Project without name', + }; + + const response = await agent.post('/api/project').send(projectData); + + expect(response.status).toBe(400); + }); }); - it('should include area information', async () => { - const response = await agent.get('/api/projects'); + describe('GET /api/projects', () => { + let project1, project2; - expect(response.status).toBe(200); - const projectWithArea = response.body.projects.find(p => p.id === project1.id); - expect(projectWithArea.Area).toBeDefined(); - expect(projectWithArea.Area.name).toBe(area.name); + beforeEach(async () => { + project1 = await Project.create({ + name: 'Project 1', + description: 'First project', + user_id: user.id, + area_id: area.id, + }); + + project2 = await Project.create({ + name: 'Project 2', + description: 'Second project', + user_id: user.id, + }); + }); + + it('should get all user projects', async () => { + const response = await agent.get('/api/projects'); + + expect(response.status).toBe(200); + expect(response.body.projects).toBeDefined(); + expect(response.body.projects.length).toBe(2); + expect(response.body.projects.map((p) => p.id)).toContain( + project1.id + ); + expect(response.body.projects.map((p) => p.id)).toContain( + project2.id + ); + }); + + it('should include area information', async () => { + const response = await agent.get('/api/projects'); + + expect(response.status).toBe(200); + const projectWithArea = response.body.projects.find( + (p) => p.id === project1.id + ); + expect(projectWithArea.Area).toBeDefined(); + expect(projectWithArea.Area.name).toBe(area.name); + }); + + it('should require authentication', async () => { + const response = await request(app).get('/api/projects'); + + expect(response.status).toBe(401); + expect(response.body.error).toBe('Authentication required'); + }); }); - it('should require authentication', async () => { - const response = await request(app).get('/api/projects'); + describe('GET /api/project/:id', () => { + let project; - expect(response.status).toBe(401); - expect(response.body.error).toBe('Authentication required'); - }); - }); + beforeEach(async () => { + project = await Project.create({ + name: 'Test Project', + description: 'Test Description', + user_id: user.id, + area_id: area.id, + }); + }); - describe('GET /api/project/:id', () => { - let project; + it('should get project by id', async () => { + const response = await agent.get(`/api/project/${project.id}`); - beforeEach(async () => { - project = await Project.create({ - name: 'Test Project', - description: 'Test Description', - user_id: user.id, - area_id: area.id - }); + expect(response.status).toBe(200); + expect(response.body.id).toBe(project.id); + expect(response.body.name).toBe(project.name); + expect(response.body.description).toBe(project.description); + }); + + it('should return 404 for non-existent project', async () => { + const response = await agent.get('/api/project/999999'); + + expect(response.status).toBe(404); + expect(response.body.error).toBe('Project not found'); + }); + + it("should not allow access to other user's projects", async () => { + const bcrypt = require('bcrypt'); + const otherUser = await User.create({ + email: 'other@example.com', + password_digest: await bcrypt.hash('password123', 10), + }); + + const otherProject = await Project.create({ + name: 'Other Project', + user_id: otherUser.id, + }); + + const response = await agent.get(`/api/project/${otherProject.id}`); + + expect(response.status).toBe(404); + expect(response.body.error).toBe('Project not found'); + }); + + it('should require authentication', async () => { + const response = await request(app).get( + `/api/project/${project.id}` + ); + + expect(response.status).toBe(401); + expect(response.body.error).toBe('Authentication required'); + }); }); - it('should get project by id', async () => { - const response = await agent.get(`/api/project/${project.id}`); + describe('PATCH /api/project/:id', () => { + let project; - expect(response.status).toBe(200); - expect(response.body.id).toBe(project.id); - expect(response.body.name).toBe(project.name); - expect(response.body.description).toBe(project.description); + beforeEach(async () => { + project = await Project.create({ + name: 'Test Project', + description: 'Test Description', + active: false, + priority: 0, + user_id: user.id, + }); + }); + + it('should update project', async () => { + const updateData = { + name: 'Updated Project', + description: 'Updated Description', + active: true, + priority: 2, + }; + + const response = await agent + .patch(`/api/project/${project.id}`) + .send(updateData); + + expect(response.status).toBe(200); + expect(response.body.name).toBe(updateData.name); + expect(response.body.description).toBe(updateData.description); + expect(response.body.active).toBe(updateData.active); + expect(response.body.priority).toBe(updateData.priority); + }); + + it('should return 404 for non-existent project', async () => { + const response = await agent + .patch('/api/project/999999') + .send({ name: 'Updated' }); + + expect(response.status).toBe(404); + expect(response.body.error).toBe('Project not found.'); + }); + + it("should not allow updating other user's projects", async () => { + const bcrypt = require('bcrypt'); + const otherUser = await User.create({ + email: 'other@example.com', + password_digest: await bcrypt.hash('password123', 10), + }); + + const otherProject = await Project.create({ + name: 'Other Project', + user_id: otherUser.id, + }); + + const response = await agent + .patch(`/api/project/${otherProject.id}`) + .send({ name: 'Updated' }); + + expect(response.status).toBe(404); + expect(response.body.error).toBe('Project not found.'); + }); + + it('should require authentication', async () => { + const response = await request(app) + .patch(`/api/project/${project.id}`) + .send({ name: 'Updated' }); + + expect(response.status).toBe(401); + expect(response.body.error).toBe('Authentication required'); + }); }); - it('should return 404 for non-existent project', async () => { - const response = await agent.get('/api/project/999999'); + describe('DELETE /api/project/:id', () => { + let project; - expect(response.status).toBe(404); - expect(response.body.error).toBe('Project not found'); + beforeEach(async () => { + project = await Project.create({ + name: 'Test Project', + user_id: user.id, + }); + }); + + it('should delete project', async () => { + const response = await agent.delete(`/api/project/${project.id}`); + + expect(response.status).toBe(200); + expect(response.body.message).toBe('Project successfully deleted'); + + // Verify project is deleted + const deletedProject = await Project.findByPk(project.id); + expect(deletedProject).toBeNull(); + }); + + it('should return 404 for non-existent project', async () => { + const response = await agent.delete('/api/project/999999'); + + expect(response.status).toBe(404); + expect(response.body.error).toBe('Project not found.'); + }); + + it("should not allow deleting other user's projects", async () => { + const bcrypt = require('bcrypt'); + const otherUser = await User.create({ + email: 'other@example.com', + password_digest: await bcrypt.hash('password123', 10), + }); + + const otherProject = await Project.create({ + name: 'Other Project', + user_id: otherUser.id, + }); + + const response = await agent.delete( + `/api/project/${otherProject.id}` + ); + + expect(response.status).toBe(404); + expect(response.body.error).toBe('Project not found.'); + }); + + it('should require authentication', async () => { + const response = await request(app).delete( + `/api/project/${project.id}` + ); + + expect(response.status).toBe(401); + expect(response.body.error).toBe('Authentication required'); + }); }); - - it('should not allow access to other user\'s projects', async () => { - const bcrypt = require('bcrypt'); - const otherUser = await User.create({ - email: 'other@example.com', - password_digest: await bcrypt.hash('password123', 10) - }); - - const otherProject = await Project.create({ - name: 'Other Project', - user_id: otherUser.id - }); - - const response = await agent.get(`/api/project/${otherProject.id}`); - - expect(response.status).toBe(404); - expect(response.body.error).toBe('Project not found'); - }); - - it('should require authentication', async () => { - const response = await request(app).get(`/api/project/${project.id}`); - - expect(response.status).toBe(401); - expect(response.body.error).toBe('Authentication required'); - }); - }); - - describe('PATCH /api/project/:id', () => { - let project; - - beforeEach(async () => { - project = await Project.create({ - name: 'Test Project', - description: 'Test Description', - active: false, - priority: 0, - user_id: user.id - }); - }); - - it('should update project', async () => { - const updateData = { - name: 'Updated Project', - description: 'Updated Description', - active: true, - priority: 2 - }; - - const response = await agent - .patch(`/api/project/${project.id}`) - .send(updateData); - - expect(response.status).toBe(200); - expect(response.body.name).toBe(updateData.name); - expect(response.body.description).toBe(updateData.description); - expect(response.body.active).toBe(updateData.active); - expect(response.body.priority).toBe(updateData.priority); - }); - - it('should return 404 for non-existent project', async () => { - const response = await agent - .patch('/api/project/999999') - .send({ name: 'Updated' }); - - expect(response.status).toBe(404); - expect(response.body.error).toBe('Project not found.'); - }); - - it('should not allow updating other user\'s projects', async () => { - const bcrypt = require('bcrypt'); - const otherUser = await User.create({ - email: 'other@example.com', - password_digest: await bcrypt.hash('password123', 10) - }); - - const otherProject = await Project.create({ - name: 'Other Project', - user_id: otherUser.id - }); - - const response = await agent - .patch(`/api/project/${otherProject.id}`) - .send({ name: 'Updated' }); - - expect(response.status).toBe(404); - expect(response.body.error).toBe('Project not found.'); - }); - - it('should require authentication', async () => { - const response = await request(app) - .patch(`/api/project/${project.id}`) - .send({ name: 'Updated' }); - - expect(response.status).toBe(401); - expect(response.body.error).toBe('Authentication required'); - }); - }); - - describe('DELETE /api/project/:id', () => { - let project; - - beforeEach(async () => { - project = await Project.create({ - name: 'Test Project', - user_id: user.id - }); - }); - - it('should delete project', async () => { - const response = await agent.delete(`/api/project/${project.id}`); - - expect(response.status).toBe(200); - expect(response.body.message).toBe('Project successfully deleted'); - - // Verify project is deleted - const deletedProject = await Project.findByPk(project.id); - expect(deletedProject).toBeNull(); - }); - - it('should return 404 for non-existent project', async () => { - const response = await agent.delete('/api/project/999999'); - - expect(response.status).toBe(404); - expect(response.body.error).toBe('Project not found.'); - }); - - it('should not allow deleting other user\'s projects', async () => { - const bcrypt = require('bcrypt'); - const otherUser = await User.create({ - email: 'other@example.com', - password_digest: await bcrypt.hash('password123', 10) - }); - - const otherProject = await Project.create({ - name: 'Other Project', - user_id: otherUser.id - }); - - const response = await agent.delete(`/api/project/${otherProject.id}`); - - expect(response.status).toBe(404); - expect(response.body.error).toBe('Project not found.'); - }); - - it('should require authentication', async () => { - const response = await request(app).delete(`/api/project/${project.id}`); - - expect(response.status).toBe(401); - expect(response.body.error).toBe('Authentication required'); - }); - }); -}); \ No newline at end of file +}); diff --git a/backend/tests/integration/quotes.test.js b/backend/tests/integration/quotes.test.js index 57b671b..9dcacec 100644 --- a/backend/tests/integration/quotes.test.js +++ b/backend/tests/integration/quotes.test.js @@ -3,166 +3,168 @@ const app = require('../../app'); const { createTestUser } = require('../helpers/testUtils'); describe('Quotes Routes', () => { - let user, agent; + let user, agent; - beforeEach(async () => { - user = await createTestUser({ - email: 'test@example.com' + beforeEach(async () => { + user = await createTestUser({ + email: 'test@example.com', + }); + + // Create authenticated agent + agent = request.agent(app); + await agent.post('/api/login').send({ + email: 'test@example.com', + password: 'password123', + }); }); - // Create authenticated agent - agent = request.agent(app); - await agent - .post('/api/login') - .send({ - email: 'test@example.com', - password: 'password123' - }); - }); + describe('GET /api/quotes/random', () => { + it('should return a random quote', async () => { + const response = await agent.get('/api/quotes/random'); - describe('GET /api/quotes/random', () => { - it('should return a random quote', async () => { - const response = await agent - .get('/api/quotes/random'); + expect(response.status).toBe(200); + expect(response.body).toHaveProperty('quote'); + expect(typeof response.body.quote).toBe('string'); + expect(response.body.quote.length).toBeGreaterThan(0); + }); - expect(response.status).toBe(200); - expect(response.body).toHaveProperty('quote'); - expect(typeof response.body.quote).toBe('string'); - expect(response.body.quote.length).toBeGreaterThan(0); + it('should return different quotes on multiple requests', async () => { + const responses = await Promise.all([ + agent.get('/api/quotes/random'), + agent.get('/api/quotes/random'), + agent.get('/api/quotes/random'), + agent.get('/api/quotes/random'), + agent.get('/api/quotes/random'), + ]); + + // All responses should be successful + responses.forEach((response) => { + expect(response.status).toBe(200); + expect(response.body).toHaveProperty('quote'); + expect(typeof response.body.quote).toBe('string'); + }); + + // With multiple requests, we should get at least some variety + // (though it's possible to get the same quote multiple times due to randomness) + const quotes = responses.map((r) => r.body.quote); + const uniqueQuotes = new Set(quotes); + + // We expect at least 1 unique quote, but likely more + expect(uniqueQuotes.size).toBeGreaterThanOrEqual(1); + }); + + it('should return valid quote structure', async () => { + const response = await agent.get('/api/quotes/random'); + + expect(response.status).toBe(200); + expect(Object.keys(response.body)).toEqual(['quote']); + expect(response.body.quote).toBeTruthy(); + expect(response.body.quote.trim()).toBe(response.body.quote); + }); }); - it('should return different quotes on multiple requests', async () => { - const responses = await Promise.all([ - agent.get('/api/quotes/random'), - agent.get('/api/quotes/random'), - agent.get('/api/quotes/random'), - agent.get('/api/quotes/random'), - agent.get('/api/quotes/random') - ]); + describe('GET /api/quotes', () => { + it('should return all quotes with count', async () => { + const response = await agent.get('/api/quotes'); - // All responses should be successful - responses.forEach(response => { - expect(response.status).toBe(200); - expect(response.body).toHaveProperty('quote'); - expect(typeof response.body.quote).toBe('string'); - }); + expect(response.status).toBe(200); + expect(response.body).toHaveProperty('quotes'); + expect(response.body).toHaveProperty('count'); + expect(Array.isArray(response.body.quotes)).toBe(true); + expect(typeof response.body.count).toBe('number'); + expect(response.body.quotes.length).toBe(response.body.count); + expect(response.body.count).toBeGreaterThan(0); + }); - // With multiple requests, we should get at least some variety - // (though it's possible to get the same quote multiple times due to randomness) - const quotes = responses.map(r => r.body.quote); - const uniqueQuotes = new Set(quotes); - - // We expect at least 1 unique quote, but likely more - expect(uniqueQuotes.size).toBeGreaterThanOrEqual(1); + it('should return valid quote array', async () => { + const response = await agent.get('/api/quotes'); + + expect(response.status).toBe(200); + + // All quotes should be non-empty strings + response.body.quotes.forEach((quote) => { + expect(typeof quote).toBe('string'); + expect(quote.length).toBeGreaterThan(0); + expect(quote.trim()).toBe(quote); + }); + }); + + it('should return consistent data across requests', async () => { + const response1 = await agent.get('/api/quotes'); + const response2 = await agent.get('/api/quotes'); + + expect(response1.status).toBe(200); + expect(response2.status).toBe(200); + + // The quotes array should be the same across requests + expect(response1.body.quotes.length).toBe( + response2.body.quotes.length + ); + expect(response1.body.count).toBe(response2.body.count); + + // Verify the actual content is the same + expect(response1.body.quotes).toEqual(response2.body.quotes); + }); + + it('should return expected quote count', async () => { + const response = await agent.get('/api/quotes'); + + expect(response.status).toBe(200); + + // Based on the configuration, we expect 20 quotes, but allow for fallback quotes + expect(response.body.count).toBeGreaterThanOrEqual(5); + expect(response.body.quotes.length).toBe(response.body.count); + }); + + it('should contain productivity-focused quotes', async () => { + const response = await agent.get('/api/quotes'); + + expect(response.status).toBe(200); + + // Look for some productivity-related keywords in the quotes + const allQuotesText = response.body.quotes.join(' ').toLowerCase(); + + // These are common themes in productivity quotes + const productivityKeywords = [ + 'progress', + 'task', + 'goal', + 'focus', + 'accomplish', + 'success', + 'work', + 'effort', + 'achieve', + 'time', + ]; + + // At least some quotes should contain productivity-related terms + const hasProductivityContent = productivityKeywords.some( + (keyword) => allQuotesText.includes(keyword) + ); + + expect(hasProductivityContent).toBe(true); + }); }); - it('should return valid quote structure', async () => { - const response = await agent - .get('/api/quotes/random'); + describe('Quote randomness and consistency', () => { + it('should have random quotes that are part of the full quote set', async () => { + // Get all quotes first + const allQuotesResponse = await agent.get('/api/quotes'); + const allQuotes = allQuotesResponse.body.quotes; - expect(response.status).toBe(200); - expect(Object.keys(response.body)).toEqual(['quote']); - expect(response.body.quote).toBeTruthy(); - expect(response.body.quote.trim()).toBe(response.body.quote); + // Get several random quotes + const randomQuoteResponses = await Promise.all([ + agent.get('/api/quotes/random'), + agent.get('/api/quotes/random'), + agent.get('/api/quotes/random'), + ]); + + // Each random quote should be from the full set + randomQuoteResponses.forEach((response) => { + expect(response.status).toBe(200); + expect(allQuotes).toContain(response.body.quote); + }); + }); }); - }); - - describe('GET /api/quotes', () => { - it('should return all quotes with count', async () => { - const response = await agent - .get('/api/quotes'); - - expect(response.status).toBe(200); - expect(response.body).toHaveProperty('quotes'); - expect(response.body).toHaveProperty('count'); - expect(Array.isArray(response.body.quotes)).toBe(true); - expect(typeof response.body.count).toBe('number'); - expect(response.body.quotes.length).toBe(response.body.count); - expect(response.body.count).toBeGreaterThan(0); - }); - - it('should return valid quote array', async () => { - const response = await agent - .get('/api/quotes'); - - expect(response.status).toBe(200); - - // All quotes should be non-empty strings - response.body.quotes.forEach(quote => { - expect(typeof quote).toBe('string'); - expect(quote.length).toBeGreaterThan(0); - expect(quote.trim()).toBe(quote); - }); - }); - - it('should return consistent data across requests', async () => { - const response1 = await agent.get('/api/quotes'); - const response2 = await agent.get('/api/quotes'); - - expect(response1.status).toBe(200); - expect(response2.status).toBe(200); - - // The quotes array should be the same across requests - expect(response1.body.quotes.length).toBe(response2.body.quotes.length); - expect(response1.body.count).toBe(response2.body.count); - - // Verify the actual content is the same - expect(response1.body.quotes).toEqual(response2.body.quotes); - }); - - it('should return expected quote count', async () => { - const response = await agent - .get('/api/quotes'); - - expect(response.status).toBe(200); - - // Based on the configuration, we expect 20 quotes, but allow for fallback quotes - expect(response.body.count).toBeGreaterThanOrEqual(5); - expect(response.body.quotes.length).toBe(response.body.count); - }); - - it('should contain productivity-focused quotes', async () => { - const response = await agent - .get('/api/quotes'); - - expect(response.status).toBe(200); - - // Look for some productivity-related keywords in the quotes - const allQuotesText = response.body.quotes.join(' ').toLowerCase(); - - // These are common themes in productivity quotes - const productivityKeywords = [ - 'progress', 'task', 'goal', 'focus', 'accomplish', - 'success', 'work', 'effort', 'achieve', 'time' - ]; - - // At least some quotes should contain productivity-related terms - const hasProductivityContent = productivityKeywords.some(keyword => - allQuotesText.includes(keyword) - ); - - expect(hasProductivityContent).toBe(true); - }); - }); - - describe('Quote randomness and consistency', () => { - it('should have random quotes that are part of the full quote set', async () => { - // Get all quotes first - const allQuotesResponse = await agent.get('/api/quotes'); - const allQuotes = allQuotesResponse.body.quotes; - - // Get several random quotes - const randomQuoteResponses = await Promise.all([ - agent.get('/api/quotes/random'), - agent.get('/api/quotes/random'), - agent.get('/api/quotes/random') - ]); - - // Each random quote should be from the full set - randomQuoteResponses.forEach(response => { - expect(response.status).toBe(200); - expect(allQuotes).toContain(response.body.quote); - }); - }); - }); -}); \ No newline at end of file +}); diff --git a/backend/tests/integration/recurring-tasks.test.js b/backend/tests/integration/recurring-tasks.test.js index b8a10b9..d9aa5a6 100644 --- a/backend/tests/integration/recurring-tasks.test.js +++ b/backend/tests/integration/recurring-tasks.test.js @@ -4,598 +4,594 @@ const { Task, User } = require('../../models'); const { createTestUser } = require('../helpers/testUtils'); describe('Recurring Tasks API', () => { - let user, agent; - - beforeEach(async () => { - user = await createTestUser({ - email: 'test@example.com' - }); - - // Create authenticated agent - agent = request.agent(app); - await agent - .post('/api/login') - .send({ - email: 'test@example.com', - password: 'password123' - }); - }); - - describe('POST /api/task - Creating recurring tasks', () => { - it('should create a daily recurring task', async () => { - const taskData = { - name: 'Daily Exercise', - recurrence_type: 'daily', - recurrence_interval: 1, - priority: 1, - completion_based: false - }; - - const response = await agent - .post('/api/task') - .send(taskData); - - expect(response.status).toBe(201); - expect(response.body.name).toBe('Daily Exercise'); - expect(response.body.recurrence_type).toBe('daily'); - expect(response.body.recurrence_interval).toBe(1); - expect(response.body.completion_based).toBe(false); - }); - - it('should create a weekly recurring task with specific weekday', async () => { - const taskData = { - name: 'Weekly Team Meeting', - recurrence_type: 'weekly', - recurrence_interval: 1, - recurrence_weekday: 1, // Monday - priority: 2 - }; - - const response = await agent - .post('/api/task') - .send(taskData); - - expect(response.status).toBe(201); - expect(response.body.name).toBe('Weekly Team Meeting'); - expect(response.body.recurrence_type).toBe('weekly'); - expect(response.body.recurrence_weekday).toBe(1); - }); - - it('should create a monthly recurring task', async () => { - const taskData = { - name: 'Pay Rent', - recurrence_type: 'monthly', - recurrence_interval: 1, - recurrence_month_day: 1, - priority: 2 - }; - - const response = await agent - .post('/api/task') - .send(taskData); - - expect(response.status).toBe(201); - expect(response.body.name).toBe('Pay Rent'); - expect(response.body.recurrence_type).toBe('monthly'); - expect(response.body.recurrence_month_day).toBe(1); - }); - - it('should create a monthly weekday recurring task', async () => { - const taskData = { - name: 'First Monday Meeting', - recurrence_type: 'monthly_weekday', - recurrence_interval: 1, - recurrence_weekday: 1, // Monday - recurrence_week_of_month: 1, // First week - priority: 1 - }; - - const response = await agent - .post('/api/task') - .send(taskData); - - expect(response.status).toBe(201); - expect(response.body.name).toBe('First Monday Meeting'); - expect(response.body.recurrence_type).toBe('monthly_weekday'); - expect(response.body.recurrence_weekday).toBe(1); - expect(response.body.recurrence_week_of_month).toBe(1); - }); - - it('should create a monthly last day recurring task', async () => { - const taskData = { - name: 'Month-end Report', - recurrence_type: 'monthly_last_day', - recurrence_interval: 1, - priority: 2 - }; - - const response = await agent - .post('/api/task') - .send(taskData); - - expect(response.status).toBe(201); - expect(response.body.name).toBe('Month-end Report'); - expect(response.body.recurrence_type).toBe('monthly_last_day'); - }); - - it('should create a completion-based recurring task', async () => { - const taskData = { - name: 'Car Maintenance', - recurrence_type: 'monthly', - recurrence_interval: 3, - completion_based: true, - priority: 1 - }; - - const response = await agent - .post('/api/task') - .send(taskData); - - expect(response.status).toBe(201); - expect(response.body.name).toBe('Car Maintenance'); - expect(response.body.completion_based).toBe(true); - }); - - it('should create recurring task with end date', async () => { - const endDate = new Date(); - endDate.setMonth(endDate.getMonth() + 6); - - const taskData = { - name: 'Temporary Recurring Task', - recurrence_type: 'weekly', - recurrence_interval: 2, - recurrence_end_date: endDate.toISOString().split('T')[0], - priority: 1 - }; - - const response = await agent - .post('/api/task') - .send(taskData); - - expect(response.status).toBe(201); - expect(response.body.name).toBe('Temporary Recurring Task'); - expect(response.body.recurrence_end_date).toContain(endDate.toISOString().split('T')[0]); - }); - - it('should default to none recurrence type if not specified', async () => { - const taskData = { - name: 'Regular Task', - priority: 1 - }; - - const response = await agent - .post('/api/task') - .send(taskData); - - expect(response.status).toBe(201); - expect(response.body.recurrence_type).toBe('none'); - }); - }); - - describe('PATCH /api/task/:id - Updating recurring tasks', () => { - let task; + let user, agent; beforeEach(async () => { - task = await Task.create({ - name: 'Test Recurring Task', - recurrence_type: 'daily', - recurrence_interval: 1, - user_id: user.id, - priority: 1 - }); + user = await createTestUser({ + email: 'test@example.com', + }); + + // Create authenticated agent + agent = request.agent(app); + await agent.post('/api/login').send({ + email: 'test@example.com', + password: 'password123', + }); }); - it('should update recurrence settings', async () => { - const updateData = { - recurrence_type: 'weekly', - recurrence_interval: 2, - recurrence_weekday: 5 // Friday - }; + describe('POST /api/task - Creating recurring tasks', () => { + it('should create a daily recurring task', async () => { + const taskData = { + name: 'Daily Exercise', + recurrence_type: 'daily', + recurrence_interval: 1, + priority: 1, + completion_based: false, + }; - const response = await agent - .patch(`/api/task/${task.id}`) - .send(updateData); + const response = await agent.post('/api/task').send(taskData); - expect(response.status).toBe(200); - expect(response.body.recurrence_type).toBe('weekly'); - expect(response.body.recurrence_interval).toBe(2); - expect(response.body.recurrence_weekday).toBe(5); + expect(response.status).toBe(201); + expect(response.body.name).toBe('Daily Exercise'); + expect(response.body.recurrence_type).toBe('daily'); + expect(response.body.recurrence_interval).toBe(1); + expect(response.body.completion_based).toBe(false); + }); + + it('should create a weekly recurring task with specific weekday', async () => { + const taskData = { + name: 'Weekly Team Meeting', + recurrence_type: 'weekly', + recurrence_interval: 1, + recurrence_weekday: 1, // Monday + priority: 2, + }; + + const response = await agent.post('/api/task').send(taskData); + + expect(response.status).toBe(201); + expect(response.body.name).toBe('Weekly Team Meeting'); + expect(response.body.recurrence_type).toBe('weekly'); + expect(response.body.recurrence_weekday).toBe(1); + }); + + it('should create a monthly recurring task', async () => { + const taskData = { + name: 'Pay Rent', + recurrence_type: 'monthly', + recurrence_interval: 1, + recurrence_month_day: 1, + priority: 2, + }; + + const response = await agent.post('/api/task').send(taskData); + + expect(response.status).toBe(201); + expect(response.body.name).toBe('Pay Rent'); + expect(response.body.recurrence_type).toBe('monthly'); + expect(response.body.recurrence_month_day).toBe(1); + }); + + it('should create a monthly weekday recurring task', async () => { + const taskData = { + name: 'First Monday Meeting', + recurrence_type: 'monthly_weekday', + recurrence_interval: 1, + recurrence_weekday: 1, // Monday + recurrence_week_of_month: 1, // First week + priority: 1, + }; + + const response = await agent.post('/api/task').send(taskData); + + expect(response.status).toBe(201); + expect(response.body.name).toBe('First Monday Meeting'); + expect(response.body.recurrence_type).toBe('monthly_weekday'); + expect(response.body.recurrence_weekday).toBe(1); + expect(response.body.recurrence_week_of_month).toBe(1); + }); + + it('should create a monthly last day recurring task', async () => { + const taskData = { + name: 'Month-end Report', + recurrence_type: 'monthly_last_day', + recurrence_interval: 1, + priority: 2, + }; + + const response = await agent.post('/api/task').send(taskData); + + expect(response.status).toBe(201); + expect(response.body.name).toBe('Month-end Report'); + expect(response.body.recurrence_type).toBe('monthly_last_day'); + }); + + it('should create a completion-based recurring task', async () => { + const taskData = { + name: 'Car Maintenance', + recurrence_type: 'monthly', + recurrence_interval: 3, + completion_based: true, + priority: 1, + }; + + const response = await agent.post('/api/task').send(taskData); + + expect(response.status).toBe(201); + expect(response.body.name).toBe('Car Maintenance'); + expect(response.body.completion_based).toBe(true); + }); + + it('should create recurring task with end date', async () => { + const endDate = new Date(); + endDate.setMonth(endDate.getMonth() + 6); + + const taskData = { + name: 'Temporary Recurring Task', + recurrence_type: 'weekly', + recurrence_interval: 2, + recurrence_end_date: endDate.toISOString().split('T')[0], + priority: 1, + }; + + const response = await agent.post('/api/task').send(taskData); + + expect(response.status).toBe(201); + expect(response.body.name).toBe('Temporary Recurring Task'); + expect(response.body.recurrence_end_date).toContain( + endDate.toISOString().split('T')[0] + ); + }); + + it('should default to none recurrence type if not specified', async () => { + const taskData = { + name: 'Regular Task', + priority: 1, + }; + + const response = await agent.post('/api/task').send(taskData); + + expect(response.status).toBe(201); + expect(response.body.recurrence_type).toBe('none'); + }); }); - it('should update completion_based setting', async () => { - const updateData = { - completion_based: true - }; + describe('PATCH /api/task/:id - Updating recurring tasks', () => { + let task; - const response = await agent - .patch(`/api/task/${task.id}`) - .send(updateData); + beforeEach(async () => { + task = await Task.create({ + name: 'Test Recurring Task', + recurrence_type: 'daily', + recurrence_interval: 1, + user_id: user.id, + priority: 1, + }); + }); - expect(response.status).toBe(200); - expect(response.body.completion_based).toBe(true); + it('should update recurrence settings', async () => { + const updateData = { + recurrence_type: 'weekly', + recurrence_interval: 2, + recurrence_weekday: 5, // Friday + }; + + const response = await agent + .patch(`/api/task/${task.id}`) + .send(updateData); + + expect(response.status).toBe(200); + expect(response.body.recurrence_type).toBe('weekly'); + expect(response.body.recurrence_interval).toBe(2); + expect(response.body.recurrence_weekday).toBe(5); + }); + + it('should update completion_based setting', async () => { + const updateData = { + completion_based: true, + }; + + const response = await agent + .patch(`/api/task/${task.id}`) + .send(updateData); + + expect(response.status).toBe(200); + expect(response.body.completion_based).toBe(true); + }); + + it('should update recurrence end date', async () => { + const endDate = new Date(); + endDate.setFullYear(endDate.getFullYear() + 1); + + const updateData = { + recurrence_end_date: endDate.toISOString().split('T')[0], + }; + + const response = await agent + .patch(`/api/task/${task.id}`) + .send(updateData); + + expect(response.status).toBe(200); + expect(response.body.recurrence_end_date).toContain( + endDate.toISOString().split('T')[0] + ); + }); + + it('should disable recurrence by setting type to none', async () => { + const updateData = { + recurrence_type: 'none', + }; + + const response = await agent + .patch(`/api/task/${task.id}`) + .send(updateData); + + expect(response.status).toBe(200); + expect(response.body.recurrence_type).toBe('none'); + }); }); - it('should update recurrence end date', async () => { - const endDate = new Date(); - endDate.setFullYear(endDate.getFullYear() + 1); - - const updateData = { - recurrence_end_date: endDate.toISOString().split('T')[0] - }; + describe('PATCH /api/task/:id - Updating parent recurrence from child task', () => { + let parentTask, childTask; - const response = await agent - .patch(`/api/task/${task.id}`) - .send(updateData); + beforeEach(async () => { + parentTask = await Task.create({ + name: 'Parent Recurring Task', + recurrence_type: 'daily', + recurrence_interval: 1, + user_id: user.id, + priority: 1, + }); - expect(response.status).toBe(200); - expect(response.body.recurrence_end_date).toContain(endDate.toISOString().split('T')[0]); + childTask = await Task.create({ + name: 'Parent Recurring Task', + recurrence_type: 'none', + recurring_parent_id: parentTask.id, + user_id: user.id, + priority: 1, + due_date: new Date(), + }); + }); + + it('should update parent recurrence settings when update_parent_recurrence is true', async () => { + const updateData = { + recurrence_type: 'weekly', + recurrence_interval: 2, + recurrence_weekday: 3, + update_parent_recurrence: true, + }; + + const response = await agent + .patch(`/api/task/${childTask.id}`) + .send(updateData); + + expect(response.status).toBe(200); + + // Check that parent task was updated + const updatedParent = await Task.findByPk(parentTask.id); + expect(updatedParent.recurrence_type).toBe('weekly'); + expect(updatedParent.recurrence_interval).toBe(2); + expect(updatedParent.recurrence_weekday).toBe(3); + }); + + it('should not update parent when update_parent_recurrence is false', async () => { + const originalParentType = parentTask.recurrence_type; + + const updateData = { + recurrence_type: 'weekly', + update_parent_recurrence: false, + }; + + const response = await agent + .patch(`/api/task/${childTask.id}`) + .send(updateData); + + expect(response.status).toBe(200); + + // Check that parent task was not updated + const updatedParent = await Task.findByPk(parentTask.id); + expect(updatedParent.recurrence_type).toBe(originalParentType); + }); + + it('should not update parent when task has no recurring_parent_id', async () => { + const standaloneTask = await Task.create({ + name: 'Standalone Task', + recurrence_type: 'none', + user_id: user.id, + priority: 1, + }); + + const updateData = { + recurrence_type: 'weekly', + update_parent_recurrence: true, + }; + + const response = await agent + .patch(`/api/task/${standaloneTask.id}`) + .send(updateData); + + expect(response.status).toBe(200); + expect(response.body.recurrence_type).toBe('weekly'); + }); }); - it('should disable recurrence by setting type to none', async () => { - const updateData = { - recurrence_type: 'none' - }; + describe('PATCH /api/task/:id/toggle_completion - Recurring task completion', () => { + it('should create next instance when completing a completion-based recurring task', async () => { + const recurringTask = await Task.create({ + name: 'Completion Based Task', + recurrence_type: 'daily', + recurrence_interval: 1, + completion_based: true, + user_id: user.id, + status: 0, // NOT_STARTED + }); - const response = await agent - .patch(`/api/task/${task.id}`) - .send(updateData); + const response = await agent.patch( + `/api/task/${recurringTask.id}/toggle_completion` + ); - expect(response.status).toBe(200); - expect(response.body.recurrence_type).toBe('none'); - }); - }); + expect(response.status).toBe(200); + expect(response.body.status).toBe(2); // DONE + expect(response.body.next_task).toBeDefined(); + expect(response.body.next_task.name).toBe('Completion Based Task'); + expect(response.body.next_task.recurring_parent_id).toBe( + recurringTask.id + ); + }); - describe('PATCH /api/task/:id - Updating parent recurrence from child task', () => { - let parentTask, childTask; + it('should not create next instance for non-completion-based recurring tasks', async () => { + const recurringTask = await Task.create({ + name: 'Schedule Based Task', + recurrence_type: 'daily', + recurrence_interval: 1, + completion_based: false, + user_id: user.id, + status: 0, // NOT_STARTED + }); - beforeEach(async () => { - parentTask = await Task.create({ - name: 'Parent Recurring Task', - recurrence_type: 'daily', - recurrence_interval: 1, - user_id: user.id, - priority: 1 - }); + const response = await agent.patch( + `/api/task/${recurringTask.id}/toggle_completion` + ); - childTask = await Task.create({ - name: 'Parent Recurring Task', - recurrence_type: 'none', - recurring_parent_id: parentTask.id, - user_id: user.id, - priority: 1, - due_date: new Date() - }); + expect(response.status).toBe(200); + expect(response.body.status).toBe(2); // DONE + expect(response.body.next_task).toBeUndefined(); + }); + + it('should not create next instance for non-recurring tasks', async () => { + const regularTask = await Task.create({ + name: 'Regular Task', + recurrence_type: 'none', + user_id: user.id, + status: 0, // NOT_STARTED + }); + + const response = await agent.patch( + `/api/task/${regularTask.id}/toggle_completion` + ); + + expect(response.status).toBe(200); + expect(response.body.status).toBe(2); // DONE + expect(response.body.next_task).toBeUndefined(); + }); + + it('should toggle completion back to not done', async () => { + const task = await Task.create({ + name: 'Test Task', + user_id: user.id, + status: 2, // DONE + }); + + const response = await agent.patch( + `/api/task/${task.id}/toggle_completion` + ); + + expect(response.status).toBe(200); + expect(response.body.status).toBe(0); // NOT_STARTED + }); + + it('should toggle to in_progress if task has a note', async () => { + const task = await Task.create({ + name: 'Test Task', + note: 'Some notes', + user_id: user.id, + status: 2, // DONE + }); + + const response = await agent.patch( + `/api/task/${task.id}/toggle_completion` + ); + + expect(response.status).toBe(200); + expect(response.body.status).toBe(1); // IN_PROGRESS + }); }); - it('should update parent recurrence settings when update_parent_recurrence is true', async () => { - const updateData = { - recurrence_type: 'weekly', - recurrence_interval: 2, - recurrence_weekday: 3, - update_parent_recurrence: true - }; + describe('POST /api/tasks/generate-recurring', () => { + beforeEach(async () => { + // Create some recurring tasks for testing + await Task.create({ + name: 'Daily Task', + recurrence_type: 'daily', + recurrence_interval: 1, + user_id: user.id, + due_date: new Date('2025-01-01'), + last_generated_date: new Date('2025-01-01'), + }); - const response = await agent - .patch(`/api/task/${childTask.id}`) - .send(updateData); + await Task.create({ + name: 'Weekly Task', + recurrence_type: 'weekly', + recurrence_interval: 1, + recurrence_weekday: 1, + user_id: user.id, + due_date: new Date('2025-01-06'), // Monday + last_generated_date: new Date('2025-01-06'), + }); + }); - expect(response.status).toBe(200); + it('should generate recurring task instances', async () => { + const response = await agent.post('/api/tasks/generate-recurring'); - // Check that parent task was updated - const updatedParent = await Task.findByPk(parentTask.id); - expect(updatedParent.recurrence_type).toBe('weekly'); - expect(updatedParent.recurrence_interval).toBe(2); - expect(updatedParent.recurrence_weekday).toBe(3); + expect(response.status).toBe(200); + expect(response.body.message).toMatch( + /Generated \d+ recurring tasks/ + ); + expect(response.body.tasks).toBeDefined(); + expect(Array.isArray(response.body.tasks)).toBe(true); + }); + + it('should require authentication', async () => { + const response = await request(app).post( + '/api/tasks/generate-recurring' + ); + + expect(response.status).toBe(401); + expect(response.body.error).toBe('Authentication required'); + }); + + it('should handle errors gracefully', async () => { + // Mock console.error to suppress expected error log in test output + const originalConsoleError = console.error; + console.error = jest.fn(); + + // Create invalid recurring task to trigger error + await Task.create({ + name: 'Invalid Task', + recurrence_type: 'invalid_type', + user_id: user.id, + }); + + const response = await agent.post('/api/tasks/generate-recurring'); + + // Should still return success even if some tasks fail + expect(response.status).toBe(200); + + // Restore original console.error + console.error = originalConsoleError; + }); }); - it('should not update parent when update_parent_recurrence is false', async () => { - const originalParentType = parentTask.recurrence_type; - - const updateData = { - recurrence_type: 'weekly', - update_parent_recurrence: false - }; + describe('GET /api/tasks - Filtering recurring tasks', () => { + beforeEach(async () => { + // Create a mix of regular and recurring tasks + await Task.create({ + name: 'Regular Task', + recurrence_type: 'none', + user_id: user.id, + status: 0, + }); - const response = await agent - .patch(`/api/task/${childTask.id}`) - .send(updateData); + const parentTask = await Task.create({ + name: 'Recurring Parent', + recurrence_type: 'daily', + recurrence_interval: 1, + user_id: user.id, + status: 0, + }); - expect(response.status).toBe(200); + await Task.create({ + name: 'Recurring Child', + recurrence_type: 'none', + recurring_parent_id: parentTask.id, + user_id: user.id, + status: 0, + }); + }); - // Check that parent task was not updated - const updatedParent = await Task.findByPk(parentTask.id); - expect(updatedParent.recurrence_type).toBe(originalParentType); + it('should return all tasks including recurring ones', async () => { + const response = await agent.get('/api/tasks'); + + expect(response.status).toBe(200); + expect(response.body.tasks).toBeDefined(); + expect(response.body.tasks.length).toBe(3); + + const taskNames = response.body.tasks.map((t) => t.name); + expect(taskNames).toContain('Regular Task'); + expect(taskNames).toContain('Recurring Parent'); + expect(taskNames).toContain('Recurring Child'); + }); + + it('should return task metrics including recurring tasks', async () => { + const response = await agent.get('/api/tasks'); + + expect(response.status).toBe(200); + expect(response.body.metrics).toBeDefined(); + expect(response.body.metrics.total_open_tasks).toBe(3); + }); }); - it('should not update parent when task has no recurring_parent_id', async () => { - const standaloneTask = await Task.create({ - name: 'Standalone Task', - recurrence_type: 'none', - user_id: user.id, - priority: 1 - }); + describe('GET /api/task/:id - Retrieving individual recurring tasks', () => { + let recurringTask; - const updateData = { - recurrence_type: 'weekly', - update_parent_recurrence: true - }; + beforeEach(async () => { + recurringTask = await Task.create({ + name: 'Test Recurring Task', + recurrence_type: 'weekly', + recurrence_interval: 2, + recurrence_weekday: 1, + completion_based: true, + user_id: user.id, + }); + }); - const response = await agent - .patch(`/api/task/${standaloneTask.id}`) - .send(updateData); + it('should return recurring task with all recurrence fields', async () => { + const response = await agent.get(`/api/task/${recurringTask.id}`); - expect(response.status).toBe(200); - expect(response.body.recurrence_type).toBe('weekly'); - }); - }); - - describe('PATCH /api/task/:id/toggle_completion - Recurring task completion', () => { - it('should create next instance when completing a completion-based recurring task', async () => { - const recurringTask = await Task.create({ - name: 'Completion Based Task', - recurrence_type: 'daily', - recurrence_interval: 1, - completion_based: true, - user_id: user.id, - status: 0 // NOT_STARTED - }); - - const response = await agent - .patch(`/api/task/${recurringTask.id}/toggle_completion`); - - expect(response.status).toBe(200); - expect(response.body.status).toBe(2); // DONE - expect(response.body.next_task).toBeDefined(); - expect(response.body.next_task.name).toBe('Completion Based Task'); - expect(response.body.next_task.recurring_parent_id).toBe(recurringTask.id); + expect(response.status).toBe(200); + expect(response.body.name).toBe('Test Recurring Task'); + expect(response.body.recurrence_type).toBe('weekly'); + expect(response.body.recurrence_interval).toBe(2); + expect(response.body.recurrence_weekday).toBe(1); + expect(response.body.completion_based).toBe(true); + }); }); - it('should not create next instance for non-completion-based recurring tasks', async () => { - const recurringTask = await Task.create({ - name: 'Schedule Based Task', - recurrence_type: 'daily', - recurrence_interval: 1, - completion_based: false, - user_id: user.id, - status: 0 // NOT_STARTED - }); + describe('DELETE /api/task/:id - Deleting recurring tasks', () => { + let parentTask, childTask; - const response = await agent - .patch(`/api/task/${recurringTask.id}/toggle_completion`); + beforeEach(async () => { + parentTask = await Task.create({ + name: 'Parent Recurring Task', + recurrence_type: 'daily', + recurrence_interval: 1, + user_id: user.id, + }); - expect(response.status).toBe(200); - expect(response.body.status).toBe(2); // DONE - expect(response.body.next_task).toBeUndefined(); + childTask = await Task.create({ + name: 'Child Task Instance', + recurrence_type: 'none', + recurring_parent_id: parentTask.id, + user_id: user.id, + }); + }); + + it('should not delete recurring parent task when child tasks exist', async () => { + const response = await agent.delete(`/api/task/${parentTask.id}`); + + expect(response.status).toBe(400); + expect(response.body.error).toBe( + 'There was a problem deleting the task.' + ); + + // Verify task still exists + const taskStillExists = await Task.findByPk(parentTask.id); + expect(taskStillExists).not.toBeNull(); + }); + + it('should delete recurring child task', async () => { + const response = await agent.delete(`/api/task/${childTask.id}`); + + expect(response.status).toBe(200); + expect(response.body.message).toBe('Task successfully deleted'); + + // Verify task is deleted + const deletedTask = await Task.findByPk(childTask.id); + expect(deletedTask).toBeNull(); + + // Verify parent still exists + const parentStillExists = await Task.findByPk(parentTask.id); + expect(parentStillExists).not.toBeNull(); + }); }); - - it('should not create next instance for non-recurring tasks', async () => { - const regularTask = await Task.create({ - name: 'Regular Task', - recurrence_type: 'none', - user_id: user.id, - status: 0 // NOT_STARTED - }); - - const response = await agent - .patch(`/api/task/${regularTask.id}/toggle_completion`); - - expect(response.status).toBe(200); - expect(response.body.status).toBe(2); // DONE - expect(response.body.next_task).toBeUndefined(); - }); - - it('should toggle completion back to not done', async () => { - const task = await Task.create({ - name: 'Test Task', - user_id: user.id, - status: 2 // DONE - }); - - const response = await agent - .patch(`/api/task/${task.id}/toggle_completion`); - - expect(response.status).toBe(200); - expect(response.body.status).toBe(0); // NOT_STARTED - }); - - it('should toggle to in_progress if task has a note', async () => { - const task = await Task.create({ - name: 'Test Task', - note: 'Some notes', - user_id: user.id, - status: 2 // DONE - }); - - const response = await agent - .patch(`/api/task/${task.id}/toggle_completion`); - - expect(response.status).toBe(200); - expect(response.body.status).toBe(1); // IN_PROGRESS - }); - }); - - describe('POST /api/tasks/generate-recurring', () => { - beforeEach(async () => { - // Create some recurring tasks for testing - await Task.create({ - name: 'Daily Task', - recurrence_type: 'daily', - recurrence_interval: 1, - user_id: user.id, - due_date: new Date('2025-01-01'), - last_generated_date: new Date('2025-01-01') - }); - - await Task.create({ - name: 'Weekly Task', - recurrence_type: 'weekly', - recurrence_interval: 1, - recurrence_weekday: 1, - user_id: user.id, - due_date: new Date('2025-01-06'), // Monday - last_generated_date: new Date('2025-01-06') - }); - }); - - it('should generate recurring task instances', async () => { - const response = await agent - .post('/api/tasks/generate-recurring'); - - expect(response.status).toBe(200); - expect(response.body.message).toMatch(/Generated \d+ recurring tasks/); - expect(response.body.tasks).toBeDefined(); - expect(Array.isArray(response.body.tasks)).toBe(true); - }); - - it('should require authentication', async () => { - const response = await request(app) - .post('/api/tasks/generate-recurring'); - - expect(response.status).toBe(401); - expect(response.body.error).toBe('Authentication required'); - }); - - it('should handle errors gracefully', async () => { - // Mock console.error to suppress expected error log in test output - const originalConsoleError = console.error; - console.error = jest.fn(); - - // Create invalid recurring task to trigger error - await Task.create({ - name: 'Invalid Task', - recurrence_type: 'invalid_type', - user_id: user.id - }); - - const response = await agent - .post('/api/tasks/generate-recurring'); - - // Should still return success even if some tasks fail - expect(response.status).toBe(200); - - // Restore original console.error - console.error = originalConsoleError; - }); - }); - - describe('GET /api/tasks - Filtering recurring tasks', () => { - beforeEach(async () => { - // Create a mix of regular and recurring tasks - await Task.create({ - name: 'Regular Task', - recurrence_type: 'none', - user_id: user.id, - status: 0 - }); - - const parentTask = await Task.create({ - name: 'Recurring Parent', - recurrence_type: 'daily', - recurrence_interval: 1, - user_id: user.id, - status: 0 - }); - - await Task.create({ - name: 'Recurring Child', - recurrence_type: 'none', - recurring_parent_id: parentTask.id, - user_id: user.id, - status: 0 - }); - }); - - it('should return all tasks including recurring ones', async () => { - const response = await agent.get('/api/tasks'); - - expect(response.status).toBe(200); - expect(response.body.tasks).toBeDefined(); - expect(response.body.tasks.length).toBe(3); - - const taskNames = response.body.tasks.map(t => t.name); - expect(taskNames).toContain('Regular Task'); - expect(taskNames).toContain('Recurring Parent'); - expect(taskNames).toContain('Recurring Child'); - }); - - it('should return task metrics including recurring tasks', async () => { - const response = await agent.get('/api/tasks'); - - expect(response.status).toBe(200); - expect(response.body.metrics).toBeDefined(); - expect(response.body.metrics.total_open_tasks).toBe(3); - }); - }); - - describe('GET /api/task/:id - Retrieving individual recurring tasks', () => { - let recurringTask; - - beforeEach(async () => { - recurringTask = await Task.create({ - name: 'Test Recurring Task', - recurrence_type: 'weekly', - recurrence_interval: 2, - recurrence_weekday: 1, - completion_based: true, - user_id: user.id - }); - }); - - it('should return recurring task with all recurrence fields', async () => { - const response = await agent.get(`/api/task/${recurringTask.id}`); - - expect(response.status).toBe(200); - expect(response.body.name).toBe('Test Recurring Task'); - expect(response.body.recurrence_type).toBe('weekly'); - expect(response.body.recurrence_interval).toBe(2); - expect(response.body.recurrence_weekday).toBe(1); - expect(response.body.completion_based).toBe(true); - }); - }); - - describe('DELETE /api/task/:id - Deleting recurring tasks', () => { - let parentTask, childTask; - - beforeEach(async () => { - parentTask = await Task.create({ - name: 'Parent Recurring Task', - recurrence_type: 'daily', - recurrence_interval: 1, - user_id: user.id - }); - - childTask = await Task.create({ - name: 'Child Task Instance', - recurrence_type: 'none', - recurring_parent_id: parentTask.id, - user_id: user.id - }); - }); - - it('should not delete recurring parent task when child tasks exist', async () => { - const response = await agent.delete(`/api/task/${parentTask.id}`); - - expect(response.status).toBe(400); - expect(response.body.error).toBe('There was a problem deleting the task.'); - - // Verify task still exists - const taskStillExists = await Task.findByPk(parentTask.id); - expect(taskStillExists).not.toBeNull(); - }); - - it('should delete recurring child task', async () => { - const response = await agent.delete(`/api/task/${childTask.id}`); - - expect(response.status).toBe(200); - expect(response.body.message).toBe('Task successfully deleted'); - - // Verify task is deleted - const deletedTask = await Task.findByPk(childTask.id); - expect(deletedTask).toBeNull(); - - // Verify parent still exists - const parentStillExists = await Task.findByPk(parentTask.id); - expect(parentStillExists).not.toBeNull(); - }); - }); -}); \ No newline at end of file +}); diff --git a/backend/tests/integration/tags.test.js b/backend/tests/integration/tags.test.js index be6e4e5..aa88f88 100644 --- a/backend/tests/integration/tags.test.js +++ b/backend/tests/integration/tags.test.js @@ -4,267 +4,259 @@ const { Tag, User } = require('../../models'); const { createTestUser } = require('../helpers/testUtils'); describe('Tags Routes', () => { - let user, agent; - - beforeEach(async () => { - user = await createTestUser({ - email: 'test@example.com' - }); - - // Create authenticated agent - agent = request.agent(app); - await agent - .post('/api/login') - .send({ - email: 'test@example.com', - password: 'password123' - }); - }); - - describe('POST /api/tag', () => { - it('should create a new tag', async () => { - const tagData = { - name: 'work' - }; - - const response = await agent - .post('/api/tag') - .send(tagData); - - expect(response.status).toBe(201); - expect(response.body.name).toBe(tagData.name); - expect(response.body.id).toBeDefined(); - }); - - it('should require authentication', async () => { - const tagData = { - name: 'work' - }; - - const response = await request(app) - .post('/api/tag') - .send(tagData); - - expect(response.status).toBe(401); - expect(response.body.error).toBe('Authentication required'); - }); - - it('should require tag name', async () => { - const tagData = {}; - - const response = await agent - .post('/api/tag') - .send(tagData); - - expect(response.status).toBe(400); - expect(response.body.error).toBe('Tag name is required'); - }); - }); - - describe('GET /api/tags', () => { - let tag1, tag2; + let user, agent; beforeEach(async () => { - tag1 = await Tag.create({ - name: 'work', - user_id: user.id - }); + user = await createTestUser({ + email: 'test@example.com', + }); - tag2 = await Tag.create({ - name: 'personal', - user_id: user.id - }); + // Create authenticated agent + agent = request.agent(app); + await agent.post('/api/login').send({ + email: 'test@example.com', + password: 'password123', + }); }); - it('should get all user tags', async () => { - const response = await agent.get('/api/tags'); + describe('POST /api/tag', () => { + it('should create a new tag', async () => { + const tagData = { + name: 'work', + }; - expect(response.status).toBe(200); - expect(response.body).toHaveLength(2); - expect(response.body.map(t => t.id)).toContain(tag1.id); - expect(response.body.map(t => t.id)).toContain(tag2.id); + const response = await agent.post('/api/tag').send(tagData); + + expect(response.status).toBe(201); + expect(response.body.name).toBe(tagData.name); + expect(response.body.id).toBeDefined(); + }); + + it('should require authentication', async () => { + const tagData = { + name: 'work', + }; + + const response = await request(app).post('/api/tag').send(tagData); + + expect(response.status).toBe(401); + expect(response.body.error).toBe('Authentication required'); + }); + + it('should require tag name', async () => { + const tagData = {}; + + const response = await agent.post('/api/tag').send(tagData); + + expect(response.status).toBe(400); + expect(response.body.error).toBe('Tag name is required'); + }); }); - it('should order tags by name', async () => { - const response = await agent.get('/api/tags'); + describe('GET /api/tags', () => { + let tag1, tag2; - expect(response.status).toBe(200); - expect(response.body[0].name).toBe('personal'); // P comes before W - expect(response.body[1].name).toBe('work'); + beforeEach(async () => { + tag1 = await Tag.create({ + name: 'work', + user_id: user.id, + }); + + tag2 = await Tag.create({ + name: 'personal', + user_id: user.id, + }); + }); + + it('should get all user tags', async () => { + const response = await agent.get('/api/tags'); + + expect(response.status).toBe(200); + expect(response.body).toHaveLength(2); + expect(response.body.map((t) => t.id)).toContain(tag1.id); + expect(response.body.map((t) => t.id)).toContain(tag2.id); + }); + + it('should order tags by name', async () => { + const response = await agent.get('/api/tags'); + + expect(response.status).toBe(200); + expect(response.body[0].name).toBe('personal'); // P comes before W + expect(response.body[1].name).toBe('work'); + }); + + it('should require authentication', async () => { + const response = await request(app).get('/api/tags'); + + expect(response.status).toBe(401); + expect(response.body.error).toBe('Authentication required'); + }); }); - it('should require authentication', async () => { - const response = await request(app).get('/api/tags'); + describe('GET /api/tag/:id', () => { + let tag; - expect(response.status).toBe(401); - expect(response.body.error).toBe('Authentication required'); - }); - }); + beforeEach(async () => { + tag = await Tag.create({ + name: 'work', + user_id: user.id, + }); + }); - describe('GET /api/tag/:id', () => { - let tag; + it('should get tag by id', async () => { + const response = await agent.get(`/api/tag/${tag.id}`); - beforeEach(async () => { - tag = await Tag.create({ - name: 'work', - user_id: user.id - }); + expect(response.status).toBe(200); + expect(response.body.id).toBe(tag.id); + expect(response.body.name).toBe(tag.name); + }); + + it('should return 404 for non-existent tag', async () => { + const response = await agent.get('/api/tag/999999'); + + expect(response.status).toBe(404); + expect(response.body.error).toBe('Tag not found'); + }); + + it("should not allow access to other user's tags", async () => { + const bcrypt = require('bcrypt'); + const otherUser = await User.create({ + email: 'other@example.com', + password_digest: await bcrypt.hash('password123', 10), + }); + + const otherTag = await Tag.create({ + name: 'other-tag', + user_id: otherUser.id, + }); + + const response = await agent.get(`/api/tag/${otherTag.id}`); + + expect(response.status).toBe(404); + expect(response.body.error).toBe('Tag not found'); + }); + + it('should require authentication', async () => { + const response = await request(app).get(`/api/tag/${tag.id}`); + + expect(response.status).toBe(401); + expect(response.body.error).toBe('Authentication required'); + }); }); - it('should get tag by id', async () => { - const response = await agent.get(`/api/tag/${tag.id}`); + describe('PATCH /api/tag/:id', () => { + let tag; - expect(response.status).toBe(200); - expect(response.body.id).toBe(tag.id); - expect(response.body.name).toBe(tag.name); + beforeEach(async () => { + tag = await Tag.create({ + name: 'work', + user_id: user.id, + }); + }); + + it('should update tag', async () => { + const updateData = { + name: 'updated-work', + }; + + const response = await agent + .patch(`/api/tag/${tag.id}`) + .send(updateData); + + expect(response.status).toBe(200); + expect(response.body.name).toBe(updateData.name); + }); + + it('should return 404 for non-existent tag', async () => { + const response = await agent + .patch('/api/tag/999999') + .send({ name: 'Updated' }); + + expect(response.status).toBe(404); + expect(response.body.error).toBe('Tag not found'); + }); + + it("should not allow updating other user's tags", async () => { + const bcrypt = require('bcrypt'); + const otherUser = await User.create({ + email: 'other@example.com', + password_digest: await bcrypt.hash('password123', 10), + }); + + const otherTag = await Tag.create({ + name: 'other-tag', + user_id: otherUser.id, + }); + + const response = await agent + .patch(`/api/tag/${otherTag.id}`) + .send({ name: 'Updated' }); + + expect(response.status).toBe(404); + expect(response.body.error).toBe('Tag not found'); + }); + + it('should require authentication', async () => { + const response = await request(app) + .patch(`/api/tag/${tag.id}`) + .send({ name: 'Updated' }); + + expect(response.status).toBe(401); + expect(response.body.error).toBe('Authentication required'); + }); }); - it('should return 404 for non-existent tag', async () => { - const response = await agent.get('/api/tag/999999'); + describe('DELETE /api/tag/:id', () => { + let tag; - expect(response.status).toBe(404); - expect(response.body.error).toBe('Tag not found'); + beforeEach(async () => { + tag = await Tag.create({ + name: 'work', + user_id: user.id, + }); + }); + + it('should delete tag', async () => { + const response = await agent.delete(`/api/tag/${tag.id}`); + + expect(response.status).toBe(200); + expect(response.body.message).toBe('Tag successfully deleted'); + + // Verify tag is deleted + const deletedTag = await Tag.findByPk(tag.id); + expect(deletedTag).toBeNull(); + }); + + it('should return 404 for non-existent tag', async () => { + const response = await agent.delete('/api/tag/999999'); + + expect(response.status).toBe(404); + expect(response.body.error).toBe('Tag not found'); + }); + + it("should not allow deleting other user's tags", async () => { + const bcrypt = require('bcrypt'); + const otherUser = await User.create({ + email: 'other@example.com', + password_digest: await bcrypt.hash('password123', 10), + }); + + const otherTag = await Tag.create({ + name: 'other-tag', + user_id: otherUser.id, + }); + + const response = await agent.delete(`/api/tag/${otherTag.id}`); + + expect(response.status).toBe(404); + expect(response.body.error).toBe('Tag not found'); + }); + + it('should require authentication', async () => { + const response = await request(app).delete(`/api/tag/${tag.id}`); + + expect(response.status).toBe(401); + expect(response.body.error).toBe('Authentication required'); + }); }); - - it('should not allow access to other user\'s tags', async () => { - const bcrypt = require('bcrypt'); - const otherUser = await User.create({ - email: 'other@example.com', - password_digest: await bcrypt.hash('password123', 10) - }); - - const otherTag = await Tag.create({ - name: 'other-tag', - user_id: otherUser.id - }); - - const response = await agent.get(`/api/tag/${otherTag.id}`); - - expect(response.status).toBe(404); - expect(response.body.error).toBe('Tag not found'); - }); - - it('should require authentication', async () => { - const response = await request(app).get(`/api/tag/${tag.id}`); - - expect(response.status).toBe(401); - expect(response.body.error).toBe('Authentication required'); - }); - }); - - describe('PATCH /api/tag/:id', () => { - let tag; - - beforeEach(async () => { - tag = await Tag.create({ - name: 'work', - user_id: user.id - }); - }); - - it('should update tag', async () => { - const updateData = { - name: 'updated-work' - }; - - const response = await agent - .patch(`/api/tag/${tag.id}`) - .send(updateData); - - expect(response.status).toBe(200); - expect(response.body.name).toBe(updateData.name); - }); - - it('should return 404 for non-existent tag', async () => { - const response = await agent - .patch('/api/tag/999999') - .send({ name: 'Updated' }); - - expect(response.status).toBe(404); - expect(response.body.error).toBe('Tag not found'); - }); - - it('should not allow updating other user\'s tags', async () => { - const bcrypt = require('bcrypt'); - const otherUser = await User.create({ - email: 'other@example.com', - password_digest: await bcrypt.hash('password123', 10) - }); - - const otherTag = await Tag.create({ - name: 'other-tag', - user_id: otherUser.id - }); - - const response = await agent - .patch(`/api/tag/${otherTag.id}`) - .send({ name: 'Updated' }); - - expect(response.status).toBe(404); - expect(response.body.error).toBe('Tag not found'); - }); - - it('should require authentication', async () => { - const response = await request(app) - .patch(`/api/tag/${tag.id}`) - .send({ name: 'Updated' }); - - expect(response.status).toBe(401); - expect(response.body.error).toBe('Authentication required'); - }); - }); - - describe('DELETE /api/tag/:id', () => { - let tag; - - beforeEach(async () => { - tag = await Tag.create({ - name: 'work', - user_id: user.id - }); - }); - - it('should delete tag', async () => { - const response = await agent.delete(`/api/tag/${tag.id}`); - - expect(response.status).toBe(200); - expect(response.body.message).toBe('Tag successfully deleted'); - - // Verify tag is deleted - const deletedTag = await Tag.findByPk(tag.id); - expect(deletedTag).toBeNull(); - }); - - it('should return 404 for non-existent tag', async () => { - const response = await agent.delete('/api/tag/999999'); - - expect(response.status).toBe(404); - expect(response.body.error).toBe('Tag not found'); - }); - - it('should not allow deleting other user\'s tags', async () => { - const bcrypt = require('bcrypt'); - const otherUser = await User.create({ - email: 'other@example.com', - password_digest: await bcrypt.hash('password123', 10) - }); - - const otherTag = await Tag.create({ - name: 'other-tag', - user_id: otherUser.id - }); - - const response = await agent.delete(`/api/tag/${otherTag.id}`); - - expect(response.status).toBe(404); - expect(response.body.error).toBe('Tag not found'); - }); - - it('should require authentication', async () => { - const response = await request(app).delete(`/api/tag/${tag.id}`); - - expect(response.status).toBe(401); - expect(response.body.error).toBe('Authentication required'); - }); - }); -}); \ No newline at end of file +}); diff --git a/backend/tests/integration/tasks.test.js b/backend/tests/integration/tasks.test.js index c6026c0..337fd92 100644 --- a/backend/tests/integration/tasks.test.js +++ b/backend/tests/integration/tasks.test.js @@ -4,271 +4,260 @@ const { Task, User } = require('../../models'); const { createTestUser } = require('../helpers/testUtils'); describe('Tasks Routes', () => { - let user, agent; - - beforeEach(async () => { - user = await createTestUser({ - email: 'test@example.com' - }); - - // Create authenticated agent - agent = request.agent(app); - await agent - .post('/api/login') - .send({ - email: 'test@example.com', - password: 'password123' - }); - }); - - describe('POST /api/task', () => { - it('should create a new task', async () => { - const taskData = { - name: 'Test Task', - note: 'Test Note', - priority: 1, - status: 0 - }; - - const response = await agent - .post('/api/task') - .send(taskData); - - expect(response.status).toBe(201); - expect(response.body.id).toBeDefined(); - expect(response.body.name).toBe(taskData.name); - expect(response.body.note).toBe(taskData.note); - expect(response.body.priority).toBe(taskData.priority); - expect(response.body.status).toBe(taskData.status); - expect(response.body.user_id).toBe(user.id); - }); - - it('should require authentication', async () => { - const taskData = { - name: 'Test Task' - }; - - const response = await request(app) - .post('/api/task') - .send(taskData); - - expect(response.status).toBe(401); - expect(response.body.error).toBe('Authentication required'); - }); - - it('should require task name', async () => { - // Mock console.error to suppress expected error log in test output - const originalConsoleError = console.error; - console.error = jest.fn(); - - const taskData = { - description: 'Test Description' - }; - - const response = await agent - .post('/api/task') - .send(taskData); - - expect(response.status).toBe(400); - - // Restore original console.error - console.error = originalConsoleError; - }); - }); - - describe('GET /api/tasks', () => { - let task1, task2; + let user, agent; beforeEach(async () => { - task1 = await Task.create({ - name: 'Task 1', - description: 'Description 1', - user_id: user.id, - today: true - }); + user = await createTestUser({ + email: 'test@example.com', + }); - task2 = await Task.create({ - name: 'Task 2', - description: 'Description 2', - user_id: user.id, - today: false - }); + // Create authenticated agent + agent = request.agent(app); + await agent.post('/api/login').send({ + email: 'test@example.com', + password: 'password123', + }); }); - it('should get all user tasks', async () => { - const response = await agent.get('/api/tasks'); + describe('POST /api/task', () => { + it('should create a new task', async () => { + const taskData = { + name: 'Test Task', + note: 'Test Note', + priority: 1, + status: 0, + }; - expect(response.status).toBe(200); - expect(response.body.tasks).toBeDefined(); - expect(response.body.tasks.length).toBe(2); - expect(response.body.tasks.map(t => t.id)).toContain(task1.id); - expect(response.body.tasks.map(t => t.id)).toContain(task2.id); + const response = await agent.post('/api/task').send(taskData); + + expect(response.status).toBe(201); + expect(response.body.id).toBeDefined(); + expect(response.body.name).toBe(taskData.name); + expect(response.body.note).toBe(taskData.note); + expect(response.body.priority).toBe(taskData.priority); + expect(response.body.status).toBe(taskData.status); + expect(response.body.user_id).toBe(user.id); + }); + + it('should require authentication', async () => { + const taskData = { + name: 'Test Task', + }; + + const response = await request(app) + .post('/api/task') + .send(taskData); + + expect(response.status).toBe(401); + expect(response.body.error).toBe('Authentication required'); + }); + + it('should require task name', async () => { + // Mock console.error to suppress expected error log in test output + const originalConsoleError = console.error; + console.error = jest.fn(); + + const taskData = { + description: 'Test Description', + }; + + const response = await agent.post('/api/task').send(taskData); + + expect(response.status).toBe(400); + + // Restore original console.error + console.error = originalConsoleError; + }); }); - it('should filter today tasks (returns all user tasks)', async () => { - const response = await agent.get('/api/tasks?type=today'); + describe('GET /api/tasks', () => { + let task1, task2; - expect(response.status).toBe(200); - expect(response.body.tasks).toBeDefined(); - expect(response.body.tasks.length).toBe(2); - // Both tasks should be returned as "today" doesn't filter by the today field + beforeEach(async () => { + task1 = await Task.create({ + name: 'Task 1', + description: 'Description 1', + user_id: user.id, + today: true, + }); + + task2 = await Task.create({ + name: 'Task 2', + description: 'Description 2', + user_id: user.id, + today: false, + }); + }); + + it('should get all user tasks', async () => { + const response = await agent.get('/api/tasks'); + + expect(response.status).toBe(200); + expect(response.body.tasks).toBeDefined(); + expect(response.body.tasks.length).toBe(2); + expect(response.body.tasks.map((t) => t.id)).toContain(task1.id); + expect(response.body.tasks.map((t) => t.id)).toContain(task2.id); + }); + + it('should filter today tasks (returns all user tasks)', async () => { + const response = await agent.get('/api/tasks?type=today'); + + expect(response.status).toBe(200); + expect(response.body.tasks).toBeDefined(); + expect(response.body.tasks.length).toBe(2); + // Both tasks should be returned as "today" doesn't filter by the today field + }); + + it('should require authentication', async () => { + const response = await request(app).get('/api/tasks'); + + expect(response.status).toBe(401); + expect(response.body.error).toBe('Authentication required'); + }); }); - it('should require authentication', async () => { - const response = await request(app).get('/api/tasks'); + // Note: No individual task GET route exists in the current API - expect(response.status).toBe(401); - expect(response.body.error).toBe('Authentication required'); - }); - }); + describe('PATCH /api/task/:id', () => { + let task; - // Note: No individual task GET route exists in the current API + beforeEach(async () => { + task = await Task.create({ + name: 'Test Task', + description: 'Test Description', + priority: 0, + status: 0, + user_id: user.id, + }); + }); - describe('PATCH /api/task/:id', () => { - let task; + it('should update task', async () => { + const updateData = { + name: 'Updated Task', + note: 'Updated Note', + priority: 2, + status: 1, + }; - beforeEach(async () => { - task = await Task.create({ - name: 'Test Task', - description: 'Test Description', - priority: 0, - status: 0, - user_id: user.id - }); + const response = await agent + .patch(`/api/task/${task.id}`) + .send(updateData); + + expect(response.status).toBe(200); + expect(response.body.id).toBeDefined(); + expect(response.body.name).toBe(updateData.name); + expect(response.body.note).toBe(updateData.note); + expect(response.body.priority).toBe(updateData.priority); + expect(response.body.status).toBe(updateData.status); + }); + + it('should return 404 for non-existent task', async () => { + const response = await agent + .patch('/api/task/999999') + .send({ name: 'Updated' }); + + expect(response.status).toBe(404); + expect(response.body.error).toBe('Task not found.'); + }); + + it("should not allow updating other user's tasks", async () => { + const bcrypt = require('bcrypt'); + const otherUser = await User.create({ + email: 'other@example.com', + password_digest: await bcrypt.hash('password123', 10), + }); + + const otherTask = await Task.create({ + name: 'Other Task', + user_id: otherUser.id, + }); + + const response = await agent + .patch(`/api/task/${otherTask.id}`) + .send({ name: 'Updated' }); + + expect(response.status).toBe(404); + expect(response.body.error).toBe('Task not found.'); + }); + + it('should require authentication', async () => { + const response = await request(app) + .patch(`/api/task/${task.id}`) + .send({ name: 'Updated' }); + + expect(response.status).toBe(401); + expect(response.body.error).toBe('Authentication required'); + }); }); - it('should update task', async () => { - const updateData = { - name: 'Updated Task', - note: 'Updated Note', - priority: 2, - status: 1 - }; + describe('DELETE /api/task/:id', () => { + let task; - const response = await agent - .patch(`/api/task/${task.id}`) - .send(updateData); + beforeEach(async () => { + task = await Task.create({ + name: 'Test Task', + user_id: user.id, + }); + }); - expect(response.status).toBe(200); - expect(response.body.id).toBeDefined(); - expect(response.body.name).toBe(updateData.name); - expect(response.body.note).toBe(updateData.note); - expect(response.body.priority).toBe(updateData.priority); - expect(response.body.status).toBe(updateData.status); + it('should delete task', async () => { + const response = await agent.delete(`/api/task/${task.id}`); + + expect(response.status).toBe(200); + expect(response.body.message).toBe('Task successfully deleted'); + + // Verify task is deleted + const deletedTask = await Task.findByPk(task.id); + expect(deletedTask).toBeNull(); + }); + + it('should return 404 for non-existent task', async () => { + const response = await agent.delete('/api/task/999999'); + + expect(response.status).toBe(404); + expect(response.body.error).toBe('Task not found.'); + }); + + it("should not allow deleting other user's tasks", async () => { + const bcrypt = require('bcrypt'); + const otherUser = await User.create({ + email: 'other@example.com', + password_digest: await bcrypt.hash('password123', 10), + }); + + const otherTask = await Task.create({ + name: 'Other Task', + user_id: otherUser.id, + }); + + const response = await agent.delete(`/api/task/${otherTask.id}`); + + expect(response.status).toBe(404); + expect(response.body.error).toBe('Task not found.'); + }); + + it('should require authentication', async () => { + const response = await request(app).delete(`/api/task/${task.id}`); + + expect(response.status).toBe(401); + expect(response.body.error).toBe('Authentication required'); + }); }); - it('should return 404 for non-existent task', async () => { - const response = await agent - .patch('/api/task/999999') - .send({ name: 'Updated' }); + describe('Task with tags', () => { + it('should create task with tags', async () => { + const taskData = { + name: 'Test Task', + tags: [{ name: 'work' }, { name: 'urgent' }], + }; - expect(response.status).toBe(404); - expect(response.body.error).toBe('Task not found.'); + const response = await agent.post('/api/task').send(taskData); + + expect(response.status).toBe(201); + expect(response.body.Tags).toBeDefined(); + expect(response.body.Tags.length).toBe(2); + expect(response.body.Tags.map((t) => t.name)).toContain('work'); + expect(response.body.Tags.map((t) => t.name)).toContain('urgent'); + }); }); - - it('should not allow updating other user\'s tasks', async () => { - const bcrypt = require('bcrypt'); - const otherUser = await User.create({ - email: 'other@example.com', - password_digest: await bcrypt.hash('password123', 10) - }); - - const otherTask = await Task.create({ - name: 'Other Task', - user_id: otherUser.id - }); - - const response = await agent - .patch(`/api/task/${otherTask.id}`) - .send({ name: 'Updated' }); - - expect(response.status).toBe(404); - expect(response.body.error).toBe('Task not found.'); - }); - - it('should require authentication', async () => { - const response = await request(app) - .patch(`/api/task/${task.id}`) - .send({ name: 'Updated' }); - - expect(response.status).toBe(401); - expect(response.body.error).toBe('Authentication required'); - }); - }); - - describe('DELETE /api/task/:id', () => { - let task; - - beforeEach(async () => { - task = await Task.create({ - name: 'Test Task', - user_id: user.id - }); - }); - - it('should delete task', async () => { - const response = await agent.delete(`/api/task/${task.id}`); - - expect(response.status).toBe(200); - expect(response.body.message).toBe('Task successfully deleted'); - - // Verify task is deleted - const deletedTask = await Task.findByPk(task.id); - expect(deletedTask).toBeNull(); - }); - - it('should return 404 for non-existent task', async () => { - const response = await agent.delete('/api/task/999999'); - - expect(response.status).toBe(404); - expect(response.body.error).toBe('Task not found.'); - }); - - it('should not allow deleting other user\'s tasks', async () => { - const bcrypt = require('bcrypt'); - const otherUser = await User.create({ - email: 'other@example.com', - password_digest: await bcrypt.hash('password123', 10) - }); - - const otherTask = await Task.create({ - name: 'Other Task', - user_id: otherUser.id - }); - - const response = await agent.delete(`/api/task/${otherTask.id}`); - - expect(response.status).toBe(404); - expect(response.body.error).toBe('Task not found.'); - }); - - it('should require authentication', async () => { - const response = await request(app).delete(`/api/task/${task.id}`); - - expect(response.status).toBe(401); - expect(response.body.error).toBe('Authentication required'); - }); - }); - - describe('Task with tags', () => { - it('should create task with tags', async () => { - const taskData = { - name: 'Test Task', - tags: [ - { name: 'work' }, - { name: 'urgent' } - ] - }; - - const response = await agent - .post('/api/task') - .send(taskData); - - expect(response.status).toBe(201); - expect(response.body.Tags).toBeDefined(); - expect(response.body.Tags.length).toBe(2); - expect(response.body.Tags.map(t => t.name)).toContain('work'); - expect(response.body.Tags.map(t => t.name)).toContain('urgent'); - }); - }); -}); \ No newline at end of file +}); diff --git a/backend/tests/integration/telegram-duplicate-scenario.test.js b/backend/tests/integration/telegram-duplicate-scenario.test.js index d581e9f..f036f73 100644 --- a/backend/tests/integration/telegram-duplicate-scenario.test.js +++ b/backend/tests/integration/telegram-duplicate-scenario.test.js @@ -3,363 +3,399 @@ const telegramPoller = require('../../services/telegramPoller'); // Mock the HTTPS module to simulate Telegram API responses jest.mock('https', () => { - const mockResponse = { - on: jest.fn((event, callback) => { - if (event === 'data') { - // Simulate API response with duplicate updates - callback(JSON.stringify({ - ok: true, - result: [ - { - update_id: 1001, - message: { - message_id: 123, - text: 'Buy groceries from the store', - chat: { id: 987654321 }, - date: Math.floor(Date.now() / 1000) - } + const mockResponse = { + on: jest.fn((event, callback) => { + if (event === 'data') { + // Simulate API response with duplicate updates + callback( + JSON.stringify({ + ok: true, + result: [ + { + update_id: 1001, + message: { + message_id: 123, + text: 'Buy groceries from the store', + chat: { id: 987654321 }, + date: Math.floor(Date.now() / 1000), + }, + }, + ], + }) + ); + } else if (event === 'end') { + callback(); } - ] - })); - } else if (event === 'end') { - callback(); - } - }) - }; + }), + }; - const mockRequest = { - on: jest.fn(), - write: jest.fn(), - end: jest.fn() - }; + const mockRequest = { + on: jest.fn(), + write: jest.fn(), + end: jest.fn(), + }; - return { - get: jest.fn((url, options, callback) => { - callback(mockResponse); - return mockRequest; - }), - request: jest.fn((url, options, callback) => { - callback(mockResponse); - return mockRequest; - }) - }; + return { + get: jest.fn((url, options, callback) => { + callback(mockResponse); + return mockRequest; + }), + request: jest.fn((url, options, callback) => { + callback(mockResponse); + return mockRequest; + }), + }; }); describe('Telegram Duplicate Message Scenario', () => { - let testUser; - let consoleMessages; + let testUser; + let consoleMessages; - beforeAll(async () => { - await sequelize.sync({ force: true }); - - // Capture console logs - consoleMessages = []; - const originalConsoleLog = console.log; - console.log = (...args) => { - consoleMessages.push(args.join(' ')); - originalConsoleLog(...args); - }; - }); + beforeAll(async () => { + await sequelize.sync({ force: true }); - beforeEach(async () => { - consoleMessages = []; - - // Create test user with Telegram configuration - testUser = await User.create({ - email: 'telegram-user@example.com', - password_digest: 'hashedpassword', - telegram_bot_token: 'real-bot-token-456', - telegram_chat_id: '987654321' + // Capture console logs + consoleMessages = []; + const originalConsoleLog = console.log; + console.log = (...args) => { + consoleMessages.push(args.join(' ')); + originalConsoleLog(...args); + }; }); - // Clear inbox - await InboxItem.destroy({ where: {} }); - - // Reset poller - telegramPoller.stopPolling(); - }); + beforeEach(async () => { + consoleMessages = []; - afterEach(async () => { - telegramPoller.stopPolling(); - await User.destroy({ where: {} }); - await InboxItem.destroy({ where: {} }); - }); + // Create test user with Telegram configuration + testUser = await User.create({ + email: 'telegram-user@example.com', + password_digest: 'hashedpassword', + telegram_bot_token: 'real-bot-token-456', + telegram_chat_id: '987654321', + }); - afterAll(async () => { - await sequelize.close(); - }); + // Clear inbox + await InboxItem.destroy({ where: {} }); - describe('Real-world Duplicate Scenarios', () => { - test('should prevent duplicates when same message is processed twice due to network issues', async () => { - const messageContent = 'Buy groceries from the store'; - const messageId = 123; - const updateId = 1001; - - // Simulate first message processing - const inboxItem1 = await InboxItem.create({ - content: messageContent, - source: 'telegram', - user_id: testUser.id, - metadata: { telegram_message_id: messageId } - }); - - // Wait a moment (simulating network delay) - await new Promise(resolve => setTimeout(resolve, 50)); - - // Simulate duplicate processing attempt (same message, different processing cycle) - const recentCutoff = new Date(Date.now() - 30000); - const existingItem = await InboxItem.findOne({ - where: { - content: messageContent, - user_id: testUser.id, - source: 'telegram', - created_at: { - [require('sequelize').Op.gte]: recentCutoff - } - } - }); - - // Should find the existing item - expect(existingItem).toBeTruthy(); - expect(existingItem.id).toBe(inboxItem1.id); - - // Verify only one item exists - const allItems = await InboxItem.findAll({ - where: { user_id: testUser.id } - }); - expect(allItems).toHaveLength(1); + // Reset poller + telegramPoller.stopPolling(); }); - test('should handle rapid consecutive messages without creating duplicates', async () => { - const messages = [ - { content: 'First message', messageId: 201, updateId: 2001 }, - { content: 'Second message', messageId: 202, updateId: 2002 }, - { content: 'First message', messageId: 203, updateId: 2003 }, // Duplicate content - { content: 'Third message', messageId: 204, updateId: 2004 } - ]; + afterEach(async () => { + telegramPoller.stopPolling(); + await User.destroy({ where: {} }); + await InboxItem.destroy({ where: {} }); + }); - // Process all messages rapidly - const createdItems = []; - for (const msg of messages) { - try { - // Check for existing item first (simulating the duplicate prevention logic) - const existingItem = await InboxItem.findOne({ - where: { - content: msg.content, - user_id: testUser.id, - source: 'telegram', - created_at: { - [require('sequelize').Op.gte]: new Date(Date.now() - 30000) - } - } - }); + afterAll(async () => { + await sequelize.close(); + }); - if (existingItem) { - console.log(`Duplicate detected: "${msg.content}"`); - createdItems.push(existingItem); - } else { - const newItem = await InboxItem.create({ - content: msg.content, - source: 'telegram', - user_id: testUser.id, - metadata: { telegram_message_id: msg.messageId } + describe('Real-world Duplicate Scenarios', () => { + test('should prevent duplicates when same message is processed twice due to network issues', async () => { + const messageContent = 'Buy groceries from the store'; + const messageId = 123; + const updateId = 1001; + + // Simulate first message processing + const inboxItem1 = await InboxItem.create({ + content: messageContent, + source: 'telegram', + user_id: testUser.id, + metadata: { telegram_message_id: messageId }, }); - createdItems.push(newItem); - } - } catch (error) { - console.error('Error processing message:', error); - } - } - // Should have 3 unique items (first, second, third) - duplicate "First message" should be prevented - const allItems = await InboxItem.findAll({ - where: { user_id: testUser.id } - }); - - expect(allItems).toHaveLength(3); - - const contentCounts = allItems.reduce((acc, item) => { - acc[item.content] = (acc[item.content] || 0) + 1; - return acc; - }, {}); - - expect(contentCounts['First message']).toBe(1); - expect(contentCounts['Second message']).toBe(1); - expect(contentCounts['Third message']).toBe(1); - }); + // Wait a moment (simulating network delay) + await new Promise((resolve) => setTimeout(resolve, 50)); - test('should track update IDs correctly to prevent reprocessing', async () => { - // Simulate the internal update tracking logic - const processedUpdates = new Set(); - - const updates = [ - { update_id: 3001, message: { text: 'Message 1', message_id: 301, chat: { id: 987654321 } } }, - { update_id: 3002, message: { text: 'Message 2', message_id: 302, chat: { id: 987654321 } } }, - { update_id: 3001, message: { text: 'Message 1', message_id: 301, chat: { id: 987654321 } } }, // Duplicate update - { update_id: 3003, message: { text: 'Message 3', message_id: 303, chat: { id: 987654321 } } } - ]; + // Simulate duplicate processing attempt (same message, different processing cycle) + const recentCutoff = new Date(Date.now() - 30000); + const existingItem = await InboxItem.findOne({ + where: { + content: messageContent, + user_id: testUser.id, + source: 'telegram', + created_at: { + [require('sequelize').Op.gte]: recentCutoff, + }, + }, + }); - const processedCount = { count: 0 }; - - for (const update of updates) { - const updateKey = `${testUser.id}-${update.update_id}`; - - if (!processedUpdates.has(updateKey)) { - // Simulate processing the update - processedUpdates.add(updateKey); - processedCount.count++; - - // Simulate creating inbox item - await InboxItem.create({ - content: update.message.text, - source: 'telegram', - user_id: testUser.id, - metadata: { - telegram_message_id: update.message.message_id, - update_id: update.update_id + // Should find the existing item + expect(existingItem).toBeTruthy(); + expect(existingItem.id).toBe(inboxItem1.id); + + // Verify only one item exists + const allItems = await InboxItem.findAll({ + where: { user_id: testUser.id }, + }); + expect(allItems).toHaveLength(1); + }); + + test('should handle rapid consecutive messages without creating duplicates', async () => { + const messages = [ + { content: 'First message', messageId: 201, updateId: 2001 }, + { content: 'Second message', messageId: 202, updateId: 2002 }, + { content: 'First message', messageId: 203, updateId: 2003 }, // Duplicate content + { content: 'Third message', messageId: 204, updateId: 2004 }, + ]; + + // Process all messages rapidly + const createdItems = []; + for (const msg of messages) { + try { + // Check for existing item first (simulating the duplicate prevention logic) + const existingItem = await InboxItem.findOne({ + where: { + content: msg.content, + user_id: testUser.id, + source: 'telegram', + created_at: { + [require('sequelize').Op.gte]: new Date( + Date.now() - 30000 + ), + }, + }, + }); + + if (existingItem) { + console.log(`Duplicate detected: "${msg.content}"`); + createdItems.push(existingItem); + } else { + const newItem = await InboxItem.create({ + content: msg.content, + source: 'telegram', + user_id: testUser.id, + metadata: { telegram_message_id: msg.messageId }, + }); + createdItems.push(newItem); + } + } catch (error) { + console.error('Error processing message:', error); + } } - }); - } else { - console.log(`Skipping already processed update: ${update.update_id}`); - } - } - // Should have processed 3 unique updates (3001, 3002, 3003) - expect(processedCount.count).toBe(3); - expect(processedUpdates.size).toBe(3); - - // Verify inbox items - const allItems = await InboxItem.findAll({ - where: { user_id: testUser.id } - }); - expect(allItems).toHaveLength(3); + // Should have 3 unique items (first, second, third) - duplicate "First message" should be prevented + const allItems = await InboxItem.findAll({ + where: { user_id: testUser.id }, + }); + + expect(allItems).toHaveLength(3); + + const contentCounts = allItems.reduce((acc, item) => { + acc[item.content] = (acc[item.content] || 0) + 1; + return acc; + }, {}); + + expect(contentCounts['First message']).toBe(1); + expect(contentCounts['Second message']).toBe(1); + expect(contentCounts['Third message']).toBe(1); + }); + + test('should track update IDs correctly to prevent reprocessing', async () => { + // Simulate the internal update tracking logic + const processedUpdates = new Set(); + + const updates = [ + { + update_id: 3001, + message: { + text: 'Message 1', + message_id: 301, + chat: { id: 987654321 }, + }, + }, + { + update_id: 3002, + message: { + text: 'Message 2', + message_id: 302, + chat: { id: 987654321 }, + }, + }, + { + update_id: 3001, + message: { + text: 'Message 1', + message_id: 301, + chat: { id: 987654321 }, + }, + }, // Duplicate update + { + update_id: 3003, + message: { + text: 'Message 3', + message_id: 303, + chat: { id: 987654321 }, + }, + }, + ]; + + const processedCount = { count: 0 }; + + for (const update of updates) { + const updateKey = `${testUser.id}-${update.update_id}`; + + if (!processedUpdates.has(updateKey)) { + // Simulate processing the update + processedUpdates.add(updateKey); + processedCount.count++; + + // Simulate creating inbox item + await InboxItem.create({ + content: update.message.text, + source: 'telegram', + user_id: testUser.id, + metadata: { + telegram_message_id: update.message.message_id, + update_id: update.update_id, + }, + }); + } else { + console.log( + `Skipping already processed update: ${update.update_id}` + ); + } + } + + // Should have processed 3 unique updates (3001, 3002, 3003) + expect(processedCount.count).toBe(3); + expect(processedUpdates.size).toBe(3); + + // Verify inbox items + const allItems = await InboxItem.findAll({ + where: { user_id: testUser.id }, + }); + expect(allItems).toHaveLength(3); + }); + + test('should handle poller restart without creating duplicates', async () => { + // Create initial inbox item + const initialItem = await InboxItem.create({ + content: 'Message before restart', + source: 'telegram', + user_id: testUser.id, + metadata: { telegram_message_id: 401, update_id: 4001 }, + }); + + // Add user to poller + await telegramPoller.addUser(testUser); + + // Simulate getting status + let status = telegramPoller.getStatus(); + expect(status.running).toBe(true); + expect(status.usersCount).toBe(1); + + // Stop poller (simulating restart) + telegramPoller.stopPolling(); + status = telegramPoller.getStatus(); + expect(status.running).toBe(false); + + // Start again + await telegramPoller.addUser(testUser); + status = telegramPoller.getStatus(); + expect(status.running).toBe(true); + + // The poller should maintain its state correctly + const allItems = await InboxItem.findAll({ + where: { user_id: testUser.id }, + }); + expect(allItems).toHaveLength(1); + expect(allItems[0].id).toBe(initialItem.id); + }); + + test('should cleanup old processed updates to prevent memory leaks', async () => { + // Test the memory management logic + const processedUpdates = new Set(); + + // Add many processed updates + for (let i = 1; i <= 1200; i++) { + processedUpdates.add(`${testUser.id}-${i}`); + } + + expect(processedUpdates.size).toBe(1200); + + // Simulate the cleanup logic (keeping only 1000 most recent) + if (processedUpdates.size > 1000) { + const allEntries = Array.from(processedUpdates); + const oldestEntries = allEntries.slice(0, 200); // Remove oldest 200 + oldestEntries.forEach((entry) => + processedUpdates.delete(entry) + ); + } + + expect(processedUpdates.size).toBe(1000); + + // Verify oldest entries are removed + expect(processedUpdates.has(`${testUser.id}-1`)).toBe(false); + expect(processedUpdates.has(`${testUser.id}-200`)).toBe(false); + + // Verify newest entries are kept + expect(processedUpdates.has(`${testUser.id}-1200`)).toBe(true); + expect(processedUpdates.has(`${testUser.id}-1000`)).toBe(true); + }); }); - test('should handle poller restart without creating duplicates', async () => { - // Create initial inbox item - const initialItem = await InboxItem.create({ - content: 'Message before restart', - source: 'telegram', - user_id: testUser.id, - metadata: { telegram_message_id: 401, update_id: 4001 } - }); + describe('Edge Cases', () => { + test('should handle identical messages from different Telegram message IDs', async () => { + const messageContent = 'Identical content'; - // Add user to poller - await telegramPoller.addUser(testUser); - - // Simulate getting status - let status = telegramPoller.getStatus(); - expect(status.running).toBe(true); - expect(status.usersCount).toBe(1); + // Create first message + const item1 = await InboxItem.create({ + content: messageContent, + source: 'telegram', + user_id: testUser.id, + metadata: { telegram_message_id: 501 }, + }); - // Stop poller (simulating restart) - telegramPoller.stopPolling(); - status = telegramPoller.getStatus(); - expect(status.running).toBe(false); + // Wait a moment + await new Promise((resolve) => setTimeout(resolve, 100)); - // Start again - await telegramPoller.addUser(testUser); - status = telegramPoller.getStatus(); - expect(status.running).toBe(true); + // Try to create with same content but different message ID + // This should be prevented by the content-based duplicate check + const recentCutoff = new Date(Date.now() - 30000); + const existingItem = await InboxItem.findOne({ + where: { + content: messageContent, + user_id: testUser.id, + source: 'telegram', + created_at: { + [require('sequelize').Op.gte]: recentCutoff, + }, + }, + }); - // The poller should maintain its state correctly - const allItems = await InboxItem.findAll({ - where: { user_id: testUser.id } - }); - expect(allItems).toHaveLength(1); - expect(allItems[0].id).toBe(initialItem.id); + expect(existingItem).toBeTruthy(); + expect(existingItem.id).toBe(item1.id); + }); + + test('should allow same content after time window expires', async () => { + const messageContent = 'Time-based test message'; + + // Create first item with old timestamp + const oldTimestamp = new Date(Date.now() - 35000); // 35 seconds ago + await InboxItem.create({ + content: messageContent, + source: 'telegram', + user_id: testUser.id, + created_at: oldTimestamp, + updated_at: oldTimestamp, + metadata: { telegram_message_id: 601 }, + }); + + // Now try to create new item with same content + const newItem = await InboxItem.create({ + content: messageContent, + source: 'telegram', + user_id: testUser.id, + metadata: { telegram_message_id: 602 }, + }); + + // Should be allowed since the old one is outside the 30-second window + const allItems = await InboxItem.findAll({ + where: { user_id: testUser.id }, + }); + expect(allItems).toHaveLength(2); + }); }); - - test('should cleanup old processed updates to prevent memory leaks', async () => { - // Test the memory management logic - const processedUpdates = new Set(); - - // Add many processed updates - for (let i = 1; i <= 1200; i++) { - processedUpdates.add(`${testUser.id}-${i}`); - } - - expect(processedUpdates.size).toBe(1200); - - // Simulate the cleanup logic (keeping only 1000 most recent) - if (processedUpdates.size > 1000) { - const allEntries = Array.from(processedUpdates); - const oldestEntries = allEntries.slice(0, 200); // Remove oldest 200 - oldestEntries.forEach(entry => processedUpdates.delete(entry)); - } - - expect(processedUpdates.size).toBe(1000); - - // Verify oldest entries are removed - expect(processedUpdates.has(`${testUser.id}-1`)).toBe(false); - expect(processedUpdates.has(`${testUser.id}-200`)).toBe(false); - - // Verify newest entries are kept - expect(processedUpdates.has(`${testUser.id}-1200`)).toBe(true); - expect(processedUpdates.has(`${testUser.id}-1000`)).toBe(true); - }); - }); - - describe('Edge Cases', () => { - test('should handle identical messages from different Telegram message IDs', async () => { - const messageContent = 'Identical content'; - - // Create first message - const item1 = await InboxItem.create({ - content: messageContent, - source: 'telegram', - user_id: testUser.id, - metadata: { telegram_message_id: 501 } - }); - - // Wait a moment - await new Promise(resolve => setTimeout(resolve, 100)); - - // Try to create with same content but different message ID - // This should be prevented by the content-based duplicate check - const recentCutoff = new Date(Date.now() - 30000); - const existingItem = await InboxItem.findOne({ - where: { - content: messageContent, - user_id: testUser.id, - source: 'telegram', - created_at: { - [require('sequelize').Op.gte]: recentCutoff - } - } - }); - - expect(existingItem).toBeTruthy(); - expect(existingItem.id).toBe(item1.id); - }); - - test('should allow same content after time window expires', async () => { - const messageContent = 'Time-based test message'; - - // Create first item with old timestamp - const oldTimestamp = new Date(Date.now() - 35000); // 35 seconds ago - await InboxItem.create({ - content: messageContent, - source: 'telegram', - user_id: testUser.id, - created_at: oldTimestamp, - updated_at: oldTimestamp, - metadata: { telegram_message_id: 601 } - }); - - // Now try to create new item with same content - const newItem = await InboxItem.create({ - content: messageContent, - source: 'telegram', - user_id: testUser.id, - metadata: { telegram_message_id: 602 } - }); - - // Should be allowed since the old one is outside the 30-second window - const allItems = await InboxItem.findAll({ - where: { user_id: testUser.id } - }); - expect(allItems).toHaveLength(2); - }); - }); -}); \ No newline at end of file +}); diff --git a/backend/tests/integration/telegram-duplicates.test.js b/backend/tests/integration/telegram-duplicates.test.js index cd69161..f8e602b 100644 --- a/backend/tests/integration/telegram-duplicates.test.js +++ b/backend/tests/integration/telegram-duplicates.test.js @@ -4,322 +4,339 @@ const { User, InboxItem, sequelize } = require('../../models'); const telegramPoller = require('../../services/telegramPoller'); describe('Telegram Duplicate Prevention Integration Tests', () => { - let testUser; - let originalConsoleLog; - let logMessages; + let testUser; + let originalConsoleLog; + let logMessages; - beforeAll(async () => { - // Capture console.log for verification - originalConsoleLog = console.log; - logMessages = []; - console.log = (...args) => { - logMessages.push(args.join(' ')); - originalConsoleLog(...args); - }; + beforeAll(async () => { + // Capture console.log for verification + originalConsoleLog = console.log; + logMessages = []; + console.log = (...args) => { + logMessages.push(args.join(' ')); + originalConsoleLog(...args); + }; - await sequelize.sync({ force: true }); - }); - - beforeEach(async () => { - logMessages = []; - - // Create test user - testUser = await User.create({ - email: 'test-telegram@example.com', - password_digest: 'hashedpassword', - telegram_bot_token: 'test-bot-token-123', - telegram_chat_id: '987654321' + await sequelize.sync({ force: true }); }); - // Clear any existing inbox items - await InboxItem.destroy({ where: {} }); - - // Stop and reset poller - telegramPoller.stopPolling(); - }); + beforeEach(async () => { + logMessages = []; - afterEach(async () => { - telegramPoller.stopPolling(); - await User.destroy({ where: {} }); - await InboxItem.destroy({ where: {} }); - }); - - afterAll(async () => { - console.log = originalConsoleLog; - await sequelize.close(); - }); - - describe('Database-level Duplicate Prevention', () => { - test('should prevent duplicate inbox items with same content within 30 seconds', async () => { - const messageContent = 'Test duplicate message'; - - // Create first inbox item - const item1 = await InboxItem.create({ - content: messageContent, - source: 'telegram', - user_id: testUser.id, - metadata: { telegram_message_id: 123 } - }); - - // Wait a moment - await new Promise(resolve => setTimeout(resolve, 100)); - - // Try to create duplicate item (should be prevented) - const duplicateCheck = await InboxItem.findOne({ - where: { - content: messageContent, - user_id: testUser.id, - source: 'telegram', - created_at: { - [require('sequelize').Op.gte]: new Date(Date.now() - 30000) - } - } - }); - - expect(duplicateCheck).toBeTruthy(); - expect(duplicateCheck.id).toBe(item1.id); - - // Verify only one item exists - const allItems = await InboxItem.findAll({ - where: { user_id: testUser.id } - }); - expect(allItems).toHaveLength(1); - }); - - test('should allow duplicate content after 30 seconds', async () => { - const messageContent = 'Test time-based duplicate'; - - // Create first item with backdated timestamp - await InboxItem.create({ - content: messageContent, - source: 'telegram', - user_id: testUser.id, - created_at: new Date(Date.now() - 35000), // 35 seconds ago - metadata: { telegram_message_id: 124 } - }); - - // Create second item (should be allowed) - const item2 = await InboxItem.create({ - content: messageContent, - source: 'telegram', - user_id: testUser.id, - metadata: { telegram_message_id: 125 } - }); - - // Verify both items exist - const allItems = await InboxItem.findAll({ - where: { user_id: testUser.id } - }); - expect(allItems).toHaveLength(2); - }); - - test('should allow same content for different users', async () => { - // Create second user - const testUser2 = await User.create({ - email: 'test2-telegram@example.com', - password_digest: 'hashedpassword', - telegram_bot_token: 'test-bot-token-456', - telegram_chat_id: '123456789' - }); - - const messageContent = 'Shared message content'; - - // Create item for first user - await InboxItem.create({ - content: messageContent, - source: 'telegram', - user_id: testUser.id, - metadata: { telegram_message_id: 126 } - }); - - // Create item for second user (should be allowed) - await InboxItem.create({ - content: messageContent, - source: 'telegram', - user_id: testUser2.id, - metadata: { telegram_message_id: 127 } - }); - - // Verify both items exist - const allItems = await InboxItem.findAll(); - expect(allItems).toHaveLength(2); - - const user1Items = allItems.filter(item => item.user_id === testUser.id); - const user2Items = allItems.filter(item => item.user_id === testUser2.id); - - expect(user1Items).toHaveLength(1); - expect(user2Items).toHaveLength(1); - }); - }); - - describe('Poller State Management', () => { - test('should add and remove users correctly', async () => { - const initialStatus = telegramPoller.getStatus(); - expect(initialStatus.usersCount).toBe(0); - expect(initialStatus.running).toBe(false); - - // Add user - const addResult = await telegramPoller.addUser(testUser); - expect(addResult).toBe(true); - - const statusAfterAdd = telegramPoller.getStatus(); - expect(statusAfterAdd.usersCount).toBe(1); - expect(statusAfterAdd.running).toBe(true); - - // Remove user - const removeResult = telegramPoller.removeUser(testUser.id); - expect(removeResult).toBe(true); - - const statusAfterRemove = telegramPoller.getStatus(); - expect(statusAfterRemove.usersCount).toBe(0); - expect(statusAfterRemove.running).toBe(false); - }); - - test('should not add user without telegram token', async () => { - const userWithoutToken = await User.create({ - email: 'no-token@example.com', - password_digest: 'hashedpassword' - // No telegram_bot_token - }); - - const addResult = await telegramPoller.addUser(userWithoutToken); - expect(addResult).toBe(false); - - const status = telegramPoller.getStatus(); - expect(status.usersCount).toBe(0); - }); - - test('should handle adding same user multiple times', async () => { - // Add user first time - await telegramPoller.addUser(testUser); - const status1 = telegramPoller.getStatus(); - expect(status1.usersCount).toBe(1); - - // Add same user again - await telegramPoller.addUser(testUser); - const status2 = telegramPoller.getStatus(); - expect(status2.usersCount).toBe(1); // Should still be 1 - }); - }); - - describe('Update Processing Logic', () => { - test('should handle updates with proper ID tracking', async () => { - await telegramPoller.addUser(testUser); - - // Simulate updates (this tests the internal logic without actual HTTP calls) - const mockUpdates = [ - { - update_id: 1001, - message: { - message_id: 501, - text: 'First message', - chat: { id: 987654321 } - } - }, - { - update_id: 1002, - message: { - message_id: 502, - text: 'Second message', - chat: { id: 987654321 } - } - } - ]; - - // Test highest update ID calculation - const highestId = telegramPoller._getHighestUpdateId(mockUpdates); - expect(highestId).toBe(1002); - - // Test update key generation (simulating internal logic) - const updateKeys = mockUpdates.map(update => `${testUser.id}-${update.update_id}`); - expect(updateKeys).toEqual([`${testUser.id}-1001`, `${testUser.id}-1002`]); - }); - - test('should properly track processed updates', async () => { - // Test the Set-based tracking logic - const processedUpdates = new Set(); - - // Add some processed updates - processedUpdates.add('1-1001'); - processedUpdates.add('1-1002'); - - // Test filtering logic - const newUpdates = [ - { update_id: 1001 }, // Should be filtered out - { update_id: 1002 }, // Should be filtered out - { update_id: 1003 } // Should remain - ].filter(update => { - const updateKey = `1-${update.update_id}`; - return !processedUpdates.has(updateKey); - }); - - expect(newUpdates).toHaveLength(1); - expect(newUpdates[0].update_id).toBe(1003); - }); - - test('should handle memory management for processed updates', async () => { - // Simulate the cleanup logic - const processedUpdates = new Set(); - - // Add many updates (more than the 1000 limit) - for (let i = 1; i <= 1100; i++) { - processedUpdates.add(`1-${i}`); - } - - expect(processedUpdates.size).toBe(1100); - - // Simulate cleanup (remove oldest 100) - if (processedUpdates.size > 1000) { - const oldestEntries = Array.from(processedUpdates).slice(0, 100); - oldestEntries.forEach(entry => processedUpdates.delete(entry)); - } - - expect(processedUpdates.size).toBe(1000); - expect(processedUpdates.has('1-1')).toBe(false); // Oldest should be removed - expect(processedUpdates.has('1-1100')).toBe(true); // Newest should remain - }); - }); - - describe('Error Handling', () => { - test('should handle database errors gracefully', async () => { - // Mock InboxItem.create to throw an error - const originalCreate = InboxItem.create; - InboxItem.create = jest.fn().mockRejectedValue(new Error('Database error')); - - try { - await InboxItem.create({ - content: 'Test error handling', - source: 'telegram', - user_id: testUser.id + // Create test user + testUser = await User.create({ + email: 'test-telegram@example.com', + password_digest: 'hashedpassword', + telegram_bot_token: 'test-bot-token-123', + telegram_chat_id: '987654321', }); - } catch (error) { - expect(error.message).toBe('Database error'); - } - - // Restore original function - InboxItem.create = originalCreate; + + // Clear any existing inbox items + await InboxItem.destroy({ where: {} }); + + // Stop and reset poller + telegramPoller.stopPolling(); }); - test('should handle invalid user data', async () => { - const invalidUser = null; - const addResult = await telegramPoller.addUser(invalidUser); - expect(addResult).toBe(false); + afterEach(async () => { + telegramPoller.stopPolling(); + await User.destroy({ where: {} }); + await InboxItem.destroy({ where: {} }); }); - test('should handle missing message properties', async () => { - // Test the processing logic with incomplete message data - const incompleteUpdate = { - update_id: 2001, - message: { - // Missing text and other properties - message_id: 601, - chat: { id: 987654321 } - } - }; - - // The actual processing would skip this message due to missing text - const hasText = incompleteUpdate.message && incompleteUpdate.message.text; - expect(hasText).toBeFalsy(); + afterAll(async () => { + console.log = originalConsoleLog; + await sequelize.close(); }); - }); -}); \ No newline at end of file + + describe('Database-level Duplicate Prevention', () => { + test('should prevent duplicate inbox items with same content within 30 seconds', async () => { + const messageContent = 'Test duplicate message'; + + // Create first inbox item + const item1 = await InboxItem.create({ + content: messageContent, + source: 'telegram', + user_id: testUser.id, + metadata: { telegram_message_id: 123 }, + }); + + // Wait a moment + await new Promise((resolve) => setTimeout(resolve, 100)); + + // Try to create duplicate item (should be prevented) + const duplicateCheck = await InboxItem.findOne({ + where: { + content: messageContent, + user_id: testUser.id, + source: 'telegram', + created_at: { + [require('sequelize').Op.gte]: new Date( + Date.now() - 30000 + ), + }, + }, + }); + + expect(duplicateCheck).toBeTruthy(); + expect(duplicateCheck.id).toBe(item1.id); + + // Verify only one item exists + const allItems = await InboxItem.findAll({ + where: { user_id: testUser.id }, + }); + expect(allItems).toHaveLength(1); + }); + + test('should allow duplicate content after 30 seconds', async () => { + const messageContent = 'Test time-based duplicate'; + + // Create first item with backdated timestamp + await InboxItem.create({ + content: messageContent, + source: 'telegram', + user_id: testUser.id, + created_at: new Date(Date.now() - 35000), // 35 seconds ago + metadata: { telegram_message_id: 124 }, + }); + + // Create second item (should be allowed) + const item2 = await InboxItem.create({ + content: messageContent, + source: 'telegram', + user_id: testUser.id, + metadata: { telegram_message_id: 125 }, + }); + + // Verify both items exist + const allItems = await InboxItem.findAll({ + where: { user_id: testUser.id }, + }); + expect(allItems).toHaveLength(2); + }); + + test('should allow same content for different users', async () => { + // Create second user + const testUser2 = await User.create({ + email: 'test2-telegram@example.com', + password_digest: 'hashedpassword', + telegram_bot_token: 'test-bot-token-456', + telegram_chat_id: '123456789', + }); + + const messageContent = 'Shared message content'; + + // Create item for first user + await InboxItem.create({ + content: messageContent, + source: 'telegram', + user_id: testUser.id, + metadata: { telegram_message_id: 126 }, + }); + + // Create item for second user (should be allowed) + await InboxItem.create({ + content: messageContent, + source: 'telegram', + user_id: testUser2.id, + metadata: { telegram_message_id: 127 }, + }); + + // Verify both items exist + const allItems = await InboxItem.findAll(); + expect(allItems).toHaveLength(2); + + const user1Items = allItems.filter( + (item) => item.user_id === testUser.id + ); + const user2Items = allItems.filter( + (item) => item.user_id === testUser2.id + ); + + expect(user1Items).toHaveLength(1); + expect(user2Items).toHaveLength(1); + }); + }); + + describe('Poller State Management', () => { + test('should add and remove users correctly', async () => { + const initialStatus = telegramPoller.getStatus(); + expect(initialStatus.usersCount).toBe(0); + expect(initialStatus.running).toBe(false); + + // Add user + const addResult = await telegramPoller.addUser(testUser); + expect(addResult).toBe(true); + + const statusAfterAdd = telegramPoller.getStatus(); + expect(statusAfterAdd.usersCount).toBe(1); + expect(statusAfterAdd.running).toBe(true); + + // Remove user + const removeResult = telegramPoller.removeUser(testUser.id); + expect(removeResult).toBe(true); + + const statusAfterRemove = telegramPoller.getStatus(); + expect(statusAfterRemove.usersCount).toBe(0); + expect(statusAfterRemove.running).toBe(false); + }); + + test('should not add user without telegram token', async () => { + const userWithoutToken = await User.create({ + email: 'no-token@example.com', + password_digest: 'hashedpassword', + // No telegram_bot_token + }); + + const addResult = await telegramPoller.addUser(userWithoutToken); + expect(addResult).toBe(false); + + const status = telegramPoller.getStatus(); + expect(status.usersCount).toBe(0); + }); + + test('should handle adding same user multiple times', async () => { + // Add user first time + await telegramPoller.addUser(testUser); + const status1 = telegramPoller.getStatus(); + expect(status1.usersCount).toBe(1); + + // Add same user again + await telegramPoller.addUser(testUser); + const status2 = telegramPoller.getStatus(); + expect(status2.usersCount).toBe(1); // Should still be 1 + }); + }); + + describe('Update Processing Logic', () => { + test('should handle updates with proper ID tracking', async () => { + await telegramPoller.addUser(testUser); + + // Simulate updates (this tests the internal logic without actual HTTP calls) + const mockUpdates = [ + { + update_id: 1001, + message: { + message_id: 501, + text: 'First message', + chat: { id: 987654321 }, + }, + }, + { + update_id: 1002, + message: { + message_id: 502, + text: 'Second message', + chat: { id: 987654321 }, + }, + }, + ]; + + // Test highest update ID calculation + const highestId = telegramPoller._getHighestUpdateId(mockUpdates); + expect(highestId).toBe(1002); + + // Test update key generation (simulating internal logic) + const updateKeys = mockUpdates.map( + (update) => `${testUser.id}-${update.update_id}` + ); + expect(updateKeys).toEqual([ + `${testUser.id}-1001`, + `${testUser.id}-1002`, + ]); + }); + + test('should properly track processed updates', async () => { + // Test the Set-based tracking logic + const processedUpdates = new Set(); + + // Add some processed updates + processedUpdates.add('1-1001'); + processedUpdates.add('1-1002'); + + // Test filtering logic + const newUpdates = [ + { update_id: 1001 }, // Should be filtered out + { update_id: 1002 }, // Should be filtered out + { update_id: 1003 }, // Should remain + ].filter((update) => { + const updateKey = `1-${update.update_id}`; + return !processedUpdates.has(updateKey); + }); + + expect(newUpdates).toHaveLength(1); + expect(newUpdates[0].update_id).toBe(1003); + }); + + test('should handle memory management for processed updates', async () => { + // Simulate the cleanup logic + const processedUpdates = new Set(); + + // Add many updates (more than the 1000 limit) + for (let i = 1; i <= 1100; i++) { + processedUpdates.add(`1-${i}`); + } + + expect(processedUpdates.size).toBe(1100); + + // Simulate cleanup (remove oldest 100) + if (processedUpdates.size > 1000) { + const oldestEntries = Array.from(processedUpdates).slice( + 0, + 100 + ); + oldestEntries.forEach((entry) => + processedUpdates.delete(entry) + ); + } + + expect(processedUpdates.size).toBe(1000); + expect(processedUpdates.has('1-1')).toBe(false); // Oldest should be removed + expect(processedUpdates.has('1-1100')).toBe(true); // Newest should remain + }); + }); + + describe('Error Handling', () => { + test('should handle database errors gracefully', async () => { + // Mock InboxItem.create to throw an error + const originalCreate = InboxItem.create; + InboxItem.create = jest + .fn() + .mockRejectedValue(new Error('Database error')); + + await expect( + InboxItem.create({ + content: 'Test error handling', + source: 'telegram', + user_id: testUser.id, + }) + ).rejects.toThrow('Database error'); + + // Restore original function + InboxItem.create = originalCreate; + }); + + test('should handle invalid user data', async () => { + const invalidUser = null; + const addResult = await telegramPoller.addUser(invalidUser); + expect(addResult).toBe(false); + }); + + test('should handle missing message properties', async () => { + // Test the processing logic with incomplete message data + const incompleteUpdate = { + update_id: 2001, + message: { + // Missing text and other properties + message_id: 601, + chat: { id: 987654321 }, + }, + }; + + // The actual processing would skip this message due to missing text + const hasText = + incompleteUpdate.message && incompleteUpdate.message.text; + expect(hasText).toBeFalsy(); + }); + }); +}); diff --git a/backend/tests/integration/telegram.test.js b/backend/tests/integration/telegram.test.js index d7972e1..1b7e397 100644 --- a/backend/tests/integration/telegram.test.js +++ b/backend/tests/integration/telegram.test.js @@ -4,149 +4,158 @@ const { User } = require('../../models'); const { createTestUser } = require('../helpers/testUtils'); describe('Telegram Routes', () => { - let user, agent; + let user, agent; - beforeEach(async () => { - user = await createTestUser({ - email: 'test@example.com' - }); - - // Create authenticated agent - agent = request.agent(app); - await agent - .post('/api/login') - .send({ - email: 'test@example.com', - password: 'password123' - }); - }); - - describe('POST /api/telegram/setup', () => { - it('should setup telegram bot token', async () => { - const botToken = '123456789:ABCdefGHIjklMNOPQRSTUVwxyz-12345678'; - - const response = await agent - .post('/api/telegram/setup') - .send({ token: botToken }); - - expect(response.status).toBe(200); - expect(response.body.message).toBe('Telegram bot token updated successfully'); - - // Verify token was saved to user - const updatedUser = await User.findByPk(user.id); - expect(updatedUser.telegram_bot_token).toBe(botToken); - }); - - it('should require authentication', async () => { - const response = await request(app) - .post('/api/telegram/setup') - .send({ token: '123456789:ABCdefGHIjklMNOPQRSTUVwxyz-1234567890' }); - - expect(response.status).toBe(401); - expect(response.body.error).toBe('Authentication required'); - }); - - it('should require token parameter', async () => { - const response = await agent - .post('/api/telegram/setup') - .send({}); - - expect(response.status).toBe(400); - expect(response.body.error).toBe('Telegram bot token is required.'); - }); - - it('should validate token format', async () => { - const response = await agent - .post('/api/telegram/setup') - .send({ token: 'invalid-token-format' }); - - expect(response.status).toBe(400); - expect(response.body.error).toBe('Invalid Telegram bot token format.'); - }); - - it('should validate token format with correct pattern', async () => { - // Test various invalid formats - const invalidTokens = [ - '123456:short', - 'notnum:ABCdefGHIjklMNOPQRSTUVwxyz-12345678', - '123456789-ABCdefGHIjklMNOPQRSTUVwxyz-12345678', - '123456789:', - ':ABCdefGHIjklMNOPQRSTUVwxyz-12345678' - ]; - - for (const token of invalidTokens) { - const response = await agent - .post('/api/telegram/setup') - .send({ token }); - - expect(response.status).toBe(400); - expect(response.body.error).toBe('Invalid Telegram bot token format.'); - } - }); - - it('should accept valid token formats', async () => { - const validTokens = [ - '123456789:ABCdefGHIjklMNOPQRSTUVwxyz-12345678', - '987654321:XYZabcDEFghiJKLmnoPQRstUVW_09876543', - '555555555:abcdefghijklmnopqrstuvwxyzABCDEFGHI' - ]; - - for (const token of validTokens) { - const response = await agent - .post('/api/telegram/setup') - .send({ token }); - - expect(response.status).toBe(200); - expect(response.body.message).toBe('Telegram bot token updated successfully'); - } - }); - }); - - describe('POST /api/telegram/start-polling', () => { beforeEach(async () => { - // Setup bot token first - await user.update({ - telegram_bot_token: '123456789:ABCdefGHIjklMNOPQRSTUVwxyz-12345678' - }); + user = await createTestUser({ + email: 'test@example.com', + }); + + // Create authenticated agent + agent = request.agent(app); + await agent.post('/api/login').send({ + email: 'test@example.com', + password: 'password123', + }); }); - it('should require authentication', async () => { - const response = await request(app) - .post('/api/telegram/start-polling'); + describe('POST /api/telegram/setup', () => { + it('should setup telegram bot token', async () => { + const botToken = '123456789:ABCdefGHIjklMNOPQRSTUVwxyz-12345678'; - expect(response.status).toBe(401); - expect(response.body.error).toBe('Authentication required'); + const response = await agent + .post('/api/telegram/setup') + .send({ token: botToken }); + + expect(response.status).toBe(200); + expect(response.body.message).toBe( + 'Telegram bot token updated successfully' + ); + + // Verify token was saved to user + const updatedUser = await User.findByPk(user.id); + expect(updatedUser.telegram_bot_token).toBe(botToken); + }); + + it('should require authentication', async () => { + const response = await request(app) + .post('/api/telegram/setup') + .send({ + token: '123456789:ABCdefGHIjklMNOPQRSTUVwxyz-1234567890', + }); + + expect(response.status).toBe(401); + expect(response.body.error).toBe('Authentication required'); + }); + + it('should require token parameter', async () => { + const response = await agent.post('/api/telegram/setup').send({}); + + expect(response.status).toBe(400); + expect(response.body.error).toBe('Telegram bot token is required.'); + }); + + it('should validate token format', async () => { + const response = await agent + .post('/api/telegram/setup') + .send({ token: 'invalid-token-format' }); + + expect(response.status).toBe(400); + expect(response.body.error).toBe( + 'Invalid Telegram bot token format.' + ); + }); + + it('should validate token format with correct pattern', async () => { + // Test various invalid formats + const invalidTokens = [ + '123456:short', + 'notnum:ABCdefGHIjklMNOPQRSTUVwxyz-12345678', + '123456789-ABCdefGHIjklMNOPQRSTUVwxyz-12345678', + '123456789:', + ':ABCdefGHIjklMNOPQRSTUVwxyz-12345678', + ]; + + for (const token of invalidTokens) { + const response = await agent + .post('/api/telegram/setup') + .send({ token }); + + expect(response.status).toBe(400); + expect(response.body.error).toBe( + 'Invalid Telegram bot token format.' + ); + } + }); + + it('should accept valid token formats', async () => { + const validTokens = [ + '123456789:ABCdefGHIjklMNOPQRSTUVwxyz-12345678', + '987654321:XYZabcDEFghiJKLmnoPQRstUVW_09876543', + '555555555:abcdefghijklmnopqrstuvwxyzABCDEFGHI', + ]; + + for (const token of validTokens) { + const response = await agent + .post('/api/telegram/setup') + .send({ token }); + + expect(response.status).toBe(200); + expect(response.body.message).toBe( + 'Telegram bot token updated successfully' + ); + } + }); }); - it('should require bot token to be configured', async () => { - // Remove bot token - await user.update({ telegram_bot_token: null }); + describe('POST /api/telegram/start-polling', () => { + beforeEach(async () => { + // Setup bot token first + await user.update({ + telegram_bot_token: + '123456789:ABCdefGHIjklMNOPQRSTUVwxyz-12345678', + }); + }); - const response = await agent - .post('/api/telegram/start-polling'); + it('should require authentication', async () => { + const response = await request(app).post( + '/api/telegram/start-polling' + ); - expect(response.status).toBe(400); - expect(response.body.error).toBe('Telegram bot token not set.'); + expect(response.status).toBe(401); + expect(response.body.error).toBe('Authentication required'); + }); + + it('should require bot token to be configured', async () => { + // Remove bot token + await user.update({ telegram_bot_token: null }); + + const response = await agent.post('/api/telegram/start-polling'); + + expect(response.status).toBe(400); + expect(response.body.error).toBe('Telegram bot token not set.'); + }); }); - }); - describe('POST /api/telegram/stop-polling', () => { - it('should require authentication', async () => { - const response = await request(app) - .post('/api/telegram/stop-polling'); + describe('POST /api/telegram/stop-polling', () => { + it('should require authentication', async () => { + const response = await request(app).post( + '/api/telegram/stop-polling' + ); - expect(response.status).toBe(401); - expect(response.body.error).toBe('Authentication required'); + expect(response.status).toBe(401); + expect(response.body.error).toBe('Authentication required'); + }); }); - }); - describe('GET /api/telegram/polling-status', () => { - it('should require authentication', async () => { - const response = await request(app) - .get('/api/telegram/polling-status'); + describe('GET /api/telegram/polling-status', () => { + it('should require authentication', async () => { + const response = await request(app).get( + '/api/telegram/polling-status' + ); - expect(response.status).toBe(401); - expect(response.body.error).toBe('Authentication required'); + expect(response.status).toBe(401); + expect(response.body.error).toBe('Authentication required'); + }); }); - }); -}); \ No newline at end of file +}); diff --git a/backend/tests/integration/url.test.js b/backend/tests/integration/url.test.js index 8dcdea6..f7a8fea 100644 --- a/backend/tests/integration/url.test.js +++ b/backend/tests/integration/url.test.js @@ -3,178 +3,191 @@ const app = require('../../app'); const { createTestUser } = require('../helpers/testUtils'); describe('URL Routes', () => { - let user, agent; + let user, agent; - beforeEach(async () => { - user = await createTestUser({ - email: 'test@example.com' + beforeEach(async () => { + user = await createTestUser({ + email: 'test@example.com', + }); + + // Create authenticated agent + agent = request.agent(app); + await agent.post('/api/login').send({ + email: 'test@example.com', + password: 'password123', + }); }); - // Create authenticated agent - agent = request.agent(app); - await agent - .post('/api/login') - .send({ - email: 'test@example.com', - password: 'password123' - }); - }); + describe('GET /api/url/title', () => { + it('should require authentication', async () => { + const response = await request(app) + .get('/api/url/title') + .query({ url: 'https://example.com' }); - describe('GET /api/url/title', () => { - it('should require authentication', async () => { - const response = await request(app) - .get('/api/url/title') - .query({ url: 'https://example.com' }); + expect(response.status).toBe(401); + expect(response.body.error).toBe('Authentication required'); + }); - expect(response.status).toBe(401); - expect(response.body.error).toBe('Authentication required'); + it('should require url parameter', async () => { + const response = await agent.get('/api/url/title'); + + expect(response.status).toBe(400); + expect(response.body.error).toBe('URL parameter is required'); + }); + + it('should return title for valid URL', async () => { + const response = await agent + .get('/api/url/title') + .query({ url: 'https://httpbin.org/html' }); + + expect(response.status).toBe(200); + expect(response.body).toHaveProperty('url'); + expect(response.body).toHaveProperty('title'); + expect(response.body.url).toBe('https://httpbin.org/html'); + // Title could be extracted or null depending on network conditions + expect( + typeof response.body.title === 'string' || + response.body.title === null + ).toBe(true); + }, 10000); + + it('should handle URL without protocol', async () => { + const response = await agent + .get('/api/url/title') + .query({ url: 'httpbin.org/html' }); + + expect(response.status).toBe(200); + expect(response.body).toHaveProperty('url'); + expect(response.body).toHaveProperty('title'); + expect(response.body.url).toBe('httpbin.org/html'); + // Title could be extracted or null depending on network conditions + expect( + typeof response.body.title === 'string' || + response.body.title === null + ).toBe(true); + }, 10000); + + it('should handle invalid URL gracefully', async () => { + const response = await agent + .get('/api/url/title') + .query({ url: 'not-a-valid-url' }); + + expect(response.status).toBe(200); + expect(response.body).toHaveProperty('url'); + expect(response.body).toHaveProperty('title'); + expect(response.body.url).toBe('not-a-valid-url'); + // Title could be null or error message + expect( + response.body.title === null || + typeof response.body.title === 'string' + ).toBe(true); + }); + + it('should handle unreachable URL', async () => { + const response = await agent + .get('/api/url/title') + .query({ url: 'https://nonexistent-domain-12345.com' }); + + expect(response.status).toBe(200); + expect(response.body).toHaveProperty('url'); + expect(response.body).toHaveProperty('title'); + expect(response.body.url).toBe( + 'https://nonexistent-domain-12345.com' + ); + expect(response.body.title).toBe(null); + }); }); - it('should require url parameter', async () => { - const response = await agent - .get('/api/url/title'); + describe('POST /api/url/extract-from-text', () => { + it('should require authentication', async () => { + const response = await request(app) + .post('/api/url/extract-from-text') + .send({ text: 'Check out https://example.com' }); - expect(response.status).toBe(400); - expect(response.body.error).toBe('URL parameter is required'); + expect(response.status).toBe(401); + expect(response.body.error).toBe('Authentication required'); + }); + + it('should require text parameter', async () => { + const response = await agent + .post('/api/url/extract-from-text') + .send({}); + + expect(response.status).toBe(400); + expect(response.body.error).toBe('Text parameter is required'); + }); + + it('should extract URL from text and get title', async () => { + const testText = + 'Check out this interesting site: https://httpbin.org/html'; + const response = await agent + .post('/api/url/extract-from-text') + .send({ text: testText }); + + expect(response.status).toBe(200); + expect(response.body.found).toBe(true); + expect(response.body.url).toBe('https://httpbin.org/html'); + expect(response.body.originalText).toBe(testText); + expect(response.body).toHaveProperty('title'); + // Title could be extracted or null depending on network conditions + expect( + typeof response.body.title === 'string' || + response.body.title === null + ).toBe(true); + }, 10000); + + it('should extract first URL when multiple URLs in text', async () => { + const testText = + 'Check out https://httpbin.org/html and also https://example.com'; + const response = await agent + .post('/api/url/extract-from-text') + .send({ text: testText }); + + expect(response.status).toBe(200); + expect(response.body.found).toBe(true); + expect(response.body.url).toBe('https://httpbin.org/html'); + expect(response.body.originalText).toBe(testText); + expect(response.body).toHaveProperty('title'); + }, 10000); + + it('should detect URLs without protocol', async () => { + const testText = 'Visit httpbin.org/html for testing'; + const response = await agent + .post('/api/url/extract-from-text') + .send({ text: testText }); + + expect(response.status).toBe(200); + expect(response.body.found).toBe(true); + expect(response.body.url).toBe('httpbin.org/html'); + expect(response.body.originalText).toBe(testText); + }); + + it('should return found false when no URL in text', async () => { + const testText = 'This text has no URLs in it at all'; + const response = await agent + .post('/api/url/extract-from-text') + .send({ text: testText }); + + expect(response.status).toBe(200); + expect(response.body.found).toBe(false); + }); + + it('should handle empty text', async () => { + const response = await agent + .post('/api/url/extract-from-text') + .send({ text: '' }); + + expect(response.status).toBe(400); + expect(response.body.error).toBe('Text parameter is required'); + }); + + it('should handle text with only whitespace', async () => { + const response = await agent + .post('/api/url/extract-from-text') + .send({ text: ' \n\t ' }); + + expect(response.status).toBe(200); + expect(response.body.found).toBe(false); + }); }); - - it('should return title for valid URL', async () => { - const response = await agent - .get('/api/url/title') - .query({ url: 'https://httpbin.org/html' }); - - expect(response.status).toBe(200); - expect(response.body).toHaveProperty('url'); - expect(response.body).toHaveProperty('title'); - expect(response.body.url).toBe('https://httpbin.org/html'); - // Title could be extracted or null depending on network conditions - expect(typeof response.body.title === 'string' || response.body.title === null).toBe(true); - }, 10000); - - it('should handle URL without protocol', async () => { - const response = await agent - .get('/api/url/title') - .query({ url: 'httpbin.org/html' }); - - expect(response.status).toBe(200); - expect(response.body).toHaveProperty('url'); - expect(response.body).toHaveProperty('title'); - expect(response.body.url).toBe('httpbin.org/html'); - // Title could be extracted or null depending on network conditions - expect(typeof response.body.title === 'string' || response.body.title === null).toBe(true); - }, 10000); - - it('should handle invalid URL gracefully', async () => { - const response = await agent - .get('/api/url/title') - .query({ url: 'not-a-valid-url' }); - - expect(response.status).toBe(200); - expect(response.body).toHaveProperty('url'); - expect(response.body).toHaveProperty('title'); - expect(response.body.url).toBe('not-a-valid-url'); - // Title could be null or error message - expect(response.body.title === null || typeof response.body.title === 'string').toBe(true); - }); - - it('should handle unreachable URL', async () => { - const response = await agent - .get('/api/url/title') - .query({ url: 'https://nonexistent-domain-12345.com' }); - - expect(response.status).toBe(200); - expect(response.body).toHaveProperty('url'); - expect(response.body).toHaveProperty('title'); - expect(response.body.url).toBe('https://nonexistent-domain-12345.com'); - expect(response.body.title).toBe(null); - }); - }); - - describe('POST /api/url/extract-from-text', () => { - it('should require authentication', async () => { - const response = await request(app) - .post('/api/url/extract-from-text') - .send({ text: 'Check out https://example.com' }); - - expect(response.status).toBe(401); - expect(response.body.error).toBe('Authentication required'); - }); - - it('should require text parameter', async () => { - const response = await agent - .post('/api/url/extract-from-text') - .send({}); - - expect(response.status).toBe(400); - expect(response.body.error).toBe('Text parameter is required'); - }); - - it('should extract URL from text and get title', async () => { - const testText = 'Check out this interesting site: https://httpbin.org/html'; - const response = await agent - .post('/api/url/extract-from-text') - .send({ text: testText }); - - expect(response.status).toBe(200); - expect(response.body.found).toBe(true); - expect(response.body.url).toBe('https://httpbin.org/html'); - expect(response.body.originalText).toBe(testText); - expect(response.body).toHaveProperty('title'); - // Title could be extracted or null depending on network conditions - expect(typeof response.body.title === 'string' || response.body.title === null).toBe(true); - }, 10000); - - it('should extract first URL when multiple URLs in text', async () => { - const testText = 'Check out https://httpbin.org/html and also https://example.com'; - const response = await agent - .post('/api/url/extract-from-text') - .send({ text: testText }); - - expect(response.status).toBe(200); - expect(response.body.found).toBe(true); - expect(response.body.url).toBe('https://httpbin.org/html'); - expect(response.body.originalText).toBe(testText); - expect(response.body).toHaveProperty('title'); - }, 10000); - - it('should detect URLs without protocol', async () => { - const testText = 'Visit httpbin.org/html for testing'; - const response = await agent - .post('/api/url/extract-from-text') - .send({ text: testText }); - - expect(response.status).toBe(200); - expect(response.body.found).toBe(true); - expect(response.body.url).toBe('httpbin.org/html'); - expect(response.body.originalText).toBe(testText); - }); - - it('should return found false when no URL in text', async () => { - const testText = 'This text has no URLs in it at all'; - const response = await agent - .post('/api/url/extract-from-text') - .send({ text: testText }); - - expect(response.status).toBe(200); - expect(response.body.found).toBe(false); - }); - - it('should handle empty text', async () => { - const response = await agent - .post('/api/url/extract-from-text') - .send({ text: '' }); - - expect(response.status).toBe(400); - expect(response.body.error).toBe('Text parameter is required'); - }); - - it('should handle text with only whitespace', async () => { - const response = await agent - .post('/api/url/extract-from-text') - .send({ text: ' \n\t ' }); - - expect(response.status).toBe(200); - expect(response.body.found).toBe(false); - }); - }); -}); \ No newline at end of file +}); diff --git a/backend/tests/integration/user-create-script.test.js b/backend/tests/integration/user-create-script.test.js index c0495ba..07b7bbb 100644 --- a/backend/tests/integration/user-create-script.test.js +++ b/backend/tests/integration/user-create-script.test.js @@ -3,323 +3,334 @@ const path = require('path'); const { User } = require('../../models'); describe('User Create Script', () => { - const scriptPath = path.join(__dirname, '../../scripts/user-create.js'); - - // Helper function to run the script and capture output - const runUserCreateScript = (args = []) => { - return new Promise((resolve, reject) => { - const child = spawn('node', [scriptPath, ...args], { - stdio: ['pipe', 'pipe', 'pipe'], - env: { ...process.env, NODE_ENV: 'test' } - }); + const scriptPath = path.join(__dirname, '../../scripts/user-create.js'); - let stdout = ''; - let stderr = ''; + // Helper function to run the script and capture output + const runUserCreateScript = (args = []) => { + return new Promise((resolve, reject) => { + const child = spawn('node', [scriptPath, ...args], { + stdio: ['pipe', 'pipe', 'pipe'], + env: { ...process.env, NODE_ENV: 'test' }, + }); - child.stdout.on('data', (data) => { - stdout += data.toString(); - }); + let stdout = ''; + let stderr = ''; - child.stderr.on('data', (data) => { - stderr += data.toString(); - }); + child.stdout.on('data', (data) => { + stdout += data.toString(); + }); - child.on('close', (code) => { - resolve({ - code, - stdout: stdout.trim(), - stderr: stderr.trim() + child.stderr.on('data', (data) => { + stderr += data.toString(); + }); + + child.on('close', (code) => { + resolve({ + code, + stdout: stdout.trim(), + stderr: stderr.trim(), + }); + }); + + child.on('error', (error) => { + reject(error); + }); }); - }); + }; - child.on('error', (error) => { - reject(error); - }); - }); - }; - - afterEach(async () => { - // Clean up any test users created during tests - await User.destroy({ - where: { - email: ['testuser@example.com', 'admin@example.com', 'invalid-email', 'existing@example.com'] - } - }); - }); - - describe('Success Cases', () => { - it('should create a new user with valid email and password', async () => { - const email = 'testuser@example.com'; - const password = 'securepassword123'; - - const result = await runUserCreateScript([email, password]); - - expect(result.code).toBe(0); - expect(result.stdout).toContain('✅ User created successfully'); - expect(result.stdout).toContain(`📧 Email: ${email}`); - expect(result.stdout).toContain('🆔 User ID:'); - expect(result.stdout).toContain('📅 Created:'); - - // Verify user was actually created in database - const createdUser = await User.findOne({ where: { email } }); - expect(createdUser).toBeTruthy(); - expect(createdUser.email).toBe(email); - expect(createdUser.password_digest).toBeTruthy(); - expect(createdUser.password_digest).not.toBe(password); // Should be hashed + afterEach(async () => { + // Clean up any test users created during tests + await User.destroy({ + where: { + email: [ + 'testuser@example.com', + 'admin@example.com', + 'invalid-email', + 'existing@example.com', + ], + }, + }); }); - it('should create user with minimum password length', async () => { - const email = 'testuser2@example.com'; - const password = '123456'; // Exactly 6 characters + describe('Success Cases', () => { + it('should create a new user with valid email and password', async () => { + const email = 'testuser@example.com'; + const password = 'securepassword123'; - const result = await runUserCreateScript([email, password]); + const result = await runUserCreateScript([email, password]); - expect(result.code).toBe(0); - expect(result.stdout).toContain('✅ User created successfully'); + expect(result.code).toBe(0); + expect(result.stdout).toContain('✅ User created successfully'); + expect(result.stdout).toContain(`📧 Email: ${email}`); + expect(result.stdout).toContain('🆔 User ID:'); + expect(result.stdout).toContain('📅 Created:'); - // Verify user was created - const createdUser = await User.findOne({ where: { email } }); - expect(createdUser).toBeTruthy(); + // Verify user was actually created in database + const createdUser = await User.findOne({ where: { email } }); + expect(createdUser).toBeTruthy(); + expect(createdUser.email).toBe(email); + expect(createdUser.password_digest).toBeTruthy(); + expect(createdUser.password_digest).not.toBe(password); // Should be hashed + }); - // Clean up - await User.destroy({ where: { email } }); + it('should create user with minimum password length', async () => { + const email = 'testuser2@example.com'; + const password = '123456'; // Exactly 6 characters + + const result = await runUserCreateScript([email, password]); + + expect(result.code).toBe(0); + expect(result.stdout).toContain('✅ User created successfully'); + + // Verify user was created + const createdUser = await User.findOne({ where: { email } }); + expect(createdUser).toBeTruthy(); + + // Clean up + await User.destroy({ where: { email } }); + }); + + it('should create user with complex email format', async () => { + const email = 'user.name+tag@sub.domain.com'; + const password = 'password123'; + + const result = await runUserCreateScript([email, password]); + + expect(result.code).toBe(0); + expect(result.stdout).toContain('✅ User created successfully'); + + // Verify user was created + const createdUser = await User.findOne({ where: { email } }); + expect(createdUser).toBeTruthy(); + + // Clean up + await User.destroy({ where: { email } }); + }); }); - it('should create user with complex email format', async () => { - const email = 'user.name+tag@sub.domain.com'; - const password = 'password123'; + describe('Error Cases', () => { + it('should show usage when no arguments provided', async () => { + const result = await runUserCreateScript([]); - const result = await runUserCreateScript([email, password]); + expect(result.code).toBe(1); + expect(result.stderr).toContain( + '❌ Usage: npm run user:create ' + ); + expect(result.stderr).toContain( + 'Example: npm run user:create admin@example.com mypassword123' + ); + }); - expect(result.code).toBe(0); - expect(result.stdout).toContain('✅ User created successfully'); + it('should show usage when only email provided', async () => { + const result = await runUserCreateScript(['test@example.com']); - // Verify user was created - const createdUser = await User.findOne({ where: { email } }); - expect(createdUser).toBeTruthy(); + expect(result.code).toBe(1); + expect(result.stderr).toContain( + '❌ Usage: npm run user:create ' + ); + }); - // Clean up - await User.destroy({ where: { email } }); - }); - }); + it('should show usage when only password provided', async () => { + const result = await runUserCreateScript(['', 'password123']); - describe('Error Cases', () => { - it('should show usage when no arguments provided', async () => { - const result = await runUserCreateScript([]); + expect(result.code).toBe(1); + expect(result.stderr).toContain( + '❌ Usage: npm run user:create ' + ); + }); - expect(result.code).toBe(1); - expect(result.stderr).toContain('❌ Usage: npm run user:create '); - expect(result.stderr).toContain('Example: npm run user:create admin@example.com mypassword123'); - }); + it('should reject invalid email format', async () => { + const invalidEmails = [ + 'invalid-email', + 'missing@domain', + '@missing-local.com', + 'spaces in@email.com', + 'double@@domain.com', + 'trailing.dot.@domain.com', + ]; - it('should show usage when only email provided', async () => { - const result = await runUserCreateScript(['test@example.com']); + for (const email of invalidEmails) { + const result = await runUserCreateScript([ + email, + 'password123', + ]); - expect(result.code).toBe(1); - expect(result.stderr).toContain('❌ Usage: npm run user:create '); - }); - - it('should show usage when only password provided', async () => { - const result = await runUserCreateScript(['', 'password123']); - - expect(result.code).toBe(1); - expect(result.stderr).toContain('❌ Usage: npm run user:create '); - }); - - it('should reject invalid email format', async () => { - const invalidEmails = [ - 'invalid-email', - 'missing@domain', - '@missing-local.com', - 'spaces in@email.com', - 'double@@domain.com', - 'trailing.dot.@domain.com' - ]; - - for (const email of invalidEmails) { - const result = await runUserCreateScript([email, 'password123']); - - expect(result.code).toBe(1); - expect(result.stderr).toContain('❌ Invalid email format'); - } - }); - - it('should reject password shorter than 6 characters', async () => { - const shortPasswords = ['', '1', '12', '123', '1234', '12345']; - - for (const password of shortPasswords) { - const result = await runUserCreateScript(['test@example.com', password]); - - expect(result.code).toBe(1); - expect(result.stderr).toContain('❌ Password must be at least 6 characters long'); - } - }); - - it('should reject duplicate email', async () => { - const email = 'existing@example.com'; - const password = 'password123'; - - // Create user first - await User.create({ - email, - password_digest: await require('bcrypt').hash(password, 10) - }); - - // Try to create same user again - const result = await runUserCreateScript([email, password]); - - expect(result.code).toBe(1); - expect(result.stderr).toContain(`❌ User with email ${email} already exists`); - }); - }); - - describe('Integration with npm script', () => { - it('should work when called via npm run command', async () => { - const email = 'npmtest@example.com'; - const password = 'testpassword123'; - - try { - // This simulates running: npm run user:create npmtest@example.com testpassword123 - const output = execSync( - `npm run user:create ${email} ${password}`, - { - cwd: path.join(__dirname, '../..'), - env: { ...process.env, NODE_ENV: 'test' }, - encoding: 'utf8', - timeout: 10000 - } - ); - - expect(output).toContain('User created successfully'); - - // Verify user was created - const createdUser = await User.findOne({ where: { email } }); - expect(createdUser).toBeTruthy(); - expect(createdUser.email).toBe(email); - - } catch (error) { - // If the command failed, check if it's due to duplicate user (from previous test runs) - if (error.stderr?.includes('already exists')) { - // Clean up and retry - await User.destroy({ where: { email } }); - - const output = execSync( - `npm run user:create ${email} ${password}`, - { - cwd: path.join(__dirname, '../..'), - env: { ...process.env, NODE_ENV: 'test' }, - encoding: 'utf8', - timeout: 10000 + expect(result.code).toBe(1); + expect(result.stderr).toContain('❌ Invalid email format'); } - ); + }); - expect(output).toContain('User created successfully'); - } else { - throw error; - } - } finally { - // Clean up - await User.destroy({ where: { email } }); - } - }); - }); + it('should reject password shorter than 6 characters', async () => { + const shortPasswords = ['', '1', '12', '123', '1234', '12345']; - describe('Database Validation', () => { - it('should hash password properly', async () => { - const email = 'hashtest@example.com'; - const password = 'plaintextpassword'; + for (const password of shortPasswords) { + const result = await runUserCreateScript([ + 'test@example.com', + password, + ]); - const result = await runUserCreateScript([email, password]); + expect(result.code).toBe(1); + expect(result.stderr).toContain( + '❌ Password must be at least 6 characters long' + ); + } + }); - expect(result.code).toBe(0); + it('should reject duplicate email', async () => { + const email = 'existing@example.com'; + const password = 'password123'; - const createdUser = await User.findOne({ where: { email } }); - expect(createdUser).toBeTruthy(); - - // Password should be hashed (bcrypt hashes start with $2b$) - expect(createdUser.password_digest).toMatch(/^\$2b\$10\$/); - expect(createdUser.password_digest).not.toBe(password); - expect(createdUser.password_digest.length).toBeGreaterThan(50); + // Create user first + await User.create({ + email, + password_digest: await require('bcrypt').hash(password, 10), + }); - // Verify the hash is valid - const bcrypt = require('bcrypt'); - const isValid = await bcrypt.compare(password, createdUser.password_digest); - expect(isValid).toBe(true); + // Try to create same user again + const result = await runUserCreateScript([email, password]); - // Clean up - await User.destroy({ where: { email } }); + expect(result.code).toBe(1); + expect(result.stderr).toContain( + `❌ User with email ${email} already exists` + ); + }); }); - it('should set correct default values', async () => { - const email = 'defaultstest@example.com'; - const password = 'password123'; + describe('Integration with npm script', () => { + it('should work when called via npm run command', async () => { + const email = 'npmtest@example.com'; + const password = 'testpassword123'; - const result = await runUserCreateScript([email, password]); + // Clean up any existing user first + await User.destroy({ where: { email } }); - expect(result.code).toBe(0); + try { + // This simulates running: npm run user:create npmtest@example.com testpassword123 + const output = execSync( + `npm run user:create ${email} ${password}`, + { + cwd: path.join(__dirname, '../..'), + env: { ...process.env, NODE_ENV: 'test' }, + encoding: 'utf8', + timeout: 10000, + } + ); - const createdUser = await User.findOne({ where: { email } }); - expect(createdUser).toBeTruthy(); - - // Check that created_at and updated_at are set - expect(createdUser.created_at).toBeTruthy(); - expect(createdUser.updated_at).toBeTruthy(); - - // Check that it's a valid date - expect(createdUser.created_at instanceof Date).toBe(true); - expect(createdUser.updated_at instanceof Date).toBe(true); + expect(output).toContain('User created successfully'); - // Clean up - await User.destroy({ where: { email } }); - }); - }); - - describe('Edge Cases', () => { - it('should handle special characters in password', async () => { - const email = 'specialchars@example.com'; - const password = 'p@ssw0rd!@#$%^&*()_+-=[]{}|;:,.<>?'; - - const result = await runUserCreateScript([email, password]); - - expect(result.code).toBe(0); - expect(result.stdout).toContain('✅ User created successfully'); - - // Verify user was created and password works - const createdUser = await User.findOne({ where: { email } }); - expect(createdUser).toBeTruthy(); - - const bcrypt = require('bcrypt'); - const isValid = await bcrypt.compare(password, createdUser.password_digest); - expect(isValid).toBe(true); - - // Clean up - await User.destroy({ where: { email } }); + // Verify user was created + const createdUser = await User.findOne({ where: { email } }); + expect(createdUser).toBeTruthy(); + expect(createdUser.email).toBe(email); + } finally { + // Clean up + await User.destroy({ where: { email } }); + } + }); }); - it('should handle very long email', async () => { - const longEmail = 'a'.repeat(50) + '@' + 'b'.repeat(50) + '.com'; - const password = 'password123'; + describe('Database Validation', () => { + it('should hash password properly', async () => { + const email = 'hashtest@example.com'; + const password = 'plaintextpassword'; - const result = await runUserCreateScript([longEmail, password]); + const result = await runUserCreateScript([email, password]); - expect(result.code).toBe(0); - expect(result.stdout).toContain('✅ User created successfully'); + expect(result.code).toBe(0); - // Clean up - await User.destroy({ where: { email: longEmail } }); + const createdUser = await User.findOne({ where: { email } }); + expect(createdUser).toBeTruthy(); + + // Password should be hashed (bcrypt hashes start with $2b$) + expect(createdUser.password_digest).toMatch(/^\$2b\$10\$/); + expect(createdUser.password_digest).not.toBe(password); + expect(createdUser.password_digest.length).toBeGreaterThan(50); + + // Verify the hash is valid + const bcrypt = require('bcrypt'); + const isValid = await bcrypt.compare( + password, + createdUser.password_digest + ); + expect(isValid).toBe(true); + + // Clean up + await User.destroy({ where: { email } }); + }); + + it('should set correct default values', async () => { + const email = 'defaultstest@example.com'; + const password = 'password123'; + + const result = await runUserCreateScript([email, password]); + + expect(result.code).toBe(0); + + const createdUser = await User.findOne({ where: { email } }); + expect(createdUser).toBeTruthy(); + + // Check that created_at and updated_at are set + expect(createdUser.created_at).toBeTruthy(); + expect(createdUser.updated_at).toBeTruthy(); + + // Check that it's a valid date + expect(createdUser.created_at instanceof Date).toBe(true); + expect(createdUser.updated_at instanceof Date).toBe(true); + + // Clean up + await User.destroy({ where: { email } }); + }); }); - it('should handle very long password', async () => { - const email = 'longpassword@example.com'; - const password = 'a'.repeat(200); // Very long password + describe('Edge Cases', () => { + it('should handle special characters in password', async () => { + const email = 'specialchars@example.com'; + const password = 'p@ssw0rd!@#$%^&*()_+-=[]{}|;:,.<>?'; - const result = await runUserCreateScript([email, password]); + const result = await runUserCreateScript([email, password]); - expect(result.code).toBe(0); - expect(result.stdout).toContain('✅ User created successfully'); + expect(result.code).toBe(0); + expect(result.stdout).toContain('✅ User created successfully'); - // Clean up - await User.destroy({ where: { email } }); + // Verify user was created and password works + const createdUser = await User.findOne({ where: { email } }); + expect(createdUser).toBeTruthy(); + + const bcrypt = require('bcrypt'); + const isValid = await bcrypt.compare( + password, + createdUser.password_digest + ); + expect(isValid).toBe(true); + + // Clean up + await User.destroy({ where: { email } }); + }); + + it('should handle very long email', async () => { + const longEmail = 'a'.repeat(50) + '@' + 'b'.repeat(50) + '.com'; + const password = 'password123'; + + const result = await runUserCreateScript([longEmail, password]); + + expect(result.code).toBe(0); + expect(result.stdout).toContain('✅ User created successfully'); + + // Clean up + await User.destroy({ where: { email: longEmail } }); + }); + + it('should handle very long password', async () => { + const email = 'longpassword@example.com'; + const password = 'a'.repeat(200); // Very long password + + const result = await runUserCreateScript([email, password]); + + expect(result.code).toBe(0); + expect(result.stdout).toContain('✅ User created successfully'); + + // Clean up + await User.destroy({ where: { email } }); + }); }); - }); -}); \ No newline at end of file +}); diff --git a/backend/tests/integration/users.test.js b/backend/tests/integration/users.test.js index cec9aeb..52296f9 100644 --- a/backend/tests/integration/users.test.js +++ b/backend/tests/integration/users.test.js @@ -4,280 +4,313 @@ const { User } = require('../../models'); const { createTestUser } = require('../helpers/testUtils'); describe('Users Routes', () => { - let user, agent; + let user, agent; - beforeEach(async () => { - user = await createTestUser({ - email: 'test@example.com' - }); - - // Create authenticated agent - agent = request.agent(app); - await agent - .post('/api/login') - .send({ - email: 'test@example.com', - password: 'password123' - }); - }); - - describe('GET /api/profile', () => { - it('should get user profile', async () => { - const response = await agent.get('/api/profile'); - - expect(response.status).toBe(200); - expect(response.body.id).toBe(user.id); - expect(response.body.email).toBe(user.email); - expect(response.body).toHaveProperty('appearance'); - expect(response.body).toHaveProperty('language'); - expect(response.body).toHaveProperty('timezone'); - expect(response.body).toHaveProperty('avatar_image'); - expect(response.body).toHaveProperty('telegram_bot_token'); - expect(response.body).toHaveProperty('telegram_chat_id'); - expect(response.body).toHaveProperty('task_summary_enabled'); - expect(response.body).toHaveProperty('task_summary_frequency'); - }); - - it('should require authentication', async () => { - const response = await request(app).get('/api/profile'); - - expect(response.status).toBe(401); - expect(response.body.error).toBe('Authentication required'); - }); - - it('should return 401 when session user no longer exists', async () => { - await User.destroy({ where: { id: user.id } }); - - const response = await agent.get('/api/profile'); - - expect(response.status).toBe(401); - expect(response.body.error).toBe('User not found'); - }); - }); - - describe('PATCH /api/profile', () => { - it('should update user profile', async () => { - const updateData = { - appearance: 'dark', - language: 'es', - timezone: 'UTC', - avatar_image: 'new-avatar.png', - telegram_bot_token: 'new-token' - }; - - const response = await agent - .patch('/api/profile') - .send(updateData); - - expect(response.status).toBe(200); - expect(response.body.appearance).toBe(updateData.appearance); - expect(response.body.language).toBe(updateData.language); - expect(response.body.timezone).toBe(updateData.timezone); - expect(response.body.avatar_image).toBe(updateData.avatar_image); - expect(response.body.telegram_bot_token).toBe(updateData.telegram_bot_token); - }); - - it('should allow partial updates', async () => { - const updateData = { - appearance: 'dark' - }; - - const response = await agent - .patch('/api/profile') - .send(updateData); - - expect(response.status).toBe(200); - expect(response.body.appearance).toBe(updateData.appearance); - expect(response.body.language).toBe(user.language); - }); - - it('should require authentication', async () => { - const updateData = { - appearance: 'dark' - }; - - const response = await request(app) - .patch('/api/profile') - .send(updateData); - - expect(response.status).toBe(401); - expect(response.body.error).toBe('Authentication required'); - }); - - it('should return 401 when session user no longer exists', async () => { - await User.destroy({ where: { id: user.id } }); - - const response = await agent - .patch('/api/profile') - .send({ appearance: 'dark' }); - - expect(response.status).toBe(401); - expect(response.body.error).toBe('User not found'); - }); - }); - - describe('POST /api/profile/task-summary/toggle', () => { beforeEach(async () => { - await user.update({ task_summary_enabled: false }); + user = await createTestUser({ + email: 'test@example.com', + }); + + // Create authenticated agent + agent = request.agent(app); + await agent.post('/api/login').send({ + email: 'test@example.com', + password: 'password123', + }); }); - it('should toggle task summary on', async () => { - const response = await agent.post('/api/profile/task-summary/toggle'); + describe('GET /api/profile', () => { + it('should get user profile', async () => { + const response = await agent.get('/api/profile'); - expect(response.status).toBe(200); - expect(response.body.success).toBe(true); - expect(response.body.enabled).toBe(true); - expect(response.body.message).toBe('Task summary notifications have been enabled.'); + expect(response.status).toBe(200); + expect(response.body.id).toBe(user.id); + expect(response.body.email).toBe(user.email); + expect(response.body).toHaveProperty('appearance'); + expect(response.body).toHaveProperty('language'); + expect(response.body).toHaveProperty('timezone'); + expect(response.body).toHaveProperty('avatar_image'); + expect(response.body).toHaveProperty('telegram_bot_token'); + expect(response.body).toHaveProperty('telegram_chat_id'); + expect(response.body).toHaveProperty('task_summary_enabled'); + expect(response.body).toHaveProperty('task_summary_frequency'); + }); + + it('should require authentication', async () => { + const response = await request(app).get('/api/profile'); + + expect(response.status).toBe(401); + expect(response.body.error).toBe('Authentication required'); + }); + + it('should return 401 when session user no longer exists', async () => { + await User.destroy({ where: { id: user.id } }); + + const response = await agent.get('/api/profile'); + + expect(response.status).toBe(401); + expect(response.body.error).toBe('User not found'); + }); }); - it('should toggle task summary off', async () => { - await user.update({ task_summary_enabled: true }); + describe('PATCH /api/profile', () => { + it('should update user profile', async () => { + const updateData = { + appearance: 'dark', + language: 'es', + timezone: 'UTC', + avatar_image: 'new-avatar.png', + telegram_bot_token: 'new-token', + }; - const response = await agent.post('/api/profile/task-summary/toggle'); + const response = await agent.patch('/api/profile').send(updateData); - expect(response.status).toBe(200); - expect(response.body.success).toBe(true); - expect(response.body.enabled).toBe(false); - expect(response.body.message).toBe('Task summary notifications have been disabled.'); + expect(response.status).toBe(200); + expect(response.body.appearance).toBe(updateData.appearance); + expect(response.body.language).toBe(updateData.language); + expect(response.body.timezone).toBe(updateData.timezone); + expect(response.body.avatar_image).toBe(updateData.avatar_image); + expect(response.body.telegram_bot_token).toBe( + updateData.telegram_bot_token + ); + }); + + it('should allow partial updates', async () => { + const updateData = { + appearance: 'dark', + }; + + const response = await agent.patch('/api/profile').send(updateData); + + expect(response.status).toBe(200); + expect(response.body.appearance).toBe(updateData.appearance); + expect(response.body.language).toBe(user.language); + }); + + it('should require authentication', async () => { + const updateData = { + appearance: 'dark', + }; + + const response = await request(app) + .patch('/api/profile') + .send(updateData); + + expect(response.status).toBe(401); + expect(response.body.error).toBe('Authentication required'); + }); + + it('should return 401 when session user no longer exists', async () => { + await User.destroy({ where: { id: user.id } }); + + const response = await agent + .patch('/api/profile') + .send({ appearance: 'dark' }); + + expect(response.status).toBe(401); + expect(response.body.error).toBe('User not found'); + }); }); - it('should require authentication', async () => { - const response = await request(app).post('/api/profile/task-summary/toggle'); + describe('POST /api/profile/task-summary/toggle', () => { + beforeEach(async () => { + await user.update({ task_summary_enabled: false }); + }); - expect(response.status).toBe(401); - expect(response.body.error).toBe('Authentication required'); + it('should toggle task summary on', async () => { + const response = await agent.post( + '/api/profile/task-summary/toggle' + ); + + expect(response.status).toBe(200); + expect(response.body.success).toBe(true); + expect(response.body.enabled).toBe(true); + expect(response.body.message).toBe( + 'Task summary notifications have been enabled.' + ); + }); + + it('should toggle task summary off', async () => { + await user.update({ task_summary_enabled: true }); + + const response = await agent.post( + '/api/profile/task-summary/toggle' + ); + + expect(response.status).toBe(200); + expect(response.body.success).toBe(true); + expect(response.body.enabled).toBe(false); + expect(response.body.message).toBe( + 'Task summary notifications have been disabled.' + ); + }); + + it('should require authentication', async () => { + const response = await request(app).post( + '/api/profile/task-summary/toggle' + ); + + expect(response.status).toBe(401); + expect(response.body.error).toBe('Authentication required'); + }); + + it('should return 401 when session user no longer exists', async () => { + await User.destroy({ where: { id: user.id } }); + + const response = await agent.post( + '/api/profile/task-summary/toggle' + ); + + expect(response.status).toBe(401); + expect(response.body.error).toBe('User not found'); + }); }); - it('should return 401 when session user no longer exists', async () => { - await User.destroy({ where: { id: user.id } }); + describe('POST /api/profile/task-summary/frequency', () => { + it('should update task summary frequency', async () => { + const response = await agent + .post('/api/profile/task-summary/frequency') + .send({ frequency: 'daily' }); - const response = await agent.post('/api/profile/task-summary/toggle'); + expect(response.status).toBe(200); + expect(response.body.success).toBe(true); + expect(response.body.frequency).toBe('daily'); + expect(response.body.message).toBe( + 'Task summary frequency has been set to daily.' + ); + }); - expect(response.status).toBe(401); - expect(response.body.error).toBe('User not found'); - }); - }); + it('should require frequency parameter', async () => { + const response = await agent + .post('/api/profile/task-summary/frequency') + .send({}); - describe('POST /api/profile/task-summary/frequency', () => { - it('should update task summary frequency', async () => { - const response = await agent - .post('/api/profile/task-summary/frequency') - .send({ frequency: 'daily' }); + expect(response.status).toBe(400); + expect(response.body.error).toBe('Frequency is required.'); + }); - expect(response.status).toBe(200); - expect(response.body.success).toBe(true); - expect(response.body.frequency).toBe('daily'); - expect(response.body.message).toBe('Task summary frequency has been set to daily.'); + it('should validate frequency value', async () => { + const response = await agent + .post('/api/profile/task-summary/frequency') + .send({ frequency: 'invalid' }); + + expect(response.status).toBe(400); + expect(response.body.error).toBe('Invalid frequency value.'); + }); + + it('should accept valid frequencies', async () => { + const validFrequencies = [ + 'daily', + 'weekdays', + 'weekly', + '1h', + '2h', + '4h', + '8h', + '12h', + ]; + + for (const frequency of validFrequencies) { + const response = await agent + .post('/api/profile/task-summary/frequency') + .send({ frequency }); + + expect(response.status).toBe(200); + expect(response.body.frequency).toBe(frequency); + } + }); + + it('should require authentication', async () => { + const response = await request(app) + .post('/api/profile/task-summary/frequency') + .send({ frequency: 'daily' }); + + expect(response.status).toBe(401); + expect(response.body.error).toBe('Authentication required'); + }); + + it('should return 401 when session user no longer exists', async () => { + await User.destroy({ where: { id: user.id } }); + + const response = await agent + .post('/api/profile/task-summary/frequency') + .send({ frequency: 'daily' }); + + expect(response.status).toBe(401); + expect(response.body.error).toBe('User not found'); + }); }); - it('should require frequency parameter', async () => { - const response = await agent - .post('/api/profile/task-summary/frequency') - .send({}); + describe('POST /api/profile/task-summary/send-now', () => { + it('should require telegram configuration', async () => { + const response = await agent.post( + '/api/profile/task-summary/send-now' + ); - expect(response.status).toBe(400); - expect(response.body.error).toBe('Frequency is required.'); + expect(response.status).toBe(400); + expect(response.body.error).toBe( + 'Telegram bot is not properly configured.' + ); + }); + + it('should require authentication', async () => { + const response = await request(app).post( + '/api/profile/task-summary/send-now' + ); + + expect(response.status).toBe(401); + expect(response.body.error).toBe('Authentication required'); + }); + + it('should return 401 when session user no longer exists', async () => { + await User.destroy({ where: { id: user.id } }); + + const response = await agent.post( + '/api/profile/task-summary/send-now' + ); + + expect(response.status).toBe(401); + expect(response.body.error).toBe('User not found'); + }); }); - it('should validate frequency value', async () => { - const response = await agent - .post('/api/profile/task-summary/frequency') - .send({ frequency: 'invalid' }); + describe('GET /api/profile/task-summary/status', () => { + it('should get task summary status', async () => { + await user.update({ + task_summary_enabled: true, + task_summary_frequency: 'daily', + }); - expect(response.status).toBe(400); - expect(response.body.error).toBe('Invalid frequency value.'); + const response = await agent.get( + '/api/profile/task-summary/status' + ); + + expect(response.status).toBe(200); + expect(response.body.success).toBe(true); + expect(response.body.enabled).toBe(true); + expect(response.body.frequency).toBe('daily'); + expect(response.body).toHaveProperty('last_run'); + expect(response.body).toHaveProperty('next_run'); + }); + + it('should require authentication', async () => { + const response = await request(app).get( + '/api/profile/task-summary/status' + ); + + expect(response.status).toBe(401); + expect(response.body.error).toBe('Authentication required'); + }); + + it('should return 401 when session user no longer exists', async () => { + await User.destroy({ where: { id: user.id } }); + + const response = await agent.get( + '/api/profile/task-summary/status' + ); + + expect(response.status).toBe(401); + expect(response.body.error).toBe('User not found'); + }); }); - - it('should accept valid frequencies', async () => { - const validFrequencies = ['daily', 'weekdays', 'weekly', '1h', '2h', '4h', '8h', '12h']; - - for (const frequency of validFrequencies) { - const response = await agent - .post('/api/profile/task-summary/frequency') - .send({ frequency }); - - expect(response.status).toBe(200); - expect(response.body.frequency).toBe(frequency); - } - }); - - it('should require authentication', async () => { - const response = await request(app) - .post('/api/profile/task-summary/frequency') - .send({ frequency: 'daily' }); - - expect(response.status).toBe(401); - expect(response.body.error).toBe('Authentication required'); - }); - - it('should return 401 when session user no longer exists', async () => { - await User.destroy({ where: { id: user.id } }); - - const response = await agent - .post('/api/profile/task-summary/frequency') - .send({ frequency: 'daily' }); - - expect(response.status).toBe(401); - expect(response.body.error).toBe('User not found'); - }); - }); - - describe('POST /api/profile/task-summary/send-now', () => { - it('should require telegram configuration', async () => { - const response = await agent.post('/api/profile/task-summary/send-now'); - - expect(response.status).toBe(400); - expect(response.body.error).toBe('Telegram bot is not properly configured.'); - }); - - it('should require authentication', async () => { - const response = await request(app).post('/api/profile/task-summary/send-now'); - - expect(response.status).toBe(401); - expect(response.body.error).toBe('Authentication required'); - }); - - it('should return 401 when session user no longer exists', async () => { - await User.destroy({ where: { id: user.id } }); - - const response = await agent.post('/api/profile/task-summary/send-now'); - - expect(response.status).toBe(401); - expect(response.body.error).toBe('User not found'); - }); - }); - - describe('GET /api/profile/task-summary/status', () => { - it('should get task summary status', async () => { - await user.update({ - task_summary_enabled: true, - task_summary_frequency: 'daily' - }); - - const response = await agent.get('/api/profile/task-summary/status'); - - expect(response.status).toBe(200); - expect(response.body.success).toBe(true); - expect(response.body.enabled).toBe(true); - expect(response.body.frequency).toBe('daily'); - expect(response.body).toHaveProperty('last_run'); - expect(response.body).toHaveProperty('next_run'); - }); - - it('should require authentication', async () => { - const response = await request(app).get('/api/profile/task-summary/status'); - - expect(response.status).toBe(401); - expect(response.body.error).toBe('Authentication required'); - }); - - it('should return 401 when session user no longer exists', async () => { - await User.destroy({ where: { id: user.id } }); - - const response = await agent.get('/api/profile/task-summary/status'); - - expect(response.status).toBe(401); - expect(response.body.error).toBe('User not found'); - }); - }); -}); \ No newline at end of file +}); diff --git a/backend/tests/unit/middleware/auth.test.js b/backend/tests/unit/middleware/auth.test.js index 92f45eb..98d75fb 100644 --- a/backend/tests/unit/middleware/auth.test.js +++ b/backend/tests/unit/middleware/auth.test.js @@ -2,129 +2,137 @@ const { requireAuth } = require('../../../middleware/auth'); const { User } = require('../../../models'); describe('Auth Middleware', () => { - let req, res, next; + let req, res, next; - beforeEach(() => { - req = { - path: '/api/tasks', - session: {} - }; - res = { - status: jest.fn().mockReturnThis(), - json: jest.fn() - }; - next = jest.fn(); - }); - - it('should skip authentication for health check', async () => { - req.path = '/api/health'; - - await requireAuth(req, res, next); - - expect(next).toHaveBeenCalled(); - expect(res.status).not.toHaveBeenCalled(); - }); - - it('should skip authentication for login route', async () => { - req.path = '/api/login'; - - await requireAuth(req, res, next); - - expect(next).toHaveBeenCalled(); - expect(res.status).not.toHaveBeenCalled(); - }); - - it('should skip authentication for current_user route', async () => { - req.path = '/api/current_user'; - - await requireAuth(req, res, next); - - expect(next).toHaveBeenCalled(); - expect(res.status).not.toHaveBeenCalled(); - }); - - it('should return 401 if no session', async () => { - req.session = null; - - await requireAuth(req, res, next); - - expect(res.status).toHaveBeenCalledWith(401); - expect(res.json).toHaveBeenCalledWith({ error: 'Authentication required' }); - expect(next).not.toHaveBeenCalled(); - }); - - it('should return 401 if no userId in session', async () => { - req.session = {}; - - await requireAuth(req, res, next); - - expect(res.status).toHaveBeenCalledWith(401); - expect(res.json).toHaveBeenCalledWith({ error: 'Authentication required' }); - expect(next).not.toHaveBeenCalled(); - }); - - it('should return 401 and destroy session if user not found', async () => { - const bcrypt = require('bcrypt'); - const user = await User.create({ - email: 'test@example.com', - password_digest: await bcrypt.hash('password123', 10) + beforeEach(() => { + req = { + path: '/api/tasks', + session: {}, + }; + res = { + status: jest.fn().mockReturnThis(), + json: jest.fn(), + }; + next = jest.fn(); }); - - req.session = { - userId: user.id + 1, // Non-existent user ID - destroy: jest.fn() - }; - - await requireAuth(req, res, next); - - expect(req.session.destroy).toHaveBeenCalled(); - expect(res.status).toHaveBeenCalledWith(401); - expect(res.json).toHaveBeenCalledWith({ error: 'User not found' }); - expect(next).not.toHaveBeenCalled(); - }); - it('should set currentUser and call next for valid session', async () => { - const bcrypt = require('bcrypt'); - const user = await User.create({ - email: 'test@example.com', - password_digest: await bcrypt.hash('password123', 10) + it('should skip authentication for health check', async () => { + req.path = '/api/health'; + + await requireAuth(req, res, next); + + expect(next).toHaveBeenCalled(); + expect(res.status).not.toHaveBeenCalled(); }); - - req.session = { - userId: user.id - }; - - await requireAuth(req, res, next); - - expect(req.currentUser).toBeDefined(); - expect(req.currentUser.id).toBe(user.id); - expect(req.currentUser.email).toBe(user.email); - expect(next).toHaveBeenCalled(); - expect(res.status).not.toHaveBeenCalled(); - }); - it('should handle database errors', async () => { - // Mock console.error to suppress expected error log in test output - const originalConsoleError = console.error; - console.error = jest.fn(); - - // Mock User.findByPk to throw an error - const originalFindByPk = User.findByPk; - User.findByPk = jest.fn().mockRejectedValue(new Error('Database connection error')); - - req.session = { - userId: 123, - destroy: jest.fn() - }; - - await requireAuth(req, res, next); - - expect(res.status).toHaveBeenCalledWith(500); - expect(res.json).toHaveBeenCalledWith({ error: 'Authentication error' }); - expect(next).not.toHaveBeenCalled(); - - // Restore original methods - User.findByPk = originalFindByPk; - console.error = originalConsoleError; - }); -}); \ No newline at end of file + it('should skip authentication for login route', async () => { + req.path = '/api/login'; + + await requireAuth(req, res, next); + + expect(next).toHaveBeenCalled(); + expect(res.status).not.toHaveBeenCalled(); + }); + + it('should skip authentication for current_user route', async () => { + req.path = '/api/current_user'; + + await requireAuth(req, res, next); + + expect(next).toHaveBeenCalled(); + expect(res.status).not.toHaveBeenCalled(); + }); + + it('should return 401 if no session', async () => { + req.session = null; + + await requireAuth(req, res, next); + + expect(res.status).toHaveBeenCalledWith(401); + expect(res.json).toHaveBeenCalledWith({ + error: 'Authentication required', + }); + expect(next).not.toHaveBeenCalled(); + }); + + it('should return 401 if no userId in session', async () => { + req.session = {}; + + await requireAuth(req, res, next); + + expect(res.status).toHaveBeenCalledWith(401); + expect(res.json).toHaveBeenCalledWith({ + error: 'Authentication required', + }); + expect(next).not.toHaveBeenCalled(); + }); + + it('should return 401 and destroy session if user not found', async () => { + const bcrypt = require('bcrypt'); + const user = await User.create({ + email: 'test@example.com', + password_digest: await bcrypt.hash('password123', 10), + }); + + req.session = { + userId: user.id + 1, // Non-existent user ID + destroy: jest.fn(), + }; + + await requireAuth(req, res, next); + + expect(req.session.destroy).toHaveBeenCalled(); + expect(res.status).toHaveBeenCalledWith(401); + expect(res.json).toHaveBeenCalledWith({ error: 'User not found' }); + expect(next).not.toHaveBeenCalled(); + }); + + it('should set currentUser and call next for valid session', async () => { + const bcrypt = require('bcrypt'); + const user = await User.create({ + email: 'test@example.com', + password_digest: await bcrypt.hash('password123', 10), + }); + + req.session = { + userId: user.id, + }; + + await requireAuth(req, res, next); + + expect(req.currentUser).toBeDefined(); + expect(req.currentUser.id).toBe(user.id); + expect(req.currentUser.email).toBe(user.email); + expect(next).toHaveBeenCalled(); + expect(res.status).not.toHaveBeenCalled(); + }); + + it('should handle database errors', async () => { + // Mock console.error to suppress expected error log in test output + const originalConsoleError = console.error; + console.error = jest.fn(); + + // Mock User.findByPk to throw an error + const originalFindByPk = User.findByPk; + User.findByPk = jest + .fn() + .mockRejectedValue(new Error('Database connection error')); + + req.session = { + userId: 123, + destroy: jest.fn(), + }; + + await requireAuth(req, res, next); + + expect(res.status).toHaveBeenCalledWith(500); + expect(res.json).toHaveBeenCalledWith({ + error: 'Authentication error', + }); + expect(next).not.toHaveBeenCalled(); + + // Restore original methods + User.findByPk = originalFindByPk; + console.error = originalConsoleError; + }); +}); diff --git a/backend/tests/unit/models/area.test.js b/backend/tests/unit/models/area.test.js index f4eb012..50b09a0 100644 --- a/backend/tests/unit/models/area.test.js +++ b/backend/tests/unit/models/area.test.js @@ -1,74 +1,74 @@ const { Area, User } = require('../../../models'); describe('Area Model', () => { - let user; + let user; - beforeEach(async () => { - const bcrypt = require('bcrypt'); - user = await User.create({ - email: 'test@example.com', - password_digest: await bcrypt.hash('password123', 10) - }); - }); - - describe('validation', () => { - it('should create an area with valid data', async () => { - const areaData = { - name: 'Work', - description: 'Work related projects', - user_id: user.id - }; - - const area = await Area.create(areaData); - - expect(area.name).toBe(areaData.name); - expect(area.description).toBe(areaData.description); - expect(area.user_id).toBe(user.id); + beforeEach(async () => { + const bcrypt = require('bcrypt'); + user = await User.create({ + email: 'test@example.com', + password_digest: await bcrypt.hash('password123', 10), + }); }); - it('should require name', async () => { - const areaData = { - description: 'Area without name', - user_id: user.id - }; + describe('validation', () => { + it('should create an area with valid data', async () => { + const areaData = { + name: 'Work', + description: 'Work related projects', + user_id: user.id, + }; - await expect(Area.create(areaData)).rejects.toThrow(); + const area = await Area.create(areaData); + + expect(area.name).toBe(areaData.name); + expect(area.description).toBe(areaData.description); + expect(area.user_id).toBe(user.id); + }); + + it('should require name', async () => { + const areaData = { + description: 'Area without name', + user_id: user.id, + }; + + await expect(Area.create(areaData)).rejects.toThrow(); + }); + + it('should require user_id', async () => { + const areaData = { + name: 'Test Area', + }; + + await expect(Area.create(areaData)).rejects.toThrow(); + }); + + it('should allow null description', async () => { + const areaData = { + name: 'Test Area', + user_id: user.id, + description: null, + }; + + const area = await Area.create(areaData); + expect(area.description).toBeNull(); + }); }); - it('should require user_id', async () => { - const areaData = { - name: 'Test Area' - }; + describe('associations', () => { + it('should belong to a user', async () => { + const area = await Area.create({ + name: 'Test Area', + user_id: user.id, + }); - await expect(Area.create(areaData)).rejects.toThrow(); + const areaWithUser = await Area.findByPk(area.id, { + include: [{ model: User }], + }); + + expect(areaWithUser.User).toBeDefined(); + expect(areaWithUser.User.id).toBe(user.id); + expect(areaWithUser.User.email).toBe(user.email); + }); }); - - it('should allow null description', async () => { - const areaData = { - name: 'Test Area', - user_id: user.id, - description: null - }; - - const area = await Area.create(areaData); - expect(area.description).toBeNull(); - }); - }); - - describe('associations', () => { - it('should belong to a user', async () => { - const area = await Area.create({ - name: 'Test Area', - user_id: user.id - }); - - const areaWithUser = await Area.findByPk(area.id, { - include: [{ model: User }] - }); - - expect(areaWithUser.User).toBeDefined(); - expect(areaWithUser.User.id).toBe(user.id); - expect(areaWithUser.User.email).toBe(user.email); - }); - }); -}); \ No newline at end of file +}); diff --git a/backend/tests/unit/models/inbox_item.test.js b/backend/tests/unit/models/inbox_item.test.js index fa707b6..54cc5b4 100644 --- a/backend/tests/unit/models/inbox_item.test.js +++ b/backend/tests/unit/models/inbox_item.test.js @@ -1,96 +1,96 @@ const { InboxItem, User } = require('../../../models'); describe('InboxItem Model', () => { - let user; + let user; - beforeEach(async () => { - const bcrypt = require('bcrypt'); - user = await User.create({ - email: 'test@example.com', - password_digest: await bcrypt.hash('password123', 10) - }); - }); - - describe('validation', () => { - it('should create an inbox item with valid data', async () => { - const inboxData = { - content: 'Remember to buy groceries', - status: 'added', - source: 'web', - user_id: user.id - }; - - const inboxItem = await InboxItem.create(inboxData); - - expect(inboxItem.content).toBe(inboxData.content); - expect(inboxItem.status).toBe(inboxData.status); - expect(inboxItem.source).toBe(inboxData.source); - expect(inboxItem.user_id).toBe(user.id); + beforeEach(async () => { + const bcrypt = require('bcrypt'); + user = await User.create({ + email: 'test@example.com', + password_digest: await bcrypt.hash('password123', 10), + }); }); - it('should require content', async () => { - const inboxData = { - user_id: user.id - }; + describe('validation', () => { + it('should create an inbox item with valid data', async () => { + const inboxData = { + content: 'Remember to buy groceries', + status: 'added', + source: 'web', + user_id: user.id, + }; - await expect(InboxItem.create(inboxData)).rejects.toThrow(); + const inboxItem = await InboxItem.create(inboxData); + + expect(inboxItem.content).toBe(inboxData.content); + expect(inboxItem.status).toBe(inboxData.status); + expect(inboxItem.source).toBe(inboxData.source); + expect(inboxItem.user_id).toBe(user.id); + }); + + it('should require content', async () => { + const inboxData = { + user_id: user.id, + }; + + await expect(InboxItem.create(inboxData)).rejects.toThrow(); + }); + + it('should require user_id', async () => { + const inboxData = { + content: 'Test content', + }; + + await expect(InboxItem.create(inboxData)).rejects.toThrow(); + }); + + it('should require status', async () => { + const inboxData = { + content: 'Test content', + user_id: user.id, + status: null, + }; + + await expect(InboxItem.create(inboxData)).rejects.toThrow(); + }); + + it('should require source', async () => { + const inboxData = { + content: 'Test content', + user_id: user.id, + source: null, + }; + + await expect(InboxItem.create(inboxData)).rejects.toThrow(); + }); }); - it('should require user_id', async () => { - const inboxData = { - content: 'Test content' - }; + describe('default values', () => { + it('should set correct default values', async () => { + const inboxItem = await InboxItem.create({ + content: 'Test content', + user_id: user.id, + }); - await expect(InboxItem.create(inboxData)).rejects.toThrow(); + expect(inboxItem.status).toBe('added'); + expect(inboxItem.source).toBe('tududi'); + }); }); - it('should require status', async () => { - const inboxData = { - content: 'Test content', - user_id: user.id, - status: null - }; + describe('associations', () => { + it('should belong to a user', async () => { + const inboxItem = await InboxItem.create({ + content: 'Test content', + user_id: user.id, + }); - await expect(InboxItem.create(inboxData)).rejects.toThrow(); + const inboxItemWithUser = await InboxItem.findByPk(inboxItem.id, { + include: [{ model: User }], + }); + + expect(inboxItemWithUser.User).toBeDefined(); + expect(inboxItemWithUser.User.id).toBe(user.id); + expect(inboxItemWithUser.User.email).toBe(user.email); + }); }); - - it('should require source', async () => { - const inboxData = { - content: 'Test content', - user_id: user.id, - source: null - }; - - await expect(InboxItem.create(inboxData)).rejects.toThrow(); - }); - }); - - describe('default values', () => { - it('should set correct default values', async () => { - const inboxItem = await InboxItem.create({ - content: 'Test content', - user_id: user.id - }); - - expect(inboxItem.status).toBe('added'); - expect(inboxItem.source).toBe('tududi'); - }); - }); - - describe('associations', () => { - it('should belong to a user', async () => { - const inboxItem = await InboxItem.create({ - content: 'Test content', - user_id: user.id - }); - - const inboxItemWithUser = await InboxItem.findByPk(inboxItem.id, { - include: [{ model: User }] - }); - - expect(inboxItemWithUser.User).toBeDefined(); - expect(inboxItemWithUser.User.id).toBe(user.id); - expect(inboxItemWithUser.User.email).toBe(user.email); - }); - }); -}); \ No newline at end of file +}); diff --git a/backend/tests/unit/models/note.test.js b/backend/tests/unit/models/note.test.js index bb08555..2f79666 100644 --- a/backend/tests/unit/models/note.test.js +++ b/backend/tests/unit/models/note.test.js @@ -1,102 +1,102 @@ const { Note, User, Project } = require('../../../models'); describe('Note Model', () => { - let user, project; + let user, project; - beforeEach(async () => { - const bcrypt = require('bcrypt'); - user = await User.create({ - email: 'test@example.com', - password_digest: await bcrypt.hash('password123', 10) + beforeEach(async () => { + const bcrypt = require('bcrypt'); + user = await User.create({ + email: 'test@example.com', + password_digest: await bcrypt.hash('password123', 10), + }); + + project = await Project.create({ + name: 'Test Project', + user_id: user.id, + }); }); - project = await Project.create({ - name: 'Test Project', - user_id: user.id - }); - }); + describe('validation', () => { + it('should create a note with valid data', async () => { + const noteData = { + title: 'Test Note', + content: 'This is a test note content', + user_id: user.id, + project_id: project.id, + }; - describe('validation', () => { - it('should create a note with valid data', async () => { - const noteData = { - title: 'Test Note', - content: 'This is a test note content', - user_id: user.id, - project_id: project.id - }; + const note = await Note.create(noteData); - const note = await Note.create(noteData); - - expect(note.title).toBe(noteData.title); - expect(note.content).toBe(noteData.content); - expect(note.user_id).toBe(user.id); - expect(note.project_id).toBe(project.id); + expect(note.title).toBe(noteData.title); + expect(note.content).toBe(noteData.content); + expect(note.user_id).toBe(user.id); + expect(note.project_id).toBe(project.id); + }); + + it('should require user_id', async () => { + const noteData = { + title: 'Test Note', + content: 'Test content', + }; + + await expect(Note.create(noteData)).rejects.toThrow(); + }); + + it('should allow title and content to be null', async () => { + const noteData = { + title: null, + content: null, + user_id: user.id, + }; + + const note = await Note.create(noteData); + expect(note.title).toBeNull(); + expect(note.content).toBeNull(); + }); + + it('should allow project_id to be null', async () => { + const noteData = { + title: 'Test Note', + content: 'Test content', + user_id: user.id, + project_id: null, + }; + + const note = await Note.create(noteData); + expect(note.project_id).toBeNull(); + }); }); - it('should require user_id', async () => { - const noteData = { - title: 'Test Note', - content: 'Test content' - }; + describe('associations', () => { + it('should belong to a user', async () => { + const note = await Note.create({ + title: 'Test Note', + user_id: user.id, + }); - await expect(Note.create(noteData)).rejects.toThrow(); + const noteWithUser = await Note.findByPk(note.id, { + include: [{ model: User }], + }); + + expect(noteWithUser.User).toBeDefined(); + expect(noteWithUser.User.id).toBe(user.id); + expect(noteWithUser.User.email).toBe(user.email); + }); + + it('should belong to a project', async () => { + const note = await Note.create({ + title: 'Test Note', + user_id: user.id, + project_id: project.id, + }); + + const noteWithProject = await Note.findByPk(note.id, { + include: [{ model: Project }], + }); + + expect(noteWithProject.Project).toBeDefined(); + expect(noteWithProject.Project.id).toBe(project.id); + expect(noteWithProject.Project.name).toBe(project.name); + }); }); - - it('should allow title and content to be null', async () => { - const noteData = { - title: null, - content: null, - user_id: user.id - }; - - const note = await Note.create(noteData); - expect(note.title).toBeNull(); - expect(note.content).toBeNull(); - }); - - it('should allow project_id to be null', async () => { - const noteData = { - title: 'Test Note', - content: 'Test content', - user_id: user.id, - project_id: null - }; - - const note = await Note.create(noteData); - expect(note.project_id).toBeNull(); - }); - }); - - describe('associations', () => { - it('should belong to a user', async () => { - const note = await Note.create({ - title: 'Test Note', - user_id: user.id - }); - - const noteWithUser = await Note.findByPk(note.id, { - include: [{ model: User }] - }); - - expect(noteWithUser.User).toBeDefined(); - expect(noteWithUser.User.id).toBe(user.id); - expect(noteWithUser.User.email).toBe(user.email); - }); - - it('should belong to a project', async () => { - const note = await Note.create({ - title: 'Test Note', - user_id: user.id, - project_id: project.id - }); - - const noteWithProject = await Note.findByPk(note.id, { - include: [{ model: Project }] - }); - - expect(noteWithProject.Project).toBeDefined(); - expect(noteWithProject.Project.id).toBe(project.id); - expect(noteWithProject.Project.name).toBe(project.name); - }); - }); -}); \ No newline at end of file +}); diff --git a/backend/tests/unit/models/project.test.js b/backend/tests/unit/models/project.test.js index 55af80d..6a37ac6 100644 --- a/backend/tests/unit/models/project.test.js +++ b/backend/tests/unit/models/project.test.js @@ -1,141 +1,141 @@ const { Project, User, Area } = require('../../../models'); describe('Project Model', () => { - let user, area; + let user, area; - beforeEach(async () => { - const bcrypt = require('bcrypt'); - user = await User.create({ - email: 'test@example.com', - password_digest: await bcrypt.hash('password123', 10) - }); - - area = await Area.create({ - name: 'Work', - user_id: user.id - }); - }); - - describe('validation', () => { - it('should create a project with valid data', async () => { - const projectData = { - name: 'Test Project', - description: 'Test Description', - active: true, - pin_to_sidebar: false, - priority: 1, - user_id: user.id, - area_id: area.id - }; - - const project = await Project.create(projectData); - - expect(project.name).toBe(projectData.name); - expect(project.description).toBe(projectData.description); - expect(project.active).toBe(projectData.active); - expect(project.pin_to_sidebar).toBe(projectData.pin_to_sidebar); - expect(project.priority).toBe(projectData.priority); - expect(project.user_id).toBe(user.id); - expect(project.area_id).toBe(area.id); - }); - - it('should require name', async () => { - const projectData = { - description: 'Project without name', - user_id: user.id - }; - - await expect(Project.create(projectData)).rejects.toThrow(); - }); - - it('should require user_id', async () => { - const projectData = { - name: 'Test Project' - }; - - await expect(Project.create(projectData)).rejects.toThrow(); - }); - - it('should validate priority range', async () => { - const projectData = { - name: 'Test Project', - user_id: user.id, - priority: 5 - }; - - await expect(Project.create(projectData)).rejects.toThrow(); - }); - - it('should allow valid priority values', async () => { - for (let priority of [0, 1, 2]) { - const project = await Project.create({ - name: `Test Project ${priority}`, - user_id: user.id, - priority: priority + beforeEach(async () => { + const bcrypt = require('bcrypt'); + user = await User.create({ + email: 'test@example.com', + password_digest: await bcrypt.hash('password123', 10), }); - expect(project.priority).toBe(priority); - } - }); - }); - describe('default values', () => { - it('should set correct default values', async () => { - const project = await Project.create({ - name: 'Test Project', - user_id: user.id - }); - - expect(project.active).toBe(false); - expect(project.pin_to_sidebar).toBe(false); - }); - }); - - describe('optional fields', () => { - it('should allow optional fields to be null', async () => { - const project = await Project.create({ - name: 'Test Project', - user_id: user.id, - description: null, - priority: null, - due_date_at: null, - area_id: null - }); - - expect(project.description).toBeNull(); - expect(project.priority).toBeNull(); - expect(project.due_date_at).toBeNull(); - expect(project.area_id).toBeNull(); - }); - }); - - describe('associations', () => { - it('should belong to a user', async () => { - const project = await Project.create({ - name: 'Test Project', - user_id: user.id - }); - - const projectWithUser = await Project.findByPk(project.id, { - include: [{ model: User }] - }); - - expect(projectWithUser.User).toBeDefined(); - expect(projectWithUser.User.id).toBe(user.id); + area = await Area.create({ + name: 'Work', + user_id: user.id, + }); }); - it('should belong to an area', async () => { - const project = await Project.create({ - name: 'Test Project', - user_id: user.id, - area_id: area.id - }); + describe('validation', () => { + it('should create a project with valid data', async () => { + const projectData = { + name: 'Test Project', + description: 'Test Description', + active: true, + pin_to_sidebar: false, + priority: 1, + user_id: user.id, + area_id: area.id, + }; - const projectWithArea = await Project.findByPk(project.id, { - include: [{ model: Area }] - }); + const project = await Project.create(projectData); - expect(projectWithArea.Area).toBeDefined(); - expect(projectWithArea.Area.id).toBe(area.id); + expect(project.name).toBe(projectData.name); + expect(project.description).toBe(projectData.description); + expect(project.active).toBe(projectData.active); + expect(project.pin_to_sidebar).toBe(projectData.pin_to_sidebar); + expect(project.priority).toBe(projectData.priority); + expect(project.user_id).toBe(user.id); + expect(project.area_id).toBe(area.id); + }); + + it('should require name', async () => { + const projectData = { + description: 'Project without name', + user_id: user.id, + }; + + await expect(Project.create(projectData)).rejects.toThrow(); + }); + + it('should require user_id', async () => { + const projectData = { + name: 'Test Project', + }; + + await expect(Project.create(projectData)).rejects.toThrow(); + }); + + it('should validate priority range', async () => { + const projectData = { + name: 'Test Project', + user_id: user.id, + priority: 5, + }; + + await expect(Project.create(projectData)).rejects.toThrow(); + }); + + it('should allow valid priority values', async () => { + for (let priority of [0, 1, 2]) { + const project = await Project.create({ + name: `Test Project ${priority}`, + user_id: user.id, + priority: priority, + }); + expect(project.priority).toBe(priority); + } + }); }); - }); -}); \ No newline at end of file + + describe('default values', () => { + it('should set correct default values', async () => { + const project = await Project.create({ + name: 'Test Project', + user_id: user.id, + }); + + expect(project.active).toBe(false); + expect(project.pin_to_sidebar).toBe(false); + }); + }); + + describe('optional fields', () => { + it('should allow optional fields to be null', async () => { + const project = await Project.create({ + name: 'Test Project', + user_id: user.id, + description: null, + priority: null, + due_date_at: null, + area_id: null, + }); + + expect(project.description).toBeNull(); + expect(project.priority).toBeNull(); + expect(project.due_date_at).toBeNull(); + expect(project.area_id).toBeNull(); + }); + }); + + describe('associations', () => { + it('should belong to a user', async () => { + const project = await Project.create({ + name: 'Test Project', + user_id: user.id, + }); + + const projectWithUser = await Project.findByPk(project.id, { + include: [{ model: User }], + }); + + expect(projectWithUser.User).toBeDefined(); + expect(projectWithUser.User.id).toBe(user.id); + }); + + it('should belong to an area', async () => { + const project = await Project.create({ + name: 'Test Project', + user_id: user.id, + area_id: area.id, + }); + + const projectWithArea = await Project.findByPk(project.id, { + include: [{ model: Area }], + }); + + expect(projectWithArea.Area).toBeDefined(); + expect(projectWithArea.Area.id).toBe(area.id); + }); + }); +}); diff --git a/backend/tests/unit/models/tag.test.js b/backend/tests/unit/models/tag.test.js index 9a334f3..20b3e9e 100644 --- a/backend/tests/unit/models/tag.test.js +++ b/backend/tests/unit/models/tag.test.js @@ -1,83 +1,83 @@ const { Tag, User } = require('../../../models'); describe('Tag Model', () => { - let user; + let user; - beforeEach(async () => { - const bcrypt = require('bcrypt'); - user = await User.create({ - email: 'test@example.com', - password_digest: await bcrypt.hash('password123', 10) - }); - }); - - describe('validation', () => { - it('should create a tag with valid data', async () => { - const tagData = { - name: 'work', - user_id: user.id - }; - - const tag = await Tag.create(tagData); - - expect(tag.name).toBe(tagData.name); - expect(tag.user_id).toBe(user.id); + beforeEach(async () => { + const bcrypt = require('bcrypt'); + user = await User.create({ + email: 'test@example.com', + password_digest: await bcrypt.hash('password123', 10), + }); }); - it('should require name', async () => { - const tagData = { - user_id: user.id - }; + describe('validation', () => { + it('should create a tag with valid data', async () => { + const tagData = { + name: 'work', + user_id: user.id, + }; - await expect(Tag.create(tagData)).rejects.toThrow(); + const tag = await Tag.create(tagData); + + expect(tag.name).toBe(tagData.name); + expect(tag.user_id).toBe(user.id); + }); + + it('should require name', async () => { + const tagData = { + user_id: user.id, + }; + + await expect(Tag.create(tagData)).rejects.toThrow(); + }); + + it('should require user_id', async () => { + const tagData = { + name: 'work', + }; + + await expect(Tag.create(tagData)).rejects.toThrow(); + }); + + it('should allow multiple tags with same name for different users', async () => { + const bcrypt = require('bcrypt'); + const otherUser = await User.create({ + email: 'other@example.com', + password_digest: await bcrypt.hash('password123', 10), + }); + + const tag1 = await Tag.create({ + name: 'work', + user_id: user.id, + }); + + const tag2 = await Tag.create({ + name: 'work', + user_id: otherUser.id, + }); + + expect(tag1.name).toBe('work'); + expect(tag2.name).toBe('work'); + expect(tag1.user_id).toBe(user.id); + expect(tag2.user_id).toBe(otherUser.id); + }); }); - it('should require user_id', async () => { - const tagData = { - name: 'work' - }; + describe('associations', () => { + it('should belong to a user', async () => { + const tag = await Tag.create({ + name: 'work', + user_id: user.id, + }); - await expect(Tag.create(tagData)).rejects.toThrow(); + const tagWithUser = await Tag.findByPk(tag.id, { + include: [{ model: User }], + }); + + expect(tagWithUser.User).toBeDefined(); + expect(tagWithUser.User.id).toBe(user.id); + expect(tagWithUser.User.email).toBe(user.email); + }); }); - - it('should allow multiple tags with same name for different users', async () => { - const bcrypt = require('bcrypt'); - const otherUser = await User.create({ - email: 'other@example.com', - password_digest: await bcrypt.hash('password123', 10) - }); - - const tag1 = await Tag.create({ - name: 'work', - user_id: user.id - }); - - const tag2 = await Tag.create({ - name: 'work', - user_id: otherUser.id - }); - - expect(tag1.name).toBe('work'); - expect(tag2.name).toBe('work'); - expect(tag1.user_id).toBe(user.id); - expect(tag2.user_id).toBe(otherUser.id); - }); - }); - - describe('associations', () => { - it('should belong to a user', async () => { - const tag = await Tag.create({ - name: 'work', - user_id: user.id - }); - - const tagWithUser = await Tag.findByPk(tag.id, { - include: [{ model: User }] - }); - - expect(tagWithUser.User).toBeDefined(); - expect(tagWithUser.User.id).toBe(user.id); - expect(tagWithUser.User.email).toBe(user.email); - }); - }); -}); \ No newline at end of file +}); diff --git a/backend/tests/unit/models/task.test.js b/backend/tests/unit/models/task.test.js index 53b34cc..ea89688 100644 --- a/backend/tests/unit/models/task.test.js +++ b/backend/tests/unit/models/task.test.js @@ -1,183 +1,183 @@ const { Task, User } = require('../../../models'); describe('Task Model', () => { - let user; - - beforeEach(async () => { - const bcrypt = require('bcrypt'); - user = await User.create({ - email: 'test@example.com', - password_digest: await bcrypt.hash('password123', 10) - }); - }); - - describe('validation', () => { - it('should create a task with valid data', async () => { - const taskData = { - name: 'Test Task', - description: 'Test Description', - user_id: user.id - }; - - const task = await Task.create(taskData); - - expect(task.name).toBe(taskData.name); - expect(task.description).toBe(taskData.description); - expect(task.user_id).toBe(user.id); - expect(task.today).toBe(false); - expect(task.priority).toBe(0); - expect(task.status).toBe(0); - expect(task.recurrence_type).toBe('none'); - }); - - it('should require name', async () => { - const taskData = { - user_id: user.id - }; - - await expect(Task.create(taskData)).rejects.toThrow(); - }); - - it('should require user_id', async () => { - const taskData = { - name: 'Test Task' - }; - - await expect(Task.create(taskData)).rejects.toThrow(); - }); - - it('should validate priority range', async () => { - const taskData = { - name: 'Test Task', - user_id: user.id, - priority: 5 - }; - - await expect(Task.create(taskData)).rejects.toThrow(); - }); - - it('should validate status range', async () => { - const taskData = { - name: 'Test Task', - user_id: user.id, - status: 10 - }; - - await expect(Task.create(taskData)).rejects.toThrow(); - }); - }); - - describe('constants', () => { - it('should have correct priority constants', () => { - expect(Task.PRIORITY.LOW).toBe(0); - expect(Task.PRIORITY.MEDIUM).toBe(1); - expect(Task.PRIORITY.HIGH).toBe(2); - }); - - it('should have correct status constants', () => { - expect(Task.STATUS.NOT_STARTED).toBe(0); - expect(Task.STATUS.IN_PROGRESS).toBe(1); - expect(Task.STATUS.DONE).toBe(2); - expect(Task.STATUS.ARCHIVED).toBe(3); - expect(Task.STATUS.WAITING).toBe(4); - }); - }); - - describe('instance methods', () => { - let task; + let user; beforeEach(async () => { - task = await Task.create({ - name: 'Test Task', - user_id: user.id - }); + const bcrypt = require('bcrypt'); + user = await User.create({ + email: 'test@example.com', + password_digest: await bcrypt.hash('password123', 10), + }); }); - it('should return correct priority name', async () => { - task.priority = Task.PRIORITY.LOW; - expect(Task.getPriorityName(task.priority)).toBe('low'); + describe('validation', () => { + it('should create a task with valid data', async () => { + const taskData = { + name: 'Test Task', + description: 'Test Description', + user_id: user.id, + }; - task.priority = Task.PRIORITY.MEDIUM; - expect(Task.getPriorityName(task.priority)).toBe('medium'); + const task = await Task.create(taskData); - task.priority = Task.PRIORITY.HIGH; - expect(Task.getPriorityName(task.priority)).toBe('high'); + expect(task.name).toBe(taskData.name); + expect(task.description).toBe(taskData.description); + expect(task.user_id).toBe(user.id); + expect(task.today).toBe(false); + expect(task.priority).toBe(0); + expect(task.status).toBe(0); + expect(task.recurrence_type).toBe('none'); + }); + + it('should require name', async () => { + const taskData = { + user_id: user.id, + }; + + await expect(Task.create(taskData)).rejects.toThrow(); + }); + + it('should require user_id', async () => { + const taskData = { + name: 'Test Task', + }; + + await expect(Task.create(taskData)).rejects.toThrow(); + }); + + it('should validate priority range', async () => { + const taskData = { + name: 'Test Task', + user_id: user.id, + priority: 5, + }; + + await expect(Task.create(taskData)).rejects.toThrow(); + }); + + it('should validate status range', async () => { + const taskData = { + name: 'Test Task', + user_id: user.id, + status: 10, + }; + + await expect(Task.create(taskData)).rejects.toThrow(); + }); }); - it('should return correct status name', async () => { - task.status = Task.STATUS.NOT_STARTED; - expect(Task.getStatusName(task.status)).toBe('not_started'); + describe('constants', () => { + it('should have correct priority constants', () => { + expect(Task.PRIORITY.LOW).toBe(0); + expect(Task.PRIORITY.MEDIUM).toBe(1); + expect(Task.PRIORITY.HIGH).toBe(2); + }); - task.status = Task.STATUS.IN_PROGRESS; - expect(Task.getStatusName(task.status)).toBe('in_progress'); - - task.status = Task.STATUS.DONE; - expect(Task.getStatusName(task.status)).toBe('done'); - - task.status = Task.STATUS.ARCHIVED; - expect(Task.getStatusName(task.status)).toBe('archived'); - - task.status = Task.STATUS.WAITING; - expect(Task.getStatusName(task.status)).toBe('waiting'); - }); - }); - - describe('default values', () => { - it('should set correct default values', async () => { - const task = await Task.create({ - name: 'Test Task', - user_id: user.id - }); - - expect(task.today).toBe(false); - expect(task.priority).toBe(0); - expect(task.status).toBe(0); - expect(task.recurrence_type).toBe('none'); - }); - }); - - describe('optional fields', () => { - it('should allow optional fields to be null', async () => { - const task = await Task.create({ - name: 'Test Task', - user_id: user.id, - description: null, - due_date: null, - note: null, - recurrence_interval: null, - recurrence_end_date: null, - last_generated_date: null, - project_id: null - }); - - expect(task.description).toBeNull(); - expect(task.due_date).toBeNull(); - expect(task.note).toBeNull(); - expect(task.recurrence_interval).toBeNull(); - expect(task.recurrence_end_date).toBeNull(); - expect(task.last_generated_date).toBeNull(); - expect(task.project_id).toBeNull(); + it('should have correct status constants', () => { + expect(Task.STATUS.NOT_STARTED).toBe(0); + expect(Task.STATUS.IN_PROGRESS).toBe(1); + expect(Task.STATUS.DONE).toBe(2); + expect(Task.STATUS.ARCHIVED).toBe(3); + expect(Task.STATUS.WAITING).toBe(4); + }); }); - it('should accept optional field values', async () => { - const dueDate = new Date(); - const task = await Task.create({ - name: 'Test Task', - description: 'Test Description', - due_date: dueDate, - today: true, - priority: Task.PRIORITY.HIGH, - status: Task.STATUS.IN_PROGRESS, - note: 'Test Note', - user_id: user.id - }); + describe('instance methods', () => { + let task; - expect(task.description).toBe('Test Description'); - expect(task.due_date).toEqual(dueDate); - expect(task.today).toBe(true); - expect(task.priority).toBe(Task.PRIORITY.HIGH); - expect(task.status).toBe(Task.STATUS.IN_PROGRESS); - expect(task.note).toBe('Test Note'); + beforeEach(async () => { + task = await Task.create({ + name: 'Test Task', + user_id: user.id, + }); + }); + + it('should return correct priority name', async () => { + task.priority = Task.PRIORITY.LOW; + expect(Task.getPriorityName(task.priority)).toBe('low'); + + task.priority = Task.PRIORITY.MEDIUM; + expect(Task.getPriorityName(task.priority)).toBe('medium'); + + task.priority = Task.PRIORITY.HIGH; + expect(Task.getPriorityName(task.priority)).toBe('high'); + }); + + it('should return correct status name', async () => { + task.status = Task.STATUS.NOT_STARTED; + expect(Task.getStatusName(task.status)).toBe('not_started'); + + task.status = Task.STATUS.IN_PROGRESS; + expect(Task.getStatusName(task.status)).toBe('in_progress'); + + task.status = Task.STATUS.DONE; + expect(Task.getStatusName(task.status)).toBe('done'); + + task.status = Task.STATUS.ARCHIVED; + expect(Task.getStatusName(task.status)).toBe('archived'); + + task.status = Task.STATUS.WAITING; + expect(Task.getStatusName(task.status)).toBe('waiting'); + }); }); - }); -}); \ No newline at end of file + + describe('default values', () => { + it('should set correct default values', async () => { + const task = await Task.create({ + name: 'Test Task', + user_id: user.id, + }); + + expect(task.today).toBe(false); + expect(task.priority).toBe(0); + expect(task.status).toBe(0); + expect(task.recurrence_type).toBe('none'); + }); + }); + + describe('optional fields', () => { + it('should allow optional fields to be null', async () => { + const task = await Task.create({ + name: 'Test Task', + user_id: user.id, + description: null, + due_date: null, + note: null, + recurrence_interval: null, + recurrence_end_date: null, + last_generated_date: null, + project_id: null, + }); + + expect(task.description).toBeNull(); + expect(task.due_date).toBeNull(); + expect(task.note).toBeNull(); + expect(task.recurrence_interval).toBeNull(); + expect(task.recurrence_end_date).toBeNull(); + expect(task.last_generated_date).toBeNull(); + expect(task.project_id).toBeNull(); + }); + + it('should accept optional field values', async () => { + const dueDate = new Date(); + const task = await Task.create({ + name: 'Test Task', + description: 'Test Description', + due_date: dueDate, + today: true, + priority: Task.PRIORITY.HIGH, + status: Task.STATUS.IN_PROGRESS, + note: 'Test Note', + user_id: user.id, + }); + + expect(task.description).toBe('Test Description'); + expect(task.due_date).toEqual(dueDate); + expect(task.today).toBe(true); + expect(task.priority).toBe(Task.PRIORITY.HIGH); + expect(task.status).toBe(Task.STATUS.IN_PROGRESS); + expect(task.note).toBe('Test Note'); + }); + }); +}); diff --git a/backend/tests/unit/models/user.test.js b/backend/tests/unit/models/user.test.js index eb682f6..69754a3 100644 --- a/backend/tests/unit/models/user.test.js +++ b/backend/tests/unit/models/user.test.js @@ -1,137 +1,152 @@ const { User } = require('../../../models'); describe('User Model', () => { - describe('validation', () => { - it('should create a user with valid data', async () => { - const bcrypt = require('bcrypt'); - const userData = { - email: 'test@example.com', - password_digest: await bcrypt.hash('password123', 10) - }; + describe('validation', () => { + it('should create a user with valid data', async () => { + const bcrypt = require('bcrypt'); + const userData = { + email: 'test@example.com', + password_digest: await bcrypt.hash('password123', 10), + }; - const user = await User.create(userData); - - expect(user.email).toBe(userData.email); - expect(user.password_digest).toBeDefined(); - expect(user.password_digest).toBe(userData.password_digest); - expect(user.appearance).toBe('light'); - expect(user.language).toBe('en'); - expect(user.timezone).toBe('UTC'); + const user = await User.create(userData); + + expect(user.email).toBe(userData.email); + expect(user.password_digest).toBeDefined(); + expect(user.password_digest).toBe(userData.password_digest); + expect(user.appearance).toBe('light'); + expect(user.language).toBe('en'); + expect(user.timezone).toBe('UTC'); + }); + + it('should require email', async () => { + const userData = { + password: 'password123', + }; + + await expect(User.create(userData)).rejects.toThrow(); + }); + + it('should require valid email format', async () => { + const userData = { + email: 'invalid-email', + password: 'password123', + }; + + await expect(User.create(userData)).rejects.toThrow(); + }); + + it('should require unique email', async () => { + const bcrypt = require('bcrypt'); + const userData = { + email: 'test@example.com', + password_digest: await bcrypt.hash('password123', 10), + }; + + await User.create(userData); + await expect(User.create(userData)).rejects.toThrow(); + }); + + it('should validate appearance values', async () => { + const userData = { + email: 'test@example.com', + password: 'password123', + appearance: 'invalid', + }; + + await expect(User.create(userData)).rejects.toThrow(); + }); + + it('should validate task_summary_frequency values', async () => { + const userData = { + email: 'test@example.com', + password: 'password123', + task_summary_frequency: 'invalid', + }; + + await expect(User.create(userData)).rejects.toThrow(); + }); }); - it('should require email', async () => { - const userData = { - password: 'password123' - }; + describe('password methods', () => { + let user; - await expect(User.create(userData)).rejects.toThrow(); + beforeEach(async () => { + const bcrypt = require('bcrypt'); + user = await User.create({ + email: 'test@example.com', + password_digest: await bcrypt.hash('password123', 10), + }); + }); + + it('should hash password on creation', async () => { + expect(user.password_digest).toBeDefined(); + expect(user.password_digest).not.toBe('password123'); + }); + + it('should check password correctly', async () => { + const isValid = await User.checkPassword( + 'password123', + user.password_digest + ); + expect(isValid).toBe(true); + + const isInvalid = await User.checkPassword( + 'wrongpassword', + user.password_digest + ); + expect(isInvalid).toBe(false); + }); + + it('should set new password using setPassword method', async () => { + const oldPasswordDigest = user.password_digest; + const newPasswordDigest = await User.hashPassword('newpassword'); + user.password_digest = newPasswordDigest; + await user.save(); + + expect(user.password_digest).not.toBe(oldPasswordDigest); + + const isValidNew = await User.checkPassword( + 'newpassword', + user.password_digest + ); + expect(isValidNew).toBe(true); + + const isValidOld = await User.checkPassword( + 'password123', + user.password_digest + ); + expect(isValidOld).toBe(false); + }); + + it('should hash password on update', async () => { + const oldPasswordDigest = user.password_digest; + user.password = 'newpassword'; + await user.save(); + + expect(user.password_digest).not.toBe(oldPasswordDigest); + + const isValidNew = await User.checkPassword( + 'newpassword', + user.password_digest + ); + expect(isValidNew).toBe(true); + }); }); - it('should require valid email format', async () => { - const userData = { - email: 'invalid-email', - password: 'password123' - }; + describe('default values', () => { + it('should set correct default values', async () => { + const bcrypt = require('bcrypt'); + const user = await User.create({ + email: 'test@example.com', + password_digest: await bcrypt.hash('password123', 10), + }); - await expect(User.create(userData)).rejects.toThrow(); + expect(user.appearance).toBe('light'); + expect(user.language).toBe('en'); + expect(user.timezone).toBe('UTC'); + expect(user.task_summary_enabled).toBe(false); + expect(user.task_summary_frequency).toBe('daily'); + }); }); - - it('should require unique email', async () => { - const bcrypt = require('bcrypt'); - const userData = { - email: 'test@example.com', - password_digest: await bcrypt.hash('password123', 10) - }; - - await User.create(userData); - await expect(User.create(userData)).rejects.toThrow(); - }); - - it('should validate appearance values', async () => { - const userData = { - email: 'test@example.com', - password: 'password123', - appearance: 'invalid' - }; - - await expect(User.create(userData)).rejects.toThrow(); - }); - - it('should validate task_summary_frequency values', async () => { - const userData = { - email: 'test@example.com', - password: 'password123', - task_summary_frequency: 'invalid' - }; - - await expect(User.create(userData)).rejects.toThrow(); - }); - }); - - describe('password methods', () => { - let user; - - beforeEach(async () => { - const bcrypt = require('bcrypt'); - user = await User.create({ - email: 'test@example.com', - password_digest: await bcrypt.hash('password123', 10) - }); - }); - - it('should hash password on creation', async () => { - expect(user.password_digest).toBeDefined(); - expect(user.password_digest).not.toBe('password123'); - }); - - it('should check password correctly', async () => { - const isValid = await User.checkPassword('password123', user.password_digest); - expect(isValid).toBe(true); - - const isInvalid = await User.checkPassword('wrongpassword', user.password_digest); - expect(isInvalid).toBe(false); - }); - - it('should set new password using setPassword method', async () => { - const oldPasswordDigest = user.password_digest; - const newPasswordDigest = await User.hashPassword('newpassword'); - user.password_digest = newPasswordDigest; - await user.save(); - - expect(user.password_digest).not.toBe(oldPasswordDigest); - - const isValidNew = await User.checkPassword('newpassword', user.password_digest); - expect(isValidNew).toBe(true); - - const isValidOld = await User.checkPassword('password123', user.password_digest); - expect(isValidOld).toBe(false); - }); - - it('should hash password on update', async () => { - const oldPasswordDigest = user.password_digest; - user.password = 'newpassword'; - await user.save(); - - expect(user.password_digest).not.toBe(oldPasswordDigest); - - const isValidNew = await User.checkPassword('newpassword', user.password_digest); - expect(isValidNew).toBe(true); - }); - }); - - describe('default values', () => { - it('should set correct default values', async () => { - const bcrypt = require('bcrypt'); - const user = await User.create({ - email: 'test@example.com', - password_digest: await bcrypt.hash('password123', 10) - }); - - expect(user.appearance).toBe('light'); - expect(user.language).toBe('en'); - expect(user.timezone).toBe('UTC'); - expect(user.task_summary_enabled).toBe(false); - expect(user.task_summary_frequency).toBe('daily'); - }); - }); -}); \ No newline at end of file +}); diff --git a/backend/tests/unit/services/functional-services.test.js b/backend/tests/unit/services/functional-services.test.js index 407ca41..fa8e5e3 100644 --- a/backend/tests/unit/services/functional-services.test.js +++ b/backend/tests/unit/services/functional-services.test.js @@ -4,110 +4,127 @@ const quotesService = require('../../../services/quotesService'); const taskSummaryService = require('../../../services/taskSummaryService'); describe('Functional Services', () => { - describe('TaskScheduler', () => { - it('should export functional interface', () => { - expect(typeof taskScheduler.initialize).toBe('function'); - expect(typeof taskScheduler.stop).toBe('function'); - expect(typeof taskScheduler.restart).toBe('function'); - expect(typeof taskScheduler.getStatus).toBe('function'); + describe('TaskScheduler', () => { + it('should export functional interface', () => { + expect(typeof taskScheduler.initialize).toBe('function'); + expect(typeof taskScheduler.stop).toBe('function'); + expect(typeof taskScheduler.restart).toBe('function'); + expect(typeof taskScheduler.getStatus).toBe('function'); + }); + + it('should have pure helper functions for testing', () => { + expect(typeof taskScheduler._createSchedulerState).toBe('function'); + expect(typeof taskScheduler._shouldDisableScheduler).toBe( + 'function' + ); + expect(typeof taskScheduler._getCronExpression).toBe('function'); + }); + + it('should return proper cron expressions', () => { + expect(taskScheduler._getCronExpression('daily')).toBe('0 7 * * *'); + expect(taskScheduler._getCronExpression('weekly')).toBe( + '0 7 * * 1' + ); + expect(taskScheduler._getCronExpression('1h')).toBe('0 * * * *'); + }); }); - it('should have pure helper functions for testing', () => { - expect(typeof taskScheduler._createSchedulerState).toBe('function'); - expect(typeof taskScheduler._shouldDisableScheduler).toBe('function'); - expect(typeof taskScheduler._getCronExpression).toBe('function'); + describe('TelegramPoller', () => { + it('should export functional interface', () => { + expect(typeof telegramPoller.addUser).toBe('function'); + expect(typeof telegramPoller.removeUser).toBe('function'); + expect(typeof telegramPoller.getStatus).toBe('function'); + expect(typeof telegramPoller.sendTelegramMessage).toBe('function'); + }); + + it('should have pure helper functions for testing', () => { + expect(typeof telegramPoller._userExistsInList).toBe('function'); + expect(typeof telegramPoller._addUserToList).toBe('function'); + expect(typeof telegramPoller._removeUserFromList).toBe('function'); + expect(typeof telegramPoller._createMessageParams).toBe('function'); + }); + + it('should handle user list operations functionally', () => { + const users = [{ id: 1 }, { id: 2 }]; + const newUser = { id: 3 }; + + expect(telegramPoller._userExistsInList(users, 1)).toBe(true); + expect(telegramPoller._userExistsInList(users, 3)).toBe(false); + + const updatedUsers = telegramPoller._addUserToList(users, newUser); + expect(updatedUsers).toHaveLength(3); + expect(users).toHaveLength(2); // Original array unchanged + + const filteredUsers = telegramPoller._removeUserFromList( + updatedUsers, + 2 + ); + expect(filteredUsers).toHaveLength(2); + expect(filteredUsers.find((u) => u.id === 2)).toBeUndefined(); + }); }); - it('should return proper cron expressions', () => { - expect(taskScheduler._getCronExpression('daily')).toBe('0 7 * * *'); - expect(taskScheduler._getCronExpression('weekly')).toBe('0 7 * * 1'); - expect(taskScheduler._getCronExpression('1h')).toBe('0 * * * *'); - }); - }); + describe('QuotesService', () => { + it('should export functional interface', () => { + expect(typeof quotesService.getRandomQuote).toBe('function'); + expect(typeof quotesService.getAllQuotes).toBe('function'); + expect(typeof quotesService.getQuotesCount).toBe('function'); + expect(typeof quotesService.reloadQuotes).toBe('function'); + }); - describe('TelegramPoller', () => { - it('should export functional interface', () => { - expect(typeof telegramPoller.addUser).toBe('function'); - expect(typeof telegramPoller.removeUser).toBe('function'); - expect(typeof telegramPoller.getStatus).toBe('function'); - expect(typeof telegramPoller.sendTelegramMessage).toBe('function'); + it('should have pure helper functions for testing', () => { + expect(typeof quotesService._createDefaultQuotes).toBe('function'); + expect(typeof quotesService._getRandomIndex).toBe('function'); + expect(typeof quotesService._validateQuotesData).toBe('function'); + }); + + it('should validate quotes data structure correctly', () => { + const validData = { quotes: ['quote1', 'quote2'] }; + const invalidData1 = { quotes: 'not-array' }; + const invalidData2 = { notQuotes: ['quote1'] }; + const invalidData3 = null; + + expect(quotesService._validateQuotesData(validData)).toBe(true); + expect(quotesService._validateQuotesData(invalidData1)).toBe(false); + expect(quotesService._validateQuotesData(invalidData2)).toBe(false); + expect(quotesService._validateQuotesData(invalidData3)).toBe(false); + }); }); - it('should have pure helper functions for testing', () => { - expect(typeof telegramPoller._userExistsInList).toBe('function'); - expect(typeof telegramPoller._addUserToList).toBe('function'); - expect(typeof telegramPoller._removeUserFromList).toBe('function'); - expect(typeof telegramPoller._createMessageParams).toBe('function'); - }); + describe('TaskSummaryService', () => { + it('should export functional interface', () => { + expect(typeof taskSummaryService.generateSummaryForUser).toBe( + 'function' + ); + expect(typeof taskSummaryService.sendSummaryToUser).toBe( + 'function' + ); + expect(typeof taskSummaryService.calculateNextRunTime).toBe( + 'function' + ); + }); - it('should handle user list operations functionally', () => { - const users = [{ id: 1 }, { id: 2 }]; - const newUser = { id: 3 }; - - expect(telegramPoller._userExistsInList(users, 1)).toBe(true); - expect(telegramPoller._userExistsInList(users, 3)).toBe(false); - - const updatedUsers = telegramPoller._addUserToList(users, newUser); - expect(updatedUsers).toHaveLength(3); - expect(users).toHaveLength(2); // Original array unchanged - - const filteredUsers = telegramPoller._removeUserFromList(updatedUsers, 2); - expect(filteredUsers).toHaveLength(2); - expect(filteredUsers.find(u => u.id === 2)).toBeUndefined(); - }); - }); + it('should have pure helper functions for testing', () => { + expect(typeof taskSummaryService._escapeMarkdown).toBe('function'); + expect(typeof taskSummaryService._getPriorityEmoji).toBe( + 'function' + ); + expect(typeof taskSummaryService._buildTaskSection).toBe( + 'function' + ); + }); - describe('QuotesService', () => { - it('should export functional interface', () => { - expect(typeof quotesService.getRandomQuote).toBe('function'); - expect(typeof quotesService.getAllQuotes).toBe('function'); - expect(typeof quotesService.getQuotesCount).toBe('function'); - expect(typeof quotesService.reloadQuotes).toBe('function'); - }); + it('should escape markdown correctly', () => { + const text = 'Task with *bold* and _italic_ text'; + const escaped = taskSummaryService._escapeMarkdown(text); + expect(escaped).toBe('Task with \\*bold\\* and \\_italic\\_ text'); + }); - it('should have pure helper functions for testing', () => { - expect(typeof quotesService._createDefaultQuotes).toBe('function'); - expect(typeof quotesService._getRandomIndex).toBe('function'); - expect(typeof quotesService._validateQuotesData).toBe('function'); + it('should return correct priority emojis', () => { + expect(taskSummaryService._getPriorityEmoji(0)).toBe('🟢'); // low + expect(taskSummaryService._getPriorityEmoji(1)).toBe('🟠'); // medium + expect(taskSummaryService._getPriorityEmoji(2)).toBe('🔴'); // high + expect(taskSummaryService._getPriorityEmoji(99)).toBe('⚪'); // unknown + }); }); - - it('should validate quotes data structure correctly', () => { - const validData = { quotes: ['quote1', 'quote2'] }; - const invalidData1 = { quotes: 'not-array' }; - const invalidData2 = { notQuotes: ['quote1'] }; - const invalidData3 = null; - - expect(quotesService._validateQuotesData(validData)).toBe(true); - expect(quotesService._validateQuotesData(invalidData1)).toBe(false); - expect(quotesService._validateQuotesData(invalidData2)).toBe(false); - expect(quotesService._validateQuotesData(invalidData3)).toBe(false); - }); - }); - - describe('TaskSummaryService', () => { - it('should export functional interface', () => { - expect(typeof taskSummaryService.generateSummaryForUser).toBe('function'); - expect(typeof taskSummaryService.sendSummaryToUser).toBe('function'); - expect(typeof taskSummaryService.calculateNextRunTime).toBe('function'); - }); - - it('should have pure helper functions for testing', () => { - expect(typeof taskSummaryService._escapeMarkdown).toBe('function'); - expect(typeof taskSummaryService._getPriorityEmoji).toBe('function'); - expect(typeof taskSummaryService._buildTaskSection).toBe('function'); - }); - - it('should escape markdown correctly', () => { - const text = 'Task with *bold* and _italic_ text'; - const escaped = taskSummaryService._escapeMarkdown(text); - expect(escaped).toBe('Task with \\*bold\\* and \\_italic\\_ text'); - }); - - it('should return correct priority emojis', () => { - expect(taskSummaryService._getPriorityEmoji(0)).toBe('🟢'); // low - expect(taskSummaryService._getPriorityEmoji(1)).toBe('🟠'); // medium - expect(taskSummaryService._getPriorityEmoji(2)).toBe('🔴'); // high - expect(taskSummaryService._getPriorityEmoji(99)).toBe('⚪'); // unknown - }); - }); -}); \ No newline at end of file +}); diff --git a/backend/tests/unit/services/parentChildRelationship.test.js b/backend/tests/unit/services/parentChildRelationship.test.js index 71b8500..f28f968 100644 --- a/backend/tests/unit/services/parentChildRelationship.test.js +++ b/backend/tests/unit/services/parentChildRelationship.test.js @@ -3,571 +3,597 @@ const RecurringTaskService = require('../../../services/recurringTaskService'); const { createTestUser } = require('../../helpers/testUtils'); describe('Parent-Child Relationship Functionality', () => { - let user; - - beforeEach(async () => { - user = await createTestUser({ email: 'test@example.com' }); - }); - - describe('Task Instance Creation', () => { - it('should create child task with correct parent relationship', async () => { - const parentTask = await Task.create({ - name: 'Parent Task', - recurrence_type: 'daily', - recurrence_interval: 1, - user_id: user.id, - priority: 1, - note: 'Parent note' - }); - - const dueDate = new Date('2025-06-20T10:00:00Z'); - const childTask = await RecurringTaskService.createTaskInstance(parentTask, dueDate); - - expect(childTask.name).toBe(parentTask.name); - expect(childTask.description).toBe(parentTask.description); - expect(childTask.priority).toBe(parentTask.priority); - expect(childTask.note).toBe(parentTask.note); - expect(childTask.user_id).toBe(parentTask.user_id); - expect(childTask.project_id).toBe(parentTask.project_id); - expect(childTask.recurring_parent_id).toBe(parentTask.id); - expect(childTask.recurrence_type).toBe('none'); - expect(childTask.status).toBe(Task.STATUS.NOT_STARTED); - expect(childTask.due_date).toEqual(dueDate); - expect(childTask.today).toBe(false); - }); - - it('should preserve project assignment in child task', async () => { - // Create a real project first or skip project validation for this test - const parentTask = await Task.create({ - name: 'Parent Task', - recurrence_type: 'weekly', - recurrence_interval: 1, - user_id: user.id, - project_id: null, // Changed to null to avoid foreign key issues - priority: 2 - }); - - const dueDate = new Date('2025-06-20T10:00:00Z'); - const childTask = await RecurringTaskService.createTaskInstance(parentTask, dueDate); - - expect(childTask.project_id).toBeNull(); - expect(childTask.recurring_parent_id).toBe(parentTask.id); - }); - - it('should handle null description and note correctly', async () => { - const parentTask = await Task.create({ - name: 'Parent Task', - recurrence_type: 'monthly', - recurrence_interval: 1, - user_id: user.id, - description: null, - note: null, - priority: 0 - }); - - const dueDate = new Date('2025-06-20T10:00:00Z'); - const childTask = await RecurringTaskService.createTaskInstance(parentTask, dueDate); - - expect(childTask.description).toBeNull(); - expect(childTask.note).toBeNull(); - expect(childTask.recurring_parent_id).toBe(parentTask.id); - }); - }); - - describe('Parent-Child Task Queries', () => { - let parentTask, childTask1, childTask2; + let user; beforeEach(async () => { - parentTask = await Task.create({ - name: 'Daily Exercise', - recurrence_type: 'daily', - recurrence_interval: 1, - user_id: user.id, - priority: 1 - }); - - childTask1 = await Task.create({ - name: 'Daily Exercise', - recurrence_type: 'none', - recurring_parent_id: parentTask.id, - user_id: user.id, - due_date: new Date('2025-06-20T10:00:00Z'), - status: Task.STATUS.NOT_STARTED - }); - - childTask2 = await Task.create({ - name: 'Daily Exercise', - recurrence_type: 'none', - recurring_parent_id: parentTask.id, - user_id: user.id, - due_date: new Date('2025-06-21T10:00:00Z'), - status: Task.STATUS.DONE - }); + user = await createTestUser({ email: 'test@example.com' }); }); - it('should find all child tasks for a parent', async () => { - const childTasks = await Task.findAll({ - where: { - recurring_parent_id: parentTask.id, - user_id: user.id - }, - order: [['due_date', 'ASC']] - }); + describe('Task Instance Creation', () => { + it('should create child task with correct parent relationship', async () => { + const parentTask = await Task.create({ + name: 'Parent Task', + recurrence_type: 'daily', + recurrence_interval: 1, + user_id: user.id, + priority: 1, + note: 'Parent note', + }); - expect(childTasks).toHaveLength(2); - expect(childTasks[0].id).toBe(childTask1.id); - expect(childTasks[1].id).toBe(childTask2.id); - expect(childTasks[0].due_date).toBeDefined(); - expect(childTasks[1].due_date).toBeDefined(); + const dueDate = new Date('2025-06-20T10:00:00Z'); + const childTask = await RecurringTaskService.createTaskInstance( + parentTask, + dueDate + ); + + expect(childTask.name).toBe(parentTask.name); + expect(childTask.description).toBe(parentTask.description); + expect(childTask.priority).toBe(parentTask.priority); + expect(childTask.note).toBe(parentTask.note); + expect(childTask.user_id).toBe(parentTask.user_id); + expect(childTask.project_id).toBe(parentTask.project_id); + expect(childTask.recurring_parent_id).toBe(parentTask.id); + expect(childTask.recurrence_type).toBe('none'); + expect(childTask.status).toBe(Task.STATUS.NOT_STARTED); + expect(childTask.due_date).toEqual(dueDate); + expect(childTask.today).toBe(false); + }); + + it('should preserve project assignment in child task', async () => { + // Create a real project first or skip project validation for this test + const parentTask = await Task.create({ + name: 'Parent Task', + recurrence_type: 'weekly', + recurrence_interval: 1, + user_id: user.id, + project_id: null, // Changed to null to avoid foreign key issues + priority: 2, + }); + + const dueDate = new Date('2025-06-20T10:00:00Z'); + const childTask = await RecurringTaskService.createTaskInstance( + parentTask, + dueDate + ); + + expect(childTask.project_id).toBeNull(); + expect(childTask.recurring_parent_id).toBe(parentTask.id); + }); + + it('should handle null description and note correctly', async () => { + const parentTask = await Task.create({ + name: 'Parent Task', + recurrence_type: 'monthly', + recurrence_interval: 1, + user_id: user.id, + description: null, + note: null, + priority: 0, + }); + + const dueDate = new Date('2025-06-20T10:00:00Z'); + const childTask = await RecurringTaskService.createTaskInstance( + parentTask, + dueDate + ); + + expect(childTask.description).toBeNull(); + expect(childTask.note).toBeNull(); + expect(childTask.recurring_parent_id).toBe(parentTask.id); + }); }); - it('should find parent task from child', async () => { - const parent = await Task.findByPk(childTask1.recurring_parent_id); + describe('Parent-Child Task Queries', () => { + let parentTask, childTask1, childTask2; - expect(parent).not.toBeNull(); - expect(parent.id).toBe(parentTask.id); - expect(parent.recurrence_type).toBe('daily'); - expect(parent.recurrence_interval).toBe(1); + beforeEach(async () => { + parentTask = await Task.create({ + name: 'Daily Exercise', + recurrence_type: 'daily', + recurrence_interval: 1, + user_id: user.id, + priority: 1, + }); + + childTask1 = await Task.create({ + name: 'Daily Exercise', + recurrence_type: 'none', + recurring_parent_id: parentTask.id, + user_id: user.id, + due_date: new Date('2025-06-20T10:00:00Z'), + status: Task.STATUS.NOT_STARTED, + }); + + childTask2 = await Task.create({ + name: 'Daily Exercise', + recurrence_type: 'none', + recurring_parent_id: parentTask.id, + user_id: user.id, + due_date: new Date('2025-06-21T10:00:00Z'), + status: Task.STATUS.DONE, + }); + }); + + it('should find all child tasks for a parent', async () => { + const childTasks = await Task.findAll({ + where: { + recurring_parent_id: parentTask.id, + user_id: user.id, + }, + order: [['due_date', 'ASC']], + }); + + expect(childTasks).toHaveLength(2); + expect(childTasks[0].id).toBe(childTask1.id); + expect(childTasks[1].id).toBe(childTask2.id); + expect(childTasks[0].due_date).toBeDefined(); + expect(childTasks[1].due_date).toBeDefined(); + }); + + it('should find parent task from child', async () => { + const parent = await Task.findByPk(childTask1.recurring_parent_id); + + expect(parent).not.toBeNull(); + expect(parent.id).toBe(parentTask.id); + expect(parent.recurrence_type).toBe('daily'); + expect(parent.recurrence_interval).toBe(1); + }); + + it('should distinguish between parent and child tasks', async () => { + const allTasks = await Task.findAll({ + where: { user_id: user.id }, + order: [['id', 'ASC']], + }); + + const parentTasks = allTasks.filter( + (t) => t.recurrence_type !== 'none' + ); + const childTasks = allTasks.filter( + (t) => t.recurring_parent_id !== null + ); + + expect(parentTasks).toHaveLength(1); + expect(childTasks).toHaveLength(2); + expect(parentTasks[0].id).toBe(parentTask.id); + }); + + it('should handle tasks with no parent relationship', async () => { + const standaloneTask = await Task.create({ + name: 'Standalone Task', + recurrence_type: 'none', + user_id: user.id, + priority: 1, + }); + + expect(standaloneTask.recurring_parent_id).toBeFalsy(); // Can be null or undefined + expect(standaloneTask.recurrence_type).toBe('none'); + }); }); - it('should distinguish between parent and child tasks', async () => { - const allTasks = await Task.findAll({ - where: { user_id: user.id }, - order: [['id', 'ASC']] - }); + describe('Completion-Based Recurring Task Generation', () => { + it('should create next instance when completing completion-based parent task', async () => { + const parentTask = await Task.create({ + name: 'Completion Based Task', + recurrence_type: 'daily', + recurrence_interval: 1, + completion_based: true, + user_id: user.id, + status: Task.STATUS.NOT_STARTED, + }); - const parentTasks = allTasks.filter(t => t.recurrence_type !== 'none'); - const childTasks = allTasks.filter(t => t.recurring_parent_id !== null); + const nextTask = + await RecurringTaskService.handleTaskCompletion(parentTask); - expect(parentTasks).toHaveLength(1); - expect(childTasks).toHaveLength(2); - expect(parentTasks[0].id).toBe(parentTask.id); + expect(nextTask).not.toBeNull(); + expect(nextTask.name).toBe(parentTask.name); + expect(nextTask.recurring_parent_id).toBe(parentTask.id); + expect(nextTask.recurrence_type).toBe('none'); + expect(nextTask.status).toBe(Task.STATUS.NOT_STARTED); + expect(nextTask.due_date).toBeDefined(); + + // Verify parent task's last_generated_date was updated + const updatedParent = await Task.findByPk(parentTask.id); + expect(updatedParent.last_generated_date).toBeDefined(); + }); + + it('should not create multiple children when called repeatedly', async () => { + const parentTask = await Task.create({ + name: 'Completion Based Task', + recurrence_type: 'daily', + recurrence_interval: 1, + completion_based: true, + user_id: user.id, + status: Task.STATUS.NOT_STARTED, + }); + + // Call completion multiple times quickly + const firstNextTask = + await RecurringTaskService.handleTaskCompletion(parentTask); + expect(firstNextTask).not.toBeNull(); + + // Check how many child tasks exist for this parent + const childTasks = await Task.findAll({ + where: { + recurring_parent_id: parentTask.id, + user_id: user.id, + }, + }); + + // Should only have one child task despite multiple generations from same parent + expect(childTasks.length).toBeGreaterThanOrEqual(1); + expect(childTasks[0].recurring_parent_id).toBe(parentTask.id); + }); + + it('should handle child task completion properly', async () => { + const parentTask = await Task.create({ + name: 'Parent Task', + recurrence_type: 'daily', + recurrence_interval: 1, + completion_based: true, + user_id: user.id, + }); + + const childTask = await Task.create({ + name: 'Parent Task', + recurrence_type: 'none', + recurring_parent_id: parentTask.id, + user_id: user.id, + due_date: new Date('2025-06-20T10:00:00Z'), + status: Task.STATUS.NOT_STARTED, + }); + + // Completing child task should not create new instances + const nextTask = + await RecurringTaskService.handleTaskCompletion(childTask); + expect(nextTask).toBeNull(); + }); }); - it('should handle tasks with no parent relationship', async () => { - const standaloneTask = await Task.create({ - name: 'Standalone Task', - recurrence_type: 'none', - user_id: user.id, - priority: 1 - }); + describe('Parent Task Updates Through Child Tasks', () => { + let parentTask, childTask; - expect(standaloneTask.recurring_parent_id).toBeFalsy(); // Can be null or undefined - expect(standaloneTask.recurrence_type).toBe('none'); - }); - }); + beforeEach(async () => { + parentTask = await Task.create({ + name: 'Parent Task', + recurrence_type: 'daily', + recurrence_interval: 1, + recurrence_weekday: null, + completion_based: false, + user_id: user.id, + priority: 1, + }); - describe('Completion-Based Recurring Task Generation', () => { - it('should create next instance when completing completion-based parent task', async () => { - const parentTask = await Task.create({ - name: 'Completion Based Task', - recurrence_type: 'daily', - recurrence_interval: 1, - completion_based: true, - user_id: user.id, - status: Task.STATUS.NOT_STARTED - }); + childTask = await Task.create({ + name: 'Parent Task', + recurrence_type: 'none', + recurring_parent_id: parentTask.id, + user_id: user.id, + due_date: new Date('2025-06-20T10:00:00Z'), + status: Task.STATUS.NOT_STARTED, + }); + }); - const nextTask = await RecurringTaskService.handleTaskCompletion(parentTask); + it('should update parent recurrence settings through child task', async () => { + // Simulate updating parent through child + const updatedParent = await Task.findByPk(parentTask.id); + await updatedParent.update({ + recurrence_type: 'weekly', + recurrence_interval: 2, + recurrence_weekday: 1, // Monday + completion_based: true, + }); - expect(nextTask).not.toBeNull(); - expect(nextTask.name).toBe(parentTask.name); - expect(nextTask.recurring_parent_id).toBe(parentTask.id); - expect(nextTask.recurrence_type).toBe('none'); - expect(nextTask.status).toBe(Task.STATUS.NOT_STARTED); - expect(nextTask.due_date).toBeDefined(); + const refreshedParent = await Task.findByPk(parentTask.id); + expect(refreshedParent.recurrence_type).toBe('weekly'); + expect(refreshedParent.recurrence_interval).toBe(2); + expect(refreshedParent.recurrence_weekday).toBe(1); + expect(refreshedParent.completion_based).toBe(true); - // Verify parent task's last_generated_date was updated - const updatedParent = await Task.findByPk(parentTask.id); - expect(updatedParent.last_generated_date).toBeDefined(); + // Verify child task is unchanged + const refreshedChild = await Task.findByPk(childTask.id); + expect(refreshedChild.recurrence_type).toBe('none'); + expect(refreshedChild.recurring_parent_id).toBe(parentTask.id); + }); + + it('should preserve child task properties when updating parent', async () => { + await childTask.update({ status: Task.STATUS.IN_PROGRESS }); + + // Update parent + const updatedParent = await Task.findByPk(parentTask.id); + await updatedParent.update({ + recurrence_type: 'monthly', + recurrence_interval: 3, + }); + + // Verify child maintains its specific properties + const refreshedChild = await Task.findByPk(childTask.id); + expect(refreshedChild.status).toBe(Task.STATUS.IN_PROGRESS); + expect(refreshedChild.due_date).toEqual( + new Date('2025-06-20T10:00:00Z') + ); + expect(refreshedChild.recurring_parent_id).toBe(parentTask.id); + }); }); - it('should not create multiple children when called repeatedly', async () => { - const parentTask = await Task.create({ - name: 'Completion Based Task', - recurrence_type: 'daily', - recurrence_interval: 1, - completion_based: true, - user_id: user.id, - status: Task.STATUS.NOT_STARTED - }); + describe('Task Deletion Scenarios', () => { + let parentTask, childTask1, childTask2; - // Call completion multiple times quickly - const firstNextTask = await RecurringTaskService.handleTaskCompletion(parentTask); - expect(firstNextTask).not.toBeNull(); + beforeEach(async () => { + parentTask = await Task.create({ + name: 'Parent Task', + recurrence_type: 'weekly', + recurrence_interval: 1, + user_id: user.id, + priority: 1, + }); - // Check how many child tasks exist for this parent - const childTasks = await Task.findAll({ - where: { - recurring_parent_id: parentTask.id, - user_id: user.id - } - }); + childTask1 = await Task.create({ + name: 'Parent Task', + recurrence_type: 'none', + recurring_parent_id: parentTask.id, + user_id: user.id, + due_date: new Date('2025-06-20T10:00:00Z'), + status: Task.STATUS.NOT_STARTED, + }); - // Should only have one child task despite multiple generations from same parent - expect(childTasks.length).toBeGreaterThanOrEqual(1); - expect(childTasks[0].recurring_parent_id).toBe(parentTask.id); + childTask2 = await Task.create({ + name: 'Parent Task', + recurrence_type: 'none', + recurring_parent_id: parentTask.id, + user_id: user.id, + due_date: new Date('2025-06-27T10:00:00Z'), + status: Task.STATUS.DONE, + }); + }); + + it('should allow deleting child tasks without affecting parent', async () => { + await childTask1.destroy(); + + // Verify child is deleted + const deletedChild = await Task.findByPk(childTask1.id); + expect(deletedChild).toBeNull(); + + // Verify parent and other child still exist + const existingParent = await Task.findByPk(parentTask.id); + const existingChild = await Task.findByPk(childTask2.id); + expect(existingParent).not.toBeNull(); + expect(existingChild).not.toBeNull(); + }); + + it('should prevent deleting parent when child tasks exist due to foreign key constraint', async () => { + await expect(parentTask.destroy()).rejects.toThrow(); + + const error = await parentTask.destroy().catch((err) => err); + expect(error.name).toBe('SequelizeForeignKeyConstraintError'); + + // Verify parent and children still exist + const existingParent = await Task.findByPk(parentTask.id); + const existingChild1 = await Task.findByPk(childTask1.id); + const existingChild2 = await Task.findByPk(childTask2.id); + expect(existingParent).not.toBeNull(); + expect(existingChild1).not.toBeNull(); + expect(existingChild2).not.toBeNull(); + }); + + it('should allow deleting parent after deleting all child tasks', async () => { + // Delete all child tasks first + await childTask1.destroy(); + await childTask2.destroy(); + + // Now parent should be deletable + await parentTask.destroy(); + + // Verify all tasks are deleted + const deletedParent = await Task.findByPk(parentTask.id); + expect(deletedParent).toBeNull(); + }); }); - it('should handle child task completion properly', async () => { - const parentTask = await Task.create({ - name: 'Parent Task', - recurrence_type: 'daily', - recurrence_interval: 1, - completion_based: true, - user_id: user.id - }); + describe('Complex Parent-Child Scenarios', () => { + it('should handle multiple parents with different recurrence patterns', async () => { + const dailyParent = await Task.create({ + name: 'Daily Task', + recurrence_type: 'daily', + recurrence_interval: 1, + user_id: user.id, + priority: 1, + }); - const childTask = await Task.create({ - name: 'Parent Task', - recurrence_type: 'none', - recurring_parent_id: parentTask.id, - user_id: user.id, - due_date: new Date('2025-06-20T10:00:00Z'), - status: Task.STATUS.NOT_STARTED - }); + const weeklyParent = await Task.create({ + name: 'Weekly Task', + recurrence_type: 'weekly', + recurrence_interval: 1, + recurrence_weekday: 1, + user_id: user.id, + priority: 2, + }); - // Completing child task should not create new instances - const nextTask = await RecurringTaskService.handleTaskCompletion(childTask); - expect(nextTask).toBeNull(); - }); - }); + // Create child tasks for each parent + const dailyChild = await RecurringTaskService.createTaskInstance( + dailyParent, + new Date('2025-06-20T10:00:00Z') + ); - describe('Parent Task Updates Through Child Tasks', () => { - let parentTask, childTask; + const weeklyChild = await RecurringTaskService.createTaskInstance( + weeklyParent, + new Date('2025-06-23T10:00:00Z') + ); - beforeEach(async () => { - parentTask = await Task.create({ - name: 'Parent Task', - recurrence_type: 'daily', - recurrence_interval: 1, - recurrence_weekday: null, - completion_based: false, - user_id: user.id, - priority: 1 - }); + expect(dailyChild.recurring_parent_id).toBe(dailyParent.id); + expect(weeklyChild.recurring_parent_id).toBe(weeklyParent.id); + expect(dailyChild.name).toBe('Daily Task'); + expect(weeklyChild.name).toBe('Weekly Task'); + }); - childTask = await Task.create({ - name: 'Parent Task', - recurrence_type: 'none', - recurring_parent_id: parentTask.id, - user_id: user.id, - due_date: new Date('2025-06-20T10:00:00Z'), - status: Task.STATUS.NOT_STARTED - }); + it('should maintain data integrity across multiple child generations', async () => { + const parentTask = await Task.create({ + name: 'Long Running Task', + recurrence_type: 'daily', + recurrence_interval: 1, + completion_based: true, + user_id: user.id, + priority: 2, + }); + + const children = []; + + // Generate 5 child tasks + for (let i = 0; i < 5; i++) { + await parentTask.update({ status: Task.STATUS.DONE }); + const nextTask = + await RecurringTaskService.handleTaskCompletion(parentTask); + if (nextTask) { + children.push(nextTask); + } + await parentTask.update({ status: Task.STATUS.NOT_STARTED }); + } + + expect(children.length).toBe(5); + + // Verify all children have correct parent relationship + for (const child of children) { + expect(child.recurring_parent_id).toBe(parentTask.id); + expect(child.name).toBe(parentTask.name); + expect(child.recurrence_type).toBe('none'); + expect(child.status).toBe(Task.STATUS.NOT_STARTED); + } + + // Verify no duplicate due dates + const dueDates = children.map((c) => c.due_date.getTime()); + const uniqueDueDates = [...new Set(dueDates)]; + expect(uniqueDueDates.length).toBe(dueDates.length); + }); + + it('should handle orphaned child tasks gracefully', async () => { + const parentTask = await Task.create({ + name: 'Parent Task', + recurrence_type: 'daily', + recurrence_interval: 1, + user_id: user.id, + priority: 1, + }); + + const childTask = await Task.create({ + name: 'Parent Task', + recurrence_type: 'none', + recurring_parent_id: parentTask.id, + user_id: user.id, + due_date: new Date('2025-06-20T10:00:00Z'), + status: Task.STATUS.NOT_STARTED, + }); + + // Verify child can be found and has correct parent reference + const foundChild = await Task.findByPk(childTask.id); + expect(foundChild.recurring_parent_id).toBe(parentTask.id); + + // Try to find parent through child + const foundParent = await Task.findByPk( + foundChild.recurring_parent_id + ); + expect(foundParent).not.toBeNull(); + expect(foundParent.id).toBe(parentTask.id); + }); }); - it('should update parent recurrence settings through child task', async () => { - // Simulate updating parent through child - const updatedParent = await Task.findByPk(parentTask.id); - await updatedParent.update({ - recurrence_type: 'weekly', - recurrence_interval: 2, - recurrence_weekday: 1, // Monday - completion_based: true - }); + describe('Data Consistency and Validation', () => { + it('should ensure child tasks cannot have recurrence settings', async () => { + // First create a parent task to reference + const parentTask = await Task.create({ + name: 'Parent Task', + recurrence_type: 'daily', + recurrence_interval: 1, + user_id: user.id, + priority: 1, + }); - const refreshedParent = await Task.findByPk(parentTask.id); - expect(refreshedParent.recurrence_type).toBe('weekly'); - expect(refreshedParent.recurrence_interval).toBe(2); - expect(refreshedParent.recurrence_weekday).toBe(1); - expect(refreshedParent.completion_based).toBe(true); + const childTask = await Task.create({ + name: 'Child Task', + recurrence_type: 'none', + recurring_parent_id: parentTask.id, + recurrence_interval: null, + recurrence_weekday: null, + recurrence_month_day: null, + recurrence_week_of_month: null, + completion_based: false, + user_id: user.id, + status: Task.STATUS.NOT_STARTED, + }); - // Verify child task is unchanged - const refreshedChild = await Task.findByPk(childTask.id); - expect(refreshedChild.recurrence_type).toBe('none'); - expect(refreshedChild.recurring_parent_id).toBe(parentTask.id); + expect(childTask.recurrence_type).toBe('none'); + expect(childTask.recurrence_interval).toBeNull(); + expect(childTask.recurrence_weekday).toBeNull(); + expect(childTask.recurrence_month_day).toBeNull(); + expect(childTask.recurrence_week_of_month).toBeNull(); + expect(childTask.completion_based).toBe(false); + }); + + it('should ensure parent tasks have valid recurrence settings', async () => { + const parentTask = await Task.create({ + name: 'Parent Task', + recurrence_type: 'weekly', + recurrence_interval: 2, + recurrence_weekday: 5, // Friday + recurring_parent_id: null, + user_id: user.id, + priority: 1, + }); + + expect(parentTask.recurrence_type).toBe('weekly'); + expect(parentTask.recurrence_interval).toBe(2); + expect(parentTask.recurrence_weekday).toBe(5); + expect(parentTask.recurring_parent_id).toBeNull(); + }); + + it('should maintain user isolation for parent-child relationships', async () => { + const otherUser = await createTestUser({ + email: 'other@example.com', + }); + + const user1Parent = await Task.create({ + name: 'User 1 Parent', + recurrence_type: 'daily', + recurrence_interval: 1, + user_id: user.id, + priority: 1, + }); + + const user2Parent = await Task.create({ + name: 'User 2 Parent', + recurrence_type: 'daily', + recurrence_interval: 1, + user_id: otherUser.id, + priority: 1, + }); + + const user1Child = await Task.create({ + name: 'User 1 Parent', + recurrence_type: 'none', + recurring_parent_id: user1Parent.id, + user_id: user.id, + due_date: new Date('2025-06-20T10:00:00Z'), + status: Task.STATUS.NOT_STARTED, + }); + + // Verify child belongs to correct user + expect(user1Child.user_id).toBe(user.id); + expect(user1Child.recurring_parent_id).toBe(user1Parent.id); + + // Verify users can't see each other's tasks + const user1Tasks = await Task.findAll({ + where: { user_id: user.id }, + }); + const user2Tasks = await Task.findAll({ + where: { user_id: otherUser.id }, + }); + + expect(user1Tasks.length).toBe(2); // parent + child + expect(user2Tasks.length).toBe(1); // just parent + expect( + user1Tasks.find((t) => t.id === user2Parent.id) + ).toBeUndefined(); + expect( + user2Tasks.find((t) => t.id === user1Parent.id) + ).toBeUndefined(); + }); }); - - it('should preserve child task properties when updating parent', async () => { - await childTask.update({ status: Task.STATUS.IN_PROGRESS }); - - // Update parent - const updatedParent = await Task.findByPk(parentTask.id); - await updatedParent.update({ - recurrence_type: 'monthly', - recurrence_interval: 3 - }); - - // Verify child maintains its specific properties - const refreshedChild = await Task.findByPk(childTask.id); - expect(refreshedChild.status).toBe(Task.STATUS.IN_PROGRESS); - expect(refreshedChild.due_date).toEqual(new Date('2025-06-20T10:00:00Z')); - expect(refreshedChild.recurring_parent_id).toBe(parentTask.id); - }); - }); - - describe('Task Deletion Scenarios', () => { - let parentTask, childTask1, childTask2; - - beforeEach(async () => { - parentTask = await Task.create({ - name: 'Parent Task', - recurrence_type: 'weekly', - recurrence_interval: 1, - user_id: user.id, - priority: 1 - }); - - childTask1 = await Task.create({ - name: 'Parent Task', - recurrence_type: 'none', - recurring_parent_id: parentTask.id, - user_id: user.id, - due_date: new Date('2025-06-20T10:00:00Z'), - status: Task.STATUS.NOT_STARTED - }); - - childTask2 = await Task.create({ - name: 'Parent Task', - recurrence_type: 'none', - recurring_parent_id: parentTask.id, - user_id: user.id, - due_date: new Date('2025-06-27T10:00:00Z'), - status: Task.STATUS.DONE - }); - }); - - it('should allow deleting child tasks without affecting parent', async () => { - await childTask1.destroy(); - - // Verify child is deleted - const deletedChild = await Task.findByPk(childTask1.id); - expect(deletedChild).toBeNull(); - - // Verify parent and other child still exist - const existingParent = await Task.findByPk(parentTask.id); - const existingChild = await Task.findByPk(childTask2.id); - expect(existingParent).not.toBeNull(); - expect(existingChild).not.toBeNull(); - }); - - it('should prevent deleting parent when child tasks exist due to foreign key constraint', async () => { - let errorThrown = false; - try { - await parentTask.destroy(); - } catch (error) { - errorThrown = true; - expect(error.name).toBe('SequelizeForeignKeyConstraintError'); - } - - expect(errorThrown).toBe(true); - - // Verify parent and children still exist - const existingParent = await Task.findByPk(parentTask.id); - const existingChild1 = await Task.findByPk(childTask1.id); - const existingChild2 = await Task.findByPk(childTask2.id); - expect(existingParent).not.toBeNull(); - expect(existingChild1).not.toBeNull(); - expect(existingChild2).not.toBeNull(); - }); - - it('should allow deleting parent after deleting all child tasks', async () => { - // Delete all child tasks first - await childTask1.destroy(); - await childTask2.destroy(); - - // Now parent should be deletable - await parentTask.destroy(); - - // Verify all tasks are deleted - const deletedParent = await Task.findByPk(parentTask.id); - expect(deletedParent).toBeNull(); - }); - }); - - describe('Complex Parent-Child Scenarios', () => { - it('should handle multiple parents with different recurrence patterns', async () => { - const dailyParent = await Task.create({ - name: 'Daily Task', - recurrence_type: 'daily', - recurrence_interval: 1, - user_id: user.id, - priority: 1 - }); - - const weeklyParent = await Task.create({ - name: 'Weekly Task', - recurrence_type: 'weekly', - recurrence_interval: 1, - recurrence_weekday: 1, - user_id: user.id, - priority: 2 - }); - - // Create child tasks for each parent - const dailyChild = await RecurringTaskService.createTaskInstance( - dailyParent, - new Date('2025-06-20T10:00:00Z') - ); - - const weeklyChild = await RecurringTaskService.createTaskInstance( - weeklyParent, - new Date('2025-06-23T10:00:00Z') - ); - - expect(dailyChild.recurring_parent_id).toBe(dailyParent.id); - expect(weeklyChild.recurring_parent_id).toBe(weeklyParent.id); - expect(dailyChild.name).toBe('Daily Task'); - expect(weeklyChild.name).toBe('Weekly Task'); - }); - - it('should maintain data integrity across multiple child generations', async () => { - const parentTask = await Task.create({ - name: 'Long Running Task', - recurrence_type: 'daily', - recurrence_interval: 1, - completion_based: true, - user_id: user.id, - priority: 2 - }); - - const children = []; - - // Generate 5 child tasks - for (let i = 0; i < 5; i++) { - await parentTask.update({ status: Task.STATUS.DONE }); - const nextTask = await RecurringTaskService.handleTaskCompletion(parentTask); - if (nextTask) { - children.push(nextTask); - } - await parentTask.update({ status: Task.STATUS.NOT_STARTED }); - } - - expect(children.length).toBe(5); - - // Verify all children have correct parent relationship - for (const child of children) { - expect(child.recurring_parent_id).toBe(parentTask.id); - expect(child.name).toBe(parentTask.name); - expect(child.recurrence_type).toBe('none'); - expect(child.status).toBe(Task.STATUS.NOT_STARTED); - } - - // Verify no duplicate due dates - const dueDates = children.map(c => c.due_date.getTime()); - const uniqueDueDates = [...new Set(dueDates)]; - expect(uniqueDueDates.length).toBe(dueDates.length); - }); - - it('should handle orphaned child tasks gracefully', async () => { - const parentTask = await Task.create({ - name: 'Parent Task', - recurrence_type: 'daily', - recurrence_interval: 1, - user_id: user.id, - priority: 1 - }); - - const childTask = await Task.create({ - name: 'Parent Task', - recurrence_type: 'none', - recurring_parent_id: parentTask.id, - user_id: user.id, - due_date: new Date('2025-06-20T10:00:00Z'), - status: Task.STATUS.NOT_STARTED - }); - - // Verify child can be found and has correct parent reference - const foundChild = await Task.findByPk(childTask.id); - expect(foundChild.recurring_parent_id).toBe(parentTask.id); - - // Try to find parent through child - const foundParent = await Task.findByPk(foundChild.recurring_parent_id); - expect(foundParent).not.toBeNull(); - expect(foundParent.id).toBe(parentTask.id); - }); - }); - - describe('Data Consistency and Validation', () => { - it('should ensure child tasks cannot have recurrence settings', async () => { - // First create a parent task to reference - const parentTask = await Task.create({ - name: 'Parent Task', - recurrence_type: 'daily', - recurrence_interval: 1, - user_id: user.id, - priority: 1 - }); - - const childTask = await Task.create({ - name: 'Child Task', - recurrence_type: 'none', - recurring_parent_id: parentTask.id, - recurrence_interval: null, - recurrence_weekday: null, - recurrence_month_day: null, - recurrence_week_of_month: null, - completion_based: false, - user_id: user.id, - status: Task.STATUS.NOT_STARTED - }); - - expect(childTask.recurrence_type).toBe('none'); - expect(childTask.recurrence_interval).toBeNull(); - expect(childTask.recurrence_weekday).toBeNull(); - expect(childTask.recurrence_month_day).toBeNull(); - expect(childTask.recurrence_week_of_month).toBeNull(); - expect(childTask.completion_based).toBe(false); - }); - - it('should ensure parent tasks have valid recurrence settings', async () => { - const parentTask = await Task.create({ - name: 'Parent Task', - recurrence_type: 'weekly', - recurrence_interval: 2, - recurrence_weekday: 5, // Friday - recurring_parent_id: null, - user_id: user.id, - priority: 1 - }); - - expect(parentTask.recurrence_type).toBe('weekly'); - expect(parentTask.recurrence_interval).toBe(2); - expect(parentTask.recurrence_weekday).toBe(5); - expect(parentTask.recurring_parent_id).toBeNull(); - }); - - it('should maintain user isolation for parent-child relationships', async () => { - const otherUser = await createTestUser({ email: 'other@example.com' }); - - const user1Parent = await Task.create({ - name: 'User 1 Parent', - recurrence_type: 'daily', - recurrence_interval: 1, - user_id: user.id, - priority: 1 - }); - - const user2Parent = await Task.create({ - name: 'User 2 Parent', - recurrence_type: 'daily', - recurrence_interval: 1, - user_id: otherUser.id, - priority: 1 - }); - - const user1Child = await Task.create({ - name: 'User 1 Parent', - recurrence_type: 'none', - recurring_parent_id: user1Parent.id, - user_id: user.id, - due_date: new Date('2025-06-20T10:00:00Z'), - status: Task.STATUS.NOT_STARTED - }); - - // Verify child belongs to correct user - expect(user1Child.user_id).toBe(user.id); - expect(user1Child.recurring_parent_id).toBe(user1Parent.id); - - // Verify users can't see each other's tasks - const user1Tasks = await Task.findAll({ where: { user_id: user.id } }); - const user2Tasks = await Task.findAll({ where: { user_id: otherUser.id } }); - - expect(user1Tasks.length).toBe(2); // parent + child - expect(user2Tasks.length).toBe(1); // just parent - expect(user1Tasks.find(t => t.id === user2Parent.id)).toBeUndefined(); - expect(user2Tasks.find(t => t.id === user1Parent.id)).toBeUndefined(); - }); - }); -}); \ No newline at end of file +}); diff --git a/backend/tests/unit/services/recurringTaskService.test.js b/backend/tests/unit/services/recurringTaskService.test.js index b4eff27..baa41d0 100644 --- a/backend/tests/unit/services/recurringTaskService.test.js +++ b/backend/tests/unit/services/recurringTaskService.test.js @@ -2,424 +2,550 @@ const RecurringTaskService = require('../../../services/recurringTaskService'); const { Task } = require('../../../models'); describe('RecurringTaskService', () => { - describe('Date Calculation Tests', () => { - describe('calculateNextDueDate', () => { - // Test daily recurrence - describe('Daily recurrence', () => { - it('should calculate next daily occurrence correctly', () => { - const task = { - recurrence_type: 'daily', - recurrence_interval: 1 - }; - const fromDate = new Date('2025-01-15T10:00:00Z'); - const nextDate = RecurringTaskService.calculateNextDueDate(task, fromDate); - - expect(nextDate).toEqual(new Date('2025-01-16T10:00:00Z')); + describe('Date Calculation Tests', () => { + describe('calculateNextDueDate', () => { + // Test daily recurrence + describe('Daily recurrence', () => { + it('should calculate next daily occurrence correctly', () => { + const task = { + recurrence_type: 'daily', + recurrence_interval: 1, + }; + const fromDate = new Date('2025-01-15T10:00:00Z'); + const nextDate = RecurringTaskService.calculateNextDueDate( + task, + fromDate + ); + + expect(nextDate).toEqual(new Date('2025-01-16T10:00:00Z')); + }); + + it('should handle custom daily intervals', () => { + const task = { + recurrence_type: 'daily', + recurrence_interval: 3, + }; + const fromDate = new Date('2025-01-15T10:00:00Z'); + const nextDate = RecurringTaskService.calculateNextDueDate( + task, + fromDate + ); + + expect(nextDate).toEqual(new Date('2025-01-18T10:00:00Z')); + }); + + it('should handle edge case with zero interval', () => { + const task = { + recurrence_type: 'daily', + recurrence_interval: 0, + }; + const fromDate = new Date('2025-01-15T10:00:00Z'); + const nextDate = RecurringTaskService.calculateNextDueDate( + task, + fromDate + ); + + expect(nextDate).toEqual(new Date('2025-01-16T10:00:00Z')); + }); + }); + + // Test weekly recurrence + describe('Weekly recurrence', () => { + it('should calculate next weekly occurrence correctly', () => { + const task = { + recurrence_type: 'weekly', + recurrence_interval: 1, + }; + const fromDate = new Date('2025-01-15T10:00:00Z'); // Wednesday + const nextDate = RecurringTaskService.calculateNextDueDate( + task, + fromDate + ); + + expect(nextDate).toEqual(new Date('2025-01-22T10:00:00Z')); + }); + + it('should handle weekly with specific weekday', () => { + const task = { + recurrence_type: 'weekly', + recurrence_interval: 1, + recurrence_weekday: 1, // Monday + }; + const fromDate = new Date('2025-01-15T10:00:00Z'); // Wednesday + const nextDate = RecurringTaskService.calculateNextDueDate( + task, + fromDate + ); + + expect(nextDate).toEqual(new Date('2025-01-20T10:00:00Z')); // Next Monday + }); + + it('should handle bi-weekly recurrence', () => { + const task = { + recurrence_type: 'weekly', + recurrence_interval: 2, + }; + const fromDate = new Date('2025-01-15T10:00:00Z'); + const nextDate = RecurringTaskService.calculateNextDueDate( + task, + fromDate + ); + + expect(nextDate).toEqual(new Date('2025-01-29T10:00:00Z')); + }); + }); + + // Test monthly recurrence + describe('Monthly recurrence', () => { + it('should calculate next monthly occurrence correctly', () => { + const task = { + recurrence_type: 'monthly', + recurrence_interval: 1, + }; + const fromDate = new Date('2025-01-15T10:00:00Z'); + const nextDate = RecurringTaskService.calculateNextDueDate( + task, + fromDate + ); + + expect(nextDate).toEqual(new Date('2025-02-15T10:00:00Z')); + }); + + it('should handle month boundaries correctly', () => { + const task = { + recurrence_type: 'monthly', + recurrence_interval: 1, + }; + const fromDate = new Date('2025-01-31T10:00:00Z'); // January 31st + const nextDate = RecurringTaskService.calculateNextDueDate( + task, + fromDate + ); + + // February only has 28 days in 2025, should go to Feb 28 + expect(nextDate).toEqual(new Date('2025-02-28T10:00:00Z')); + }); + + it('should handle leap year correctly', () => { + const task = { + recurrence_type: 'monthly', + recurrence_interval: 1, + }; + const fromDate = new Date('2024-01-29T10:00:00Z'); // 2024 is a leap year + const nextDate = RecurringTaskService.calculateNextDueDate( + task, + fromDate + ); + + expect(nextDate).toEqual(new Date('2024-02-29T10:00:00Z')); + }); + + it('should handle custom monthly intervals', () => { + const task = { + recurrence_type: 'monthly', + recurrence_interval: 3, + }; + const fromDate = new Date('2025-01-15T10:00:00Z'); + const nextDate = RecurringTaskService.calculateNextDueDate( + task, + fromDate + ); + + expect(nextDate).toEqual(new Date('2025-04-15T10:00:00Z')); + }); + + it('should handle monthly with specific day', () => { + const task = { + recurrence_type: 'monthly', + recurrence_interval: 1, + recurrence_month_day: 5, + }; + const fromDate = new Date('2025-01-15T10:00:00Z'); + const nextDate = RecurringTaskService.calculateNextDueDate( + task, + fromDate + ); + + expect(nextDate).toEqual(new Date('2025-02-05T10:00:00Z')); + }); + }); + + // Test monthly weekday recurrence + describe('Monthly weekday recurrence', () => { + it('should calculate first Monday of month correctly', () => { + const task = { + recurrence_type: 'monthly_weekday', + recurrence_interval: 1, + recurrence_weekday: 1, // Monday + recurrence_week_of_month: 1, // First week + }; + const fromDate = new Date('2025-01-15T10:00:00Z'); + const nextDate = RecurringTaskService.calculateNextDueDate( + task, + fromDate + ); + + // First Monday of February 2025 is February 3rd + expect(nextDate).toEqual(new Date('2025-02-03T10:00:00Z')); + }); + + it('should calculate last Friday of month correctly', () => { + const task = { + recurrence_type: 'monthly_weekday', + recurrence_interval: 1, + recurrence_weekday: 5, // Friday + recurrence_week_of_month: 5, // Last week (represented as 5) + }; + const fromDate = new Date('2025-01-15T10:00:00Z'); + const nextDate = RecurringTaskService.calculateNextDueDate( + task, + fromDate + ); + + // Last Friday of February 2025 is February 28th + expect(nextDate).toEqual(new Date('2025-02-28T10:00:00Z')); + }); + + it('should handle third Wednesday of month', () => { + const task = { + recurrence_type: 'monthly_weekday', + recurrence_interval: 1, + recurrence_weekday: 3, // Wednesday + recurrence_week_of_month: 3, // Third week + }; + const fromDate = new Date('2025-01-15T10:00:00Z'); + const nextDate = RecurringTaskService.calculateNextDueDate( + task, + fromDate + ); + + // Third Wednesday of February 2025 is February 19th + expect(nextDate).toEqual(new Date('2025-02-19T10:00:00Z')); + }); + }); + + // Test monthly last day recurrence + describe('Monthly last day recurrence', () => { + it('should calculate last day of month correctly', () => { + const task = { + recurrence_type: 'monthly_last_day', + recurrence_interval: 1, + }; + const fromDate = new Date('2025-01-15T10:00:00Z'); + const nextDate = RecurringTaskService.calculateNextDueDate( + task, + fromDate + ); + + // Last day of February 2025 is February 28th + expect(nextDate).toEqual(new Date('2025-02-28T10:00:00Z')); + }); + + it('should handle leap year last day correctly', () => { + const task = { + recurrence_type: 'monthly_last_day', + recurrence_interval: 1, + }; + const fromDate = new Date('2024-01-15T10:00:00Z'); // 2024 is a leap year + const nextDate = RecurringTaskService.calculateNextDueDate( + task, + fromDate + ); + + // Last day of February 2024 is February 29th + expect(nextDate).toEqual(new Date('2024-02-29T10:00:00Z')); + }); + + it('should handle different month lengths', () => { + const task = { + recurrence_type: 'monthly_last_day', + recurrence_interval: 1, + }; + const fromDate = new Date('2025-04-15T10:00:00Z'); // April has 30 days + const nextDate = RecurringTaskService.calculateNextDueDate( + task, + fromDate + ); + + // Last day of May 2025 is May 31st + expect(nextDate).toEqual(new Date('2025-05-31T10:00:00Z')); + }); + }); + + // Test edge cases and invalid inputs + describe('Edge cases and invalid inputs', () => { + it('should return null for unsupported recurrence type', () => { + const task = { + recurrence_type: 'invalid_type', + recurrence_interval: 1, + }; + const fromDate = new Date('2025-01-15T10:00:00Z'); + const nextDate = RecurringTaskService.calculateNextDueDate( + task, + fromDate + ); + + expect(nextDate).toBeNull(); + }); + + it('should return null for none recurrence type', () => { + const task = { + recurrence_type: 'none', + recurrence_interval: 1, + }; + const fromDate = new Date('2025-01-15T10:00:00Z'); + const nextDate = RecurringTaskService.calculateNextDueDate( + task, + fromDate + ); + + expect(nextDate).toBeNull(); + }); + + it('should handle invalid date inputs gracefully', () => { + const task = { + recurrence_type: 'daily', + recurrence_interval: 1, + }; + const fromDate = new Date('invalid-date'); + const nextDate = RecurringTaskService.calculateNextDueDate( + task, + fromDate + ); + + expect(nextDate).toBeNull(); + }); + + it('should handle missing task properties', () => { + const task = {}; // No recurrence properties + const fromDate = new Date('2025-01-15T10:00:00Z'); + const nextDate = RecurringTaskService.calculateNextDueDate( + task, + fromDate + ); + + expect(nextDate).toBeNull(); + }); + }); }); - it('should handle custom daily intervals', () => { - const task = { - recurrence_type: 'daily', - recurrence_interval: 3 - }; - const fromDate = new Date('2025-01-15T10:00:00Z'); - const nextDate = RecurringTaskService.calculateNextDueDate(task, fromDate); - - expect(nextDate).toEqual(new Date('2025-01-18T10:00:00Z')); - }); + describe('Helper Functions', () => { + describe('_getFirstWeekdayOfMonth', () => { + it('should find first Monday of January 2025', () => { + const date = RecurringTaskService._getFirstWeekdayOfMonth( + 2025, + 0, + 1 + ); // January, Monday + expect(date.getDate()).toBe(6); // January 6, 2025 is the first Monday + }); - it('should handle edge case with zero interval', () => { - const task = { - recurrence_type: 'daily', - recurrence_interval: 0 - }; - const fromDate = new Date('2025-01-15T10:00:00Z'); - const nextDate = RecurringTaskService.calculateNextDueDate(task, fromDate); - - expect(nextDate).toEqual(new Date('2025-01-16T10:00:00Z')); - }); - }); + it('should find first Sunday of February 2025', () => { + const date = RecurringTaskService._getFirstWeekdayOfMonth( + 2025, + 1, + 0 + ); // February, Sunday + expect(date.getDate()).toBe(2); // February 2, 2025 is the first Sunday + }); + }); - // Test weekly recurrence - describe('Weekly recurrence', () => { - it('should calculate next weekly occurrence correctly', () => { - const task = { - recurrence_type: 'weekly', - recurrence_interval: 1 - }; - const fromDate = new Date('2025-01-15T10:00:00Z'); // Wednesday - const nextDate = RecurringTaskService.calculateNextDueDate(task, fromDate); - - expect(nextDate).toEqual(new Date('2025-01-22T10:00:00Z')); - }); + describe('_getLastWeekdayOfMonth', () => { + it('should find last Friday of January 2025', () => { + const date = RecurringTaskService._getLastWeekdayOfMonth( + 2025, + 0, + 5 + ); // January, Friday + expect(date.getDate()).toBe(31); // January 31, 2025 is the last Friday + }); - it('should handle weekly with specific weekday', () => { - const task = { - recurrence_type: 'weekly', - recurrence_interval: 1, - recurrence_weekday: 1 // Monday - }; - const fromDate = new Date('2025-01-15T10:00:00Z'); // Wednesday - const nextDate = RecurringTaskService.calculateNextDueDate(task, fromDate); - - expect(nextDate).toEqual(new Date('2025-01-20T10:00:00Z')); // Next Monday - }); + it('should find last Monday of February 2025', () => { + const date = RecurringTaskService._getLastWeekdayOfMonth( + 2025, + 1, + 1 + ); // February, Monday + expect(date.getDate()).toBe(24); // February 24, 2025 is the last Monday + }); + }); - it('should handle bi-weekly recurrence', () => { - const task = { - recurrence_type: 'weekly', - recurrence_interval: 2 - }; - const fromDate = new Date('2025-01-15T10:00:00Z'); - const nextDate = RecurringTaskService.calculateNextDueDate(task, fromDate); - - expect(nextDate).toEqual(new Date('2025-01-29T10:00:00Z')); - }); - }); + describe('_getNthWeekdayOfMonth', () => { + it('should find second Tuesday of March 2025', () => { + const date = RecurringTaskService._getNthWeekdayOfMonth( + 2025, + 2, + 2, + 2 + ); // March, Tuesday, 2nd + expect(date.getDate()).toBe(11); // March 11, 2025 is the second Tuesday + }); - // Test monthly recurrence - describe('Monthly recurrence', () => { - it('should calculate next monthly occurrence correctly', () => { - const task = { - recurrence_type: 'monthly', - recurrence_interval: 1 - }; - const fromDate = new Date('2025-01-15T10:00:00Z'); - const nextDate = RecurringTaskService.calculateNextDueDate(task, fromDate); - - expect(nextDate).toEqual(new Date('2025-02-15T10:00:00Z')); + it('should find fourth Thursday of April 2025', () => { + const date = RecurringTaskService._getNthWeekdayOfMonth( + 2025, + 3, + 4, + 4 + ); // April, Thursday, 4th + expect(date.getDate()).toBe(24); // April 24, 2025 is the fourth Thursday + }); + }); }); - - it('should handle month boundaries correctly', () => { - const task = { - recurrence_type: 'monthly', - recurrence_interval: 1 - }; - const fromDate = new Date('2025-01-31T10:00:00Z'); // January 31st - const nextDate = RecurringTaskService.calculateNextDueDate(task, fromDate); - - // February only has 28 days in 2025, should go to Feb 28 - expect(nextDate).toEqual(new Date('2025-02-28T10:00:00Z')); - }); - - it('should handle leap year correctly', () => { - const task = { - recurrence_type: 'monthly', - recurrence_interval: 1 - }; - const fromDate = new Date('2024-01-29T10:00:00Z'); // 2024 is a leap year - const nextDate = RecurringTaskService.calculateNextDueDate(task, fromDate); - - expect(nextDate).toEqual(new Date('2024-02-29T10:00:00Z')); - }); - - it('should handle custom monthly intervals', () => { - const task = { - recurrence_type: 'monthly', - recurrence_interval: 3 - }; - const fromDate = new Date('2025-01-15T10:00:00Z'); - const nextDate = RecurringTaskService.calculateNextDueDate(task, fromDate); - - expect(nextDate).toEqual(new Date('2025-04-15T10:00:00Z')); - }); - - it('should handle monthly with specific day', () => { - const task = { - recurrence_type: 'monthly', - recurrence_interval: 1, - recurrence_month_day: 5 - }; - const fromDate = new Date('2025-01-15T10:00:00Z'); - const nextDate = RecurringTaskService.calculateNextDueDate(task, fromDate); - - expect(nextDate).toEqual(new Date('2025-02-05T10:00:00Z')); - }); - }); - - // Test monthly weekday recurrence - describe('Monthly weekday recurrence', () => { - it('should calculate first Monday of month correctly', () => { - const task = { - recurrence_type: 'monthly_weekday', - recurrence_interval: 1, - recurrence_weekday: 1, // Monday - recurrence_week_of_month: 1 // First week - }; - const fromDate = new Date('2025-01-15T10:00:00Z'); - const nextDate = RecurringTaskService.calculateNextDueDate(task, fromDate); - - // First Monday of February 2025 is February 3rd - expect(nextDate).toEqual(new Date('2025-02-03T10:00:00Z')); - }); - - it('should calculate last Friday of month correctly', () => { - const task = { - recurrence_type: 'monthly_weekday', - recurrence_interval: 1, - recurrence_weekday: 5, // Friday - recurrence_week_of_month: 5 // Last week (represented as 5) - }; - const fromDate = new Date('2025-01-15T10:00:00Z'); - const nextDate = RecurringTaskService.calculateNextDueDate(task, fromDate); - - // Last Friday of February 2025 is February 28th - expect(nextDate).toEqual(new Date('2025-02-28T10:00:00Z')); - }); - - it('should handle third Wednesday of month', () => { - const task = { - recurrence_type: 'monthly_weekday', - recurrence_interval: 1, - recurrence_weekday: 3, // Wednesday - recurrence_week_of_month: 3 // Third week - }; - const fromDate = new Date('2025-01-15T10:00:00Z'); - const nextDate = RecurringTaskService.calculateNextDueDate(task, fromDate); - - // Third Wednesday of February 2025 is February 19th - expect(nextDate).toEqual(new Date('2025-02-19T10:00:00Z')); - }); - }); - - // Test monthly last day recurrence - describe('Monthly last day recurrence', () => { - it('should calculate last day of month correctly', () => { - const task = { - recurrence_type: 'monthly_last_day', - recurrence_interval: 1 - }; - const fromDate = new Date('2025-01-15T10:00:00Z'); - const nextDate = RecurringTaskService.calculateNextDueDate(task, fromDate); - - // Last day of February 2025 is February 28th - expect(nextDate).toEqual(new Date('2025-02-28T10:00:00Z')); - }); - - it('should handle leap year last day correctly', () => { - const task = { - recurrence_type: 'monthly_last_day', - recurrence_interval: 1 - }; - const fromDate = new Date('2024-01-15T10:00:00Z'); // 2024 is a leap year - const nextDate = RecurringTaskService.calculateNextDueDate(task, fromDate); - - // Last day of February 2024 is February 29th - expect(nextDate).toEqual(new Date('2024-02-29T10:00:00Z')); - }); - - it('should handle different month lengths', () => { - const task = { - recurrence_type: 'monthly_last_day', - recurrence_interval: 1 - }; - const fromDate = new Date('2025-04-15T10:00:00Z'); // April has 30 days - const nextDate = RecurringTaskService.calculateNextDueDate(task, fromDate); - - // Last day of May 2025 is May 31st - expect(nextDate).toEqual(new Date('2025-05-31T10:00:00Z')); - }); - }); - - // Test edge cases and invalid inputs - describe('Edge cases and invalid inputs', () => { - it('should return null for unsupported recurrence type', () => { - const task = { - recurrence_type: 'invalid_type', - recurrence_interval: 1 - }; - const fromDate = new Date('2025-01-15T10:00:00Z'); - const nextDate = RecurringTaskService.calculateNextDueDate(task, fromDate); - - expect(nextDate).toBeNull(); - }); - - it('should return null for none recurrence type', () => { - const task = { - recurrence_type: 'none', - recurrence_interval: 1 - }; - const fromDate = new Date('2025-01-15T10:00:00Z'); - const nextDate = RecurringTaskService.calculateNextDueDate(task, fromDate); - - expect(nextDate).toBeNull(); - }); - - it('should handle invalid date inputs gracefully', () => { - const task = { - recurrence_type: 'daily', - recurrence_interval: 1 - }; - const fromDate = new Date('invalid-date'); - const nextDate = RecurringTaskService.calculateNextDueDate(task, fromDate); - - expect(nextDate).toBeNull(); - }); - - it('should handle missing task properties', () => { - const task = {}; // No recurrence properties - const fromDate = new Date('2025-01-15T10:00:00Z'); - const nextDate = RecurringTaskService.calculateNextDueDate(task, fromDate); - - expect(nextDate).toBeNull(); - }); - }); }); - describe('Helper Functions', () => { - describe('_getFirstWeekdayOfMonth', () => { - it('should find first Monday of January 2025', () => { - const date = RecurringTaskService._getFirstWeekdayOfMonth(2025, 0, 1); // January, Monday - expect(date.getDate()).toBe(6); // January 6, 2025 is the first Monday + describe('Task Generation Tests', () => { + describe('createTaskInstance', () => { + it('should create a task instance with correct parent relationship', async () => { + const template = { + id: 1, + name: 'Test Recurring Task', + description: 'Test description', + priority: 1, + note: 'Test note', + user_id: 1, + project_id: 2, + }; + const dueDate = new Date('2025-01-20T10:00:00Z'); + + // Mock Task.create + const mockCreate = jest.fn().mockResolvedValue({ + id: 10, + name: template.name, + description: template.description, + due_date: dueDate, + priority: template.priority, + status: 0, // NOT_STARTED + note: template.note, + user_id: template.user_id, + project_id: template.project_id, + recurrence_type: 'none', + recurring_parent_id: template.id, + }); + Task.create = mockCreate; + + const result = await RecurringTaskService.createTaskInstance( + template, + dueDate + ); + + expect(mockCreate).toHaveBeenCalledWith({ + name: template.name, + description: template.description, + due_date: dueDate, + today: false, + priority: template.priority, + status: 0, // Task.STATUS.NOT_STARTED + note: template.note, + user_id: template.user_id, + project_id: template.project_id, + recurrence_type: 'none', + recurring_parent_id: template.id, + }); + + expect(result.recurring_parent_id).toBe(template.id); + expect(result.recurrence_type).toBe('none'); + }); }); - - it('should find first Sunday of February 2025', () => { - const date = RecurringTaskService._getFirstWeekdayOfMonth(2025, 1, 0); // February, Sunday - expect(date.getDate()).toBe(2); // February 2, 2025 is the first Sunday - }); - }); - - describe('_getLastWeekdayOfMonth', () => { - it('should find last Friday of January 2025', () => { - const date = RecurringTaskService._getLastWeekdayOfMonth(2025, 0, 5); // January, Friday - expect(date.getDate()).toBe(31); // January 31, 2025 is the last Friday - }); - - it('should find last Monday of February 2025', () => { - const date = RecurringTaskService._getLastWeekdayOfMonth(2025, 1, 1); // February, Monday - expect(date.getDate()).toBe(24); // February 24, 2025 is the last Monday - }); - }); - - describe('_getNthWeekdayOfMonth', () => { - it('should find second Tuesday of March 2025', () => { - const date = RecurringTaskService._getNthWeekdayOfMonth(2025, 2, 2, 2); // March, Tuesday, 2nd - expect(date.getDate()).toBe(11); // March 11, 2025 is the second Tuesday - }); - - it('should find fourth Thursday of April 2025', () => { - const date = RecurringTaskService._getNthWeekdayOfMonth(2025, 3, 4, 4); // April, Thursday, 4th - expect(date.getDate()).toBe(24); // April 24, 2025 is the fourth Thursday - }); - }); - }); - }); - - describe('Task Generation Tests', () => { - describe('createTaskInstance', () => { - it('should create a task instance with correct parent relationship', async () => { - const template = { - id: 1, - name: 'Test Recurring Task', - description: 'Test description', - priority: 1, - note: 'Test note', - user_id: 1, - project_id: 2 - }; - const dueDate = new Date('2025-01-20T10:00:00Z'); - - // Mock Task.create - const mockCreate = jest.fn().mockResolvedValue({ - id: 10, - name: template.name, - description: template.description, - due_date: dueDate, - priority: template.priority, - status: 0, // NOT_STARTED - note: template.note, - user_id: template.user_id, - project_id: template.project_id, - recurrence_type: 'none', - recurring_parent_id: template.id - }); - Task.create = mockCreate; - - const result = await RecurringTaskService.createTaskInstance(template, dueDate); - - expect(mockCreate).toHaveBeenCalledWith({ - name: template.name, - description: template.description, - due_date: dueDate, - today: false, - priority: template.priority, - status: 0, // Task.STATUS.NOT_STARTED - note: template.note, - user_id: template.user_id, - project_id: template.project_id, - recurrence_type: 'none', - recurring_parent_id: template.id - }); - - expect(result.recurring_parent_id).toBe(template.id); - expect(result.recurrence_type).toBe('none'); - }); - }); - }); - - describe('End Date Validation', () => { - describe('shouldGenerateNextTask', () => { - it('should generate task when no end date is set', () => { - const task = { - recurrence_type: 'daily', - recurrence_end_date: null - }; - const nextDate = new Date('2025-12-31T10:00:00Z'); - - const shouldGenerate = RecurringTaskService._shouldGenerateNextTask(task, nextDate); - expect(shouldGenerate).toBe(true); - }); - - it('should generate task when next date is before end date', () => { - const task = { - recurrence_type: 'daily', - recurrence_end_date: new Date('2025-12-31T10:00:00Z') - }; - const nextDate = new Date('2025-06-15T10:00:00Z'); - - const shouldGenerate = RecurringTaskService._shouldGenerateNextTask(task, nextDate); - expect(shouldGenerate).toBe(true); - }); - - it('should not generate task when next date is after end date', () => { - const task = { - recurrence_type: 'daily', - recurrence_end_date: new Date('2025-06-15T10:00:00Z') - }; - const nextDate = new Date('2025-12-31T10:00:00Z'); - - const shouldGenerate = RecurringTaskService._shouldGenerateNextTask(task, nextDate); - expect(shouldGenerate).toBe(false); - }); - - it('should not generate task when next date equals end date', () => { - const endDate = new Date('2025-06-15T10:00:00Z'); - const task = { - recurrence_type: 'daily', - recurrence_end_date: endDate - }; - const nextDate = new Date('2025-06-15T10:00:00Z'); - - const shouldGenerate = RecurringTaskService._shouldGenerateNextTask(task, nextDate); - expect(shouldGenerate).toBe(false); - }); - }); - }); - - describe('Service Interface', () => { - it('should export all required methods', () => { - expect(typeof RecurringTaskService.generateRecurringTasks).toBe('function'); - expect(typeof RecurringTaskService.processRecurringTask).toBe('function'); - expect(typeof RecurringTaskService.calculateNextDueDate).toBe('function'); - expect(typeof RecurringTaskService.createTaskInstance).toBe('function'); - expect(typeof RecurringTaskService.handleTaskCompletion).toBe('function'); }); - it('should have helper functions for testing', () => { - expect(typeof RecurringTaskService._getFirstWeekdayOfMonth).toBe('function'); - expect(typeof RecurringTaskService._getLastWeekdayOfMonth).toBe('function'); - expect(typeof RecurringTaskService._getNthWeekdayOfMonth).toBe('function'); - expect(typeof RecurringTaskService._shouldGenerateNextTask).toBe('function'); + describe('End Date Validation', () => { + describe('shouldGenerateNextTask', () => { + it('should generate task when no end date is set', () => { + const task = { + recurrence_type: 'daily', + recurrence_end_date: null, + }; + const nextDate = new Date('2025-12-31T10:00:00Z'); + + const shouldGenerate = + RecurringTaskService._shouldGenerateNextTask( + task, + nextDate + ); + expect(shouldGenerate).toBe(true); + }); + + it('should generate task when next date is before end date', () => { + const task = { + recurrence_type: 'daily', + recurrence_end_date: new Date('2025-12-31T10:00:00Z'), + }; + const nextDate = new Date('2025-06-15T10:00:00Z'); + + const shouldGenerate = + RecurringTaskService._shouldGenerateNextTask( + task, + nextDate + ); + expect(shouldGenerate).toBe(true); + }); + + it('should not generate task when next date is after end date', () => { + const task = { + recurrence_type: 'daily', + recurrence_end_date: new Date('2025-06-15T10:00:00Z'), + }; + const nextDate = new Date('2025-12-31T10:00:00Z'); + + const shouldGenerate = + RecurringTaskService._shouldGenerateNextTask( + task, + nextDate + ); + expect(shouldGenerate).toBe(false); + }); + + it('should not generate task when next date equals end date', () => { + const endDate = new Date('2025-06-15T10:00:00Z'); + const task = { + recurrence_type: 'daily', + recurrence_end_date: endDate, + }; + const nextDate = new Date('2025-06-15T10:00:00Z'); + + const shouldGenerate = + RecurringTaskService._shouldGenerateNextTask( + task, + nextDate + ); + expect(shouldGenerate).toBe(false); + }); + }); }); - }); -}); \ No newline at end of file + + describe('Service Interface', () => { + it('should export all required methods', () => { + expect(typeof RecurringTaskService.generateRecurringTasks).toBe( + 'function' + ); + expect(typeof RecurringTaskService.processRecurringTask).toBe( + 'function' + ); + expect(typeof RecurringTaskService.calculateNextDueDate).toBe( + 'function' + ); + expect(typeof RecurringTaskService.createTaskInstance).toBe( + 'function' + ); + expect(typeof RecurringTaskService.handleTaskCompletion).toBe( + 'function' + ); + }); + + it('should have helper functions for testing', () => { + expect(typeof RecurringTaskService._getFirstWeekdayOfMonth).toBe( + 'function' + ); + expect(typeof RecurringTaskService._getLastWeekdayOfMonth).toBe( + 'function' + ); + expect(typeof RecurringTaskService._getNthWeekdayOfMonth).toBe( + 'function' + ); + expect(typeof RecurringTaskService._shouldGenerateNextTask).toBe( + 'function' + ); + }); + }); +}); diff --git a/backend/tests/unit/services/telegramPoller.test.js b/backend/tests/unit/services/telegramPoller.test.js index 7ef5f95..3ead735 100644 --- a/backend/tests/unit/services/telegramPoller.test.js +++ b/backend/tests/unit/services/telegramPoller.test.js @@ -3,170 +3,204 @@ const telegramPoller = require('../../../services/telegramPoller'); // Mock the database models jest.mock('../../../models', () => ({ - User: { - update: jest.fn(), - findAll: jest.fn(), - findOne: jest.fn() - }, - InboxItem: { - create: jest.fn(), - findOne: jest.fn() - } + User: { + update: jest.fn(), + findAll: jest.fn(), + findOne: jest.fn(), + }, + InboxItem: { + create: jest.fn(), + findOne: jest.fn(), + }, })); // Mock https module jest.mock('https', () => ({ - get: jest.fn(), - request: jest.fn() + get: jest.fn(), + request: jest.fn(), })); describe('TelegramPoller Duplicate Prevention', () => { - let mockUser; - - beforeEach(() => { - jest.clearAllMocks(); - - mockUser = { - id: 1, - telegram_bot_token: 'test-token', - telegram_chat_id: '123456789' - }; - - // Reset poller state - telegramPoller.stopPolling(); - }); + let mockUser; - describe('Update ID Tracking', () => { - test('should filter out already processed updates', () => { - const updates = [ - { update_id: 100, message: { text: 'Hello 1', message_id: 1, chat: { id: 123 } } }, - { update_id: 101, message: { text: 'Hello 2', message_id: 2, chat: { id: 123 } } }, - { update_id: 102, message: { text: 'Hello 3', message_id: 3, chat: { id: 123 } } } - ]; + beforeEach(() => { + jest.clearAllMocks(); - // Test internal function for filtering - const processedUpdates = new Set(['1-100', '1-101']); - const newUpdates = updates.filter(update => { - const updateKey = `1-${update.update_id}`; - return !processedUpdates.has(updateKey); - }); + mockUser = { + id: 1, + telegram_bot_token: 'test-token', + telegram_chat_id: '123456789', + }; - expect(newUpdates).toHaveLength(1); - expect(newUpdates[0].update_id).toBe(102); + // Reset poller state + telegramPoller.stopPolling(); }); - test('should track highest update ID correctly', () => { - const updates = [ - { update_id: 98 }, - { update_id: 101 }, - { update_id: 99 } - ]; + describe('Update ID Tracking', () => { + test('should filter out already processed updates', () => { + const updates = [ + { + update_id: 100, + message: { + text: 'Hello 1', + message_id: 1, + chat: { id: 123 }, + }, + }, + { + update_id: 101, + message: { + text: 'Hello 2', + message_id: 2, + chat: { id: 123 }, + }, + }, + { + update_id: 102, + message: { + text: 'Hello 3', + message_id: 3, + chat: { id: 123 }, + }, + }, + ]; - const highestUpdateId = telegramPoller._getHighestUpdateId(updates); - expect(highestUpdateId).toBe(101); + // Test internal function for filtering + const processedUpdates = new Set(['1-100', '1-101']); + const newUpdates = updates.filter((update) => { + const updateKey = `1-${update.update_id}`; + return !processedUpdates.has(updateKey); + }); + + expect(newUpdates).toHaveLength(1); + expect(newUpdates[0].update_id).toBe(102); + }); + + test('should track highest update ID correctly', () => { + const updates = [ + { update_id: 98 }, + { update_id: 101 }, + { update_id: 99 }, + ]; + + const highestUpdateId = telegramPoller._getHighestUpdateId(updates); + expect(highestUpdateId).toBe(101); + }); + + test('should handle empty updates array', () => { + const highestUpdateId = telegramPoller._getHighestUpdateId([]); + expect(highestUpdateId).toBe(0); + }); }); - test('should handle empty updates array', () => { - const highestUpdateId = telegramPoller._getHighestUpdateId([]); - expect(highestUpdateId).toBe(0); - }); - }); + describe('User List Management', () => { + test('should not add duplicate users', () => { + const users = [{ id: 1, name: 'User 1' }]; + const newUser = { id: 1, name: 'User 1 Updated' }; - describe('User List Management', () => { - test('should not add duplicate users', () => { - const users = [{ id: 1, name: 'User 1' }]; - const newUser = { id: 1, name: 'User 1 Updated' }; - - const userExists = telegramPoller._userExistsInList(users, 1); - expect(userExists).toBe(true); - - const updatedUsers = telegramPoller._addUserToList(users, newUser); - expect(updatedUsers).toHaveLength(1); - expect(updatedUsers).toEqual(users); // Should return original array unchanged + const userExists = telegramPoller._userExistsInList(users, 1); + expect(userExists).toBe(true); + + const updatedUsers = telegramPoller._addUserToList(users, newUser); + expect(updatedUsers).toHaveLength(1); + expect(updatedUsers).toEqual(users); // Should return original array unchanged + }); + + test('should add new users correctly', () => { + const users = [{ id: 1, name: 'User 1' }]; + const newUser = { id: 2, name: 'User 2' }; + + const userExists = telegramPoller._userExistsInList(users, 2); + expect(userExists).toBe(false); + + const updatedUsers = telegramPoller._addUserToList(users, newUser); + expect(updatedUsers).toHaveLength(2); + expect(updatedUsers).toContain(newUser); + }); + + test('should remove users correctly', () => { + const users = [ + { id: 1, name: 'User 1' }, + { id: 2, name: 'User 2' }, + { id: 3, name: 'User 3' }, + ]; + + const updatedUsers = telegramPoller._removeUserFromList(users, 2); + expect(updatedUsers).toHaveLength(2); + expect(updatedUsers.find((u) => u.id === 2)).toBeUndefined(); + expect(updatedUsers.find((u) => u.id === 1)).toBeDefined(); + expect(updatedUsers.find((u) => u.id === 3)).toBeDefined(); + }); }); - test('should add new users correctly', () => { - const users = [{ id: 1, name: 'User 1' }]; - const newUser = { id: 2, name: 'User 2' }; - - const userExists = telegramPoller._userExistsInList(users, 2); - expect(userExists).toBe(false); - - const updatedUsers = telegramPoller._addUserToList(users, newUser); - expect(updatedUsers).toHaveLength(2); - expect(updatedUsers).toContain(newUser); + describe('Message Parameters', () => { + test('should create message parameters without reply', () => { + const params = telegramPoller._createMessageParams( + '123', + 'Hello World' + ); + expect(params).toEqual({ + chat_id: '123', + text: 'Hello World', + }); + }); + + test('should create message parameters with reply', () => { + const params = telegramPoller._createMessageParams( + '123', + 'Hello World', + 456 + ); + expect(params).toEqual({ + chat_id: '123', + text: 'Hello World', + reply_to_message_id: 456, + }); + }); }); - test('should remove users correctly', () => { - const users = [ - { id: 1, name: 'User 1' }, - { id: 2, name: 'User 2' }, - { id: 3, name: 'User 3' } - ]; - - const updatedUsers = telegramPoller._removeUserFromList(users, 2); - expect(updatedUsers).toHaveLength(2); - expect(updatedUsers.find(u => u.id === 2)).toBeUndefined(); - expect(updatedUsers.find(u => u.id === 1)).toBeDefined(); - expect(updatedUsers.find(u => u.id === 3)).toBeDefined(); - }); - }); + describe('Telegram URL Creation', () => { + test('should create URL without parameters', () => { + const url = telegramPoller._createTelegramUrl('token123', 'getMe'); + expect(url).toBe('https://api.telegram.org/bottoken123/getMe'); + }); - describe('Message Parameters', () => { - test('should create message parameters without reply', () => { - const params = telegramPoller._createMessageParams('123', 'Hello World'); - expect(params).toEqual({ - chat_id: '123', - text: 'Hello World' - }); + test('should create URL with parameters', () => { + const url = telegramPoller._createTelegramUrl( + 'token123', + 'getUpdates', + { + offset: '100', + timeout: '30', + } + ); + expect(url).toBe( + 'https://api.telegram.org/bottoken123/getUpdates?offset=100&timeout=30' + ); + }); }); - test('should create message parameters with reply', () => { - const params = telegramPoller._createMessageParams('123', 'Hello World', 456); - expect(params).toEqual({ - chat_id: '123', - text: 'Hello World', - reply_to_message_id: 456 - }); - }); - }); + describe('State Management', () => { + test('should return correct initial state', () => { + const state = telegramPoller._createPollerState(); + expect(state).toEqual({ + running: false, + interval: null, + pollInterval: 5000, + usersToPool: [], + userStatus: {}, + processedUpdates: expect.any(Set), + }); + }); - describe('Telegram URL Creation', () => { - test('should create URL without parameters', () => { - const url = telegramPoller._createTelegramUrl('token123', 'getMe'); - expect(url).toBe('https://api.telegram.org/bottoken123/getMe'); + test('should track poller status correctly', () => { + const status = telegramPoller.getStatus(); + expect(status).toEqual({ + running: false, + usersCount: 0, + pollInterval: 5000, + userStatus: {}, + }); + }); }); - - test('should create URL with parameters', () => { - const url = telegramPoller._createTelegramUrl('token123', 'getUpdates', { - offset: '100', - timeout: '30' - }); - expect(url).toBe('https://api.telegram.org/bottoken123/getUpdates?offset=100&timeout=30'); - }); - }); - - describe('State Management', () => { - test('should return correct initial state', () => { - const state = telegramPoller._createPollerState(); - expect(state).toEqual({ - running: false, - interval: null, - pollInterval: 5000, - usersToPool: [], - userStatus: {}, - processedUpdates: expect.any(Set) - }); - }); - - test('should track poller status correctly', () => { - const status = telegramPoller.getStatus(); - expect(status).toEqual({ - running: false, - usersCount: 0, - pollInterval: 5000, - userStatus: {} - }); - }); - }); -}); \ No newline at end of file +}); diff --git a/frontend/package-lock.json b/frontend/package-lock.json deleted file mode 100644 index 7ef6bfa..0000000 --- a/frontend/package-lock.json +++ /dev/null @@ -1,17090 +0,0 @@ -{ - "name": "frontend", - "version": "0.3.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "frontend", - "version": "0.3.0", - "license": "ISC", - "dependencies": { - "@heroicons/react": "^2.1.5", - "@yaireo/tagify": "^4.31.3", - "date-fns": "^4.1.0", - "highlight.js": "^11.11.1", - "i18next": "^24.2.3", - "i18next-browser-languagedetector": "^8.0.4", - "i18next-http-backend": "^3.0.2", - "react": "^18.3.1", - "react-dom": "^18.3.1", - "react-i18next": "^15.4.1", - "react-markdown": "^10.1.0", - "react-router-dom": "^6.26.2", - "react-tagify": "^1.0.7", - "recharts": "^2.15.4", - "rehype-highlight": "^7.0.2", - "remark-gfm": "^4.0.1", - "swr": "^2.2.5", - "tagify": "^0.1.1", - "zustand": "^5.0.3" - }, - "devDependencies": { - "@babel/core": "^7.25.7", - "@babel/preset-env": "^7.25.7", - "@babel/preset-react": "^7.25.7", - "@babel/preset-typescript": "^7.25.7", - "@eslint/js": "^9.13.0", - "@pmmmwh/react-refresh-webpack-plugin": "^0.5.15", - "@testing-library/jest-dom": "^6.0.0", - "@testing-library/react": "^14.0.0", - "@testing-library/user-event": "^14.0.0", - "@types/jest": "^29.0.0", - "@types/react": "^18.3.10", - "@types/react-dom": "^18.3.0", - "@types/react-router-dom": "^5.3.3", - "@typescript-eslint/eslint-plugin": "^8.11.0", - "@typescript-eslint/parser": "^8.11.0", - "autoprefixer": "^10.4.20", - "babel-jest": "^29.0.0", - "babel-loader": "^9.2.1", - "css-loader": "^7.1.2", - "eslint": "^9.13.0", - "eslint-plugin-react": "^7.37.2", - "globals": "^15.11.0", - "html-webpack-plugin": "^5.6.3", - "identity-obj-proxy": "^3.0.0", - "jest": "^29.0.0", - "jest-environment-jsdom": "^29.0.0", - "postcss": "^8.4.47", - "postcss-loader": "^8.1.1", - "react-refresh": "^0.14.2", - "style-loader": "^4.0.0", - "tailwindcss": "^3.4.13", - "ts-jest": "^29.0.0", - "ts-loader": "^9.5.1", - "typescript": "^5.6.2", - "typescript-eslint": "^8.11.0", - "webpack": "^5.95.0", - "webpack-cli": "^5.1.4", - "webpack-dev-server": "^5.1.0" - } - }, - "node_modules/@adobe/css-tools": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.4.3.tgz", - "integrity": "sha512-VQKMkwriZbaOgVCby1UDY/LDk5fIjhQicCvVPFqfe+69fWaPWydbWJ3wRt59/YzIwda1I81loas3oCoHxnqvdA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@alloc/quick-lru": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", - "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@ampproject/remapping": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", - "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", - "license": "Apache-2.0", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", - "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.27.1", - "js-tokens": "^4.0.0", - "picocolors": "^1.1.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.27.5", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.27.5.tgz", - "integrity": "sha512-KiRAp/VoJaWkkte84TvUd9qjdbZAdiqyvMxrGl1N6vzFogKmaLgoM3L1kgtLicp2HP5fBJS8JrZKLVIZGVJAVg==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core": { - "version": "7.27.4", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.27.4.tgz", - "integrity": "sha512-bXYxrXFubeYdvB0NhD/NBB3Qi6aZeV20GOWVI47t2dkecCEoneR4NPVcb7abpXDEvejgrUfFtG6vG/zxAKmg+g==", - "license": "MIT", - "dependencies": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.27.3", - "@babel/helper-compilation-targets": "^7.27.2", - "@babel/helper-module-transforms": "^7.27.3", - "@babel/helpers": "^7.27.4", - "@babel/parser": "^7.27.4", - "@babel/template": "^7.27.2", - "@babel/traverse": "^7.27.4", - "@babel/types": "^7.27.3", - "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.27.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.5.tgz", - "integrity": "sha512-ZGhA37l0e/g2s1Cnzdix0O3aLYm66eF8aufiVteOgnwxgnRP8GoyMj7VWsgWnQbVKXyge7hqrFh2K2TQM6t1Hw==", - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.27.5", - "@babel/types": "^7.27.3", - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25", - "jsesc": "^3.0.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-annotate-as-pure": { - "version": "7.27.3", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz", - "integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==", - "license": "MIT", - "dependencies": { - "@babel/types": "^7.27.3" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", - "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.27.2", - "@babel/helper-validator-option": "^7.27.1", - "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.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.27.1.tgz", - "integrity": "sha512-QwGAmuvM17btKU5VqXfb+Giw4JcN0hjuufz3DYnpeVDvZLAObloM77bhMXiqry3Iio+Ai4phVRDwl6WU10+r5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.1", - "@babel/helper-member-expression-to-functions": "^7.27.1", - "@babel/helper-optimise-call-expression": "^7.27.1", - "@babel/helper-replace-supers": "^7.27.1", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", - "@babel/traverse": "^7.27.1", - "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.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.27.1.tgz", - "integrity": "sha512-uVDC72XVf8UbrH5qQTc18Agb8emwjTiZrQE11Nv3CuBEZmVvTwwE9CBUEvHku06gQCAyYf8Nv6ja1IN+6LMbxQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.1", - "regexpu-core": "^6.2.0", - "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.4", - "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.4.tgz", - "integrity": "sha512-jljfR1rGnXXNWnmQg2K3+bvhkxB51Rl32QRaOTuwwjviGrHzIbSc8+x9CpraDtbT7mfyjXObULP4w/adunNwAw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-compilation-targets": "^7.22.6", - "@babel/helper-plugin-utils": "^7.22.5", - "debug": "^4.1.1", - "lodash.debounce": "^4.0.8", - "resolve": "^1.14.2" - }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" - } - }, - "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.27.1.tgz", - "integrity": "sha512-E5chM8eWjTp/aNoVpcbfM7mLxu9XGLWYise2eBKGQomAk/Mb4XoxyqXTZbuTohbsl8EKqdlMhnDI2CCLfcs9wA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", - "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.27.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.27.3.tgz", - "integrity": "sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg==", - "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1", - "@babel/traverse": "^7.27.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-optimise-call-expression": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.27.1.tgz", - "integrity": "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", - "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-remap-async-to-generator": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.27.1.tgz", - "integrity": "sha512-7fiA521aVw8lSPeI4ZOD3vRFkoqkJcS+z4hFo82bFSH/2tNd6eJ5qCVMS5OzDmZh/kaHQeBaeyxK6wljcPtveA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.1", - "@babel/helper-wrap-function": "^7.27.1", - "@babel/traverse": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-replace-supers": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.27.1.tgz", - "integrity": "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-member-expression-to-functions": "^7.27.1", - "@babel/helper-optimise-call-expression": "^7.27.1", - "@babel/traverse": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.27.1.tgz", - "integrity": "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", - "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", - "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-option": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", - "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-wrap-function": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.27.1.tgz", - "integrity": "sha512-NFJK2sHUvrjo8wAU/nQTWU890/zB2jj0qBcCbZbbf+005cAsv6tMjXz31fBign6M5ov1o0Bllu+9nbqkfsjjJQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/template": "^7.27.1", - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.27.6", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.6.tgz", - "integrity": "sha512-muE8Tt8M22638HU31A3CgfSUciwz1fhATfoVai05aPXGor//CdWDCbnlY1yvBPo07njuVOCNGCSp/GTt12lIug==", - "license": "MIT", - "dependencies": { - "@babel/template": "^7.27.2", - "@babel/types": "^7.27.6" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/parser": { - "version": "7.27.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.5.tgz", - "integrity": "sha512-OsQd175SxWkGlzbny8J3K8TnnDD0N3lrIUtB92xwyRpzaenGZhxDvxN/JgU00U3CDZNj9tPuDJ5H0WS4Nt3vKg==", - "license": "MIT", - "dependencies": { - "@babel/types": "^7.27.3" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.27.1.tgz", - "integrity": "sha512-QPG3C9cCVRQLxAVwmefEmwdTanECuUBMQZ/ym5kiw3XKCGA7qkuQLcjWWHcrD/GKbn/WmJwaezfuuAOcyKlRPA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/traverse": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-bugfix-safari-class-field-initializer-scope": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.27.1.tgz", - "integrity": "sha512-qNeq3bCKnGgLkEXUuFry6dPlGfCdQNZbn7yUAPCInwAJHMU7THJfrBSozkcWq5sNM6RcF3S8XyQL2A52KNR9IA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "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.27.1", - "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.27.1.tgz", - "integrity": "sha512-g4L7OYun04N1WyqMNjldFwlfPCLVkgB54A/YCXICZYBsvJJE3kByKv9c9+R/nAfmIfjl2rKYLNyMHboYbZaWaA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.27.1.tgz", - "integrity": "sha512-oO02gcONcD5O1iTLi/6frMJBIwWEHceWGSGqrpCmEL8nogiS6J9PBlE48CaK20/Jx1LuRml9aDftLgdjXT8+Cw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", - "@babel/plugin-transform-optional-chaining": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.13.0" - } - }, - "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.27.1.tgz", - "integrity": "sha512-6BpaYGDavZqkI6yT+KSPdpZFfpnd68UKXbcjI9pJ13pvHhPrCKWOOLp+ysvMeA+DxnhuPpgIaRpxRxo5A9t5jw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/traverse": "^7.27.1" - }, - "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-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-bigint": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", - "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", - "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-class-static-block": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", - "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-import-assertions": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.27.1.tgz", - "integrity": "sha512-UT/Jrhw57xg4ILHLFnzFpPDlMbcdEicaAtjPQpbj9wa8T4r5KVWCimHcL/460g8Ht0DMxDyjsLgiWSkVjnwPFg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-import-attributes": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.27.1.tgz", - "integrity": "sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-import-meta": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", - "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz", - "integrity": "sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==", - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-private-property-in-object": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", - "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", - "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz", - "integrity": "sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "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.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.27.1.tgz", - "integrity": "sha512-8Z4TGic6xW70FKThA5HYEKKyBpOOsucTOD1DjU3fZxDg+K3zBJcXMFnt/4yQiZnf5+MiOMSXQ9PaEK/Ilh1DeA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-async-generator-functions": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.27.1.tgz", - "integrity": "sha512-eST9RrwlpaoJBDHShc+DS2SG4ATTi2MYNb4OxYkf3n+7eb49LWpnS+HSpVfW4x927qQwgk8A2hGNVaajAEw0EA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-remap-async-to-generator": "^7.27.1", - "@babel/traverse": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-async-to-generator": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.27.1.tgz", - "integrity": "sha512-NREkZsZVJS4xmTr8qzE5y8AfIPqsdQfRuUiLRTEzb7Qii8iFWCyDKaUV2c0rCuh4ljDZ98ALHP/PetiBV2nddA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-remap-async-to-generator": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-block-scoped-functions": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.27.1.tgz", - "integrity": "sha512-cnqkuOtZLapWYZUYM5rVIdv1nXYuFVIltZ6ZJ7nIj585QsjKM5dhL2Fu/lICXZ1OyIAFc7Qy+bvDAtTXqGrlhg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.27.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.27.5.tgz", - "integrity": "sha512-JF6uE2s67f0y2RZcm2kpAUEbD50vH62TyWVebxwHAlbSdM49VqPz8t4a1uIjp4NIOIZ4xzLfjY5emt/RCyC7TQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-class-properties": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.27.1.tgz", - "integrity": "sha512-D0VcalChDMtuRvJIu3U/fwWjf8ZMykz5iZsg77Nuj821vCKI3zCyRLwRdWbsuJ/uRwZhZ002QtCqIkwC/ZkvbA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-class-static-block": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.27.1.tgz", - "integrity": "sha512-s734HmYU78MVzZ++joYM+NkJusItbdRcbm+AGRgJCt3iA+yux0QpD9cBVdz3tKyrjVYWRl7j0mHSmv4lhV0aoA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.12.0" - } - }, - "node_modules/@babel/plugin-transform-classes": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.27.1.tgz", - "integrity": "sha512-7iLhfFAubmpeJe/Wo2TVuDrykh/zlWXLzPNdL0Jqn/Xu8R3QQ8h9ff8FQoISZOsw74/HFqFI7NX63HN7QFIHKA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.1", - "@babel/helper-compilation-targets": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-replace-supers": "^7.27.1", - "@babel/traverse": "^7.27.1", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-classes/node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/plugin-transform-computed-properties": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.27.1.tgz", - "integrity": "sha512-lj9PGWvMTVksbWiDT2tW68zGS/cyo4AkZ/QTp0sQT0mjPopCmrSkzxeXkznjqBxzDI6TclZhOJbBmbBLjuOZUw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/template": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-destructuring": { - "version": "7.27.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.27.3.tgz", - "integrity": "sha512-s4Jrok82JpiaIprtY2nHsYmrThKvvwgHwjgd7UMiYhZaN0asdXNLr0y+NjTfkA7SyQE5i2Fb7eawUOZmLvyqOA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-dotall-regex": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.27.1.tgz", - "integrity": "sha512-gEbkDVGRvjj7+T1ivxrfgygpT7GUd4vmODtYpbs0gZATdkX8/iSnOtZSxiZnsgm1YjTgjI6VKBGSJJevkrclzw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-duplicate-keys": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.27.1.tgz", - "integrity": "sha512-MTyJk98sHvSs+cvZ4nOauwTTG1JeonDjSGvGGUNHreGQns+Mpt6WX/dVzWBHgg+dYZhkC4X+zTDfkTU+Vy9y7Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-duplicate-named-capturing-groups-regex": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.27.1.tgz", - "integrity": "sha512-hkGcueTEzuhB30B3eJCbCYeCaaEQOmQR0AdvzpD4LoN0GXMWzzGSuRrxR2xTnCrvNbVwK9N6/jQ92GSLfiZWoQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-transform-dynamic-import": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.27.1.tgz", - "integrity": "sha512-MHzkWQcEmjzzVW9j2q8LGjwGWpG2mjwaaB0BNQwst3FIjqsg8Ct/mIZlvSPJvfi9y2AC8mi/ktxbFVL9pZ1I4A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-exponentiation-operator": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.27.1.tgz", - "integrity": "sha512-uspvXnhHvGKf2r4VVtBpeFnuDWsJLQ6MF6lGJLC89jBR1uoVeqM416AZtTuhTezOfgHicpJQmoD5YUakO/YmXQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-export-namespace-from": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.27.1.tgz", - "integrity": "sha512-tQvHWSZ3/jH2xuq/vZDy0jNn+ZdXJeM8gHvX4lnJmsc3+50yPlWdZXIc5ay+umX+2/tJIqHqiEqcJvxlmIvRvQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-for-of": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.27.1.tgz", - "integrity": "sha512-BfbWFFEJFQzLCQ5N8VocnCtA8J1CLkNTe2Ms2wocj75dd6VpiqS5Z5quTYcUoo4Yq+DN0rtikODccuv7RU81sw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-function-name": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.27.1.tgz", - "integrity": "sha512-1bQeydJF9Nr1eBCMMbC+hdwmRlsv5XYOMu03YSWFwNs0HsAmtSxxF1fyuYPqemVldVyFmlCU7w8UE14LupUSZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-compilation-targets": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/traverse": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-json-strings": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.27.1.tgz", - "integrity": "sha512-6WVLVJiTjqcQauBhn1LkICsR2H+zm62I3h9faTDKt1qP4jn2o72tSvqMwtGFKGTpojce0gJs+76eZ2uCHRZh0Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-literals": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.27.1.tgz", - "integrity": "sha512-0HCFSepIpLTkLcsi86GG3mTUzxV5jpmbv97hTETW3yzrAij8aqlD36toB1D0daVFJM8NK6GvKO0gslVQmm+zZA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-logical-assignment-operators": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.27.1.tgz", - "integrity": "sha512-SJvDs5dXxiae4FbSL1aBJlG4wvl594N6YEVVn9e3JGulwioy6z3oPjx/sQBO3Y4NwUu5HNix6KJ3wBZoewcdbw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-member-expression-literals": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.27.1.tgz", - "integrity": "sha512-hqoBX4dcZ1I33jCSWcXrP+1Ku7kdqXf1oeah7ooKOIiAdKQ+uqftgCFNOSzA5AMS2XIHEYeGFg4cKRCdpxzVOQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-amd": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.27.1.tgz", - "integrity": "sha512-iCsytMg/N9/oFq6n+gFTvUYDZQOMK5kEdeYxmxt91fcJGycfxVP9CnrxoliM0oumFERba2i8ZtwRUCMhvP1LnA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-module-transforms": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.27.1.tgz", - "integrity": "sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-module-transforms": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-systemjs": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.27.1.tgz", - "integrity": "sha512-w5N1XzsRbc0PQStASMksmUeqECuzKuTJer7kFagK8AXgpCMkeDMO5S+aaFb7A51ZYDF7XI34qsTX+fkHiIm5yA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-module-transforms": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1", - "@babel/traverse": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-modules-umd": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.27.1.tgz", - "integrity": "sha512-iQBE/xC5BV1OxJbp6WG7jq9IWiD+xxlZhLrdwpPkTX3ydmXdvoCpyfJN7acaIBZaOqTfr76pgzqBJflNbeRK+w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-module-transforms": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.27.1.tgz", - "integrity": "sha512-SstR5JYy8ddZvD6MhV0tM/j16Qds4mIpJTOd1Yu9J9pJjH93bxHECF7pgtc28XvkzTD6Pxcm/0Z73Hvk7kb3Ng==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-transform-new-target": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.27.1.tgz", - "integrity": "sha512-f6PiYeqXQ05lYq3TIfIDu/MtliKUbNwkGApPUvyo6+tc7uaR4cPjPe7DFPr15Uyycg2lZU6btZ575CuQoYh7MQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.27.1.tgz", - "integrity": "sha512-aGZh6xMo6q9vq1JGcw58lZ1Z0+i0xB2x0XaauNIUXd6O1xXc3RwoWEBlsTQrY4KQ9Jf0s5rgD6SiNkaUdJegTA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-numeric-separator": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.27.1.tgz", - "integrity": "sha512-fdPKAcujuvEChxDBJ5c+0BTaS6revLV7CJL08e4m3de8qJfNIuCc2nc7XJYOjBoTMJeqSmwXJ0ypE14RCjLwaw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-object-rest-spread": { - "version": "7.27.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.27.3.tgz", - "integrity": "sha512-7ZZtznF9g4l2JCImCo5LNKFHB5eXnN39lLtLY5Tg+VkR0jwOt7TBciMckuiQIOIW7L5tkQOCh3bVGYeXgMx52Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-compilation-targets": "^7.27.2", - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/plugin-transform-destructuring": "^7.27.3", - "@babel/plugin-transform-parameters": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-object-super": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.27.1.tgz", - "integrity": "sha512-SFy8S9plRPbIcxlJ8A6mT/CxFdJx/c04JEctz4jf8YZaVS2px34j7NXRrlGlHkN/M2gnpL37ZpGRGVFLd3l8Ng==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-replace-supers": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-optional-catch-binding": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.27.1.tgz", - "integrity": "sha512-txEAEKzYrHEX4xSZN4kJ+OfKXFVSWKB2ZxM9dpcE3wT7smwkNmXo5ORRlVzMVdJbD+Q8ILTgSD7959uj+3Dm3Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-optional-chaining": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.27.1.tgz", - "integrity": "sha512-BQmKPPIuc8EkZgNKsv0X4bPmOoayeu4F1YCwx2/CfmDSXDbp7GnzlUH+/ul5VGfRg1AoFPsrIThlEBj2xb4CAg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-parameters": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.27.1.tgz", - "integrity": "sha512-018KRk76HWKeZ5l4oTj2zPpSh+NbGdt0st5S6x0pga6HgrjBOJb24mMDHorFopOOd6YHkLgOZ+zaCjZGPO4aKg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-private-methods": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.27.1.tgz", - "integrity": "sha512-10FVt+X55AjRAYI9BrdISN9/AQWHqldOeZDUoLyif1Kn05a56xVBXb8ZouL8pZ9jem8QpXaOt8TS7RHUIS+GPA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-private-property-in-object": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.27.1.tgz", - "integrity": "sha512-5J+IhqTi1XPa0DXF83jYOaARrX+41gOewWbkPyjMNRDqgOCqdffGh8L3f/Ek5utaEBZExjSAzcyjmV9SSAWObQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.1", - "@babel/helper-create-class-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-property-literals": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.27.1.tgz", - "integrity": "sha512-oThy3BCuCha8kDZ8ZkgOg2exvPYUlprMukKQXI1r1pJ47NCvxfkEy8vK+r/hT9nF0Aa4H1WUPZZjHTFtAhGfmQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-display-name": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-display-name/-/plugin-transform-react-display-name-7.27.1.tgz", - "integrity": "sha512-p9+Vl3yuHPmkirRrg021XiP+EETmPMQTLr6Ayjj85RLNEbb3Eya/4VI0vAdzQG9SEAl2Lnt7fy5lZyMzjYoZQQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-jsx": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.27.1.tgz", - "integrity": "sha512-2KH4LWGSrJIkVf5tSiBFYuXDAoWRq2MMwgivCf+93dd0GQi8RXLjKA/0EvRnVV5G0hrHczsquXuD01L8s6dmBw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.1", - "@babel/helper-module-imports": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/plugin-syntax-jsx": "^7.27.1", - "@babel/types": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-jsx-development": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.27.1.tgz", - "integrity": "sha512-ykDdF5yI4f1WrAolLqeF3hmYU12j9ntLQl/AOG1HAS21jxyg1Q0/J/tpREuYLfatGdGmXp/3yS0ZA76kOlVq9Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/plugin-transform-react-jsx": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-pure-annotations": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-pure-annotations/-/plugin-transform-react-pure-annotations-7.27.1.tgz", - "integrity": "sha512-JfuinvDOsD9FVMTHpzA/pBLisxpv1aSf+OIV8lgH3MuWrks19R27e6a6DipIg4aX1Zm9Wpb04p8wljfKrVSnPA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-regenerator": { - "version": "7.27.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.27.5.tgz", - "integrity": "sha512-uhB8yHerfe3MWnuLAhEbeQ4afVoqv8BQsPqrTv7e/jZ9y00kJL6l9a/f4OWaKxotmjzewfEyXE1vgDJenkQ2/Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-regexp-modifiers": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.27.1.tgz", - "integrity": "sha512-TtEciroaiODtXvLZv4rmfMhkCv8jx3wgKpL68PuiPh2M4fvz5jhsA7697N1gMvkvr/JTF13DrFYyEbY9U7cVPA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/plugin-transform-reserved-words": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.27.1.tgz", - "integrity": "sha512-V2ABPHIJX4kC7HegLkYoDpfg9PVmuWy/i6vUM5eGK22bx4YVFD3M5F0QQnWQoDs6AGsUWTVOopBiMFQgHaSkVw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-shorthand-properties": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.27.1.tgz", - "integrity": "sha512-N/wH1vcn4oYawbJ13Y/FxcQrWk63jhfNa7jef0ih7PHSIHX2LB7GWE1rkPrOnka9kwMxb6hMl19p7lidA+EHmQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-spread": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.27.1.tgz", - "integrity": "sha512-kpb3HUqaILBJcRFVhFUs6Trdd4mkrzcGXss+6/mxUd273PfbWqSDHRzMT2234gIg2QYfAjvXLSquP1xECSg09Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-sticky-regex": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.27.1.tgz", - "integrity": "sha512-lhInBO5bi/Kowe2/aLdBAawijx+q1pQzicSgnkB6dUPc1+RC8QmJHKf2OjvU+NZWitguJHEaEmbV6VWEouT58g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-template-literals": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.27.1.tgz", - "integrity": "sha512-fBJKiV7F2DxZUkg5EtHKXQdbsbURW3DZKQUWphDum0uRP6eHGGa/He9mc0mypL680pb+e/lDIthRohlv8NCHkg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-typeof-symbol": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.27.1.tgz", - "integrity": "sha512-RiSILC+nRJM7FY5srIyc4/fGIwUhyDuuBSdWn4y6yT6gm652DpCHZjIipgn6B7MQ1ITOUnAKWixEUjQRIBIcLw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-typescript": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.27.1.tgz", - "integrity": "sha512-Q5sT5+O4QUebHdbwKedFBEwRLb02zJ7r4A5Gg2hUoLuU3FjdMcyqcywqUrLCaDsFCxzokf7u9kuy7qz51YUuAg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.27.1", - "@babel/helper-create-class-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", - "@babel/plugin-syntax-typescript": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-unicode-escapes": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.27.1.tgz", - "integrity": "sha512-Ysg4v6AmF26k9vpfFuTZg8HRfVWzsh1kVfowA23y9j/Gu6dOuahdUVhkLqpObp3JIv27MLSii6noRnuKN8H0Mg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-unicode-property-regex": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.27.1.tgz", - "integrity": "sha512-uW20S39PnaTImxp39O5qFlHLS9LJEmANjMG7SxIhap8rCHqu0Ik+tLEPX5DKmHn6CsWQ7j3lix2tFOa5YtL12Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-unicode-regex": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.27.1.tgz", - "integrity": "sha512-xvINq24TRojDuyt6JGtHmkVkrfVV3FPT16uytxImLeBZqW3/H52yN+kM1MGuyPkIQxrzKwPHs5U/MP3qKyzkGw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-unicode-sets-regex": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.27.1.tgz", - "integrity": "sha512-EtkOujbc4cgvb0mlpQefi4NTPBzhSIevblFevACNLUspmrALgmEBdL/XfnyyITfd8fKBZrZys92zOWcik7j9Tw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.27.1", - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/preset-env": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.27.2.tgz", - "integrity": "sha512-Ma4zSuYSlGNRlCLO+EAzLnCmJK2vdstgv+n7aUP+/IKZrOfWHOJVdSJtuub8RzHTj3ahD37k5OKJWvzf16TQyQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.27.2", - "@babel/helper-compilation-targets": "^7.27.2", - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-validator-option": "^7.27.1", - "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.27.1", - "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.27.1", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.27.1", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.27.1", - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.27.1", - "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", - "@babel/plugin-syntax-import-assertions": "^7.27.1", - "@babel/plugin-syntax-import-attributes": "^7.27.1", - "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", - "@babel/plugin-transform-arrow-functions": "^7.27.1", - "@babel/plugin-transform-async-generator-functions": "^7.27.1", - "@babel/plugin-transform-async-to-generator": "^7.27.1", - "@babel/plugin-transform-block-scoped-functions": "^7.27.1", - "@babel/plugin-transform-block-scoping": "^7.27.1", - "@babel/plugin-transform-class-properties": "^7.27.1", - "@babel/plugin-transform-class-static-block": "^7.27.1", - "@babel/plugin-transform-classes": "^7.27.1", - "@babel/plugin-transform-computed-properties": "^7.27.1", - "@babel/plugin-transform-destructuring": "^7.27.1", - "@babel/plugin-transform-dotall-regex": "^7.27.1", - "@babel/plugin-transform-duplicate-keys": "^7.27.1", - "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.27.1", - "@babel/plugin-transform-dynamic-import": "^7.27.1", - "@babel/plugin-transform-exponentiation-operator": "^7.27.1", - "@babel/plugin-transform-export-namespace-from": "^7.27.1", - "@babel/plugin-transform-for-of": "^7.27.1", - "@babel/plugin-transform-function-name": "^7.27.1", - "@babel/plugin-transform-json-strings": "^7.27.1", - "@babel/plugin-transform-literals": "^7.27.1", - "@babel/plugin-transform-logical-assignment-operators": "^7.27.1", - "@babel/plugin-transform-member-expression-literals": "^7.27.1", - "@babel/plugin-transform-modules-amd": "^7.27.1", - "@babel/plugin-transform-modules-commonjs": "^7.27.1", - "@babel/plugin-transform-modules-systemjs": "^7.27.1", - "@babel/plugin-transform-modules-umd": "^7.27.1", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.27.1", - "@babel/plugin-transform-new-target": "^7.27.1", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.27.1", - "@babel/plugin-transform-numeric-separator": "^7.27.1", - "@babel/plugin-transform-object-rest-spread": "^7.27.2", - "@babel/plugin-transform-object-super": "^7.27.1", - "@babel/plugin-transform-optional-catch-binding": "^7.27.1", - "@babel/plugin-transform-optional-chaining": "^7.27.1", - "@babel/plugin-transform-parameters": "^7.27.1", - "@babel/plugin-transform-private-methods": "^7.27.1", - "@babel/plugin-transform-private-property-in-object": "^7.27.1", - "@babel/plugin-transform-property-literals": "^7.27.1", - "@babel/plugin-transform-regenerator": "^7.27.1", - "@babel/plugin-transform-regexp-modifiers": "^7.27.1", - "@babel/plugin-transform-reserved-words": "^7.27.1", - "@babel/plugin-transform-shorthand-properties": "^7.27.1", - "@babel/plugin-transform-spread": "^7.27.1", - "@babel/plugin-transform-sticky-regex": "^7.27.1", - "@babel/plugin-transform-template-literals": "^7.27.1", - "@babel/plugin-transform-typeof-symbol": "^7.27.1", - "@babel/plugin-transform-unicode-escapes": "^7.27.1", - "@babel/plugin-transform-unicode-property-regex": "^7.27.1", - "@babel/plugin-transform-unicode-regex": "^7.27.1", - "@babel/plugin-transform-unicode-sets-regex": "^7.27.1", - "@babel/preset-modules": "0.1.6-no-external-plugins", - "babel-plugin-polyfill-corejs2": "^0.4.10", - "babel-plugin-polyfill-corejs3": "^0.11.0", - "babel-plugin-polyfill-regenerator": "^0.6.1", - "core-js-compat": "^3.40.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/preset-react": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.27.1.tgz", - "integrity": "sha512-oJHWh2gLhU9dW9HHr42q0cI0/iHHXTLGe39qvpAZZzagHy0MzYLCnCVV0symeRvzmjHyVU7mw2K06E6u/JwbhA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-validator-option": "^7.27.1", - "@babel/plugin-transform-react-display-name": "^7.27.1", - "@babel/plugin-transform-react-jsx": "^7.27.1", - "@babel/plugin-transform-react-jsx-development": "^7.27.1", - "@babel/plugin-transform-react-pure-annotations": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/preset-typescript": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.27.1.tgz", - "integrity": "sha512-l7WfQfX0WK4M0v2RudjuQK4u99BS6yLHYEmdtVPP7lKV013zr9DygFuWNlnbvQ9LR+LS0Egz/XAvGx5U9MX0fQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1", - "@babel/helper-validator-option": "^7.27.1", - "@babel/plugin-syntax-jsx": "^7.27.1", - "@babel/plugin-transform-modules-commonjs": "^7.27.1", - "@babel/plugin-transform-typescript": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/runtime": { - "version": "7.27.6", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.6.tgz", - "integrity": "sha512-vbavdySgbTTrmFE+EsiqUTzlOr5bzlnJtUv9PynGCAKvfQqjIXbvFdumPM/GxMDfyuGMJaJAU6TO4zc1Jf1i8Q==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/template": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", - "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/parser": "^7.27.2", - "@babel/types": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.27.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.4.tgz", - "integrity": "sha512-oNcu2QbHqts9BtOWJosOVJapWjBDSxGCpFvikNR5TGDYDQf3JwpIoMzIKrvfoti93cLfPJEG4tH9SPVeyCGgdA==", - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.27.3", - "@babel/parser": "^7.27.4", - "@babel/template": "^7.27.2", - "@babel/types": "^7.27.3", - "debug": "^4.3.1", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse/node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/types": { - "version": "7.27.6", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.6.tgz", - "integrity": "sha512-ETyHEk2VHHvl9b9jZP5IHPavHYk57EhanlRRuae9XCpb/j5bDCbPPMOBfCWhnl/7EDJz0jEMCi/RhccCE8r1+Q==", - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@bcoe/v8-coverage": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", - "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@discoveryjs/json-ext": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", - "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/@emotion/is-prop-valid": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.3.1.tgz", - "integrity": "sha512-/ACwoqx7XQi9knQs/G0qKvv5teDMhD7bXYns9N/wM8ah8iNb8jZ2uNO0YOgiq2o2poIvVtJS2YALasQuMSQ7Kw==", - "license": "MIT", - "peer": true, - "dependencies": { - "@emotion/memoize": "^0.9.0" - } - }, - "node_modules/@emotion/memoize": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz", - "integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==", - "license": "MIT", - "peer": true - }, - "node_modules/@emotion/stylis": { - "version": "0.8.5", - "resolved": "https://registry.npmjs.org/@emotion/stylis/-/stylis-0.8.5.tgz", - "integrity": "sha512-h6KtPihKFn3T9fuIrwvXXUOwlx3rfUvfZIcP5a6rh8Y7zjE3O06hT5Ss4S/YI1AYhuZ1kjaE/5EaOOI2NqSylQ==", - "license": "MIT", - "peer": true - }, - "node_modules/@emotion/unitless": { - "version": "0.7.5", - "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz", - "integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==", - "license": "MIT", - "peer": true - }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", - "integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==", - "dev": true, - "license": "MIT", - "dependencies": { - "eslint-visitor-keys": "^3.4.3" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" - } - }, - "node_modules/@eslint-community/regexpp": { - "version": "4.12.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", - "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" - } - }, - "node_modules/@eslint/config-array": { - "version": "0.20.1", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.20.1.tgz", - "integrity": "sha512-OL0RJzC/CBzli0DrrR31qzj6d6i6Mm3HByuhflhl4LOBiWxN+3i6/t/ZQQNii4tjksXi8r2CRW1wMpWA2ULUEw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/object-schema": "^2.1.6", - "debug": "^4.3.1", - "minimatch": "^3.1.2" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/config-array/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@eslint/config-array/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/@eslint/config-helpers": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.2.3.tgz", - "integrity": "sha512-u180qk2Um1le4yf0ruXH3PYFeEZeYC3p/4wCTKrr2U1CmGdzGi3KtY0nuPDH48UJxlKCC5RDzbcbh4X0XlqgHg==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/core": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.14.0.tgz", - "integrity": "sha512-qIbV0/JZr7iSDjqAc60IqbLdsj9GDt16xQtWD+B78d/HAlvysGdZZ6rpJHGAc2T0FQx1X6thsSPdnoiGKdNtdg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@types/json-schema": "^7.0.15" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/eslintrc": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", - "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^10.0.1", - "globals": "^14.0.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint/eslintrc/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true, - "license": "Python-2.0" - }, - "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", - "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@eslint/eslintrc/node_modules/ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/@eslint/eslintrc/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/@eslint/eslintrc/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/@eslint/js": { - "version": "9.29.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.29.0.tgz", - "integrity": "sha512-3PIF4cBw/y+1u2EazflInpV+lYsSG0aByVIQzAgb1m1MhHFSbqTyNqtBKHgWf/9Ykud+DhILS9EGkmekVhbKoQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://eslint.org/donate" - } - }, - "node_modules/@eslint/object-schema": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", - "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/plugin-kit": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.2.tgz", - "integrity": "sha512-4SaFZCNfJqvk/kenHpI8xvN42DMaoycy4PzKc5otHxRswww1kAt82OlBuwRVLofCACCTZEcla2Ydxv8scMXaTg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/core": "^0.15.0", - "levn": "^0.4.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/plugin-kit/node_modules/@eslint/core": { - "version": "0.15.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.0.tgz", - "integrity": "sha512-b7ePw78tEWWkpgZCDYkbqDOP8dmM6qe+AOC6iuJqlq1R/0ahMAeH3qynpnqKFGkMltrp44ohV4ubGyvLX28tzw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@types/json-schema": "^7.0.15" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@heroicons/react": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@heroicons/react/-/react-2.2.0.tgz", - "integrity": "sha512-LMcepvRaS9LYHJGsF0zzmgKCUim/X3N/DQKc4jepAXJ7l8QxJ1PmxJzqplF2Z3FE4PqBAIGyJAQ/w4B5dsqbtQ==", - "license": "MIT", - "peerDependencies": { - "react": ">= 16 || ^19.0.0-rc" - } - }, - "node_modules/@humanfs/core": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", - "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@humanfs/node": { - "version": "0.16.6", - "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", - "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@humanfs/core": "^0.19.1", - "@humanwhocodes/retry": "^0.3.0" - }, - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", - "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=12.22" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@humanwhocodes/retry": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", - "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@isaacs/cliui": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@isaacs/cliui/node_modules/ansi-regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@isaacs/cliui/node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@isaacs/cliui/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/console": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", - "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/core": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", - "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/console": "^29.7.0", - "@jest/reporters": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-changed-files": "^29.7.0", - "jest-config": "^29.7.0", - "jest-haste-map": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-resolve-dependencies": "^29.7.0", - "jest-runner": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "jest-watcher": "^29.7.0", - "micromatch": "^4.0.4", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/@jest/core/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@jest/core/node_modules/pretty-format": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", - "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/core/node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@jest/environment": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", - "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-mock": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/expect": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", - "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "expect": "^29.7.0", - "jest-snapshot": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/expect-utils": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", - "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", - "dev": true, - "license": "MIT", - "dependencies": { - "jest-get-type": "^29.6.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/fake-timers": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", - "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "@sinonjs/fake-timers": "^10.0.2", - "@types/node": "*", - "jest-message-util": "^29.7.0", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/globals": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", - "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/expect": "^29.7.0", - "@jest/types": "^29.6.3", - "jest-mock": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/reporters": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", - "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@jridgewell/trace-mapping": "^0.3.18", - "@types/node": "*", - "chalk": "^4.0.0", - "collect-v8-coverage": "^1.0.0", - "exit": "^0.1.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^6.0.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.1.3", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "jest-worker": "^29.7.0", - "slash": "^3.0.0", - "string-length": "^4.0.1", - "strip-ansi": "^6.0.0", - "v8-to-istanbul": "^9.0.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/@jest/reporters/node_modules/istanbul-lib-instrument": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", - "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@babel/core": "^7.23.9", - "@babel/parser": "^7.23.9", - "@istanbuljs/schema": "^0.1.3", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^7.5.4" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@jest/reporters/node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@jest/schemas": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", - "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@sinclair/typebox": "^0.27.8" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/source-map": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", - "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.18", - "callsites": "^3.0.0", - "graceful-fs": "^4.2.9" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/test-result": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", - "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/console": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/test-sequencer": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", - "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/test-result": "^29.7.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/transform": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", - "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.11.6", - "@jest/types": "^29.6.3", - "@jridgewell/trace-mapping": "^0.3.18", - "babel-plugin-istanbul": "^6.1.1", - "chalk": "^4.0.0", - "convert-source-map": "^2.0.0", - "fast-json-stable-stringify": "^2.1.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-util": "^29.7.0", - "micromatch": "^4.0.4", - "pirates": "^4.0.4", - "slash": "^3.0.0", - "write-file-atomic": "^4.0.2" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/types": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", - "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/schemas": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", - "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", - "license": "MIT", - "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/set-array": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/source-map": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", - "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", - "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.0", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", - "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@jsonjoy.com/base64": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/base64/-/base64-1.1.2.tgz", - "integrity": "sha512-q6XAnWQDIMA3+FTiOYajoYqySkO+JSat0ytXGSuRdq9uXE7o92gzuQwQM14xaCRlBLGq3v5miDGC4vkVTn54xA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=10.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/streamich" - }, - "peerDependencies": { - "tslib": "2" - } - }, - "node_modules/@jsonjoy.com/json-pack": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/json-pack/-/json-pack-1.2.0.tgz", - "integrity": "sha512-io1zEbbYcElht3tdlqEOFxZ0dMTYrHz9iMf0gqn1pPjZFTCgM5R4R5IMA20Chb2UPYYsxjzs8CgZ7Nb5n2K2rA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@jsonjoy.com/base64": "^1.1.1", - "@jsonjoy.com/util": "^1.1.2", - "hyperdyperid": "^1.2.0", - "thingies": "^1.20.0" - }, - "engines": { - "node": ">=10.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/streamich" - }, - "peerDependencies": { - "tslib": "2" - } - }, - "node_modules/@jsonjoy.com/util": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@jsonjoy.com/util/-/util-1.6.0.tgz", - "integrity": "sha512-sw/RMbehRhN68WRtcKCpQOPfnH6lLP4GJfqzi3iYej8tnzpZUDr6UkZYJjcjjC0FWEJOJbyM3PTIwxucUmDG2A==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=10.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/streamich" - }, - "peerDependencies": { - "tslib": "2" - } - }, - "node_modules/@leichtgewicht/ip-codec": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz", - "integrity": "sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">=14" - } - }, - "node_modules/@pmmmwh/react-refresh-webpack-plugin": { - "version": "0.5.16", - "resolved": "https://registry.npmjs.org/@pmmmwh/react-refresh-webpack-plugin/-/react-refresh-webpack-plugin-0.5.16.tgz", - "integrity": "sha512-kLQc9xz6QIqd2oIYyXRUiAp79kGpFBm3fEM9ahfG1HI0WI5gdZ2OVHWdmZYnwODt7ISck+QuQ6sBPrtvUBML7Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-html": "^0.0.9", - "core-js-pure": "^3.23.3", - "error-stack-parser": "^2.0.6", - "html-entities": "^2.1.0", - "loader-utils": "^2.0.4", - "schema-utils": "^4.2.0", - "source-map": "^0.7.3" - }, - "engines": { - "node": ">= 10.13" - }, - "peerDependencies": { - "@types/webpack": "4.x || 5.x", - "react-refresh": ">=0.10.0 <1.0.0", - "sockjs-client": "^1.4.0", - "type-fest": ">=0.17.0 <5.0.0", - "webpack": ">=4.43.0 <6.0.0", - "webpack-dev-server": "3.x || 4.x || 5.x", - "webpack-hot-middleware": "2.x", - "webpack-plugin-serve": "0.x || 1.x" - }, - "peerDependenciesMeta": { - "@types/webpack": { - "optional": true - }, - "sockjs-client": { - "optional": true - }, - "type-fest": { - "optional": true - }, - "webpack-dev-server": { - "optional": true - }, - "webpack-hot-middleware": { - "optional": true - }, - "webpack-plugin-serve": { - "optional": true - } - } - }, - "node_modules/@remix-run/router": { - "version": "1.23.0", - "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.23.0.tgz", - "integrity": "sha512-O3rHJzAQKamUz1fvE0Qaw0xSFqsA/yafi2iqeE0pvdFtCO1viYx8QL6f3Ln/aCCTLxs68SLf0KPM9eSeM8yBnA==", - "license": "MIT", - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@sinclair/typebox": { - "version": "0.27.8", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", - "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@sinonjs/commons": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", - "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "type-detect": "4.0.8" - } - }, - "node_modules/@sinonjs/fake-timers": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", - "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@sinonjs/commons": "^3.0.0" - } - }, - "node_modules/@testing-library/dom": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.0.tgz", - "integrity": "sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==", - "dev": true, - "license": "MIT", - "peer": true, - "dependencies": { - "@babel/code-frame": "^7.10.4", - "@babel/runtime": "^7.12.5", - "@types/aria-query": "^5.0.1", - "aria-query": "5.3.0", - "chalk": "^4.1.0", - "dom-accessibility-api": "^0.5.9", - "lz-string": "^1.5.0", - "pretty-format": "^27.0.2" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@testing-library/jest-dom": { - "version": "6.6.3", - "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.6.3.tgz", - "integrity": "sha512-IteBhl4XqYNkM54f4ejhLRJiZNqcSCoXUOG2CPK7qbD322KjQozM4kHQOfkG2oln9b9HTYqs+Sae8vBATubxxA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@adobe/css-tools": "^4.4.0", - "aria-query": "^5.0.0", - "chalk": "^3.0.0", - "css.escape": "^1.5.1", - "dom-accessibility-api": "^0.6.3", - "lodash": "^4.17.21", - "redent": "^3.0.0" - }, - "engines": { - "node": ">=14", - "npm": ">=6", - "yarn": ">=1" - } - }, - "node_modules/@testing-library/jest-dom/node_modules/chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@testing-library/jest-dom/node_modules/dom-accessibility-api": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.6.3.tgz", - "integrity": "sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==", - "dev": true, - "license": "MIT" - }, - "node_modules/@testing-library/react": { - "version": "14.3.1", - "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-14.3.1.tgz", - "integrity": "sha512-H99XjUhWQw0lTgyMN05W3xQG1Nh4lq574D8keFf1dDoNTJgp66VbJozRaczoF+wsiaPJNt/TcnfpLGufGxSrZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.12.5", - "@testing-library/dom": "^9.0.0", - "@types/react-dom": "^18.0.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "react": "^18.0.0", - "react-dom": "^18.0.0" - } - }, - "node_modules/@testing-library/react/node_modules/@testing-library/dom": { - "version": "9.3.4", - "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-9.3.4.tgz", - "integrity": "sha512-FlS4ZWlp97iiNWig0Muq8p+3rVDjRiYE+YKGbAqXOu9nwJFFOdL00kFpz42M+4huzYi86vAK1sOOfyOG45muIQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.10.4", - "@babel/runtime": "^7.12.5", - "@types/aria-query": "^5.0.1", - "aria-query": "5.1.3", - "chalk": "^4.1.0", - "dom-accessibility-api": "^0.5.9", - "lz-string": "^1.5.0", - "pretty-format": "^27.0.2" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/@testing-library/react/node_modules/aria-query": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.1.3.tgz", - "integrity": "sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "deep-equal": "^2.0.5" - } - }, - "node_modules/@testing-library/user-event": { - "version": "14.6.1", - "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.6.1.tgz", - "integrity": "sha512-vq7fv0rnt+QTXgPxr5Hjc210p6YKq2kmdziLgnsZGgLJ9e6VAShx1pACLuRjd/AS/sr7phAR58OIIpf0LlmQNw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12", - "npm": ">=6" - }, - "peerDependencies": { - "@testing-library/dom": ">=7.21.4" - } - }, - "node_modules/@tootallnate/once": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", - "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 10" - } - }, - "node_modules/@types/aria-query": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", - "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", - "dev": true, - "license": "MIT" - }, - "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.20.7", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.7.tgz", - "integrity": "sha512-dkO5fhS7+/oos4ciWxyEyjWe48zmG6wbCheo/G2ZnHx4fs3EU6YC6UM8rk56gAjNJ9P3MTH2jo5jb92/K6wbng==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.20.7" - } - }, - "node_modules/@types/body-parser": { - "version": "1.19.6", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", - "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/connect": "*", - "@types/node": "*" - } - }, - "node_modules/@types/bonjour": { - "version": "3.5.13", - "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.13.tgz", - "integrity": "sha512-z9fJ5Im06zvUL548KvYNecEVlA7cVDkGUi6kZusb04mpyEFKCIZJvloCcmpmLaIahDpOQGHaHmG6imtPMmPXGQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/connect": { - "version": "3.4.38", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", - "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/connect-history-api-fallback": { - "version": "1.5.4", - "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.4.tgz", - "integrity": "sha512-n6Cr2xS1h4uAulPRdlw6Jl6s1oG8KrVilPN2yUITEs+K48EzMJJ3W1xy8K5eWuFvjp3R74AOIGSmp2UfBJ8HFw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/express-serve-static-core": "*", - "@types/node": "*" - } - }, - "node_modules/@types/d3-array": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.1.tgz", - "integrity": "sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==", - "license": "MIT" - }, - "node_modules/@types/d3-color": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz", - "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==", - "license": "MIT" - }, - "node_modules/@types/d3-ease": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz", - "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==", - "license": "MIT" - }, - "node_modules/@types/d3-interpolate": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", - "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==", - "license": "MIT", - "dependencies": { - "@types/d3-color": "*" - } - }, - "node_modules/@types/d3-path": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz", - "integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==", - "license": "MIT" - }, - "node_modules/@types/d3-scale": { - "version": "4.0.9", - "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz", - "integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==", - "license": "MIT", - "dependencies": { - "@types/d3-time": "*" - } - }, - "node_modules/@types/d3-shape": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.7.tgz", - "integrity": "sha512-VLvUQ33C+3J+8p+Daf+nYSOsjB4GXp19/S/aGo60m9h1v6XaxjiT82lKVWJCfzhtuZ3yD7i/TPeC/fuKLLOSmg==", - "license": "MIT", - "dependencies": { - "@types/d3-path": "*" - } - }, - "node_modules/@types/d3-time": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz", - "integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==", - "license": "MIT" - }, - "node_modules/@types/d3-timer": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz", - "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==", - "license": "MIT" - }, - "node_modules/@types/debug": { - "version": "4.1.12", - "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", - "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", - "license": "MIT", - "dependencies": { - "@types/ms": "*" - } - }, - "node_modules/@types/eslint": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz", - "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "*", - "@types/json-schema": "*" - } - }, - "node_modules/@types/eslint-scope": { - "version": "3.7.7", - "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.7.tgz", - "integrity": "sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/eslint": "*", - "@types/estree": "*" - } - }, - "node_modules/@types/estree": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", - "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", - "license": "MIT" - }, - "node_modules/@types/estree-jsx": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@types/estree-jsx/-/estree-jsx-1.0.5.tgz", - "integrity": "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==", - "license": "MIT", - "dependencies": { - "@types/estree": "*" - } - }, - "node_modules/@types/express": { - "version": "4.17.23", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.23.tgz", - "integrity": "sha512-Crp6WY9aTYP3qPi2wGDo9iUe/rceX01UMhnF1jmwDcKCFM6cx7YhGP/Mpr3y9AASpfHixIG0E6azCcL5OcDHsQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "^4.17.33", - "@types/qs": "*", - "@types/serve-static": "*" - } - }, - "node_modules/@types/express-serve-static-core": { - "version": "4.19.6", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.6.tgz", - "integrity": "sha512-N4LZ2xG7DatVqhCZzOGb1Yi5lMbXSZcmdLDe9EzSndPV2HpWYWzRbaerl2n27irrm94EPpprqa8KpskPT085+A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*", - "@types/send": "*" - } - }, - "node_modules/@types/graceful-fs": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", - "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/hast": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", - "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", - "license": "MIT", - "dependencies": { - "@types/unist": "*" - } - }, - "node_modules/@types/history": { - "version": "4.7.11", - "resolved": "https://registry.npmjs.org/@types/history/-/history-4.7.11.tgz", - "integrity": "sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/html-minifier-terser": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", - "integrity": "sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/http-errors": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", - "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/http-proxy": { - "version": "1.17.16", - "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.16.tgz", - "integrity": "sha512-sdWoUajOB1cd0A8cRRQ1cfyWNbmFKLAqBB89Y8x5iYyG/mkJHc0YUH8pdWBy2omi9qtCpiIgGjuwO0dQST2l5w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/istanbul-lib-coverage": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", - "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/istanbul-lib-report": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", - "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/istanbul-lib-coverage": "*" - } - }, - "node_modules/@types/istanbul-reports": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", - "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/istanbul-lib-report": "*" - } - }, - "node_modules/@types/jest": { - "version": "29.5.14", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.14.tgz", - "integrity": "sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "expect": "^29.0.0", - "pretty-format": "^29.0.0" - } - }, - "node_modules/@types/jest/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@types/jest/node_modules/pretty-format": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", - "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@types/jest/node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/jsdom": { - "version": "20.0.1", - "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-20.0.1.tgz", - "integrity": "sha512-d0r18sZPmMQr1eG35u12FZfhIXNrnsPU/g5wvRKCUf/tOGilKKwYMYGqh33BNR6ba+2gkHw1EUiHoN3mn7E5IQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*", - "@types/tough-cookie": "*", - "parse5": "^7.0.0" - } - }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/mdast": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", - "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", - "license": "MIT", - "dependencies": { - "@types/unist": "*" - } - }, - "node_modules/@types/mime": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", - "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/ms": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", - "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", - "license": "MIT" - }, - "node_modules/@types/node": { - "version": "24.0.3", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.0.3.tgz", - "integrity": "sha512-R4I/kzCYAdRLzfiCabn9hxWfbuHS573x+r0dJMkkzThEa7pbrcDWK+9zu3e7aBOouf+rQAciqPFMnxwr0aWgKg==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~7.8.0" - } - }, - "node_modules/@types/node-forge": { - "version": "1.3.11", - "resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.3.11.tgz", - "integrity": "sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/prop-types": { - "version": "15.7.15", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz", - "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==", - "license": "MIT" - }, - "node_modules/@types/qs": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.14.0.tgz", - "integrity": "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/range-parser": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", - "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/react": { - "version": "18.3.23", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.23.tgz", - "integrity": "sha512-/LDXMQh55EzZQ0uVAZmKKhfENivEvWz6E+EYzh+/MCjMhNsotd+ZHhBGIjFDTi6+fz0OhQQQLbTgdQIxxCsC0w==", - "license": "MIT", - "dependencies": { - "@types/prop-types": "*", - "csstype": "^3.0.2" - } - }, - "node_modules/@types/react-dom": { - "version": "18.3.7", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.7.tgz", - "integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "@types/react": "^18.0.0" - } - }, - "node_modules/@types/react-router": { - "version": "5.1.20", - "resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.20.tgz", - "integrity": "sha512-jGjmu/ZqS7FjSH6owMcD5qpq19+1RS9DeVRqfl1FeBMxTDQAGwlMWOcs52NDoXaNKyG3d1cYQFMs9rCrb88o9Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/history": "^4.7.11", - "@types/react": "*" - } - }, - "node_modules/@types/react-router-dom": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/@types/react-router-dom/-/react-router-dom-5.3.3.tgz", - "integrity": "sha512-kpqnYK4wcdm5UaWI3fLcELopqLrHgLqNsdpHauzlQktfkHL3npOSwtj1Uz9oKBAzs7lFtVkV8j83voAz2D8fhw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/history": "^4.7.11", - "@types/react": "*", - "@types/react-router": "*" - } - }, - "node_modules/@types/retry": { - "version": "0.12.2", - "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.2.tgz", - "integrity": "sha512-XISRgDJ2Tc5q4TRqvgJtzsRkFYNJzZrhTdtMoGVBttwzzQJkPnS3WWTFc7kuDRoPtPakl+T+OfdEUjYJj7Jbow==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/send": { - "version": "0.17.5", - "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.5.tgz", - "integrity": "sha512-z6F2D3cOStZvuk2SaP6YrwkNO65iTZcwA2ZkSABegdkAh/lf+Aa/YQndZVfmEXT5vgAp6zv06VQ3ejSVjAny4w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/mime": "^1", - "@types/node": "*" - } - }, - "node_modules/@types/serve-index": { - "version": "1.9.4", - "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.4.tgz", - "integrity": "sha512-qLpGZ/c2fhSs5gnYsQxtDEq3Oy8SXPClIXkW5ghvAvsNuVSA8k+gCONcUCS/UjLEYvYps+e8uBtfgXgvhwfNug==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/express": "*" - } - }, - "node_modules/@types/serve-static": { - "version": "1.15.8", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.8.tgz", - "integrity": "sha512-roei0UY3LhpOJvjbIP6ZZFngyLKl5dskOtDhxY5THRSpO+ZI+nzJ+m5yUMzGrp89YRa7lvknKkMYjqQFGwA7Sg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/http-errors": "*", - "@types/node": "*", - "@types/send": "*" - } - }, - "node_modules/@types/sockjs": { - "version": "0.3.36", - "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.36.tgz", - "integrity": "sha512-MK9V6NzAS1+Ud7JV9lJLFqW85VbC9dq3LmwZCuBe4wBDgKC0Kj/jd8Xl+nSviU+Qc3+m7umHHyHg//2KSa0a0Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/stack-utils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", - "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/tough-cookie": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", - "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/unist": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", - "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", - "license": "MIT" - }, - "node_modules/@types/ws": { - "version": "8.18.1", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", - "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/yargs": { - "version": "17.0.33", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", - "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@types/yargs-parser": { - "version": "21.0.3", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", - "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.34.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.34.1.tgz", - "integrity": "sha512-STXcN6ebF6li4PxwNeFnqF8/2BNDvBupf2OPx2yWNzr6mKNGF7q49VM00Pz5FaomJyqvbXpY6PhO+T9w139YEQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.34.1", - "@typescript-eslint/type-utils": "8.34.1", - "@typescript-eslint/utils": "8.34.1", - "@typescript-eslint/visitor-keys": "8.34.1", - "graphemer": "^1.4.0", - "ignore": "^7.0.0", - "natural-compare": "^1.4.0", - "ts-api-utils": "^2.1.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^8.34.1", - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/parser": { - "version": "8.34.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.34.1.tgz", - "integrity": "sha512-4O3idHxhyzjClSMJ0a29AcoK0+YwnEqzI6oz3vlRf3xw0zbzt15MzXwItOlnr5nIth6zlY2RENLsOPvhyrKAQA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/scope-manager": "8.34.1", - "@typescript-eslint/types": "8.34.1", - "@typescript-eslint/typescript-estree": "8.34.1", - "@typescript-eslint/visitor-keys": "8.34.1", - "debug": "^4.3.4" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/project-service": { - "version": "8.34.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.34.1.tgz", - "integrity": "sha512-nuHlOmFZfuRwLJKDGQOVc0xnQrAmuq1Mj/ISou5044y1ajGNp2BNliIqp7F2LPQ5sForz8lempMFCovfeS1XoA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.34.1", - "@typescript-eslint/types": "^8.34.1", - "debug": "^4.3.4" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "8.34.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.34.1.tgz", - "integrity": "sha512-beu6o6QY4hJAgL1E8RaXNC071G4Kso2MGmJskCFQhRhg8VOH/FDbC8soP8NHN7e/Hdphwp8G8cE6OBzC8o41ZA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.34.1", - "@typescript-eslint/visitor-keys": "8.34.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.34.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.34.1.tgz", - "integrity": "sha512-K4Sjdo4/xF9NEeA2khOb7Y5nY6NSXBnod87uniVYW9kHP+hNlDV8trUSFeynA2uxWam4gIWgWoygPrv9VMWrYg==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/type-utils": { - "version": "8.34.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.34.1.tgz", - "integrity": "sha512-Tv7tCCr6e5m8hP4+xFugcrwTOucB8lshffJ6zf1mF1TbU67R+ntCc6DzLNKM+s/uzDyv8gLq7tufaAhIBYeV8g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/typescript-estree": "8.34.1", - "@typescript-eslint/utils": "8.34.1", - "debug": "^4.3.4", - "ts-api-utils": "^2.1.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/types": { - "version": "8.34.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.34.1.tgz", - "integrity": "sha512-rjLVbmE7HR18kDsjNIZQHxmv9RZwlgzavryL5Lnj2ujIRTeXlKtILHgRNmQ3j4daw7zd+mQgy+uyt6Zo6I0IGA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.34.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.34.1.tgz", - "integrity": "sha512-rjCNqqYPuMUF5ODD+hWBNmOitjBWghkGKJg6hiCHzUvXRy6rK22Jd3rwbP2Xi+R7oYVvIKhokHVhH41BxPV5mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/project-service": "8.34.1", - "@typescript-eslint/tsconfig-utils": "8.34.1", - "@typescript-eslint/types": "8.34.1", - "@typescript-eslint/visitor-keys": "8.34.1", - "debug": "^4.3.4", - "fast-glob": "^3.3.2", - "is-glob": "^4.0.3", - "minimatch": "^9.0.4", - "semver": "^7.6.0", - "ts-api-utils": "^2.1.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@typescript-eslint/utils": { - "version": "8.34.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.34.1.tgz", - "integrity": "sha512-mqOwUdZ3KjtGk7xJJnLbHxTuWVn3GO2WZZuM+Slhkun4+qthLdXx32C8xIXbO1kfCECb3jIs3eoxK3eryk7aoQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.34.1", - "@typescript-eslint/types": "8.34.1", - "@typescript-eslint/typescript-estree": "8.34.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" - } - }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.34.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.34.1.tgz", - "integrity": "sha512-xoh5rJ+tgsRKoXnkBPFRLZ7rjKM0AfVbC68UZ/ECXoDbfggb9RbEySN359acY1vS3qZ0jVTVWzbtfapwm5ztxw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.34.1", - "eslint-visitor-keys": "^4.2.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", - "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@ungap/structured-clone": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", - "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", - "license": "ISC" - }, - "node_modules/@webassemblyjs/ast": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.14.1.tgz", - "integrity": "sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/helper-numbers": "1.13.2", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2" - } - }, - "node_modules/@webassemblyjs/floating-point-hex-parser": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.13.2.tgz", - "integrity": "sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@webassemblyjs/helper-api-error": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.13.2.tgz", - "integrity": "sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@webassemblyjs/helper-buffer": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.14.1.tgz", - "integrity": "sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@webassemblyjs/helper-numbers": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.13.2.tgz", - "integrity": "sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/floating-point-hex-parser": "1.13.2", - "@webassemblyjs/helper-api-error": "1.13.2", - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@webassemblyjs/helper-wasm-bytecode": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.13.2.tgz", - "integrity": "sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@webassemblyjs/helper-wasm-section": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.14.1.tgz", - "integrity": "sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-buffer": "1.14.1", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2", - "@webassemblyjs/wasm-gen": "1.14.1" - } - }, - "node_modules/@webassemblyjs/ieee754": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.13.2.tgz", - "integrity": "sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@xtuc/ieee754": "^1.2.0" - } - }, - "node_modules/@webassemblyjs/leb128": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.13.2.tgz", - "integrity": "sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@webassemblyjs/utf8": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.13.2.tgz", - "integrity": "sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@webassemblyjs/wasm-edit": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.14.1.tgz", - "integrity": "sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-buffer": "1.14.1", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2", - "@webassemblyjs/helper-wasm-section": "1.14.1", - "@webassemblyjs/wasm-gen": "1.14.1", - "@webassemblyjs/wasm-opt": "1.14.1", - "@webassemblyjs/wasm-parser": "1.14.1", - "@webassemblyjs/wast-printer": "1.14.1" - } - }, - "node_modules/@webassemblyjs/wasm-gen": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.14.1.tgz", - "integrity": "sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2", - "@webassemblyjs/ieee754": "1.13.2", - "@webassemblyjs/leb128": "1.13.2", - "@webassemblyjs/utf8": "1.13.2" - } - }, - "node_modules/@webassemblyjs/wasm-opt": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.14.1.tgz", - "integrity": "sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-buffer": "1.14.1", - "@webassemblyjs/wasm-gen": "1.14.1", - "@webassemblyjs/wasm-parser": "1.14.1" - } - }, - "node_modules/@webassemblyjs/wasm-parser": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.14.1.tgz", - "integrity": "sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@webassemblyjs/helper-api-error": "1.13.2", - "@webassemblyjs/helper-wasm-bytecode": "1.13.2", - "@webassemblyjs/ieee754": "1.13.2", - "@webassemblyjs/leb128": "1.13.2", - "@webassemblyjs/utf8": "1.13.2" - } - }, - "node_modules/@webassemblyjs/wast-printer": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.14.1.tgz", - "integrity": "sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@webassemblyjs/ast": "1.14.1", - "@xtuc/long": "4.2.2" - } - }, - "node_modules/@webpack-cli/configtest": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-2.1.1.tgz", - "integrity": "sha512-wy0mglZpDSiSS0XHrVR+BAdId2+yxPSoJW8fsna3ZpYSlufjvxnP4YbKTCBZnNIcGN4r6ZPXV55X4mYExOfLmw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14.15.0" - }, - "peerDependencies": { - "webpack": "5.x.x", - "webpack-cli": "5.x.x" - } - }, - "node_modules/@webpack-cli/info": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-2.0.2.tgz", - "integrity": "sha512-zLHQdI/Qs1UyT5UBdWNqsARasIA+AaF8t+4u2aS2nEpBQh2mWIVb8qAklq0eUENnC5mOItrIB4LiS9xMtph18A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14.15.0" - }, - "peerDependencies": { - "webpack": "5.x.x", - "webpack-cli": "5.x.x" - } - }, - "node_modules/@webpack-cli/serve": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-2.0.5.tgz", - "integrity": "sha512-lqaoKnRYBdo1UgDX8uF24AfGMifWK19TxPmM5FHc2vAGxrJ/qtyUyFBWoY1tISZdelsQ5fBcOusifo5o5wSJxQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14.15.0" - }, - "peerDependencies": { - "webpack": "5.x.x", - "webpack-cli": "5.x.x" - }, - "peerDependenciesMeta": { - "webpack-dev-server": { - "optional": true - } - } - }, - "node_modules/@xtuc/ieee754": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", - "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/@xtuc/long": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", - "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", - "dev": true, - "license": "Apache-2.0" - }, - "node_modules/@yaireo/tagify": { - "version": "4.35.1", - "resolved": "https://registry.npmjs.org/@yaireo/tagify/-/tagify-4.35.1.tgz", - "integrity": "sha512-PF6nwp1lHMVS2IVpqZTxx53fQyEBUDjdDQsi7gD9Vx/EQ398gRH8LByEL8lawws9iJHB90on74VWG3pvwznrEg==", - "license": "MIT", - "engines": { - "node": ">=16.15.0", - "npm": ">=9.0.0" - }, - "peerDependencies": { - "prop-types": ">15.5.7", - "react": "*", - "react-dom": "*" - } - }, - "node_modules/abab": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", - "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", - "deprecated": "Use your platform's native atob() and btoa() methods instead", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "dev": true, - "license": "MIT", - "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/accepts/node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/acorn": { - "version": "8.15.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", - "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", - "dev": true, - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-globals": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-7.0.1.tgz", - "integrity": "sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "acorn": "^8.1.0", - "acorn-walk": "^8.0.2" - } - }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/acorn-walk": { - "version": "8.3.4", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", - "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", - "dev": true, - "license": "MIT", - "dependencies": { - "acorn": "^8.11.0" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "4" - }, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ajv-formats": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", - "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ajv": "^8.0.0" - }, - "peerDependencies": { - "ajv": "^8.0.0" - }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } - } - }, - "node_modules/ajv-formats/node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", - "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/ajv-formats/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/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-html": { - "version": "0.0.9", - "resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.9.tgz", - "integrity": "sha512-ozbS3LuenHVxNRh/wdnN16QapUHzauqSomAl1jwwJRRsGwFwtj644lIhxfWu0Fy0acCij2+AEgHvjscq3dlVXg==", - "dev": true, - "engines": [ - "node >= 0.8.0" - ], - "license": "Apache-2.0", - "bin": { - "ansi-html": "bin/ansi-html" - } - }, - "node_modules/ansi-html-community": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/ansi-html-community/-/ansi-html-community-0.0.8.tgz", - "integrity": "sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==", - "dev": true, - "engines": [ - "node >= 0.8.0" - ], - "license": "Apache-2.0", - "bin": { - "ansi-html": "bin/ansi-html" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/any-promise": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", - "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", - "dev": true, - "license": "MIT" - }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, - "license": "ISC", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/arg": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", - "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", - "dev": true, - "license": "MIT" - }, - "node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "license": "MIT", - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/aria-query": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", - "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "dequal": "^2.0.3" - } - }, - "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/array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", - "dev": true, - "license": "MIT" - }, - "node_modules/array-includes": { - "version": "3.1.9", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.9.tgz", - "integrity": "sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.4", - "define-properties": "^1.2.1", - "es-abstract": "^1.24.0", - "es-object-atoms": "^1.1.1", - "get-intrinsic": "^1.3.0", - "is-string": "^1.1.1", - "math-intrinsics": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.findlast": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", - "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", - "es-shim-unscopables": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.flat": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz", - "integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.5", - "es-shim-unscopables": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.flatmap": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz", - "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.5", - "es-shim-unscopables": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.tosorted": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz", - "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.3", - "es-errors": "^1.3.0", - "es-shim-unscopables": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "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/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/autoprefixer": { - "version": "10.4.21", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.21.tgz", - "integrity": "sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/autoprefixer" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "browserslist": "^4.24.4", - "caniuse-lite": "^1.0.30001702", - "fraction.js": "^4.3.7", - "normalize-range": "^0.1.2", - "picocolors": "^1.1.1", - "postcss-value-parser": "^4.2.0" - }, - "bin": { - "autoprefixer": "bin/autoprefixer" - }, - "engines": { - "node": "^10 || ^12 || >=14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/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-jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", - "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/transform": "^29.7.0", - "@types/babel__core": "^7.1.14", - "babel-plugin-istanbul": "^6.1.1", - "babel-preset-jest": "^29.6.3", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.8.0" - } - }, - "node_modules/babel-loader": { - "version": "9.2.1", - "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-9.2.1.tgz", - "integrity": "sha512-fqe8naHt46e0yIdkjUZYqddSXfej3AHajX+CSO5X7oy0EmPc6o5Xh+RClNoHjnieWz9AW4kZxW9yyFMhVB1QLA==", - "dev": true, - "license": "MIT", - "dependencies": { - "find-cache-dir": "^4.0.0", - "schema-utils": "^4.0.0" - }, - "engines": { - "node": ">= 14.15.0" - }, - "peerDependencies": { - "@babel/core": "^7.12.0", - "webpack": ">=5" - } - }, - "node_modules/babel-plugin-istanbul": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", - "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-instrument": "^5.0.4", - "test-exclude": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-plugin-jest-hoist": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", - "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/template": "^7.3.3", - "@babel/types": "^7.3.3", - "@types/babel__core": "^7.1.14", - "@types/babel__traverse": "^7.0.6" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/babel-plugin-polyfill-corejs2": { - "version": "0.4.13", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.13.tgz", - "integrity": "sha512-3sX/eOms8kd3q2KZ6DAhKPc0dgm525Gqq5NtWKZ7QYYZEv57OQ54KtblzJzH1lQF/eQxO8KjWGIK9IPUJNus5g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.22.6", - "@babel/helper-define-polyfill-provider": "^0.6.4", - "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.11.1", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.11.1.tgz", - "integrity": "sha512-yGCqvBT4rwMczo28xkH/noxJ6MZ4nJfkVYdoDaC/utLtWrXxv27HVrzAeSbqR8SxDsp46n0YF47EbHoixy6rXQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.6.3", - "core-js-compat": "^3.40.0" - }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" - } - }, - "node_modules/babel-plugin-polyfill-regenerator": { - "version": "0.6.4", - "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.4.tgz", - "integrity": "sha512-7gD3pRadPrbjhjLyxebmx/WrFYcuSjZ0XbdUujQMZ/fcE9oeewk2U/7PCvez84UeuK3oSjmPZ0Ch0dlupQvGzw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-define-polyfill-provider": "^0.6.4" - }, - "peerDependencies": { - "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0" - } - }, - "node_modules/babel-plugin-styled-components": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/babel-plugin-styled-components/-/babel-plugin-styled-components-2.1.4.tgz", - "integrity": "sha512-Xgp9g+A/cG47sUyRwwYxGM4bR/jDRg5N6it/8+HxCnbT5XNKSKDT9xm4oag/osgqjC2It/vH0yXsomOG6k558g==", - "license": "MIT", - "peer": true, - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.22.5", - "@babel/helper-module-imports": "^7.22.5", - "@babel/plugin-syntax-jsx": "^7.22.5", - "lodash": "^4.17.21", - "picomatch": "^2.3.1" - }, - "peerDependencies": { - "styled-components": ">= 2" - } - }, - "node_modules/babel-preset-current-node-syntax": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.1.0.tgz", - "integrity": "sha512-ldYss8SbBlWva1bs28q78Ju5Zq1F+8BrqBZZ0VFhLBvhh6lCpC2o3gDJi/5DRLs9FgYZCnmPYIVFU4lRXCkyUw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-bigint": "^7.8.3", - "@babel/plugin-syntax-class-properties": "^7.12.13", - "@babel/plugin-syntax-class-static-block": "^7.14.5", - "@babel/plugin-syntax-import-attributes": "^7.24.7", - "@babel/plugin-syntax-import-meta": "^7.10.4", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5", - "@babel/plugin-syntax-top-level-await": "^7.14.5" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/babel-preset-jest": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", - "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", - "dev": true, - "license": "MIT", - "dependencies": { - "babel-plugin-jest-hoist": "^29.6.3", - "babel-preset-current-node-syntax": "^1.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/bail": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", - "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/batch": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", - "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==", - "dev": true, - "license": "MIT" - }, - "node_modules/big.js": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", - "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "*" - } - }, - "node_modules/binary-extensions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", - "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/body-parser": { - "version": "1.20.3", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", - "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", - "dev": true, - "license": "MIT", - "dependencies": { - "bytes": "3.1.2", - "content-type": "~1.0.5", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.13.0", - "raw-body": "2.5.2", - "type-is": "~1.6.18", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/body-parser/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/body-parser/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true, - "license": "MIT" - }, - "node_modules/bonjour-service": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.3.0.tgz", - "integrity": "sha512-3YuAUiSkWykd+2Azjgyxei8OWf8thdn8AITIog2M4UICzoqfjlqr64WIjEXZllf/W6vK1goqleSR6brGomxQqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "multicast-dns": "^7.2.5" - } - }, - "node_modules/boolbase": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", - "dev": true, - "license": "ISC" - }, - "node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, - "license": "MIT", - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browserslist": { - "version": "4.25.0", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.0.tgz", - "integrity": "sha512-PJ8gYKeS5e/whHBh8xrwYK+dAvEj7JXtz6uTucnMRB8OiGTsKccFekoRrjajPBHV8oOY+2tI4uxeceSimKwMFA==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "caniuse-lite": "^1.0.30001718", - "electron-to-chromium": "^1.5.160", - "node-releases": "^2.0.19", - "update-browserslist-db": "^1.1.3" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/bs-logger": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", - "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-json-stable-stringify": "2.x" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/bser": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", - "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "node-int64": "^0.4.0" - } - }, - "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/bundle-name": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", - "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "run-applescript": "^7.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/call-bind": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", - "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.0", - "es-define-property": "^1.0.0", - "get-intrinsic": "^1.2.4", - "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/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/camel-case": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", - "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", - "dev": true, - "license": "MIT", - "dependencies": { - "pascal-case": "^3.1.2", - "tslib": "^2.0.3" - } - }, - "node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/camelcase-css": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", - "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/camelize": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.1.tgz", - "integrity": "sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==", - "license": "MIT", - "peer": true, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001724", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001724.tgz", - "integrity": "sha512-WqJo7p0TbHDOythNTqYujmaJTvtYRZrjpP8TCvH6Vb9CYJerJNKamKzIWOM4BkQatWj9H2lYulpdAQNBe7QhNA==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "CC-BY-4.0" - }, - "node_modules/ccount": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", - "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/char-regex": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", - "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/character-entities": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", - "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/character-entities-html4": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", - "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/character-entities-legacy": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", - "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/character-reference-invalid": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz", - "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "dev": true, - "license": "MIT", - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/chokidar/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/chrome-trace-event": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.4.tgz", - "integrity": "sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0" - } - }, - "node_modules/ci-info": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", - "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/cjs-module-lexer": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz", - "integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/clean-css": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.3.tgz", - "integrity": "sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==", - "dev": true, - "license": "MIT", - "dependencies": { - "source-map": "~0.6.0" - }, - "engines": { - "node": ">= 10.0" - } - }, - "node_modules/clean-css/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/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/clone-deep": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", - "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-plain-object": "^2.0.4", - "kind-of": "^6.0.2", - "shallow-clone": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/clsx": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", - "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", - "dev": true, - "license": "MIT", - "engines": { - "iojs": ">= 1.0.0", - "node": ">= 0.12.0" - } - }, - "node_modules/collect-v8-coverage": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", - "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, - "node_modules/colorette": { - "version": "2.0.20", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", - "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", - "dev": true, - "license": "MIT" - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, - "license": "MIT", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/comma-separated-tokens": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", - "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/commander": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", - "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 12" - } - }, - "node_modules/common-path-prefix": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/common-path-prefix/-/common-path-prefix-3.0.0.tgz", - "integrity": "sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w==", - "dev": true, - "license": "ISC" - }, - "node_modules/compressible": { - "version": "2.0.18", - "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", - "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", - "dev": true, - "license": "MIT", - "dependencies": { - "mime-db": ">= 1.43.0 < 2" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/compression": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/compression/-/compression-1.8.0.tgz", - "integrity": "sha512-k6WLKfunuqCYD3t6AsuPGvQWaKwuLLh2/xHNcX4qE+vIfDNXpSqnrhwA7O53R7WVQUnt8dVAIW+YHr7xTgOgGA==", - "dev": true, - "license": "MIT", - "dependencies": { - "bytes": "3.1.2", - "compressible": "~2.0.18", - "debug": "2.6.9", - "negotiator": "~0.6.4", - "on-headers": "~1.0.2", - "safe-buffer": "5.2.1", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/compression/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/compression/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true, - "license": "MIT" - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true, - "license": "MIT" - }, - "node_modules/connect-history-api-fallback": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz", - "integrity": "sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8" - } - }, - "node_modules/content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "5.2.1" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "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==", - "license": "MIT" - }, - "node_modules/cookie": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", - "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/core-js-compat": { - "version": "3.43.0", - "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.43.0.tgz", - "integrity": "sha512-2GML2ZsCc5LR7hZYz4AXmjQw8zuy2T//2QntwdnpuYI7jteT6GVYJL7F6C2C57R7gSYrcqVW3lAALefdbhBLDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "browserslist": "^4.25.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" - } - }, - "node_modules/core-js-pure": { - "version": "3.43.0", - "resolved": "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.43.0.tgz", - "integrity": "sha512-i/AgxU2+A+BbJdMxh3v7/vxi2SbFqxiFmg6VsDwYB4jkucrd1BZNA9a9gphC0fYMG5IBSgQcbQnk865VCLe7xA==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" - } - }, - "node_modules/core-util-is": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/cosmiconfig": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz", - "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==", - "dev": true, - "license": "MIT", - "dependencies": { - "env-paths": "^2.2.1", - "import-fresh": "^3.3.0", - "js-yaml": "^4.1.0", - "parse-json": "^5.2.0" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/d-fischer" - }, - "peerDependencies": { - "typescript": ">=4.9.5" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/cosmiconfig/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true, - "license": "Python-2.0" - }, - "node_modules/cosmiconfig/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/create-jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", - "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-config": "^29.7.0", - "jest-util": "^29.7.0", - "prompts": "^2.0.1" - }, - "bin": { - "create-jest": "bin/create-jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/cross-fetch": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz", - "integrity": "sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==", - "license": "MIT", - "dependencies": { - "node-fetch": "^2.6.12" - } - }, - "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/css-color-keywords": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz", - "integrity": "sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==", - "license": "ISC", - "peer": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/css-loader": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-7.1.2.tgz", - "integrity": "sha512-6WvYYn7l/XEGN8Xu2vWFt9nVzrCn39vKyTEFf/ExEyoksJjjSZV/0/35XPlMbpnr6VGhZIUg5yJrL8tGfes/FA==", - "dev": true, - "license": "MIT", - "dependencies": { - "icss-utils": "^5.1.0", - "postcss": "^8.4.33", - "postcss-modules-extract-imports": "^3.1.0", - "postcss-modules-local-by-default": "^4.0.5", - "postcss-modules-scope": "^3.2.0", - "postcss-modules-values": "^4.0.0", - "postcss-value-parser": "^4.2.0", - "semver": "^7.5.4" - }, - "engines": { - "node": ">= 18.12.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "@rspack/core": "0.x || 1.x", - "webpack": "^5.27.0" - }, - "peerDependenciesMeta": { - "@rspack/core": { - "optional": true - }, - "webpack": { - "optional": true - } - } - }, - "node_modules/css-loader/node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/css-select": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", - "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "boolbase": "^1.0.0", - "css-what": "^6.0.1", - "domhandler": "^4.3.1", - "domutils": "^2.8.0", - "nth-check": "^2.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/css-to-react-native": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.2.0.tgz", - "integrity": "sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==", - "license": "MIT", - "peer": true, - "dependencies": { - "camelize": "^1.0.0", - "css-color-keywords": "^1.0.0", - "postcss-value-parser": "^4.0.2" - } - }, - "node_modules/css-what": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", - "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">= 6" - }, - "funding": { - "url": "https://github.com/sponsors/fb55" - } - }, - "node_modules/css.escape": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", - "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==", - "dev": true, - "license": "MIT" - }, - "node_modules/cssesc": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", - "dev": true, - "license": "MIT", - "bin": { - "cssesc": "bin/cssesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/cssom": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz", - "integrity": "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==", - "dev": true, - "license": "MIT" - }, - "node_modules/cssstyle": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", - "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", - "dev": true, - "license": "MIT", - "dependencies": { - "cssom": "~0.3.6" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/cssstyle/node_modules/cssom": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", - "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", - "dev": true, - "license": "MIT" - }, - "node_modules/csstype": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", - "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "license": "MIT" - }, - "node_modules/d3-array": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", - "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", - "license": "ISC", - "dependencies": { - "internmap": "1 - 2" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-color": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", - "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-ease": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", - "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-format": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz", - "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-interpolate": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", - "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", - "license": "ISC", - "dependencies": { - "d3-color": "1 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-path": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", - "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-scale": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", - "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", - "license": "ISC", - "dependencies": { - "d3-array": "2.10.0 - 3", - "d3-format": "1 - 3", - "d3-interpolate": "1.2.0 - 3", - "d3-time": "2.1.1 - 3", - "d3-time-format": "2 - 4" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-shape": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", - "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", - "license": "ISC", - "dependencies": { - "d3-path": "^3.1.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-time": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", - "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", - "license": "ISC", - "dependencies": { - "d3-array": "2 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-time-format": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", - "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", - "license": "ISC", - "dependencies": { - "d3-time": "1 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-timer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", - "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/data-urls": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-3.0.2.tgz", - "integrity": "sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "abab": "^2.0.6", - "whatwg-mimetype": "^3.0.0", - "whatwg-url": "^11.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "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/date-fns": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz", - "integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/kossnocorp" - } - }, - "node_modules/debug": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", - "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/decimal.js": { - "version": "10.5.0", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.5.0.tgz", - "integrity": "sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw==", - "dev": true, - "license": "MIT" - }, - "node_modules/decimal.js-light": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz", - "integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==", - "license": "MIT" - }, - "node_modules/decode-named-character-reference": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.2.0.tgz", - "integrity": "sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q==", - "license": "MIT", - "dependencies": { - "character-entities": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/dedent": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.6.0.tgz", - "integrity": "sha512-F1Z+5UCFpmQUzJa11agbyPVMbpgT/qA3/SKyJ1jyBgm7dUcUEa8v9JwDkerSQXfakBwFljIxhOJqGkjUwZ9FSA==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "babel-plugin-macros": "^3.1.0" - }, - "peerDependenciesMeta": { - "babel-plugin-macros": { - "optional": true - } - } - }, - "node_modules/deep-equal": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-2.2.3.tgz", - "integrity": "sha512-ZIwpnevOurS8bpT4192sqAowWM76JDKSHYzMLty3BZGSswgq6pBaH3DhCSW5xVAZICZyKdOBPjwww5wfgT/6PA==", - "dev": true, - "license": "MIT", - "dependencies": { - "array-buffer-byte-length": "^1.0.0", - "call-bind": "^1.0.5", - "es-get-iterator": "^1.1.3", - "get-intrinsic": "^1.2.2", - "is-arguments": "^1.1.1", - "is-array-buffer": "^3.0.2", - "is-date-object": "^1.0.5", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.2", - "isarray": "^2.0.5", - "object-is": "^1.1.5", - "object-keys": "^1.1.1", - "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.5.1", - "side-channel": "^1.0.4", - "which-boxed-primitive": "^1.0.2", - "which-collection": "^1.0.1", - "which-typed-array": "^1.1.13" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/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/default-browser": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.2.1.tgz", - "integrity": "sha512-WY/3TUME0x3KPYdRRxEJJvXRHV4PyPoUsxtZa78lwItwRQRHhd2U9xOscaT/YTf8uCXIAjeJOFBVEh/7FtD8Xg==", - "dev": true, - "license": "MIT", - "dependencies": { - "bundle-name": "^4.1.0", - "default-browser-id": "^5.0.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/default-browser-id": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.0.tgz", - "integrity": "sha512-A6p/pu/6fyBcA1TRz/GqWYPViplrftcW2gZC9q79ngNCKAeR/X3gcEdXQHl4KNXV+3wgIJ1CPkJQ3IHM6lcsyA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "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-lazy-prop": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", - "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "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/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/dequal": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", - "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/detect-newline": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", - "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/detect-node": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", - "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", - "dev": true, - "license": "MIT" - }, - "node_modules/devlop": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", - "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", - "license": "MIT", - "dependencies": { - "dequal": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/didyoumean": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", - "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", - "dev": true, - "license": "Apache-2.0" - }, - "node_modules/diff-sequences": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", - "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/dlv": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", - "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", - "dev": true, - "license": "MIT" - }, - "node_modules/dns-packet": { - "version": "5.6.1", - "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz", - "integrity": "sha512-l4gcSouhcgIKRvyy99RNVOgxXiicE+2jZoNmaNmZ6JXiGajBOJAesk1OBlJuM5k2c+eudGdLxDqXuPCKIj6kpw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@leichtgewicht/ip-codec": "^2.0.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/doctrine": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", - "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/dom-accessibility-api": { - "version": "0.5.16", - "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", - "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", - "dev": true, - "license": "MIT" - }, - "node_modules/dom-converter": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/dom-converter/-/dom-converter-0.2.0.tgz", - "integrity": "sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==", - "dev": true, - "license": "MIT", - "dependencies": { - "utila": "~0.4" - } - }, - "node_modules/dom-helpers": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", - "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.8.7", - "csstype": "^3.0.2" - } - }, - "node_modules/dom-serializer": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", - "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", - "dev": true, - "license": "MIT", - "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^4.2.0", - "entities": "^2.0.0" - }, - "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" - } - }, - "node_modules/dom-serializer/node_modules/entities": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", - "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", - "dev": true, - "license": "BSD-2-Clause", - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/domelementtype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", - "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], - "license": "BSD-2-Clause" - }, - "node_modules/domexception": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz", - "integrity": "sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==", - "deprecated": "Use your platform's native DOMException instead", - "dev": true, - "license": "MIT", - "dependencies": { - "webidl-conversions": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/domhandler": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", - "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "domelementtype": "^2.2.0" - }, - "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" - } - }, - "node_modules/domutils": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", - "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "dom-serializer": "^1.0.1", - "domelementtype": "^2.2.0", - "domhandler": "^4.2.0" - }, - "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" - } - }, - "node_modules/dot-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", - "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", - "dev": true, - "license": "MIT", - "dependencies": { - "no-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "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/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true, - "license": "MIT" - }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", - "dev": true, - "license": "MIT" - }, - "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.171", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.171.tgz", - "integrity": "sha512-scWpzXEJEMrGJa4Y6m/tVotb0WuvNmasv3wWVzUAeCgKU0ToFOhUW6Z+xWnRQANMYGxN4ngJXIThgBJOqzVPCQ==", - "license": "ISC" - }, - "node_modules/emittery": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", - "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sindresorhus/emittery?sponsor=1" - } - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" - }, - "node_modules/emojis-list": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", - "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/encodeurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", - "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/enhanced-resolve": { - "version": "5.18.1", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.1.tgz", - "integrity": "sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==", - "dev": true, - "license": "MIT", - "dependencies": { - "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/entities": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", - "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/env-paths": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", - "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/envinfo": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.14.0.tgz", - "integrity": "sha512-CO40UI41xDQzhLB1hWyqUKgFhs250pNcGbyGKe1l/e4FSaI/+YE4IMG76GDt0In67WLPACIITC+sOi08x4wIvg==", - "dev": true, - "license": "MIT", - "bin": { - "envinfo": "dist/cli.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, - "node_modules/error-stack-parser": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.1.4.tgz", - "integrity": "sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "stackframe": "^1.3.4" - } - }, - "node_modules/es-abstract": { - "version": "1.24.0", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.0.tgz", - "integrity": "sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg==", - "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-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-get-iterator": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/es-get-iterator/-/es-get-iterator-1.1.3.tgz", - "integrity": "sha512-sPZmqHBe6JIiTfN5q2pEi//TwxmAFHwj/XEuYjTuse78i8KxaqMTTzxPoFKuzRpDpTJ+0NAbpfenkmH2rePtuw==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.3", - "has-symbols": "^1.0.3", - "is-arguments": "^1.1.1", - "is-map": "^2.0.2", - "is-set": "^2.0.2", - "is-string": "^1.0.7", - "isarray": "^2.0.5", - "stop-iteration-iterator": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/es-iterator-helpers": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.2.1.tgz", - "integrity": "sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w==", - "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-set-tostringtag": "^2.0.3", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.6", - "globalthis": "^1.0.4", - "gopd": "^1.2.0", - "has-property-descriptors": "^1.0.2", - "has-proto": "^1.2.0", - "has-symbols": "^1.1.0", - "internal-slot": "^1.1.0", - "iterator.prototype": "^1.1.4", - "safe-array-concat": "^1.1.3" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-module-lexer": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", - "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", - "dev": true, - "license": "MIT" - }, - "node_modules/es-object-atoms": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "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-shim-unscopables": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz", - "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==", - "dev": true, - "license": "MIT", - "dependencies": { - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-to-primitive": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", - "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-callable": "^1.2.7", - "is-date-object": "^1.0.5", - "is-symbol": "^1.0.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", - "dev": true, - "license": "MIT" - }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/escodegen": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", - "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "esprima": "^4.0.1", - "estraverse": "^5.2.0", - "esutils": "^2.0.2" - }, - "bin": { - "escodegen": "bin/escodegen.js", - "esgenerate": "bin/esgenerate.js" - }, - "engines": { - "node": ">=6.0" - }, - "optionalDependencies": { - "source-map": "~0.6.1" - } - }, - "node_modules/escodegen/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", - "optional": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/eslint": { - "version": "9.29.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.29.0.tgz", - "integrity": "sha512-GsGizj2Y1rCWDu6XoEekL3RLilp0voSePurjZIkxL3wlm5o5EC9VpgaP7lrCvjnkuLvzFBQWB3vWB3K5KQTveQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.12.1", - "@eslint/config-array": "^0.20.1", - "@eslint/config-helpers": "^0.2.1", - "@eslint/core": "^0.14.0", - "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.29.0", - "@eslint/plugin-kit": "^0.3.1", - "@humanfs/node": "^0.16.6", - "@humanwhocodes/module-importer": "^1.0.1", - "@humanwhocodes/retry": "^0.4.2", - "@types/estree": "^1.0.6", - "@types/json-schema": "^7.0.15", - "ajv": "^6.12.4", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.6", - "debug": "^4.3.2", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^8.4.0", - "eslint-visitor-keys": "^4.2.1", - "espree": "^10.4.0", - "esquery": "^1.5.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^8.0.0", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://eslint.org/donate" - }, - "peerDependencies": { - "jiti": "*" - }, - "peerDependenciesMeta": { - "jiti": { - "optional": true - } - } - }, - "node_modules/eslint-plugin-react": { - "version": "7.37.5", - "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz", - "integrity": "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==", - "dev": true, - "license": "MIT", - "dependencies": { - "array-includes": "^3.1.8", - "array.prototype.findlast": "^1.2.5", - "array.prototype.flatmap": "^1.3.3", - "array.prototype.tosorted": "^1.1.4", - "doctrine": "^2.1.0", - "es-iterator-helpers": "^1.2.1", - "estraverse": "^5.3.0", - "hasown": "^2.0.2", - "jsx-ast-utils": "^2.4.1 || ^3.0.0", - "minimatch": "^3.1.2", - "object.entries": "^1.1.9", - "object.fromentries": "^2.0.8", - "object.values": "^1.2.1", - "prop-types": "^15.8.1", - "resolve": "^2.0.0-next.5", - "semver": "^6.3.1", - "string.prototype.matchall": "^4.0.12", - "string.prototype.repeat": "^1.0.0" - }, - "engines": { - "node": ">=4" - }, - "peerDependencies": { - "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" - } - }, - "node_modules/eslint-plugin-react/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/eslint-plugin-react/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/eslint-plugin-react/node_modules/resolve": { - "version": "2.0.0-next.5", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz", - "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-core-module": "^2.13.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/eslint-scope": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", - "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/eslint/node_modules/eslint-visitor-keys": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", - "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint/node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint/node_modules/ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/eslint/node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/eslint/node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/espree": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", - "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "acorn": "^8.15.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^4.2.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/espree/node_modules/eslint-visitor-keys": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", - "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, - "license": "BSD-2-Clause", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/esquery": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", - "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estree-util-is-identifier-name": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz", - "integrity": "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "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/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/eventemitter3": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", - "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", - "license": "MIT" - }, - "node_modules/events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.x" - } - }, - "node_modules/execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dev": true, - "license": "MIT", - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/expect": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", - "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/expect-utils": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/express": { - "version": "4.21.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", - "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", - "dev": true, - "license": "MIT", - "dependencies": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "1.20.3", - "content-disposition": "0.5.4", - "content-type": "~1.0.4", - "cookie": "0.7.1", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "2.0.0", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "1.3.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "merge-descriptors": "1.0.3", - "methods": "~1.1.2", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.12", - "proxy-addr": "~2.0.7", - "qs": "6.13.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "0.19.0", - "serve-static": "1.16.2", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.10.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/express/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/express/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true, - "license": "MIT" - }, - "node_modules/extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "license": "MIT" - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-equals": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.2.2.tgz", - "integrity": "sha512-V7/RktU11J3I36Nwq2JnZEM7tNm17eBJz+u25qdxBZeCKiX6BkVSZQjwWIr+IobgnZy+ag73tTZgZi7tr0LrBw==", - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/fast-glob": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", - "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.8" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fast-glob/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-uri": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz", - "integrity": "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "BSD-3-Clause" - }, - "node_modules/fastest-levenshtein": { - "version": "1.0.16", - "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", - "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4.9.1" - } - }, - "node_modules/fastq": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", - "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/faye-websocket": { - "version": "0.11.4", - "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", - "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "websocket-driver": ">=0.5.1" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/fb-watchman": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", - "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "bser": "2.1.1" - } - }, - "node_modules/file-entry-cache": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", - "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "flat-cache": "^4.0.0" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/filelist": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", - "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "minimatch": "^5.0.1" - } - }, - "node_modules/filelist/node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, - "license": "MIT", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/finalhandler": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", - "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "2.6.9", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "statuses": "2.0.1", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/finalhandler/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/finalhandler/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true, - "license": "MIT" - }, - "node_modules/find-cache-dir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-4.0.0.tgz", - "integrity": "sha512-9ZonPT4ZAK4a+1pUPVPZJapbi7O5qbbJPdYw/NOQWZZbVLdDTYM3A4R9z/DpAM08IDaFGsvPgiGZ82WEwUDWjg==", - "dev": true, - "license": "MIT", - "dependencies": { - "common-path-prefix": "^3.0.0", - "pkg-dir": "^7.0.0" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/flat": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", - "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", - "dev": true, - "license": "BSD-3-Clause", - "bin": { - "flat": "cli.js" - } - }, - "node_modules/flat-cache": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", - "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", - "dev": true, - "license": "MIT", - "dependencies": { - "flatted": "^3.2.9", - "keyv": "^4.5.4" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/flatted": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", - "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", - "dev": true, - "license": "ISC" - }, - "node_modules/follow-redirects": { - "version": "1.15.9", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", - "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "license": "MIT", - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, - "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/foreground-child/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/form-data": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.3.tgz", - "integrity": "sha512-qsITQPfmvMOSAdeyZ+12I1c+CKSstAFAwu+97zrnWAbIr5u8wfsExUzCesVLC8NgHuRUqNN4Zy6UPWUTRGslcA==", - "dev": true, - "license": "MIT", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "es-set-tostringtag": "^2.1.0", - "hasown": "^2.0.2", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fraction.js": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", - "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", - "dev": true, - "license": "MIT", - "engines": { - "node": "*" - }, - "funding": { - "type": "patreon", - "url": "https://github.com/sponsors/rawify" - } - }, - "node_modules/fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true, - "license": "ISC" - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "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.1.8", - "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz", - "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.3", - "define-properties": "^1.2.1", - "functions-have-names": "^1.2.3", - "hasown": "^2.0.2", - "is-callable": "^1.2.7" - }, - "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/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==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true, - "license": "ISC", - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "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-package-type": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.0.0" - } - }, - "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-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "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": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "dev": true, - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/glob-to-regexp": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", - "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", - "dev": true, - "license": "BSD-2-Clause" - }, - "node_modules/glob/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/glob/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/globals": { - "version": "15.15.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz", - "integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "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/graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true, - "license": "MIT" - }, - "node_modules/handle-thing": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", - "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", - "dev": true, - "license": "MIT" - }, - "node_modules/harmony-reflect": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/harmony-reflect/-/harmony-reflect-1.6.2.tgz", - "integrity": "sha512-HIp/n38R9kQjDEziXyDTuW3vvoxxyxjxFzXLrBr18uB47GnSt+G9D29fqrpM5ZkspMcPICud3XsBJQ4Y2URg8g==", - "dev": true, - "license": "(Apache-2.0 OR MPL-1.1)" - }, - "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-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/has-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.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/hast-util-is-element": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/hast-util-is-element/-/hast-util-is-element-3.0.0.tgz", - "integrity": "sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-to-jsx-runtime": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.6.tgz", - "integrity": "sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "@types/hast": "^3.0.0", - "@types/unist": "^3.0.0", - "comma-separated-tokens": "^2.0.0", - "devlop": "^1.0.0", - "estree-util-is-identifier-name": "^3.0.0", - "hast-util-whitespace": "^3.0.0", - "mdast-util-mdx-expression": "^2.0.0", - "mdast-util-mdx-jsx": "^3.0.0", - "mdast-util-mdxjs-esm": "^2.0.0", - "property-information": "^7.0.0", - "space-separated-tokens": "^2.0.0", - "style-to-js": "^1.0.0", - "unist-util-position": "^5.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-to-text": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/hast-util-to-text/-/hast-util-to-text-4.0.2.tgz", - "integrity": "sha512-KK6y/BN8lbaq654j7JgBydev7wuNMcID54lkRav1P0CaE1e47P72AWWPiGKXTJU271ooYzcvTAn/Zt0REnvc7A==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/unist": "^3.0.0", - "hast-util-is-element": "^3.0.0", - "unist-util-find-after": "^5.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-whitespace": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", - "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "dev": true, - "license": "MIT", - "bin": { - "he": "bin/he" - } - }, - "node_modules/highlight.js": { - "version": "11.11.1", - "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.11.1.tgz", - "integrity": "sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/hoist-non-react-statics": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", - "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", - "license": "BSD-3-Clause", - "peer": true, - "dependencies": { - "react-is": "^16.7.0" - } - }, - "node_modules/hoist-non-react-statics/node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "license": "MIT", - "peer": true - }, - "node_modules/hpack.js": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", - "integrity": "sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "inherits": "^2.0.1", - "obuf": "^1.0.0", - "readable-stream": "^2.0.1", - "wbuf": "^1.1.0" - } - }, - "node_modules/hpack.js/node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/hpack.js/node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "dev": true, - "license": "MIT", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/hpack.js/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true, - "license": "MIT" - }, - "node_modules/hpack.js/node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/html-encoding-sniffer": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", - "integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==", - "dev": true, - "license": "MIT", - "dependencies": { - "whatwg-encoding": "^2.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/html-entities": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.6.0.tgz", - "integrity": "sha512-kig+rMn/QOVRvr7c86gQ8lWXq+Hkv6CbAH1hLu+RG338StTpE8Z0b44SDVaqVu7HGKf27frdmUYEs9hTUX/cLQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/mdevils" - }, - { - "type": "patreon", - "url": "https://patreon.com/mdevils" - } - ], - "license": "MIT" - }, - "node_modules/html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true, - "license": "MIT" - }, - "node_modules/html-minifier-terser": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", - "integrity": "sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==", - "dev": true, - "license": "MIT", - "dependencies": { - "camel-case": "^4.1.2", - "clean-css": "^5.2.2", - "commander": "^8.3.0", - "he": "^1.2.0", - "param-case": "^3.0.4", - "relateurl": "^0.2.7", - "terser": "^5.10.0" - }, - "bin": { - "html-minifier-terser": "cli.js" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/html-parse-stringify": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz", - "integrity": "sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==", - "license": "MIT", - "dependencies": { - "void-elements": "3.1.0" - } - }, - "node_modules/html-url-attributes": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/html-url-attributes/-/html-url-attributes-3.0.1.tgz", - "integrity": "sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/html-webpack-plugin": { - "version": "5.6.3", - "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.6.3.tgz", - "integrity": "sha512-QSf1yjtSAsmf7rYBV7XX86uua4W/vkhIt0xNXKbsi2foEeW7vjJQz4bhnpL3xH+l1ryl1680uNv968Z+X6jSYg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/html-minifier-terser": "^6.0.0", - "html-minifier-terser": "^6.0.2", - "lodash": "^4.17.21", - "pretty-error": "^4.0.0", - "tapable": "^2.0.0" - }, - "engines": { - "node": ">=10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/html-webpack-plugin" - }, - "peerDependencies": { - "@rspack/core": "0.x || 1.x", - "webpack": "^5.20.0" - }, - "peerDependenciesMeta": { - "@rspack/core": { - "optional": true - }, - "webpack": { - "optional": true - } - } - }, - "node_modules/htmlparser2": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz", - "integrity": "sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==", - "dev": true, - "funding": [ - "https://github.com/fb55/htmlparser2?sponsor=1", - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], - "license": "MIT", - "dependencies": { - "domelementtype": "^2.0.1", - "domhandler": "^4.0.0", - "domutils": "^2.5.2", - "entities": "^2.0.0" - } - }, - "node_modules/htmlparser2/node_modules/entities": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", - "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", - "dev": true, - "license": "BSD-2-Clause", - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/http-deceiver": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", - "integrity": "sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==", - "dev": true, - "license": "MIT" - }, - "node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/http-parser-js": { - "version": "0.5.10", - "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.10.tgz", - "integrity": "sha512-Pysuw9XpUq5dVc/2SMHpuTY01RFl8fttgcyunjL7eEMhGM3cI4eOmiCycJDVCo/7O7ClfQD3SaI6ftDzqOXYMA==", - "dev": true, - "license": "MIT" - }, - "node_modules/http-proxy": { - "version": "1.18.1", - "resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.18.1.tgz", - "integrity": "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "eventemitter3": "^4.0.0", - "follow-redirects": "^1.0.0", - "requires-port": "^1.0.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/http-proxy-agent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", - "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@tootallnate/once": "2", - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/http-proxy-middleware": { - "version": "2.0.9", - "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.9.tgz", - "integrity": "sha512-c1IyJYLYppU574+YI7R4QyX2ystMtVXZwIdzazUIPIJsHuWNd+mho2j+bKoHftndicGj9yh+xjd+l0yj7VeT1Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/http-proxy": "^1.17.8", - "http-proxy": "^1.18.1", - "is-glob": "^4.0.1", - "is-plain-obj": "^3.0.0", - "micromatch": "^4.0.2" - }, - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "@types/express": "^4.17.13" - }, - "peerDependenciesMeta": { - "@types/express": { - "optional": true - } - } - }, - "node_modules/http-proxy-middleware/node_modules/is-plain-obj": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", - "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", - "dev": true, - "license": "MIT", - "dependencies": { - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=10.17.0" - } - }, - "node_modules/hyperdyperid": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/hyperdyperid/-/hyperdyperid-1.2.0.tgz", - "integrity": "sha512-Y93lCzHYgGWdrJ66yIktxiaGULYc6oGiABxhcO5AufBeOyoIdZF7bIfLaOrbM0iGIOXQQgxxRrFEnb+Y6w1n4A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10.18" - } - }, - "node_modules/i18next": { - "version": "24.2.3", - "resolved": "https://registry.npmjs.org/i18next/-/i18next-24.2.3.tgz", - "integrity": "sha512-lfbf80OzkocvX7nmZtu7nSTNbrTYR52sLWxPtlXX1zAhVw8WEnFk4puUkCR4B1dNQwbSpEHHHemcZu//7EcB7A==", - "funding": [ - { - "type": "individual", - "url": "https://locize.com" - }, - { - "type": "individual", - "url": "https://locize.com/i18next.html" - }, - { - "type": "individual", - "url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project" - } - ], - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.26.10" - }, - "peerDependencies": { - "typescript": "^5" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/i18next-browser-languagedetector": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/i18next-browser-languagedetector/-/i18next-browser-languagedetector-8.2.0.tgz", - "integrity": "sha512-P+3zEKLnOF0qmiesW383vsLdtQVyKtCNA9cjSoKCppTKPQVfKd2W8hbVo5ZhNJKDqeM7BOcvNoKJOjpHh4Js9g==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.23.2" - } - }, - "node_modules/i18next-http-backend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/i18next-http-backend/-/i18next-http-backend-3.0.2.tgz", - "integrity": "sha512-PdlvPnvIp4E1sYi46Ik4tBYh/v/NbYfFFgTjkwFl0is8A18s7/bx9aXqsrOax9WUbeNS6mD2oix7Z0yGGf6m5g==", - "license": "MIT", - "dependencies": { - "cross-fetch": "4.0.0" - } - }, - "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dev": true, - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/icss-utils": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", - "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/identity-obj-proxy": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/identity-obj-proxy/-/identity-obj-proxy-3.0.0.tgz", - "integrity": "sha512-00n6YnVHKrinT9t0d9+5yZC6UBNJANpYEQvL2LlX6Ab9lnmxzIRcEmTPuyGScvl1+jKuCICX1Z0Ab1pPKKdikA==", - "dev": true, - "license": "MIT", - "dependencies": { - "harmony-reflect": "^1.4.6" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/ignore": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", - "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/import-fresh": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", - "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/import-fresh/node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/import-local": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", - "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", - "dev": true, - "license": "MIT", - "dependencies": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - }, - "bin": { - "import-local-fixture": "fixtures/cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/import-local/node_modules/pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "find-up": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", - "dev": true, - "license": "ISC", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/inline-style-parser": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.4.tgz", - "integrity": "sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q==", - "license": "MIT" - }, - "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/internmap": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", - "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/interpret": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-3.1.1.tgz", - "integrity": "sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/ipaddr.js": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.2.0.tgz", - "integrity": "sha512-Ag3wB2o37wslZS19hZqorUnrnzSkpOVy+IiiDEiTqNubEYpYuHWIf6K4psgN2ZWKExS4xhVCrRVfb/wfW8fWJA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 10" - } - }, - "node_modules/is-alphabetical": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz", - "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/is-alphanumerical": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz", - "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==", - "license": "MIT", - "dependencies": { - "is-alphabetical": "^2.0.0", - "is-decimal": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/is-arguments": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.2.0.tgz", - "integrity": "sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==", - "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-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-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true, - "license": "MIT" - }, - "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-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "license": "MIT", - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "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.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", - "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", - "dev": true, - "license": "MIT", - "dependencies": { - "hasown": "^2.0.2" - }, - "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-decimal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz", - "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/is-docker": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", - "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", - "dev": true, - "license": "MIT", - "bin": { - "is-docker": "cli.js" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "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-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-generator-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", - "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/is-generator-function": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.0.tgz", - "integrity": "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "get-proto": "^1.0.0", - "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-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-hexadecimal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz", - "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/is-inside-container": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", - "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-docker": "^3.0.0" - }, - "bin": { - "is-inside-container": "cli.js" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-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-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-network-error": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-network-error/-/is-network-error-1.1.0.tgz", - "integrity": "sha512-tUdRRAnhT+OtCZR/LxZelH/C7QtjtFrTu5tXCA8pl55eTUElUHT+GPYV8MBMBvea/j+NxQqVt3LbWMRir7Gx9g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, - "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-plain-obj": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", - "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "dev": true, - "license": "MIT", - "dependencies": { - "isobject": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-potential-custom-element-name": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", - "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/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-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/is-wsl": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.0.tgz", - "integrity": "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-inside-container": "^1.0.0" - }, - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "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/isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/istanbul-lib-coverage": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", - "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-instrument": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", - "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-report": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", - "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^4.0.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-source-maps": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", - "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-source-maps/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/istanbul-reports": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", - "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/iterator.prototype": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.5.tgz", - "integrity": "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==", - "dev": true, - "license": "MIT", - "dependencies": { - "define-data-property": "^1.1.4", - "es-object-atoms": "^1.0.0", - "get-intrinsic": "^1.2.6", - "get-proto": "^1.0.0", - "has-symbols": "^1.1.0", - "set-function-name": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/jackspeak": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", - "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" - } - }, - "node_modules/jake": { - "version": "10.9.2", - "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.2.tgz", - "integrity": "sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "async": "^3.2.3", - "chalk": "^4.0.2", - "filelist": "^1.0.4", - "minimatch": "^3.1.2" - }, - "bin": { - "jake": "bin/cli.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/jake/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/jake/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", - "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/core": "^29.7.0", - "@jest/types": "^29.6.3", - "import-local": "^3.0.2", - "jest-cli": "^29.7.0" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/jest-changed-files": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", - "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", - "dev": true, - "license": "MIT", - "dependencies": { - "execa": "^5.0.0", - "jest-util": "^29.7.0", - "p-limit": "^3.1.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-circus": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", - "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/expect": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "dedent": "^1.0.0", - "is-generator-fn": "^2.0.0", - "jest-each": "^29.7.0", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "p-limit": "^3.1.0", - "pretty-format": "^29.7.0", - "pure-rand": "^6.0.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-circus/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-circus/node_modules/pretty-format": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", - "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-circus/node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "dev": true, - "license": "MIT" - }, - "node_modules/jest-cli": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", - "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/core": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "create-jest": "^29.7.0", - "exit": "^0.1.2", - "import-local": "^3.0.2", - "jest-config": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "yargs": "^17.3.1" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/jest-config": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", - "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.11.6", - "@jest/test-sequencer": "^29.7.0", - "@jest/types": "^29.6.3", - "babel-jest": "^29.7.0", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "deepmerge": "^4.2.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-circus": "^29.7.0", - "jest-environment-node": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-runner": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "micromatch": "^4.0.4", - "parse-json": "^5.2.0", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@types/node": "*", - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "ts-node": { - "optional": true - } - } - }, - "node_modules/jest-config/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-config/node_modules/pretty-format": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", - "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-config/node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "dev": true, - "license": "MIT" - }, - "node_modules/jest-diff": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", - "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^4.0.0", - "diff-sequences": "^29.6.3", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-diff/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-diff/node_modules/pretty-format": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", - "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-diff/node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "dev": true, - "license": "MIT" - }, - "node_modules/jest-docblock": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", - "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", - "dev": true, - "license": "MIT", - "dependencies": { - "detect-newline": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-each": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", - "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "jest-get-type": "^29.6.3", - "jest-util": "^29.7.0", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-each/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-each/node_modules/pretty-format": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", - "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-each/node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "dev": true, - "license": "MIT" - }, - "node_modules/jest-environment-jsdom": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-29.7.0.tgz", - "integrity": "sha512-k9iQbsf9OyOfdzWH8HDmrRT0gSIcX+FLNW7IQq94tFX0gynPwqDTW0Ho6iMVNjGz/nb+l/vW3dWM2bbLLpkbXA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/jsdom": "^20.0.0", - "@types/node": "*", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0", - "jsdom": "^20.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "canvas": "^2.5.0" - }, - "peerDependenciesMeta": { - "canvas": { - "optional": true - } - } - }, - "node_modules/jest-environment-node": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", - "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-get-type": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", - "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-haste-map": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", - "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "@types/graceful-fs": "^4.1.3", - "@types/node": "*", - "anymatch": "^3.0.3", - "fb-watchman": "^2.0.0", - "graceful-fs": "^4.2.9", - "jest-regex-util": "^29.6.3", - "jest-util": "^29.7.0", - "jest-worker": "^29.7.0", - "micromatch": "^4.0.4", - "walker": "^1.0.8" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "optionalDependencies": { - "fsevents": "^2.3.2" - } - }, - "node_modules/jest-leak-detector": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", - "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", - "dev": true, - "license": "MIT", - "dependencies": { - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-leak-detector/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-leak-detector/node_modules/pretty-format": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", - "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-leak-detector/node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "dev": true, - "license": "MIT" - }, - "node_modules/jest-matcher-utils": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", - "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^4.0.0", - "jest-diff": "^29.7.0", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-matcher-utils/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-matcher-utils/node_modules/pretty-format": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", - "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-matcher-utils/node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "dev": true, - "license": "MIT" - }, - "node_modules/jest-message-util": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", - "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^29.6.3", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-message-util/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-message-util/node_modules/pretty-format": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", - "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-message-util/node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "dev": true, - "license": "MIT" - }, - "node_modules/jest-mock": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", - "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-pnp-resolver": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", - "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - }, - "peerDependencies": { - "jest-resolve": "*" - }, - "peerDependenciesMeta": { - "jest-resolve": { - "optional": true - } - } - }, - "node_modules/jest-regex-util": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", - "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-resolve": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", - "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-pnp-resolver": "^1.2.2", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "resolve": "^1.20.0", - "resolve.exports": "^2.0.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-resolve-dependencies": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", - "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", - "dev": true, - "license": "MIT", - "dependencies": { - "jest-regex-util": "^29.6.3", - "jest-snapshot": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-runner": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", - "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/console": "^29.7.0", - "@jest/environment": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "graceful-fs": "^4.2.9", - "jest-docblock": "^29.7.0", - "jest-environment-node": "^29.7.0", - "jest-haste-map": "^29.7.0", - "jest-leak-detector": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-resolve": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-util": "^29.7.0", - "jest-watcher": "^29.7.0", - "jest-worker": "^29.7.0", - "p-limit": "^3.1.0", - "source-map-support": "0.5.13" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-runtime": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", - "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/fake-timers": "^29.7.0", - "@jest/globals": "^29.7.0", - "@jest/source-map": "^29.6.3", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "cjs-module-lexer": "^1.0.0", - "collect-v8-coverage": "^1.0.0", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-mock": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "slash": "^3.0.0", - "strip-bom": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-snapshot": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", - "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.11.6", - "@babel/generator": "^7.7.2", - "@babel/plugin-syntax-jsx": "^7.7.2", - "@babel/plugin-syntax-typescript": "^7.7.2", - "@babel/types": "^7.3.3", - "@jest/expect-utils": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "babel-preset-current-node-syntax": "^1.0.0", - "chalk": "^4.0.0", - "expect": "^29.7.0", - "graceful-fs": "^4.2.9", - "jest-diff": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "natural-compare": "^1.4.0", - "pretty-format": "^29.7.0", - "semver": "^7.5.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-snapshot/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-snapshot/node_modules/pretty-format": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", - "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-snapshot/node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "dev": true, - "license": "MIT" - }, - "node_modules/jest-snapshot/node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/jest-util": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", - "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-validate": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", - "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "camelcase": "^6.2.0", - "chalk": "^4.0.0", - "jest-get-type": "^29.6.3", - "leven": "^3.1.0", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-validate/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-validate/node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/jest-validate/node_modules/pretty-format": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", - "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-validate/node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "dev": true, - "license": "MIT" - }, - "node_modules/jest-watcher": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", - "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "jest-util": "^29.7.0", - "string-length": "^4.0.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-worker": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", - "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*", - "jest-util": "^29.7.0", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-worker/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/jiti": { - "version": "1.21.7", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", - "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", - "dev": true, - "license": "MIT", - "bin": { - "jiti": "bin/jiti.js" - } - }, - "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/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, - "license": "MIT", - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsdom": { - "version": "20.0.3", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-20.0.3.tgz", - "integrity": "sha512-SYhBvTh89tTfCD/CRdSOm13mOBa42iTaTyfyEWBdKcGdPxPtLFBXuHR8XHb33YNYaP+lLbmSvBTsnoesCNJEsQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "abab": "^2.0.6", - "acorn": "^8.8.1", - "acorn-globals": "^7.0.0", - "cssom": "^0.5.0", - "cssstyle": "^2.3.0", - "data-urls": "^3.0.2", - "decimal.js": "^10.4.2", - "domexception": "^4.0.0", - "escodegen": "^2.0.0", - "form-data": "^4.0.0", - "html-encoding-sniffer": "^3.0.0", - "http-proxy-agent": "^5.0.0", - "https-proxy-agent": "^5.0.1", - "is-potential-custom-element-name": "^1.0.1", - "nwsapi": "^2.2.2", - "parse5": "^7.1.1", - "saxes": "^6.0.0", - "symbol-tree": "^3.2.4", - "tough-cookie": "^4.1.2", - "w3c-xmlserializer": "^4.0.0", - "webidl-conversions": "^7.0.0", - "whatwg-encoding": "^2.0.0", - "whatwg-mimetype": "^3.0.0", - "whatwg-url": "^11.0.0", - "ws": "^8.11.0", - "xml-name-validator": "^4.0.0" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "canvas": "^2.5.0" - }, - "peerDependenciesMeta": { - "canvas": { - "optional": true - } - } - }, - "node_modules/jsesc": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", - "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "license": "MIT", - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/jsx-ast-utils": { - "version": "3.3.5", - "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", - "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "array-includes": "^3.1.6", - "array.prototype.flat": "^1.3.1", - "object.assign": "^4.1.4", - "object.values": "^1.1.6" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "dev": true, - "license": "MIT", - "dependencies": { - "json-buffer": "3.0.1" - } - }, - "node_modules/kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/kleur": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/launch-editor": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/launch-editor/-/launch-editor-2.10.0.tgz", - "integrity": "sha512-D7dBRJo/qcGX9xlvt/6wUYzQxjh5G1RvZPgPv8vi4KRU99DVQL/oW7tnVOCCTm2HGeo3C5HvGE5Yrh6UBoZ0vA==", - "dev": true, - "license": "MIT", - "dependencies": { - "picocolors": "^1.0.0", - "shell-quote": "^1.8.1" - } - }, - "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/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/lilconfig": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", - "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/antonk52" - } - }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true, - "license": "MIT" - }, - "node_modules/loader-runner": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", - "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.11.5" - } - }, - "node_modules/loader-utils": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", - "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", - "dev": true, - "license": "MIT", - "dependencies": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^2.1.2" - }, - "engines": { - "node": ">=8.9.0" - } - }, - "node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "license": "MIT" - }, - "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.memoize": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/longest-streak": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", - "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/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/lower-case": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", - "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", - "dev": true, - "license": "MIT", - "dependencies": { - "tslib": "^2.0.3" - } - }, - "node_modules/lowlight": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/lowlight/-/lowlight-3.3.0.tgz", - "integrity": "sha512-0JNhgFoPvP6U6lE/UdVsSq99tn6DhjjpAj5MxG49ewd2mOBVtwWYIT8ClyABhq198aXXODMU6Ox8DrGy/CpTZQ==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "devlop": "^1.0.0", - "highlight.js": "~11.11.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "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==", - "license": "ISC", - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/lz-string": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", - "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", - "dev": true, - "license": "MIT", - "bin": { - "lz-string": "bin/bin.js" - } - }, - "node_modules/make-dir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", - "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", - "dev": true, - "license": "MIT", - "dependencies": { - "semver": "^7.5.3" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/make-dir/node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true, - "license": "ISC" - }, - "node_modules/makeerror": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", - "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "tmpl": "1.0.5" - } - }, - "node_modules/markdown-table": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz", - "integrity": "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/math-intrinsics": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/mdast-util-find-and-replace": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.2.tgz", - "integrity": "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "escape-string-regexp": "^5.0.0", - "unist-util-is": "^6.0.0", - "unist-util-visit-parents": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-find-and-replace/node_modules/escape-string-regexp": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", - "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/mdast-util-from-markdown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.2.tgz", - "integrity": "sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "@types/unist": "^3.0.0", - "decode-named-character-reference": "^1.0.0", - "devlop": "^1.0.0", - "mdast-util-to-string": "^4.0.0", - "micromark": "^4.0.0", - "micromark-util-decode-numeric-character-reference": "^2.0.0", - "micromark-util-decode-string": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0", - "unist-util-stringify-position": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-3.1.0.tgz", - "integrity": "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==", - "license": "MIT", - "dependencies": { - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-gfm-autolink-literal": "^2.0.0", - "mdast-util-gfm-footnote": "^2.0.0", - "mdast-util-gfm-strikethrough": "^2.0.0", - "mdast-util-gfm-table": "^2.0.0", - "mdast-util-gfm-task-list-item": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-autolink-literal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.1.tgz", - "integrity": "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "ccount": "^2.0.0", - "devlop": "^1.0.0", - "mdast-util-find-and-replace": "^3.0.0", - "micromark-util-character": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-footnote": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.1.0.tgz", - "integrity": "sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "devlop": "^1.1.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-strikethrough": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz", - "integrity": "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-table": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz", - "integrity": "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "devlop": "^1.0.0", - "markdown-table": "^3.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-task-list-item": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz", - "integrity": "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "devlop": "^1.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-mdx-expression": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz", - "integrity": "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==", - "license": "MIT", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "devlop": "^1.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-mdx-jsx": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.2.0.tgz", - "integrity": "sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==", - "license": "MIT", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "@types/unist": "^3.0.0", - "ccount": "^2.0.0", - "devlop": "^1.1.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0", - "parse-entities": "^4.0.0", - "stringify-entities": "^4.0.0", - "unist-util-stringify-position": "^4.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-mdxjs-esm": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz", - "integrity": "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==", - "license": "MIT", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "devlop": "^1.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-phrasing": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz", - "integrity": "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "unist-util-is": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-to-hast": { - "version": "13.2.0", - "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.0.tgz", - "integrity": "sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "@ungap/structured-clone": "^1.0.0", - "devlop": "^1.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "trim-lines": "^3.0.0", - "unist-util-position": "^5.0.0", - "unist-util-visit": "^5.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-to-markdown": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.2.tgz", - "integrity": "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "@types/unist": "^3.0.0", - "longest-streak": "^3.0.0", - "mdast-util-phrasing": "^4.0.0", - "mdast-util-to-string": "^4.0.0", - "micromark-util-classify-character": "^2.0.0", - "micromark-util-decode-string": "^2.0.0", - "unist-util-visit": "^5.0.0", - "zwitch": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-to-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", - "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/memfs": { - "version": "4.17.2", - "resolved": "https://registry.npmjs.org/memfs/-/memfs-4.17.2.tgz", - "integrity": "sha512-NgYhCOWgovOXSzvYgUW0LQ7Qy72rWQMGGFJDoWg4G30RHd3z77VbYdtJ4fembJXBy8pMIUA31XNAupobOQlwdg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@jsonjoy.com/json-pack": "^1.0.3", - "@jsonjoy.com/util": "^1.3.0", - "tree-dump": "^1.0.1", - "tslib": "^2.0.0" - }, - "engines": { - "node": ">= 4.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/streamich" - } - }, - "node_modules/merge-descriptors": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", - "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true, - "license": "MIT" - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/micromark": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz", - "integrity": "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "@types/debug": "^4.0.0", - "debug": "^4.0.0", - "decode-named-character-reference": "^1.0.0", - "devlop": "^1.0.0", - "micromark-core-commonmark": "^2.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-combine-extensions": "^2.0.0", - "micromark-util-decode-numeric-character-reference": "^2.0.0", - "micromark-util-encode": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-resolve-all": "^2.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "micromark-util-subtokenize": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-core-commonmark": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz", - "integrity": "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "decode-named-character-reference": "^1.0.0", - "devlop": "^1.0.0", - "micromark-factory-destination": "^2.0.0", - "micromark-factory-label": "^2.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-factory-title": "^2.0.0", - "micromark-factory-whitespace": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-classify-character": "^2.0.0", - "micromark-util-html-tag-name": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-resolve-all": "^2.0.0", - "micromark-util-subtokenize": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-extension-gfm": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz", - "integrity": "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==", - "license": "MIT", - "dependencies": { - "micromark-extension-gfm-autolink-literal": "^2.0.0", - "micromark-extension-gfm-footnote": "^2.0.0", - "micromark-extension-gfm-strikethrough": "^2.0.0", - "micromark-extension-gfm-table": "^2.0.0", - "micromark-extension-gfm-tagfilter": "^2.0.0", - "micromark-extension-gfm-task-list-item": "^2.0.0", - "micromark-util-combine-extensions": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-autolink-literal": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz", - "integrity": "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==", - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-footnote": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz", - "integrity": "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==", - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-core-commonmark": "^2.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-strikethrough": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.1.0.tgz", - "integrity": "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==", - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-classify-character": "^2.0.0", - "micromark-util-resolve-all": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-table": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.1.tgz", - "integrity": "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==", - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-tagfilter": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz", - "integrity": "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==", - "license": "MIT", - "dependencies": { - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-task-list-item": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.1.0.tgz", - "integrity": "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==", - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-factory-destination": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz", - "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-label": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz", - "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-space": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", - "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-title": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz", - "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-whitespace": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz", - "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-character": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", - "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-chunked": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz", - "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-classify-character": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz", - "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-combine-extensions": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz", - "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-chunked": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-decode-numeric-character-reference": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz", - "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-decode-string": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz", - "integrity": "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "decode-named-character-reference": "^1.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-decode-numeric-character-reference": "^2.0.0", - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-encode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", - "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-util-html-tag-name": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz", - "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-util-normalize-identifier": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz", - "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-resolve-all": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz", - "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-sanitize-uri": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", - "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-encode": "^2.0.0", - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-subtokenize": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz", - "integrity": "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-symbol": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", - "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-util-types": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz", - "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "dev": true, - "license": "MIT", - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", - "dev": true, - "license": "MIT", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "dev": true, - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/min-indent": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", - "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/minimalistic-assert": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", - "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", - "dev": true, - "license": "ISC" - }, - "node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "dev": true, - "license": "ISC", - "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==", - "license": "MIT" - }, - "node_modules/multicast-dns": { - "version": "7.2.5", - "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz", - "integrity": "sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==", - "dev": true, - "license": "MIT", - "dependencies": { - "dns-packet": "^5.2.2", - "thunky": "^1.0.2" - }, - "bin": { - "multicast-dns": "cli.js" - } - }, - "node_modules/mz": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", - "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "any-promise": "^1.0.0", - "object-assign": "^4.0.1", - "thenify-all": "^1.0.0" - } - }, - "node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", - "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/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true, - "license": "MIT" - }, - "node_modules/negotiator": { - "version": "0.6.4", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz", - "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true, - "license": "MIT" - }, - "node_modules/no-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", - "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", - "dev": true, - "license": "MIT", - "dependencies": { - "lower-case": "^2.0.2", - "tslib": "^2.0.3" - } - }, - "node_modules/node-fetch": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "license": "MIT", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, - "node_modules/node-fetch/node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "license": "MIT" - }, - "node_modules/node-fetch/node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", - "license": "BSD-2-Clause" - }, - "node_modules/node-fetch/node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "license": "MIT", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, - "node_modules/node-forge": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", - "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", - "dev": true, - "license": "(BSD-3-Clause OR GPL-2.0)", - "engines": { - "node": ">= 6.13.0" - } - }, - "node_modules/node-int64": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", - "dev": true, - "license": "MIT" - }, - "node_modules/node-releases": { - "version": "2.0.19", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", - "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", - "license": "MIT" - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/normalize-range": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", - "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/nth-check": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", - "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "boolbase": "^1.0.0" - }, - "funding": { - "url": "https://github.com/fb55/nth-check?sponsor=1" - } - }, - "node_modules/nwsapi": { - "version": "2.2.20", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.20.tgz", - "integrity": "sha512-/ieB+mDe4MrrKMT8z+mQL8klXydZWGR5Dowt4RAGKbJ3kIGEx3X4ljUo+6V73IXtUPWgfOlU5B9MlGxFO5T+cA==", - "dev": true, - "license": "MIT" - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-hash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", - "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "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-is": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.6.tgz", - "integrity": "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1" - }, - "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/object.entries": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.9.tgz", - "integrity": "sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.4", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.fromentries": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", - "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object.values": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz", - "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==", - "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" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/obuf": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", - "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", - "dev": true, - "license": "MIT" - }, - "node_modules/on-finished": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", - "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", - "dev": true, - "license": "MIT", - "dependencies": { - "ee-first": "1.1.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/on-headers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", - "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, - "license": "ISC", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/open": { - "version": "10.1.2", - "resolved": "https://registry.npmjs.org/open/-/open-10.1.2.tgz", - "integrity": "sha512-cxN6aIDPz6rm8hbebcP7vrQNhvRcveZoJU72Y7vskh4oIm+BZwBECnx5nTmrlres1Qapvx27Qo1Auukpf8PKXw==", - "dev": true, - "license": "MIT", - "dependencies": { - "default-browser": "^5.2.1", - "define-lazy-prop": "^3.0.0", - "is-inside-container": "^1.0.0", - "is-wsl": "^3.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/optimist": { - "version": "0.3.7", - "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.3.7.tgz", - "integrity": "sha512-TCx0dXQzVtSCg2OgY/bO9hjM9cV4XYx09TVK+s3+FhkjT6LovsLe+pPMzpWf+6yXK/hUizs2gUoTw3jHM0VaTQ==", - "license": "MIT/X11", - "dependencies": { - "wordwrap": "~0.0.2" - } - }, - "node_modules/optionator": { - "version": "0.9.4", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", - "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", - "dev": true, - "license": "MIT", - "dependencies": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.5" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/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/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/p-locate/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-retry": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-6.2.1.tgz", - "integrity": "sha512-hEt02O4hUct5wtwg4H4KcWgDdm+l1bOaEy/hWzd8xtXB9BqxTWBBhb+2ImAtH4Cv4rPjV76xN3Zumqk3k3AhhQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/retry": "0.12.2", - "is-network-error": "^1.0.0", - "retry": "^0.13.1" - }, - "engines": { - "node": ">=16.17" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/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/param-case": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", - "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", - "dev": true, - "license": "MIT", - "dependencies": { - "dot-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "license": "MIT", - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/parse-entities": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.2.tgz", - "integrity": "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==", - "license": "MIT", - "dependencies": { - "@types/unist": "^2.0.0", - "character-entities-legacy": "^3.0.0", - "character-reference-invalid": "^2.0.0", - "decode-named-character-reference": "^1.0.0", - "is-alphanumerical": "^2.0.0", - "is-decimal": "^2.0.0", - "is-hexadecimal": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/parse-entities/node_modules/@types/unist": { - "version": "2.0.11", - "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", - "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", - "license": "MIT" - }, - "node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/parse5": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", - "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", - "dev": true, - "license": "MIT", - "dependencies": { - "entities": "^6.0.0" - }, - "funding": { - "url": "https://github.com/inikulin/parse5?sponsor=1" - } - }, - "node_modules/parseurl": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", - "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/pascal-case": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", - "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", - "dev": true, - "license": "MIT", - "dependencies": { - "no-case": "^3.0.4", - "tslib": "^2.0.3" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.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": "1.11.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", - "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" - }, - "engines": { - "node": ">=16 || 14 >=14.18" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/path-scurry/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/path-to-regexp": { - "version": "0.1.12", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", - "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "license": "ISC" - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/pirates": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", - "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/pkg-dir": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-7.0.0.tgz", - "integrity": "sha512-Ie9z/WINcxxLp27BKOCHGde4ITq9UklYKDzVo1nhk5sqGEXU3FpkwP5GM2voTGJkGd9B3Otl+Q4uwSOeSUtOBA==", - "dev": true, - "license": "MIT", - "dependencies": { - "find-up": "^6.3.0" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/pkg-dir/node_modules/find-up": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-6.3.0.tgz", - "integrity": "sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^7.1.0", - "path-exists": "^5.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/pkg-dir/node_modules/locate-path": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz", - "integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^6.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/pkg-dir/node_modules/p-limit": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", - "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "yocto-queue": "^1.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/pkg-dir/node_modules/p-locate": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz", - "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/pkg-dir/node_modules/path-exists": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", - "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - } - }, - "node_modules/pkg-dir/node_modules/yocto-queue": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.2.1.tgz", - "integrity": "sha512-AyeEbWOu/TAXdxlV9wmGcR0+yh2j3vYPGOECcIj2S7MkrLyC7ne+oye2BKTItt0ii2PHk4cDy+95+LshzbXnGg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12.20" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "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.6", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", - "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", - "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.11", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/postcss-import": { - "version": "15.1.0", - "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", - "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", - "dev": true, - "license": "MIT", - "dependencies": { - "postcss-value-parser": "^4.0.0", - "read-cache": "^1.0.0", - "resolve": "^1.1.7" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "postcss": "^8.0.0" - } - }, - "node_modules/postcss-js": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", - "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", - "dev": true, - "license": "MIT", - "dependencies": { - "camelcase-css": "^2.0.1" - }, - "engines": { - "node": "^12 || ^14 || >= 16" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - "peerDependencies": { - "postcss": "^8.4.21" - } - }, - "node_modules/postcss-load-config": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz", - "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "lilconfig": "^3.0.0", - "yaml": "^2.3.4" - }, - "engines": { - "node": ">= 14" - }, - "peerDependencies": { - "postcss": ">=8.0.9", - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "postcss": { - "optional": true - }, - "ts-node": { - "optional": true - } - } - }, - "node_modules/postcss-loader": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-8.1.1.tgz", - "integrity": "sha512-0IeqyAsG6tYiDRCYKQJLAmgQr47DX6N7sFSWvQxt6AcupX8DIdmykuk/o/tx0Lze3ErGHJEp5OSRxrelC6+NdQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "cosmiconfig": "^9.0.0", - "jiti": "^1.20.0", - "semver": "^7.5.4" - }, - "engines": { - "node": ">= 18.12.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "@rspack/core": "0.x || 1.x", - "postcss": "^7.0.0 || ^8.0.1", - "webpack": "^5.0.0" - }, - "peerDependenciesMeta": { - "@rspack/core": { - "optional": true - }, - "webpack": { - "optional": true - } - } - }, - "node_modules/postcss-loader/node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/postcss-modules-extract-imports": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.1.0.tgz", - "integrity": "sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-modules-local-by-default": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.2.0.tgz", - "integrity": "sha512-5kcJm/zk+GJDSfw+V/42fJ5fhjL5YbFDl8nVdXkJPLLW+Vf9mTD5Xe0wqIaDnLuL2U6cDNpTr+UQ+v2HWIBhzw==", - "dev": true, - "license": "MIT", - "dependencies": { - "icss-utils": "^5.0.0", - "postcss-selector-parser": "^7.0.0", - "postcss-value-parser": "^4.1.0" - }, - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-modules-scope": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.2.1.tgz", - "integrity": "sha512-m9jZstCVaqGjTAuny8MdgE88scJnCiQSlSrOWcTQgM2t32UBe+MUmFSO5t7VMSfAf/FJKImAxBav8ooCHJXCJA==", - "dev": true, - "license": "ISC", - "dependencies": { - "postcss-selector-parser": "^7.0.0" - }, - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-modules-values": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", - "integrity": "sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "icss-utils": "^5.0.0" - }, - "engines": { - "node": "^10 || ^12 || >= 14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/postcss-nested": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", - "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "postcss-selector-parser": "^6.1.1" - }, - "engines": { - "node": ">=12.0" - }, - "peerDependencies": { - "postcss": "^8.2.14" - } - }, - "node_modules/postcss-nested/node_modules/postcss-selector-parser": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", - "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", - "dev": true, - "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/postcss-selector-parser": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.0.tgz", - "integrity": "sha512-8sLjZwK0R+JlxlYcTuVnyT2v+htpdrjDOKuMcOVdYjt52Lh8hWRYpxBPoKx/Zg+bcjc3wx6fmQevMmUztS/ccA==", - "dev": true, - "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/postcss-value-parser": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", - "license": "MIT" - }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/pretty-error": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/pretty-error/-/pretty-error-4.0.0.tgz", - "integrity": "sha512-AoJ5YMAcXKYxKhuJGdcvse+Voc6v1RgnsR3nWcYU7q4t6z0Q6T86sv5Zq8VIRbOWWFpvdGE83LtdSMNd+6Y0xw==", - "dev": true, - "license": "MIT", - "dependencies": { - "lodash": "^4.17.20", - "renderkid": "^3.0.0" - } - }, - "node_modules/pretty-format": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", - "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1", - "ansi-styles": "^5.0.0", - "react-is": "^17.0.1" - }, - "engines": { - "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" - } - }, - "node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/pretty-format/node_modules/react-is": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", - "dev": true, - "license": "MIT" - }, - "node_modules/process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "dev": true, - "license": "MIT" - }, - "node_modules/prompts": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", - "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/prop-types": { - "version": "15.8.1", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", - "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "license": "MIT", - "dependencies": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.13.1" - } - }, - "node_modules/prop-types/node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "license": "MIT" - }, - "node_modules/property-information": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.1.0.tgz", - "integrity": "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "dev": true, - "license": "MIT", - "dependencies": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/proxy-addr/node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/psl": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz", - "integrity": "sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==", - "dev": true, - "license": "MIT", - "dependencies": { - "punycode": "^2.3.1" - }, - "funding": { - "url": "https://github.com/sponsors/lupomontero" - } - }, - "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/pure-rand": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", - "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/dubzzz" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fast-check" - } - ], - "license": "MIT" - }, - "node_modules/qs": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", - "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "side-channel": "^1.0.6" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/querystringify": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", - "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/randombytes": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", - "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "^5.1.0" - } - }, - "node_modules/range-parser": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", - "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/raw-body": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", - "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", - "dev": true, - "license": "MIT", - "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "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-i18next": { - "version": "15.5.3", - "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-15.5.3.tgz", - "integrity": "sha512-ypYmOKOnjqPEJZO4m1BI0kS8kWqkBNsKYyhVUfij0gvjy9xJNoG/VcGkxq5dRlVwzmrmY1BQMAmpbbUBLwC4Kw==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.27.6", - "html-parse-stringify": "^3.0.1" - }, - "peerDependencies": { - "i18next": ">= 23.2.3", - "react": ">= 16.8.0", - "typescript": "^5" - }, - "peerDependenciesMeta": { - "react-dom": { - "optional": true - }, - "react-native": { - "optional": true - }, - "typescript": { - "optional": true - } - } - }, - "node_modules/react-is": { - "version": "19.1.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.1.0.tgz", - "integrity": "sha512-Oe56aUPnkHyyDxxkvqtd7KkdQP5uIUfHxd5XTb3wE9d/kRnZLmKbDB0GWk919tdQ+mxxPtG6EAs6RMT6i1qtHg==", - "license": "MIT", - "peer": true - }, - "node_modules/react-markdown": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-10.1.0.tgz", - "integrity": "sha512-qKxVopLT/TyA6BX3Ue5NwabOsAzm0Q7kAPwq6L+wWDwisYs7R8vZ0nRXqq6rkueboxpkjvLGU9fWifiX/ZZFxQ==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "devlop": "^1.0.0", - "hast-util-to-jsx-runtime": "^2.0.0", - "html-url-attributes": "^3.0.0", - "mdast-util-to-hast": "^13.0.0", - "remark-parse": "^11.0.0", - "remark-rehype": "^11.0.0", - "unified": "^11.0.0", - "unist-util-visit": "^5.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - }, - "peerDependencies": { - "@types/react": ">=18", - "react": ">=18" - } - }, - "node_modules/react-refresh": { - "version": "0.14.2", - "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz", - "integrity": "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-router": { - "version": "6.30.1", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.30.1.tgz", - "integrity": "sha512-X1m21aEmxGXqENEPG3T6u0Th7g0aS4ZmoNynhbs+Cn+q+QGTLt+d5IQ2bHAXKzKcxGJjxACpVbnYQSCRcfxHlQ==", - "license": "MIT", - "dependencies": { - "@remix-run/router": "1.23.0" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "react": ">=16.8" - } - }, - "node_modules/react-router-dom": { - "version": "6.30.1", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.30.1.tgz", - "integrity": "sha512-llKsgOkZdbPU1Eg3zK8lCn+sjD9wMRZZPuzmdWWX5SUs8OFkN5HnFVC0u5KMeMaC9aoancFI/KoLuKPqN+hxHw==", - "license": "MIT", - "dependencies": { - "@remix-run/router": "1.23.0", - "react-router": "6.30.1" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "react": ">=16.8", - "react-dom": ">=16.8" - } - }, - "node_modules/react-smooth": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/react-smooth/-/react-smooth-4.0.4.tgz", - "integrity": "sha512-gnGKTpYwqL0Iii09gHobNolvX4Kiq4PKx6eWBCYYix+8cdw+cGo3do906l1NBPKkSWx1DghC1dlWG9L2uGd61Q==", - "license": "MIT", - "dependencies": { - "fast-equals": "^5.0.1", - "prop-types": "^15.8.1", - "react-transition-group": "^4.4.5" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" - } - }, - "node_modules/react-tagify": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/react-tagify/-/react-tagify-1.0.7.tgz", - "integrity": "sha512-lZrDn39llKftcUsK86oRQo6Bc8JZxRWSzY4VIq0GrONrA5i2+W595f9wImtibBOf60qAZQJrQ2uEAOPv+rb0BA==", - "license": "MIT", - "dependencies": { - "react": "^16.8.0 || 17.x", - "react-dom": "^16.8.0 || 17.x" - }, - "peerDependencies": { - "react": "^16.8.0 || 17.x", - "react-dom": "^16.8.0 || 17.x", - "styled-components": "^5.0.0" - } - }, - "node_modules/react-tagify/node_modules/react": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react/-/react-17.0.2.tgz", - "integrity": "sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==", - "license": "MIT", - "dependencies": { - "loose-envify": "^1.1.0", - "object-assign": "^4.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-tagify/node_modules/react-dom": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-17.0.2.tgz", - "integrity": "sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==", - "license": "MIT", - "dependencies": { - "loose-envify": "^1.1.0", - "object-assign": "^4.1.1", - "scheduler": "^0.20.2" - }, - "peerDependencies": { - "react": "17.0.2" - } - }, - "node_modules/react-tagify/node_modules/scheduler": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.20.2.tgz", - "integrity": "sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==", - "license": "MIT", - "dependencies": { - "loose-envify": "^1.1.0", - "object-assign": "^4.1.1" - } - }, - "node_modules/react-transition-group": { - "version": "4.4.5", - "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", - "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", - "license": "BSD-3-Clause", - "dependencies": { - "@babel/runtime": "^7.5.5", - "dom-helpers": "^5.0.1", - "loose-envify": "^1.4.0", - "prop-types": "^15.6.2" - }, - "peerDependencies": { - "react": ">=16.6.0", - "react-dom": ">=16.6.0" - } - }, - "node_modules/read-cache": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", - "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", - "dev": true, - "license": "MIT", - "dependencies": { - "pify": "^2.3.0" - } - }, - "node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dev": true, - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "license": "MIT", - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/recharts": { - "version": "2.15.4", - "resolved": "https://registry.npmjs.org/recharts/-/recharts-2.15.4.tgz", - "integrity": "sha512-UT/q6fwS3c1dHbXv2uFgYJ9BMFHu3fwnd7AYZaEQhXuYQ4hgsxLvsUXzGdKeZrW5xopzDCvuA2N41WJ88I7zIw==", - "license": "MIT", - "dependencies": { - "clsx": "^2.0.0", - "eventemitter3": "^4.0.1", - "lodash": "^4.17.21", - "react-is": "^18.3.1", - "react-smooth": "^4.0.4", - "recharts-scale": "^0.4.4", - "tiny-invariant": "^1.3.1", - "victory-vendor": "^36.6.8" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "react": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", - "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" - } - }, - "node_modules/recharts-scale": { - "version": "0.4.5", - "resolved": "https://registry.npmjs.org/recharts-scale/-/recharts-scale-0.4.5.tgz", - "integrity": "sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w==", - "license": "MIT", - "dependencies": { - "decimal.js-light": "^2.4.1" - } - }, - "node_modules/recharts/node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "license": "MIT" - }, - "node_modules/rechoir": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz", - "integrity": "sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "resolve": "^1.20.0" - }, - "engines": { - "node": ">= 10.13.0" - } - }, - "node_modules/redent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", - "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", - "dev": true, - "license": "MIT", - "dependencies": { - "indent-string": "^4.0.0", - "strip-indent": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/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.0", - "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.0.tgz", - "integrity": "sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA==", - "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.2.0", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.2.0.tgz", - "integrity": "sha512-H66BPQMrv+V16t8xtmq+UC0CBpiTBA60V8ibS1QVReIp8T1z8hwFxqcGzm9K6lgsN7sB5edVH8a+ze6Fqm4weA==", - "dev": true, - "license": "MIT", - "dependencies": { - "regenerate": "^1.4.2", - "regenerate-unicode-properties": "^10.2.0", - "regjsgen": "^0.8.0", - "regjsparser": "^0.12.0", - "unicode-match-property-ecmascript": "^2.0.0", - "unicode-match-property-value-ecmascript": "^2.1.0" - }, - "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.12.0", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.12.0.tgz", - "integrity": "sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "jsesc": "~3.0.2" - }, - "bin": { - "regjsparser": "bin/parser" - } - }, - "node_modules/regjsparser/node_modules/jsesc": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz", - "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==", - "dev": true, - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/rehype-highlight": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/rehype-highlight/-/rehype-highlight-7.0.2.tgz", - "integrity": "sha512-k158pK7wdC2qL3M5NcZROZ2tR/l7zOzjxXd5VGdcfIyoijjQqpHd3JKtYSBDpDZ38UI2WJWuFAtkMDxmx5kstA==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "hast-util-to-text": "^4.0.0", - "lowlight": "^3.0.0", - "unist-util-visit": "^5.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/relateurl": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", - "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/remark-gfm": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.1.tgz", - "integrity": "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-gfm": "^3.0.0", - "micromark-extension-gfm": "^3.0.0", - "remark-parse": "^11.0.0", - "remark-stringify": "^11.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-parse": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz", - "integrity": "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-from-markdown": "^2.0.0", - "micromark-util-types": "^2.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-rehype": { - "version": "11.1.2", - "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.1.2.tgz", - "integrity": "sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "mdast-util-to-hast": "^13.0.0", - "unified": "^11.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-stringify": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-11.0.0.tgz", - "integrity": "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-to-markdown": "^2.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/renderkid": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/renderkid/-/renderkid-3.0.0.tgz", - "integrity": "sha512-q/7VIQA8lmM1hF+jn+sFSPWGlMkSAeNYcPLmDQx2zzuiDfaLrOmumR8iaUKlenFgh0XRPIUeSPlH3A+AW3Z5pg==", - "dev": true, - "license": "MIT", - "dependencies": { - "css-select": "^4.1.3", - "dom-converter": "^0.2.0", - "htmlparser2": "^6.1.0", - "lodash": "^4.17.21", - "strip-ansi": "^6.0.1" - } - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "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/requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/resolve": { - "version": "1.22.10", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", - "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-core-module": "^2.16.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-cwd": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", - "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve.exports": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.3.tgz", - "integrity": "sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/retry": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", - "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/reusify": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", - "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", - "dev": true, - "license": "MIT", - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/run-applescript": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.0.0.tgz", - "integrity": "sha512-9by4Ij99JUr/MCFBUkDKLWK3G9HVXmabKz9U5MlIAIuvuzkiOicRYs8XJLxX+xahD+mLiiCYDqF9dKAgtzKP1A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "node_modules/safe-array-concat": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", - "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.2", - "get-intrinsic": "^1.2.6", - "has-symbols": "^1.1.0", - "isarray": "^2.0.5" - }, - "engines": { - "node": ">=0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/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/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true, - "license": "MIT" - }, - "node_modules/saxes": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", - "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", - "dev": true, - "license": "ISC", - "dependencies": { - "xmlchars": "^2.2.0" - }, - "engines": { - "node": ">=v12.22.7" - } - }, - "node_modules/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/schema-utils": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.2.tgz", - "integrity": "sha512-Gn/JaSk/Mt9gYubxTtSn/QCV4em9mpAPiR1rqy/Ocu19u/G9J5WWdNoUT4SiV6mFC3y6cxyFcFwdzPM3FgxGAQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/json-schema": "^7.0.9", - "ajv": "^8.9.0", - "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.1.0" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - } - }, - "node_modules/schema-utils/node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", - "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/schema-utils/node_modules/ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3" - }, - "peerDependencies": { - "ajv": "^8.8.2" - } - }, - "node_modules/schema-utils/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/select-hose": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", - "integrity": "sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==", - "dev": true, - "license": "MIT" - }, - "node_modules/selfsigned": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.4.1.tgz", - "integrity": "sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node-forge": "^1.3.0", - "node-forge": "^1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/send": { - "version": "0.19.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", - "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "encodeurl": "~1.0.2", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "mime": "1.6.0", - "ms": "2.1.3", - "on-finished": "2.4.1", - "range-parser": "~1.2.1", - "statuses": "2.0.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/send/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/send/node_modules/debug/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true, - "license": "MIT" - }, - "node_modules/send/node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/serialize-javascript": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", - "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "randombytes": "^2.1.0" - } - }, - "node_modules/serve-index": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", - "integrity": "sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==", - "dev": true, - "license": "MIT", - "dependencies": { - "accepts": "~1.3.4", - "batch": "0.6.1", - "debug": "2.6.9", - "escape-html": "~1.0.3", - "http-errors": "~1.6.2", - "mime-types": "~2.1.17", - "parseurl": "~1.3.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/serve-index/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/serve-index/node_modules/depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/serve-index/node_modules/http-errors": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", - "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==", - "dev": true, - "license": "MIT", - "dependencies": { - "depd": "~1.1.2", - "inherits": "2.0.3", - "setprototypeof": "1.1.0", - "statuses": ">= 1.4.0 < 2" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/serve-index/node_modules/inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", - "dev": true, - "license": "ISC" - }, - "node_modules/serve-index/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true, - "license": "MIT" - }, - "node_modules/serve-index/node_modules/setprototypeof": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz", - "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/serve-index/node_modules/statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/serve-static": { - "version": "1.16.2", - "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", - "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", - "dev": true, - "license": "MIT", - "dependencies": { - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "parseurl": "~1.3.3", - "send": "0.19.0" - }, - "engines": { - "node": ">= 0.8.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/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", - "dev": true, - "license": "ISC" - }, - "node_modules/shallow-clone": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", - "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", - "dev": true, - "license": "MIT", - "dependencies": { - "kind-of": "^6.0.2" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shallowequal": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", - "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==", - "license": "MIT", - "peer": true - }, - "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/shell-quote": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz", - "integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", - "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3", - "side-channel-list": "^1.0.0", - "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.0", - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", - "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3" - }, - "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": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/sisteransi": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", - "dev": true, - "license": "MIT" - }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/sockjs": { - "version": "0.3.24", - "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", - "integrity": "sha512-GJgLTZ7vYb/JtPSSZ10hsOYIvEYsjbNU+zPdIHcUaWVNUEPivzxku31865sSSud0Da0W4lEeOPlmw93zLQchuQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "faye-websocket": "^0.11.3", - "uuid": "^8.3.2", - "websocket-driver": "^0.7.4" - } - }, - "node_modules/source-map": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", - "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", - "dev": true, - "license": "BSD-3-Clause", - "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.13", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", - "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", - "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/space-separated-tokens": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", - "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/spdy": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", - "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^4.1.0", - "handle-thing": "^2.0.0", - "http-deceiver": "^1.2.7", - "select-hose": "^2.0.0", - "spdy-transport": "^3.0.0" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/spdy-transport": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spdy-transport/-/spdy-transport-3.0.0.tgz", - "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", - "dev": true, - "license": "MIT", - "dependencies": { - "debug": "^4.1.0", - "detect-node": "^2.0.4", - "hpack.js": "^2.1.6", - "obuf": "^1.1.2", - "readable-stream": "^3.0.6", - "wbuf": "^1.7.3" - } - }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/stack-utils": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", - "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "escape-string-regexp": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/stack-utils/node_modules/escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/stackframe": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.3.4.tgz", - "integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==", - "dev": true, - "license": "MIT" - }, - "node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "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_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, - "node_modules/string-length": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", - "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "char-regex": "^1.0.2", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width-cjs": { - "name": "string-width", - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/string.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.repeat": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz", - "integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==", - "dev": true, - "license": "MIT", - "dependencies": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.5" - } - }, - "node_modules/string.prototype.trim": { - "version": "1.2.10", - "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", - "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.2", - "define-data-property": "^1.1.4", - "define-properties": "^1.2.1", - "es-abstract": "^1.23.5", - "es-object-atoms": "^1.0.0", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trimend": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", - "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.8", - "call-bound": "^1.0.2", - "define-properties": "^1.2.1", - "es-object-atoms": "^1.0.0" - }, - "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-entities": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", - "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", - "license": "MIT", - "dependencies": { - "character-entities-html4": "^2.0.0", - "character-entities-legacy": "^3.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi-cjs": { - "name": "strip-ansi", - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/strip-indent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", - "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "min-indent": "^1.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/style-loader": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-4.0.0.tgz", - "integrity": "sha512-1V4WqhhZZgjVAVJyt7TdDPZoPBPNHbekX4fWnCJL1yQukhCeZhJySUL+gL9y6sNdN95uEOS83Y55SqHcP7MzLA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 18.12.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.27.0" - } - }, - "node_modules/style-to-js": { - "version": "1.1.17", - "resolved": "https://registry.npmjs.org/style-to-js/-/style-to-js-1.1.17.tgz", - "integrity": "sha512-xQcBGDxJb6jjFCTzvQtfiPn6YvvP2O8U1MDIPNfJQlWMYfktPy+iGsHE7cssjs7y84d9fQaK4UF3RIJaAHSoYA==", - "license": "MIT", - "dependencies": { - "style-to-object": "1.0.9" - } - }, - "node_modules/style-to-object": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.9.tgz", - "integrity": "sha512-G4qppLgKu/k6FwRpHiGiKPaPTFcG3g4wNVX/Qsfu+RqQM30E7Tyu/TEgxcL9PNLF5pdRLwQdE3YKKf+KF2Dzlw==", - "license": "MIT", - "dependencies": { - "inline-style-parser": "0.2.4" - } - }, - "node_modules/styled-components": { - "version": "5.3.11", - "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-5.3.11.tgz", - "integrity": "sha512-uuzIIfnVkagcVHv9nE0VPlHPSCmXIUGKfJ42LNjxCCTDTL5sgnJ8Z7GZBq0EnLYGln77tPpEpExt2+qa+cZqSw==", - "license": "MIT", - "peer": true, - "dependencies": { - "@babel/helper-module-imports": "^7.0.0", - "@babel/traverse": "^7.4.5", - "@emotion/is-prop-valid": "^1.1.0", - "@emotion/stylis": "^0.8.4", - "@emotion/unitless": "^0.7.4", - "babel-plugin-styled-components": ">= 1.12.0", - "css-to-react-native": "^3.0.0", - "hoist-non-react-statics": "^3.0.0", - "shallowequal": "^1.1.0", - "supports-color": "^5.5.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/styled-components" - }, - "peerDependencies": { - "react": ">= 16.8.0", - "react-dom": ">= 16.8.0", - "react-is": ">= 16.8.0" - } - }, - "node_modules/styled-components/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "license": "MIT", - "peer": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/styled-components/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "license": "MIT", - "peer": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/sucrase": { - "version": "3.35.0", - "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", - "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.2", - "commander": "^4.0.0", - "glob": "^10.3.10", - "lines-and-columns": "^1.1.6", - "mz": "^2.7.0", - "pirates": "^4.0.1", - "ts-interface-checker": "^0.1.9" - }, - "bin": { - "sucrase": "bin/sucrase", - "sucrase-node": "bin/sucrase-node" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/sucrase/node_modules/commander": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", - "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/sucrase/node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", - "dev": true, - "license": "ISC", - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/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/swr": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/swr/-/swr-2.3.3.tgz", - "integrity": "sha512-dshNvs3ExOqtZ6kJBaAsabhPdHyeY4P2cKwRCniDVifBMoG/SVI7tfLWqPXriVspf2Rg4tPzXJTnwaihIeFw2A==", - "license": "MIT", - "dependencies": { - "dequal": "^2.0.3", - "use-sync-external-store": "^1.4.0" - }, - "peerDependencies": { - "react": "^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" - } - }, - "node_modules/symbol-tree": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", - "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", - "dev": true, - "license": "MIT" - }, - "node_modules/tagify": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/tagify/-/tagify-0.1.1.tgz", - "integrity": "sha512-IKFIcrNowtkkMzJgwCE6F5MSPcdW7QqDHV7/m3S6p9pgjQC4VmGpFAE8swx4haLbohnBrI/GSosuUNCzN8N/lw==", - "dependencies": { - "optimist": "~0.3" - }, - "engines": { - "node": "*" - } - }, - "node_modules/tailwindcss": { - "version": "3.4.17", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.17.tgz", - "integrity": "sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==", - "dev": true, - "license": "MIT", - "dependencies": { - "@alloc/quick-lru": "^5.2.0", - "arg": "^5.0.2", - "chokidar": "^3.6.0", - "didyoumean": "^1.2.2", - "dlv": "^1.1.3", - "fast-glob": "^3.3.2", - "glob-parent": "^6.0.2", - "is-glob": "^4.0.3", - "jiti": "^1.21.6", - "lilconfig": "^3.1.3", - "micromatch": "^4.0.8", - "normalize-path": "^3.0.0", - "object-hash": "^3.0.0", - "picocolors": "^1.1.1", - "postcss": "^8.4.47", - "postcss-import": "^15.1.0", - "postcss-js": "^4.0.1", - "postcss-load-config": "^4.0.2", - "postcss-nested": "^6.2.0", - "postcss-selector-parser": "^6.1.2", - "resolve": "^1.22.8", - "sucrase": "^3.35.0" - }, - "bin": { - "tailwind": "lib/cli.js", - "tailwindcss": "lib/cli.js" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/tailwindcss/node_modules/postcss-selector-parser": { - "version": "6.1.2", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", - "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", - "dev": true, - "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/tapable": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.2.tgz", - "integrity": "sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/terser": { - "version": "5.43.1", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.43.1.tgz", - "integrity": "sha512-+6erLbBm0+LROX2sPXlUYx/ux5PyE9K/a92Wrt6oA+WDAoFTdpHE5tCYCI5PNzq2y8df4rA+QgHLJuR4jNymsg==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "@jridgewell/source-map": "^0.3.3", - "acorn": "^8.14.0", - "commander": "^2.20.0", - "source-map-support": "~0.5.20" - }, - "bin": { - "terser": "bin/terser" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/terser-webpack-plugin": { - "version": "5.3.14", - "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.14.tgz", - "integrity": "sha512-vkZjpUjb6OMS7dhV+tILUW6BhpDR7P2L/aQSAv+Uwk+m8KATX9EccViHTJR2qDtACKPIYndLGCyl3FMo+r2LMw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.25", - "jest-worker": "^27.4.5", - "schema-utils": "^4.3.0", - "serialize-javascript": "^6.0.2", - "terser": "^5.31.1" - }, - "engines": { - "node": ">= 10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.1.0" - }, - "peerDependenciesMeta": { - "@swc/core": { - "optional": true - }, - "esbuild": { - "optional": true - }, - "uglify-js": { - "optional": true - } - } - }, - "node_modules/terser-webpack-plugin/node_modules/jest-worker": { - "version": "27.5.1", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", - "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "engines": { - "node": ">= 10.13.0" - } - }, - "node_modules/terser-webpack-plugin/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/terser/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/terser/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/terser/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/test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", - "dev": true, - "license": "ISC", - "dependencies": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/test-exclude/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/test-exclude/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/thenify": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", - "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", - "dev": true, - "license": "MIT", - "dependencies": { - "any-promise": "^1.0.0" - } - }, - "node_modules/thenify-all": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", - "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", - "dev": true, - "license": "MIT", - "dependencies": { - "thenify": ">= 3.1.0 < 4" - }, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/thingies": { - "version": "1.21.0", - "resolved": "https://registry.npmjs.org/thingies/-/thingies-1.21.0.tgz", - "integrity": "sha512-hsqsJsFMsV+aD4s3CWKk85ep/3I9XzYV/IXaSouJMYIoDlgyi11cBhsqYe9/geRfB0YIikBQg6raRaM+nIMP9g==", - "dev": true, - "license": "Unlicense", - "engines": { - "node": ">=10.18" - }, - "peerDependencies": { - "tslib": "^2" - } - }, - "node_modules/thunky": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", - "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", - "dev": true, - "license": "MIT" - }, - "node_modules/tiny-invariant": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", - "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==", - "license": "MIT" - }, - "node_modules/tmpl": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", - "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/tough-cookie": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz", - "integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "psl": "^1.1.33", - "punycode": "^2.1.1", - "universalify": "^0.2.0", - "url-parse": "^1.5.3" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/tr46": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", - "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==", - "dev": true, - "license": "MIT", - "dependencies": { - "punycode": "^2.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/tree-dump": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/tree-dump/-/tree-dump-1.0.3.tgz", - "integrity": "sha512-il+Cv80yVHFBwokQSfd4bldvr1Md951DpgAGfmhydt04L+YzHgubm2tQ7zueWDcGENKHq0ZvGFR/hjvNXilHEg==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=10.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/streamich" - }, - "peerDependencies": { - "tslib": "2" - } - }, - "node_modules/trim-lines": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", - "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/trough": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz", - "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/ts-api-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", - "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18.12" - }, - "peerDependencies": { - "typescript": ">=4.8.4" - } - }, - "node_modules/ts-interface-checker": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", - "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", - "dev": true, - "license": "Apache-2.0" - }, - "node_modules/ts-jest": { - "version": "29.4.0", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.4.0.tgz", - "integrity": "sha512-d423TJMnJGu80/eSgfQ5w/R+0zFJvdtTxwtF9KzFFunOpSeD+79lHJQIiAhluJoyGRbvj9NZJsl9WjCUo0ND7Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "bs-logger": "^0.2.6", - "ejs": "^3.1.10", - "fast-json-stable-stringify": "^2.1.0", - "json5": "^2.2.3", - "lodash.memoize": "^4.1.2", - "make-error": "^1.3.6", - "semver": "^7.7.2", - "type-fest": "^4.41.0", - "yargs-parser": "^21.1.1" - }, - "bin": { - "ts-jest": "cli.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0" - }, - "peerDependencies": { - "@babel/core": ">=7.0.0-beta.0 <8", - "@jest/transform": "^29.0.0 || ^30.0.0", - "@jest/types": "^29.0.0 || ^30.0.0", - "babel-jest": "^29.0.0 || ^30.0.0", - "jest": "^29.0.0 || ^30.0.0", - "jest-util": "^29.0.0 || ^30.0.0", - "typescript": ">=4.3 <6" - }, - "peerDependenciesMeta": { - "@babel/core": { - "optional": true - }, - "@jest/transform": { - "optional": true - }, - "@jest/types": { - "optional": true - }, - "babel-jest": { - "optional": true - }, - "esbuild": { - "optional": true - }, - "jest-util": { - "optional": true - } - } - }, - "node_modules/ts-jest/node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/ts-jest/node_modules/type-fest": { - "version": "4.41.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", - "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ts-loader": { - "version": "9.5.2", - "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.5.2.tgz", - "integrity": "sha512-Qo4piXvOTWcMGIgRiuFa6nHNm+54HbYaZCKqc9eeZCLRy3XqafQgwX2F7mofrbJG3g7EEb+lkiR+z2Lic2s3Zw==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^4.1.0", - "enhanced-resolve": "^5.0.0", - "micromatch": "^4.0.0", - "semver": "^7.3.4", - "source-map": "^0.7.4" - }, - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "typescript": "*", - "webpack": "^5.0.0" - } - }, - "node_modules/ts-loader/node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "dev": true, - "license": "0BSD" - }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "license": "MIT", - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "dev": true, - "license": "MIT", - "dependencies": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - }, - "engines": { - "node": ">= 0.6" - } - }, - "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.7", - "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz", - "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.7", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "is-typed-array": "^1.1.13", - "possible-typed-array-names": "^1.0.0", - "reflect.getprototypeof": "^1.0.6" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typescript": { - "version": "5.8.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", - "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", - "devOptional": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/typescript-eslint": { - "version": "8.34.1", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.34.1.tgz", - "integrity": "sha512-XjS+b6Vg9oT1BaIUfkW3M3LvqZE++rbzAMEHuccCfO/YkP43ha6w3jTEMilQxMF92nVOYCcdjv1ZUhAa1D/0ow==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/eslint-plugin": "8.34.1", - "@typescript-eslint/parser": "8.34.1", - "@typescript-eslint/utils": "8.34.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <5.9.0" - } - }, - "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/undici-types": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.8.0.tgz", - "integrity": "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==", - "dev": true, - "license": "MIT" - }, - "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.0", - "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.0.tgz", - "integrity": "sha512-4IehN3V/+kkr5YeSSDDQG8QLqO26XpL2XP3GQtqwlT/QYSECAwFztxVHjlbh0+gjJ3XmNLS0zDsbgs9jWKExLg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/unicode-property-aliases-ecmascript": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", - "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/unified": { - "version": "11.0.5", - "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz", - "integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "bail": "^2.0.0", - "devlop": "^1.0.0", - "extend": "^3.0.0", - "is-plain-obj": "^4.0.0", - "trough": "^2.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-find-after": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/unist-util-find-after/-/unist-util-find-after-5.0.0.tgz", - "integrity": "sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-is": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz", - "integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-position": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", - "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-stringify-position": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", - "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-visit": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz", - "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0", - "unist-util-visit-parents": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-visit-parents": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz", - "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/universalify": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", - "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/unpipe": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", - "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/update-browserslist-db": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", - "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "escalade": "^3.2.0", - "picocolors": "^1.1.1" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/url-parse": { - "version": "1.5.10", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", - "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "querystringify": "^2.1.1", - "requires-port": "^1.0.0" - } - }, - "node_modules/use-sync-external-store": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.5.0.tgz", - "integrity": "sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==", - "license": "MIT", - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true, - "license": "MIT" - }, - "node_modules/utila": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/utila/-/utila-0.4.0.tgz", - "integrity": "sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA==", - "dev": true, - "license": "MIT" - }, - "node_modules/utils-merge": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", - "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "dev": true, - "license": "MIT", - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/v8-to-istanbul": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", - "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", - "dev": true, - "license": "ISC", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.12", - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^2.0.0" - }, - "engines": { - "node": ">=10.12.0" - } - }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/vfile": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", - "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/vfile-message": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.2.tgz", - "integrity": "sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-stringify-position": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/victory-vendor": { - "version": "36.9.2", - "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-36.9.2.tgz", - "integrity": "sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ==", - "license": "MIT AND ISC", - "dependencies": { - "@types/d3-array": "^3.0.3", - "@types/d3-ease": "^3.0.0", - "@types/d3-interpolate": "^3.0.1", - "@types/d3-scale": "^4.0.2", - "@types/d3-shape": "^3.1.0", - "@types/d3-time": "^3.0.0", - "@types/d3-timer": "^3.0.0", - "d3-array": "^3.1.6", - "d3-ease": "^3.0.1", - "d3-interpolate": "^3.0.1", - "d3-scale": "^4.0.2", - "d3-shape": "^3.1.0", - "d3-time": "^3.0.0", - "d3-timer": "^3.0.1" - } - }, - "node_modules/void-elements": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz", - "integrity": "sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/w3c-xmlserializer": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz", - "integrity": "sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw==", - "dev": true, - "license": "MIT", - "dependencies": { - "xml-name-validator": "^4.0.0" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/walker": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", - "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "makeerror": "1.0.12" - } - }, - "node_modules/watchpack": { - "version": "2.4.4", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.4.tgz", - "integrity": "sha512-c5EGNOiyxxV5qmTtAB7rbiXxi1ooX1pQKMLX/MIabJjRA0SJBQOjKF+KSVfHkr9U1cADPon0mRiVe/riyaiDUA==", - "dev": true, - "license": "MIT", - "dependencies": { - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.1.2" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/wbuf": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/wbuf/-/wbuf-1.7.3.tgz", - "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "minimalistic-assert": "^1.0.0" - } - }, - "node_modules/webidl-conversions": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", - "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=12" - } - }, - "node_modules/webpack": { - "version": "5.99.9", - "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.99.9.tgz", - "integrity": "sha512-brOPwM3JnmOa+7kd3NsmOUOwbDAj8FT9xDsG3IW0MgbN9yZV7Oi/s/+MNQ/EcSMqw7qfoRyXPoeEWT8zLVdVGg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/eslint-scope": "^3.7.7", - "@types/estree": "^1.0.6", - "@types/json-schema": "^7.0.15", - "@webassemblyjs/ast": "^1.14.1", - "@webassemblyjs/wasm-edit": "^1.14.1", - "@webassemblyjs/wasm-parser": "^1.14.1", - "acorn": "^8.14.0", - "browserslist": "^4.24.0", - "chrome-trace-event": "^1.0.2", - "enhanced-resolve": "^5.17.1", - "es-module-lexer": "^1.2.1", - "eslint-scope": "5.1.1", - "events": "^3.2.0", - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.2.11", - "json-parse-even-better-errors": "^2.3.1", - "loader-runner": "^4.2.0", - "mime-types": "^2.1.27", - "neo-async": "^2.6.2", - "schema-utils": "^4.3.2", - "tapable": "^2.1.1", - "terser-webpack-plugin": "^5.3.11", - "watchpack": "^2.4.1", - "webpack-sources": "^3.2.3" - }, - "bin": { - "webpack": "bin/webpack.js" - }, - "engines": { - "node": ">=10.13.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependenciesMeta": { - "webpack-cli": { - "optional": true - } - } - }, - "node_modules/webpack-cli": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-5.1.4.tgz", - "integrity": "sha512-pIDJHIEI9LR0yxHXQ+Qh95k2EvXpWzZ5l+d+jIo+RdSm9MiHfzazIxwwni/p7+x4eJZuvG1AJwgC4TNQ7NRgsg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@discoveryjs/json-ext": "^0.5.0", - "@webpack-cli/configtest": "^2.1.1", - "@webpack-cli/info": "^2.0.2", - "@webpack-cli/serve": "^2.0.5", - "colorette": "^2.0.14", - "commander": "^10.0.1", - "cross-spawn": "^7.0.3", - "envinfo": "^7.7.3", - "fastest-levenshtein": "^1.0.12", - "import-local": "^3.0.2", - "interpret": "^3.1.1", - "rechoir": "^0.8.0", - "webpack-merge": "^5.7.3" - }, - "bin": { - "webpack-cli": "bin/cli.js" - }, - "engines": { - "node": ">=14.15.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "5.x.x" - }, - "peerDependenciesMeta": { - "@webpack-cli/generators": { - "optional": true - }, - "webpack-bundle-analyzer": { - "optional": true - }, - "webpack-dev-server": { - "optional": true - } - } - }, - "node_modules/webpack-cli/node_modules/commander": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", - "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14" - } - }, - "node_modules/webpack-dev-middleware": { - "version": "7.4.2", - "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-7.4.2.tgz", - "integrity": "sha512-xOO8n6eggxnwYpy1NlzUKpvrjfJTvae5/D6WOK0S2LSo7vjmo5gCM1DbLUmFqrMTJP+W/0YZNctm7jasWvLuBA==", - "dev": true, - "license": "MIT", - "dependencies": { - "colorette": "^2.0.10", - "memfs": "^4.6.0", - "mime-types": "^2.1.31", - "on-finished": "^2.4.1", - "range-parser": "^1.2.1", - "schema-utils": "^4.0.0" - }, - "engines": { - "node": ">= 18.12.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.0.0" - }, - "peerDependenciesMeta": { - "webpack": { - "optional": true - } - } - }, - "node_modules/webpack-dev-server": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-5.2.2.tgz", - "integrity": "sha512-QcQ72gh8a+7JO63TAx/6XZf/CWhgMzu5m0QirvPfGvptOusAxG12w2+aua1Jkjr7hzaWDnJ2n6JFeexMHI+Zjg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/bonjour": "^3.5.13", - "@types/connect-history-api-fallback": "^1.5.4", - "@types/express": "^4.17.21", - "@types/express-serve-static-core": "^4.17.21", - "@types/serve-index": "^1.9.4", - "@types/serve-static": "^1.15.5", - "@types/sockjs": "^0.3.36", - "@types/ws": "^8.5.10", - "ansi-html-community": "^0.0.8", - "bonjour-service": "^1.2.1", - "chokidar": "^3.6.0", - "colorette": "^2.0.10", - "compression": "^1.7.4", - "connect-history-api-fallback": "^2.0.0", - "express": "^4.21.2", - "graceful-fs": "^4.2.6", - "http-proxy-middleware": "^2.0.9", - "ipaddr.js": "^2.1.0", - "launch-editor": "^2.6.1", - "open": "^10.0.3", - "p-retry": "^6.2.0", - "schema-utils": "^4.2.0", - "selfsigned": "^2.4.1", - "serve-index": "^1.9.1", - "sockjs": "^0.3.24", - "spdy": "^4.0.2", - "webpack-dev-middleware": "^7.4.2", - "ws": "^8.18.0" - }, - "bin": { - "webpack-dev-server": "bin/webpack-dev-server.js" - }, - "engines": { - "node": ">= 18.12.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/webpack" - }, - "peerDependencies": { - "webpack": "^5.0.0" - }, - "peerDependenciesMeta": { - "webpack": { - "optional": true - }, - "webpack-cli": { - "optional": true - } - } - }, - "node_modules/webpack-merge": { - "version": "5.10.0", - "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.10.0.tgz", - "integrity": "sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA==", - "dev": true, - "license": "MIT", - "dependencies": { - "clone-deep": "^4.0.1", - "flat": "^5.0.2", - "wildcard": "^2.0.0" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/webpack-sources": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.3.3.tgz", - "integrity": "sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/webpack/node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/webpack/node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/websocket-driver": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", - "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "http-parser-js": ">=0.5.1", - "safe-buffer": ">=5.1.0", - "websocket-extensions": ">=0.1.1" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/websocket-extensions": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", - "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/whatwg-encoding": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", - "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==", - "dev": true, - "license": "MIT", - "dependencies": { - "iconv-lite": "0.6.3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/whatwg-encoding/node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "dev": true, - "license": "MIT", - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/whatwg-mimetype": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz", - "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - } - }, - "node_modules/whatwg-url": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz", - "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "tr46": "^3.0.0", - "webidl-conversions": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "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.19", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz", - "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==", - "dev": true, - "license": "MIT", - "dependencies": { - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.8", - "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/wildcard": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz", - "integrity": "sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/word-wrap": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", - "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/wordwrap": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", - "integrity": "sha512-1tMA907+V4QmxV7dbRvb4/8MaRALK6q9Abid3ndMYnbyo8piisCmeONVqVSXqQA3KaP4SLt5b7ud6E2sqP8TFw==", - "license": "MIT", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs": { - "name": "wrap-ansi", - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/write-file-atomic": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", - "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", - "dev": true, - "license": "ISC", - "dependencies": { - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.7" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/ws": { - "version": "8.18.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.2.tgz", - "integrity": "sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/xml-name-validator": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz", - "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=12" - } - }, - "node_modules/xmlchars": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", - "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", - "dev": true, - "license": "MIT" - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "license": "ISC" - }, - "node_modules/yaml": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.0.tgz", - "integrity": "sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==", - "dev": true, - "license": "ISC", - "bin": { - "yaml": "bin.mjs" - }, - "engines": { - "node": ">= 14.6" - } - }, - "node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/zustand": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.5.tgz", - "integrity": "sha512-mILtRfKW9xM47hqxGIxCv12gXusoY/xTSHBYApXozR0HmQv299whhBeeAcRy+KrPPybzosvJBCOmVjq6x12fCg==", - "license": "MIT", - "engines": { - "node": ">=12.20.0" - }, - "peerDependencies": { - "@types/react": ">=18.0.0", - "immer": ">=9.0.6", - "react": ">=18.0.0", - "use-sync-external-store": ">=1.2.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "immer": { - "optional": true - }, - "react": { - "optional": true - }, - "use-sync-external-store": { - "optional": true - } - } - }, - "node_modules/zwitch": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", - "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - } - } -}