diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..831249f
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,19 @@
+# .editorconfig — 统一编辑器配置
+root = true
+
+[*]
+charset = utf-8
+end_of_line = lf
+insert_final_newline = true
+trim_trailing_whitespace = true
+indent_style = space
+indent_size = 4
+
+[*.{json,yml,yaml}]
+indent_size = 2
+
+[*.md]
+trim_trailing_whitespace = false
+
+[*.vue]
+indent_size = 4
diff --git a/.env.example b/.env.example
new file mode 100644
index 0000000..e5a8b4c
--- /dev/null
+++ b/.env.example
@@ -0,0 +1,18 @@
+# 复制为 .env 后填入实际值(所有项可选;只配需要的功能即可)
+
+# 服务监听
+PORT=8787
+HOST=127.0.0.1
+NODE_ENV=production
+
+# 城市(默认)
+APP_CITY=Beijing
+
+# 天气 API(任选其一)
+QWECHAT_API_KEY=
+QWECHAT_HOST=devapi.qweather.com
+OPENWEATHERMAP_API_KEY=
+
+# Grocy 集成
+GROCY_URL=
+GROCY_API_TOKEN=
diff --git a/.eslintrc.json b/.eslintrc.json
new file mode 100644
index 0000000..251cf8b
--- /dev/null
+++ b/.eslintrc.json
@@ -0,0 +1,35 @@
+{
+ "root": true,
+ "env": {
+ "browser": true,
+ "es2022": true,
+ "node": true
+ },
+ "parser": "espree",
+ "parserOptions": {
+ "ecmaVersion": "latest",
+ "sourceType": "module",
+ "ecmaFeatures": { "jsx": false }
+ },
+ "extends": ["eslint:recommended"],
+ "rules": {
+ "no-unused-vars": ["warn", { "argsIgnorePattern": "^_", "varsIgnorePattern": "^_" }],
+ "no-undef": "error",
+ "no-console": "off",
+ "no-var": "error",
+ "prefer-const": "warn",
+ "eqeqeq": ["error", "always", { "null": "ignore" }],
+ "no-implicit-coercion": "warn",
+ "no-throw-literal": "error"
+ },
+ "overrides": [
+ {
+ "files": ["client/src/**/*.vue"],
+ "parser": "vue-eslint-parser",
+ "parserOptions": {
+ "parser": "espree"
+ }
+ }
+ ],
+ "ignorePatterns": ["node_modules/", "client/dist/", "server/storage/", "uploads/", "*.min.js"]
+}
diff --git a/.gitignore b/.gitignore
index 86824e2..afadaed 100644
--- a/.gitignore
+++ b/.gitignore
@@ -44,4 +44,4 @@ yarn-error.log*
*.tar.gz
# Mavis
-.mavis/
\ No newline at end of file
+.mavis/.DS_Store
diff --git a/.husky/pre-commit b/.husky/pre-commit
new file mode 100644
index 0000000..996328a
--- /dev/null
+++ b/.husky/pre-commit
@@ -0,0 +1,5 @@
+#!/usr/bin/env sh
+# .husky/pre-commit — 提交前自动 lint + format 已暂存文件
+. "$(dirname -- "$0")/_/husky.sh"
+
+npx --no-install lint-staged
diff --git a/.pa11yci.json b/.pa11yci.json
new file mode 100644
index 0000000..1096125
--- /dev/null
+++ b/.pa11yci.json
@@ -0,0 +1,14 @@
+{
+ "chromeLaunchConfig": {
+ "args": ["--no-sandbox", "--disable-setuid-sandbox", "--headless=new"]
+ },
+ "standard": "WCAG2AA",
+ "runners": ["htmlcs"],
+ "ignore": [
+ "WCAG2AA.Principle1.Guideline1_3.1_3_1.H49.AlignAttr",
+ "WCAG2AA.Principle1.Guideline1_4.1_4_3.Contrast",
+ "color-contrast"
+ ],
+ "rules": [],
+ "timeout": 60000
+}
diff --git a/.prettierignore b/.prettierignore
new file mode 100644
index 0000000..723c064
--- /dev/null
+++ b/.prettierignore
@@ -0,0 +1,26 @@
+# 依赖
+node_modules/
+client/node_modules/
+server/node_modules/
+
+# 构建产物
+client/dist/
+server/storage/
+uploads/
+
+# 数据
+server/data/*.sqlite
+server/data/*.sqlite-shm
+server/data/*.sqlite-wal
+server/data/*.db
+
+# 日志 / 临时
+*.log
+.DS_Store
+.setup_done
+.env
+.env.local
+
+# 备份
+*.zip
+*.tar.gz
diff --git a/.prettierrc.json b/.prettierrc.json
new file mode 100644
index 0000000..fd90265
--- /dev/null
+++ b/.prettierrc.json
@@ -0,0 +1,12 @@
+{
+ "semi": true,
+ "singleQuote": true,
+ "tabWidth": 4,
+ "useTabs": false,
+ "printWidth": 120,
+ "trailingComma": "es5",
+ "bracketSpacing": true,
+ "arrowParens": "always",
+ "endOfLine": "lf",
+ "vueIndentScriptAndStyle": false
+}
diff --git a/carlog-init.sql b/carlog-init.sql
new file mode 100644
index 0000000..b27c2be
--- /dev/null
+++ b/carlog-init.sql
@@ -0,0 +1,459 @@
+-- ============================================================
+-- 洗车管理系统 MySQL 一键初始化(v2.1)
+-- 适用 MySQL 8.0+ / utf8mb4
+--
+-- 用法:
+-- 1. 宝塔:选 carlog 库 →『导入』→ 选本文件
+-- 2. 命令行:mysql -uroot -p carlog < carlog-init.sql
+--
+-- 完全幂等,反复重跑不会破坏数据
+-- ============================================================
+
+DROP PROCEDURE IF EXISTS _add_index_if_missing;
+CREATE PROCEDURE _add_index_if_missing(IN p_table VARCHAR(64), IN p_index VARCHAR(64), IN p_def TEXT, IN p_unique INT) BEGIN
+ IF NOT EXISTS (SELECT 1 FROM information_schema.statistics WHERE table_schema = DATABASE() AND table_name = p_table AND index_name = p_index) THEN
+ IF p_unique = 1 THEN SET @s = CONCAT('CREATE UNIQUE INDEX ', p_index, ' ON ', p_table, ' ', p_def);
+ ELSE SET @s = CONCAT('CREATE INDEX ', p_index, ' ON ', p_table, ' ', p_def); END IF;
+ PREPARE st FROM @s; EXECUTE st; DEALLOCATE PREPARE st;
+ END IF;
+END;
+
+DROP PROCEDURE IF EXISTS _try_sql;
+CREATE PROCEDURE _try_sql(IN p_sql TEXT) BEGIN
+ DECLARE EXIT HANDLER FOR SQLEXCEPTION BEGIN END;
+ SET @s = p_sql;
+ PREPARE st FROM @s; EXECUTE st; DEALLOCATE PREPARE st;
+END;
+
+
+-- >>> 0001_init.sql
+-- ==========================================================
+-- =============================================================================
+-- 洗车记录系统 - Migration 0001: 基础表 (MySQL 8.x)
+-- =============================================================================
+-- -----------------------------------------------------------------------------
+-- 1. chemicals - 药剂字典(Grocy 缓存层)
+-- -----------------------------------------------------------------------------
+CREATE TABLE IF NOT EXISTS chemicals (
+grocy_product_id VARCHAR(255) NOT NULL,
+name VARCHAR(255) NOT NULL,
+category VARCHAR(255) DEFAULT NULL,
+unit VARCHAR(50) NOT NULL DEFAULT 'ml',
+standard_dose DOUBLE DEFAULT NULL,
+notes TEXT DEFAULT NULL,
+is_active TINYINT(1) NOT NULL DEFAULT 1,
+fetched_at DATETIME DEFAULT NULL,
+created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
+updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+PRIMARY KEY (grocy_product_id)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+CALL _add_index_if_missing('chemicals', 'idx_chemicals_category', '(category)', 0);
+CALL _add_index_if_missing('chemicals', 'idx_chemicals_active', '(is_active)', 0);
+CALL _add_index_if_missing('chemicals', 'idx_chemicals_fetched', '(fetched_at)', 0);
+-- -----------------------------------------------------------------------------
+-- 2. weather_snapshots - 天气快照
+-- -----------------------------------------------------------------------------
+CREATE TABLE IF NOT EXISTS weather_snapshots (
+id INT AUTO_INCREMENT PRIMARY KEY,
+snapshot_date VARCHAR(10) NOT NULL,
+city VARCHAR(100) NOT NULL,
+provider VARCHAR(50) NOT NULL,
+temp_c DOUBLE DEFAULT NULL,
+humidity INT DEFAULT NULL,
+weather_desc VARCHAR(255) DEFAULT NULL,
+weather_code VARCHAR(20) DEFAULT NULL,
+wind_kph DOUBLE DEFAULT NULL,
+precip_mm DOUBLE DEFAULT NULL,
+raw_json TEXT DEFAULT NULL,
+fetched_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+CALL _add_index_if_missing('weather_snapshots', 'uk_weather_city_date', '(city, snapshot_date)', 1);
+CALL _add_index_if_missing('weather_snapshots', 'idx_weather_date', '(snapshot_date)', 0);
+-- -----------------------------------------------------------------------------
+-- 3. wash_records - 洗车记录
+-- -----------------------------------------------------------------------------
+CREATE TABLE IF NOT EXISTS wash_records (
+id INT AUTO_INCREMENT PRIMARY KEY,
+wash_date VARCHAR(10) NOT NULL,
+wash_type VARCHAR(20) NOT NULL,
+weather_snapshot_id INT DEFAULT NULL,
+location VARCHAR(255) DEFAULT NULL,
+cost DOUBLE NOT NULL DEFAULT 0,
+duration_min INT DEFAULT NULL,
+notes TEXT DEFAULT NULL,
+created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
+updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+CONSTRAINT chk_wash_type CHECK (wash_type IN ('quick','full','detail','other')),
+CONSTRAINT fk_wash_weather FOREIGN KEY (weather_snapshot_id) REFERENCES weather_snapshots(id) ON DELETE SET NULL
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+CALL _add_index_if_missing('wash_records', 'idx_wash_records_date', '(wash_date)', 0);
+CALL _add_index_if_missing('wash_records', 'idx_wash_records_type', '(wash_type)', 0);
+CALL _add_index_if_missing('wash_records', 'idx_wash_records_weather', '(weather_snapshot_id)', 0);
+-- -----------------------------------------------------------------------------
+-- 4. chemical_usage - 药剂消耗
+-- -----------------------------------------------------------------------------
+CREATE TABLE IF NOT EXISTS chemical_usage (
+id INT AUTO_INCREMENT PRIMARY KEY,
+usage_date VARCHAR(10) NOT NULL,
+chemical_id VARCHAR(255) NOT NULL,
+amount DOUBLE NOT NULL,
+wash_record_id INT DEFAULT NULL,
+notes TEXT DEFAULT NULL,
+sync_status VARCHAR(20) NOT NULL DEFAULT 'pending',
+sync_at DATETIME DEFAULT NULL,
+created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
+updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+CONSTRAINT chk_sync_status CHECK (sync_status IN ('pending','synced','failed')),
+CONSTRAINT fk_usage_chemical FOREIGN KEY (chemical_id) REFERENCES chemicals(grocy_product_id) ON DELETE RESTRICT,
+CONSTRAINT fk_usage_wash FOREIGN KEY (wash_record_id) REFERENCES wash_records(id) ON DELETE SET NULL
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+CALL _add_index_if_missing('chemical_usage', 'idx_usage_date', '(usage_date)', 0);
+CALL _add_index_if_missing('chemical_usage', 'idx_usage_chemical', '(chemical_id)', 0);
+CALL _add_index_if_missing('chemical_usage', 'idx_usage_wash', '(wash_record_id)', 0);
+CALL _add_index_if_missing('chemical_usage', 'idx_usage_sync', '(sync_status)', 0);
+-- -----------------------------------------------------------------------------
+-- 5. settings - 运行时配置 KV
+-- -----------------------------------------------------------------------------
+CREATE TABLE IF NOT EXISTS settings (
+`key` VARCHAR(100) NOT NULL PRIMARY KEY,
+value TEXT DEFAULT NULL,
+is_secret TINYINT(1) NOT NULL DEFAULT 0,
+description TEXT DEFAULT NULL,
+updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+INSERT IGNORE INTO settings (`key`, value, is_secret, description) VALUES
+('app_city', NULL, 0, '所在城市(用于天气查询)'),
+('app_timezone', 'Asia/Shanghai', 0, '时区'),
+('grocy_url', NULL, 0, 'Grocy 实例 URL'),
+('grocy_api_token', NULL, 1, 'Grocy REST API token'),
+('backup_keep_count', '10', 0, '本地备份保留份数'),
+('backup_dir', 'storage/backups', 0, '备份输出目录');
+-- -----------------------------------------------------------------------------
+-- 6. views
+-- -----------------------------------------------------------------------------
+DROP VIEW IF EXISTS v_wash_monthly_count;
+CALL _try_sql('CREATE VIEW v_wash_monthly_count AS SELECT SUBSTRING(wash_date, 1, 7) AS month, COUNT(*) AS wash_count, SUM(COALESCE(cost, 0)) AS total_cost FROM wash_records GROUP BY SUBSTRING(wash_date, 1, 7) ORDER BY month DESC');
+DROP VIEW IF EXISTS v_chemical_monthly_usage;
+CALL _try_sql('CREATE VIEW v_chemical_monthly_usage AS SELECT SUBSTRING(cu.usage_date, 1, 7) AS month, c.grocy_product_id AS grocy_product_id, c.name AS chemical_name, c.unit AS unit, SUM(cu.amount) AS total_amount, COUNT(*) AS usage_count FROM chemical_usage cu JOIN chemicals c ON c.grocy_product_id = cu.chemical_id GROUP BY SUBSTRING(cu.usage_date, 1, 7), c.grocy_product_id ORDER BY month DESC, total_amount DESC');
+DROP VIEW IF EXISTS v_last_wash;
+CALL _try_sql('CREATE VIEW v_last_wash AS SELECT id AS wash_id, wash_date, wash_type, DATEDIFF(NOW(), STR_TO_DATE(wash_date, ''%Y-%m-%d'')) AS days_since FROM wash_records ORDER BY wash_date DESC, id DESC LIMIT 1');
+
+-- >>> 0002_auth.sql
+-- ==========================================================
+-- 0002_auth.sql - 用户认证 + 防撞库 (MySQL)
+CREATE TABLE IF NOT EXISTS users (
+id INT AUTO_INCREMENT PRIMARY KEY,
+username VARCHAR(50) NOT NULL UNIQUE,
+password_hash VARCHAR(255) NOT NULL,
+role VARCHAR(20) NOT NULL DEFAULT 'user',
+is_active TINYINT(1) NOT NULL DEFAULT 1,
+last_login_at DATETIME DEFAULT NULL,
+last_login_ip VARCHAR(45) DEFAULT NULL,
+created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
+updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+CONSTRAINT chk_role CHECK (role IN ('user','admin'))
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+CALL _add_index_if_missing('users', 'idx_users_active', '(is_active)', 0);
+CREATE TABLE IF NOT EXISTS login_attempts (
+id INT AUTO_INCREMENT PRIMARY KEY,
+attempted_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ip_address VARCHAR(45) NOT NULL,
+username VARCHAR(50) NOT NULL,
+success TINYINT(1) NOT NULL,
+user_agent VARCHAR(500) DEFAULT NULL,
+failure_reason VARCHAR(100) DEFAULT NULL,
+CONSTRAINT chk_success CHECK (success IN (0, 1))
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+CALL _add_index_if_missing('login_attempts', 'idx_attempts_ip_time', '(ip_address, attempted_at)', 0);
+CALL _add_index_if_missing('login_attempts', 'idx_attempts_user_time', '(username, attempted_at)', 0);
+CALL _add_index_if_missing('login_attempts', 'idx_attempts_time', '(attempted_at)', 0);
+CREATE TABLE IF NOT EXISTS auth_locks (
+lock_key VARCHAR(100) PRIMARY KEY,
+lock_type VARCHAR(10) NOT NULL,
+target VARCHAR(50) NOT NULL,
+locked_until DATETIME NOT NULL,
+reason VARCHAR(255) DEFAULT NULL,
+attempts INT NOT NULL DEFAULT 0,
+created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
+CONSTRAINT chk_lock_type CHECK (lock_type IN ('ip','user'))
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+CALL _add_index_if_missing('auth_locks', 'idx_locks_until', '(locked_until)', 0);
+INSERT IGNORE INTO settings (`key`, value, is_secret, description) VALUES
+('session_lifetime_days', '30', 0, '登录 session 有效期(天)'),
+('session_cookie_secure', 'auto', 0, 'Cookie secure 标志:true/false/auto'),
+('login_max_failures_ip', '5', 0, '每 IP 允许的最大连续失败次数'),
+('login_max_failures_user', '5', 0, '每用户名允许的最大连续失败次数'),
+('login_lock_minutes_ip', '15', 0, 'IP 级别锁定时长(分钟)'),
+('login_lock_minutes_user', '30', 0, '用户名级别锁定时长(分钟)'),
+('login_global_max_failures', '10', 0, '触发全局 IP 封锁的失败次数'),
+('login_global_lock_hours', '1', 0, '全局 IP 封锁时长(小时)'),
+('login_attempts_retention_days', '30', 0, 'login_attempts 保留天数'),
+('csrf_token_lifetime_hours', '12', 0, 'CSRF token 有效期(小时)'),
+('bcrypt_cost', '12', 0, 'bcrypt cost factor');
+
+-- >>> 0003_vehicles.sql
+-- ==========================================================
+-- 0003_vehicles.sql - 车辆管理 (MySQL)
+CREATE TABLE IF NOT EXISTS vehicles (
+id INT AUTO_INCREMENT PRIMARY KEY,
+name VARCHAR(100) NOT NULL,
+plate VARCHAR(20) DEFAULT NULL,
+type VARCHAR(20) NOT NULL DEFAULT 'car',
+color VARCHAR(30) DEFAULT NULL,
+notes TEXT DEFAULT NULL,
+is_active TINYINT(1) NOT NULL DEFAULT 1,
+sort_order INT NOT NULL DEFAULT 0,
+created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
+updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+CONSTRAINT chk_vehicle_type CHECK (type IN ('car','suv','mpv','truck','other'))
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+CALL _add_index_if_missing('vehicles', 'idx_vehicles_active', '(is_active)', 0);
+CALL _add_index_if_missing('vehicles', 'idx_vehicles_sort', '(sort_order)', 0);
+CALL _try_sql('ALTER TABLE wash_records ADD COLUMN vehicle_id INT DEFAULT NULL');
+CALL _add_index_if_missing('wash_records', 'idx_wash_records_vehicle', '(vehicle_id)', 0);
+DROP VIEW IF EXISTS v_last_wash;
+CALL _try_sql('CREATE VIEW v_last_wash AS SELECT w.id AS wash_id, w.wash_date, w.wash_type, w.vehicle_id, v.name AS vehicle_name, DATEDIFF(NOW(), STR_TO_DATE(w.wash_date, ''%Y-%m-%d'')) AS days_since FROM wash_records w LEFT JOIN vehicles v ON v.id = w.vehicle_id ORDER BY w.wash_date DESC, w.id DESC LIMIT 1');
+
+-- >>> 0004_grocy_full.sql
+-- ==========================================================
+-- 0004_grocy_full.sql - Grocy 主数据同步字段 (MySQL)
+CALL _try_sql('ALTER TABLE chemicals ADD COLUMN description TEXT DEFAULT NULL, ADD COLUMN current_amount DOUBLE NOT NULL DEFAULT 0, ADD COLUMN current_value DOUBLE NOT NULL DEFAULT 0, ADD COLUMN min_stock_amount DOUBLE NOT NULL DEFAULT 0, ADD COLUMN best_before_date VARCHAR(20) DEFAULT NULL, ADD COLUMN location VARCHAR(255) DEFAULT NULL, ADD COLUMN product_group_id INT DEFAULT NULL, ADD COLUMN qu_id INT DEFAULT NULL, ADD COLUMN location_id INT DEFAULT NULL, ADD COLUMN picture_file_name VARCHAR(255) DEFAULT NULL, ADD COLUMN last_synced_at DATETIME DEFAULT NULL');
+CALL _add_index_if_missing('chemicals', 'idx_chem_amount', '(current_amount)', 0);
+CALL _add_index_if_missing('chemicals', 'idx_chem_pg', '(product_group_id)', 0);
+CALL _add_index_if_missing('chemicals', 'idx_chem_synced', '(last_synced_at)', 0);
+INSERT IGNORE INTO settings (`key`, value, is_secret, description) VALUES
+('grocy_sync_batch', '50', 0, 'Grocy 扣减同步每批条数'),
+('grocy_low_stock_pct', '20', 0, '低库存阈值(百分比)'),
+('grocy_pull_auto', '0', 0, 'Grocy 拉取模式:0=手动,1=启动时自动拉');
+
+-- >>> 0005_inventory_detail.sql
+-- ==========================================================
+-- 0005_inventory_detail.sql (MySQL)
+CALL _try_sql('ALTER TABLE chemicals ADD COLUMN source VARCHAR(20) NOT NULL DEFAULT ''manual'', ADD COLUMN grocy_last_pulled_at DATETIME DEFAULT NULL');
+CREATE TABLE IF NOT EXISTS category_mappings (
+grocy_group_id INT PRIMARY KEY,
+display_name VARCHAR(100) NOT NULL,
+sort_order INT NOT NULL DEFAULT 0,
+created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+CREATE TABLE IF NOT EXISTS chemical_inventory_log (
+id INT AUTO_INCREMENT PRIMARY KEY,
+chemical_id VARCHAR(255) NOT NULL,
+change_type VARCHAR(20) NOT NULL,
+amount_delta DOUBLE NOT NULL,
+amount_after DOUBLE DEFAULT NULL,
+source VARCHAR(20) NOT NULL DEFAULT 'local',
+source_ref VARCHAR(255) DEFAULT NULL,
+note TEXT DEFAULT NULL,
+occurred_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
+created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
+CONSTRAINT chk_change_type CHECK (change_type IN ('purchase','consume','inventory','transfer','adjust'))
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+CALL _add_index_if_missing('chemical_inventory_log', 'idx_invlog_chem', '(chemical_id, occurred_at DESC)', 0);
+CALL _add_index_if_missing('chemical_inventory_log', 'idx_invlog_type', '(change_type)', 0);
+INSERT IGNORE INTO settings (`key`, value, is_secret, description) VALUES
+('grocy_categories_json', '[]', 0, 'Grocy 分类映射 JSON');
+
+-- >>> 0006_unit_conversion.sql
+-- ==========================================================
+-- 0006_unit_conversion.sql (MySQL)
+CALL _try_sql('ALTER TABLE chemicals ADD COLUMN qu_factor DOUBLE NOT NULL DEFAULT 1.0, ADD COLUMN consume_unit_id INT DEFAULT NULL, ADD COLUMN consume_unit_name VARCHAR(100) DEFAULT NULL');
+CALL _try_sql('ALTER TABLE chemical_usage ADD COLUMN unit VARCHAR(50) DEFAULT NULL, ADD COLUMN stock_amount DOUBLE DEFAULT NULL, ADD COLUMN consume_unit_id INT DEFAULT NULL');
+
+-- >>> 0007_vehicle_logs.sql
+-- ==========================================================
+-- 0007_vehicle_logs.sql (MySQL)
+CREATE TABLE IF NOT EXISTS maintenance_records (
+id INT AUTO_INCREMENT PRIMARY KEY,
+vehicle_id INT NOT NULL,
+maint_date VARCHAR(10) NOT NULL,
+odometer_km INT DEFAULT NULL,
+total_cost DOUBLE NOT NULL DEFAULT 0,
+shop VARCHAR(255) DEFAULT NULL,
+items_json JSON NOT NULL DEFAULT ('[]'),
+next_due_date VARCHAR(10) DEFAULT NULL,
+next_due_km INT DEFAULT NULL,
+notes TEXT DEFAULT NULL,
+created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
+updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+CALL _add_index_if_missing('maintenance_records', 'idx_maint_vehicle_date', '(vehicle_id, maint_date DESC)', 0);
+CALL _add_index_if_missing('maintenance_records', 'idx_maint_date', '(maint_date DESC)', 0);
+CREATE TABLE IF NOT EXISTS refuel_records (
+id INT AUTO_INCREMENT PRIMARY KEY,
+vehicle_id INT NOT NULL,
+refuel_date VARCHAR(10) NOT NULL,
+odometer_km INT DEFAULT NULL,
+liters DOUBLE NOT NULL,
+price_per_liter DOUBLE DEFAULT NULL,
+total_cost DOUBLE NOT NULL,
+fuel_type VARCHAR(20) DEFAULT NULL,
+is_full TINYINT(1) NOT NULL DEFAULT 0,
+station VARCHAR(255) DEFAULT NULL,
+notes TEXT DEFAULT NULL,
+created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
+updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+CALL _add_index_if_missing('refuel_records', 'idx_refuel_vehicle_date', '(vehicle_id, refuel_date DESC)', 0);
+CALL _add_index_if_missing('refuel_records', 'idx_refuel_date', '(refuel_date DESC)', 0);
+CREATE TABLE IF NOT EXISTS charging_records (
+id INT AUTO_INCREMENT PRIMARY KEY,
+vehicle_id INT NOT NULL,
+charge_date VARCHAR(10) NOT NULL,
+odometer_km INT DEFAULT NULL,
+kwh DOUBLE NOT NULL,
+price_per_kwh DOUBLE DEFAULT NULL,
+total_cost DOUBLE NOT NULL,
+charge_type VARCHAR(20) DEFAULT NULL,
+start_soc INT DEFAULT NULL,
+end_soc INT DEFAULT NULL,
+station VARCHAR(255) DEFAULT NULL,
+notes TEXT DEFAULT NULL,
+created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
+updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+CALL _add_index_if_missing('charging_records', 'idx_charging_vehicle_date', '(vehicle_id, charge_date DESC)', 0);
+CALL _add_index_if_missing('charging_records', 'idx_charging_date', '(charge_date DESC)', 0);
+DROP VIEW IF EXISTS v_recent_logs;
+CALL _try_sql('CREATE VIEW v_recent_logs AS SELECT ''maintenance'' AS log_type, id, vehicle_id, maint_date AS log_date, total_cost, odometer_km, shop AS location FROM maintenance_records UNION ALL SELECT ''refuel'' AS log_type, id, vehicle_id, refuel_date AS log_date, total_cost, odometer_km, station AS location FROM refuel_records UNION ALL SELECT ''charging'' AS log_type, id, vehicle_id, charge_date AS log_date, total_cost, odometer_km, station AS location FROM charging_records');
+
+-- >>> 0008_mileage_and_insurance.sql
+-- ==========================================================
+-- 0008_mileage_and_insurance.sql (MySQL)
+CALL _try_sql('ALTER TABLE maintenance_records ADD COLUMN ev_km INT DEFAULT NULL, ADD COLUMN hev_km INT DEFAULT NULL');
+CREATE TABLE IF NOT EXISTS insurance_records (
+id INT AUTO_INCREMENT PRIMARY KEY,
+vehicle_id INT NOT NULL,
+insurance_type VARCHAR(50) NOT NULL,
+company VARCHAR(100) DEFAULT NULL,
+policy_no VARCHAR(100) DEFAULT NULL,
+start_date VARCHAR(10) NOT NULL,
+end_date VARCHAR(10) NOT NULL,
+premium DOUBLE DEFAULT NULL,
+coverage_amount DOUBLE DEFAULT NULL,
+notes TEXT DEFAULT NULL,
+attachment_path VARCHAR(500) DEFAULT NULL,
+attachment_name VARCHAR(255) DEFAULT NULL,
+attachment_mime VARCHAR(100) DEFAULT NULL,
+attachment_size INT DEFAULT NULL,
+created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
+updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+CALL _add_index_if_missing('insurance_records', 'idx_insurance_vehicle', '(vehicle_id)', 0);
+CALL _add_index_if_missing('insurance_records', 'idx_insurance_end_date', '(end_date)', 0);
+
+-- >>> 0009_vehicle_powertrain.sql
+-- ==========================================================
+-- 0009_vehicle_powertrain.sql (MySQL)
+CALL _try_sql('ALTER TABLE vehicles ADD COLUMN powertrain VARCHAR(10) NOT NULL DEFAULT ''ice''');
+
+-- >>> 0010_operation_logs.sql
+-- ==========================================================
+-- 0010_operation_logs.sql (MySQL)
+CREATE TABLE IF NOT EXISTS operation_logs (
+id INT AUTO_INCREMENT PRIMARY KEY,
+user_id INT DEFAULT NULL,
+username VARCHAR(50) DEFAULT NULL,
+action VARCHAR(50) NOT NULL,
+target_type VARCHAR(50) NOT NULL,
+target_ids TEXT NOT NULL,
+target_summary TEXT DEFAULT NULL,
+detail_json TEXT DEFAULT NULL,
+ip VARCHAR(45) DEFAULT NULL,
+user_agent VARCHAR(500) DEFAULT NULL,
+created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+CALL _add_index_if_missing('operation_logs', 'idx_oplog_created', '(created_at DESC)', 0);
+CALL _add_index_if_missing('operation_logs', 'idx_oplog_user_time', '(username, created_at DESC)', 0);
+CALL _add_index_if_missing('operation_logs', 'idx_oplog_action', '(action, target_type, created_at DESC)', 0);
+
+-- >>> 0011_soft_delete.sql
+-- ==========================================================
+-- 0011_soft_delete.sql (MySQL)
+CALL _try_sql('ALTER TABLE vehicles ADD COLUMN is_deleted TINYINT(1) NOT NULL DEFAULT 0');
+CALL _try_sql('ALTER TABLE wash_records ADD COLUMN is_deleted TINYINT(1) NOT NULL DEFAULT 0');
+CALL _try_sql('ALTER TABLE chemical_usage ADD COLUMN is_deleted TINYINT(1) NOT NULL DEFAULT 0');
+CALL _try_sql('ALTER TABLE maintenance_records ADD COLUMN is_deleted TINYINT(1) NOT NULL DEFAULT 0');
+CALL _try_sql('ALTER TABLE refuel_records ADD COLUMN is_deleted TINYINT(1) NOT NULL DEFAULT 0');
+CALL _try_sql('ALTER TABLE charging_records ADD COLUMN is_deleted TINYINT(1) NOT NULL DEFAULT 0');
+CALL _try_sql('ALTER TABLE insurance_records ADD COLUMN is_deleted TINYINT(1) NOT NULL DEFAULT 0');
+CALL _add_index_if_missing('vehicles', 'ix_vehicles_is_deleted', '(is_deleted)', 0);
+CALL _add_index_if_missing('wash_records', 'ix_wash_records_is_deleted', '(is_deleted)', 0);
+CALL _add_index_if_missing('maintenance_records', 'ix_maintenance_is_deleted', '(is_deleted)', 0);
+CALL _add_index_if_missing('refuel_records', 'ix_refuel_is_deleted', '(is_deleted)', 0);
+CALL _add_index_if_missing('charging_records', 'ix_charging_is_deleted', '(is_deleted)', 0);
+CALL _add_index_if_missing('insurance_records', 'ix_insurance_is_deleted', '(is_deleted)', 0);
+
+-- >>> 0012_operation_logs_recovery.sql
+-- ==========================================================
+-- 0012_operation_logs_recovery.sql (MySQL)
+CALL _try_sql('ALTER TABLE operation_logs ADD COLUMN recovered_at DATETIME DEFAULT NULL');
+
+-- >>> 0013_weather_wttr.sql
+-- ==========================================================
+-- 0013_weather_wttr.sql (MySQL)
+-- 删除旧 CHECK 并重建(MySQL 允许 ALTER TABLE 改 CHECK,但为保险用 ALTER COLUMN)
+CALL _try_sql('ALTER TABLE weather_snapshots MODIFY COLUMN provider VARCHAR(50) NOT NULL');
+
+-- >>> 0014_grocy_auth.sql
+-- ==========================================================
+-- 0014_grocy_auth.sql (MySQL)
+INSERT IGNORE INTO settings (`key`, value, is_secret, description) VALUES
+('grocy_username', '', 1, 'Grocy 用户名(session cookie 鉴权)'),
+('grocy_password', '', 1, 'Grocy 密码(session cookie 鉴权)'),
+('app_city_default', '', 0, '天气默认城市(永久生效)');
+CREATE TABLE IF NOT EXISTS grocy_sync_logs (
+id INT AUTO_INCREMENT PRIMARY KEY,
+action VARCHAR(50) NOT NULL,
+status VARCHAR(20) NOT NULL,
+ok_count INT NOT NULL DEFAULT 0,
+fail_count INT NOT NULL DEFAULT 0,
+detail TEXT DEFAULT NULL,
+started_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
+finished_at DATETIME DEFAULT NULL
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+CALL _add_index_if_missing('grocy_sync_logs', 'idx_grocy_sync_logs_action', '(action)', 0);
+CALL _add_index_if_missing('grocy_sync_logs', 'idx_grocy_sync_logs_started', '(started_at DESC)', 0);
+
+-- >>> 0015_wash_photos.sql
+-- ==========================================================
+-- 0015_wash_photos.sql (MySQL) — 洗车对比照(before / after / detail)
+CREATE TABLE IF NOT EXISTS wash_photos (
+id INT AUTO_INCREMENT PRIMARY KEY,
+wash_id INT NOT NULL,
+photo_type VARCHAR(20) NOT NULL DEFAULT 'detail', -- before / after / detail / scene
+file_path VARCHAR(500) NOT NULL,
+file_name VARCHAR(255) NOT NULL,
+mime_type VARCHAR(50) DEFAULT NULL,
+file_size INT DEFAULT NULL,
+width INT DEFAULT NULL,
+height INT DEFAULT NULL,
+caption VARCHAR(255) DEFAULT NULL,
+sort_order INT NOT NULL DEFAULT 0,
+created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
+is_deleted TINYINT(1) NOT NULL DEFAULT 0,
+INDEX idx_wash_photos_wash (wash_id, is_deleted),
+INDEX idx_wash_photos_type (photo_type),
+INDEX idx_wash_photos_created (created_at DESC)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
+DROP PROCEDURE IF EXISTS _add_index_if_missing;
+DROP PROCEDURE IF EXISTS _try_sql;
+
+CREATE TABLE IF NOT EXISTS schema_migrations (filename VARCHAR(255) PRIMARY KEY, applied_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
+INSERT IGNORE INTO schema_migrations (filename) VALUES ('0001_init.sql');
+INSERT IGNORE INTO schema_migrations (filename) VALUES ('0002_auth.sql');
+INSERT IGNORE INTO schema_migrations (filename) VALUES ('0003_vehicles.sql');
+INSERT IGNORE INTO schema_migrations (filename) VALUES ('0004_grocy_full.sql');
+INSERT IGNORE INTO schema_migrations (filename) VALUES ('0005_inventory_detail.sql');
+INSERT IGNORE INTO schema_migrations (filename) VALUES ('0006_unit_conversion.sql');
+INSERT IGNORE INTO schema_migrations (filename) VALUES ('0007_vehicle_logs.sql');
+INSERT IGNORE INTO schema_migrations (filename) VALUES ('0008_mileage_and_insurance.sql');
+INSERT IGNORE INTO schema_migrations (filename) VALUES ('0009_vehicle_powertrain.sql');
+INSERT IGNORE INTO schema_migrations (filename) VALUES ('0010_operation_logs.sql');
+INSERT IGNORE INTO schema_migrations (filename) VALUES ('0011_soft_delete.sql');
+INSERT IGNORE INTO schema_migrations (filename) VALUES ('0012_operation_logs_recovery.sql');
+INSERT IGNORE INTO schema_migrations (filename) VALUES ('0013_weather_wttr.sql');
+INSERT IGNORE INTO schema_migrations (filename) VALUES ('0014_grocy_auth.sql');
+INSERT IGNORE INTO schema_migrations (filename) VALUES ('0015_wash_photos.sql');
diff --git a/client/index.html b/client/index.html
new file mode 100644
index 0000000..577acdc
--- /dev/null
+++ b/client/index.html
@@ -0,0 +1,42 @@
+
+
+
+
+
+ CarLog 车记 · 个人爱车管理
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/client/package-lock.json b/client/package-lock.json
new file mode 100644
index 0000000..0eb3572
--- /dev/null
+++ b/client/package-lock.json
@@ -0,0 +1,6905 @@
+{
+ "name": "carwash-client",
+ "version": "2.0.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "carwash-client",
+ "version": "2.0.0",
+ "dependencies": {
+ "axios": "^1.7.7",
+ "chart.js": "^4.4.4",
+ "pinia": "^2.2.4",
+ "vue": "^3.5.12",
+ "vue-router": "^4.4.5"
+ },
+ "devDependencies": {
+ "@vitejs/plugin-vue": "^5.1.4",
+ "sharp": "^0.35.1",
+ "vite": "^5.4.10",
+ "vite-plugin-pwa": "^1.3.0"
+ }
+ },
+ "node_modules/@apideck/better-ajv-errors": {
+ "version": "0.3.7",
+ "resolved": "https://registry.npmmirror.com/@apideck/better-ajv-errors/-/better-ajv-errors-0.3.7.tgz",
+ "integrity": "sha512-TajUJwGWbDwkCx/CZi7tRE8PVB7simCvKJfHUsSdvps+aTM/PDPP4gkLmKnc+x3CE//y9i/nj74GqdL/hwk7Iw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "jsonpointer": "^5.0.1",
+ "leven": "^3.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "peerDependencies": {
+ "ajv": ">=8"
+ }
+ },
+ "node_modules/@babel/code-frame": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmmirror.com/@babel/code-frame/-/code-frame-7.29.7.tgz",
+ "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-validator-identifier": "^7.29.7",
+ "js-tokens": "^4.0.0",
+ "picocolors": "^1.1.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/compat-data": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmmirror.com/@babel/compat-data/-/compat-data-7.29.7.tgz",
+ "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/core": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmmirror.com/@babel/core/-/core-7.29.7.tgz",
+ "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@babel/code-frame": "^7.29.7",
+ "@babel/generator": "^7.29.7",
+ "@babel/helper-compilation-targets": "^7.29.7",
+ "@babel/helper-module-transforms": "^7.29.7",
+ "@babel/helpers": "^7.29.7",
+ "@babel/parser": "^7.29.7",
+ "@babel/template": "^7.29.7",
+ "@babel/traverse": "^7.29.7",
+ "@babel/types": "^7.29.7",
+ "@jridgewell/remapping": "^2.3.5",
+ "convert-source-map": "^2.0.0",
+ "debug": "^4.1.0",
+ "gensync": "^1.0.0-beta.2",
+ "json5": "^2.2.3",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/babel"
+ }
+ },
+ "node_modules/@babel/generator": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmmirror.com/@babel/generator/-/generator-7.29.7.tgz",
+ "integrity": "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.29.7",
+ "@babel/types": "^7.29.7",
+ "@jridgewell/gen-mapping": "^0.3.12",
+ "@jridgewell/trace-mapping": "^0.3.28",
+ "jsesc": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-annotate-as-pure": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmmirror.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.29.7.tgz",
+ "integrity": "sha512-OoK6239jHPuSQOoS0kfTVKn0b/rVTk0seKq4Gd2UMLtmOVLjDC0ki3e+c90Trqv2gMfvJFqkiljrr568+qddiw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-compilation-targets": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmmirror.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz",
+ "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/compat-data": "^7.29.7",
+ "@babel/helper-validator-option": "^7.29.7",
+ "browserslist": "^4.24.0",
+ "lru-cache": "^5.1.1",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-create-class-features-plugin": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmmirror.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.29.7.tgz",
+ "integrity": "sha512-IY3ZD9Tmooqr3TUhc3DUWxiuo8xx1DWLhd5M7hQ+ZWJamqM2BbalrBJb2MisSLoYorOj75U03qULCxQTY9r3hg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-annotate-as-pure": "^7.29.7",
+ "@babel/helper-member-expression-to-functions": "^7.29.7",
+ "@babel/helper-optimise-call-expression": "^7.29.7",
+ "@babel/helper-replace-supers": "^7.29.7",
+ "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7",
+ "@babel/traverse": "^7.29.7",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/helper-create-regexp-features-plugin": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmmirror.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.29.7.tgz",
+ "integrity": "sha512-907Uymvqgg1dwUA+7IGwFAOSYzQOuzPXKNJ1yxzwPffzkYFg2q2eHi1fIOs6sXkG9NbIUMunnUlkYsfRFNvomg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-annotate-as-pure": "^7.29.7",
+ "regexpu-core": "^6.3.1",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/helper-define-polyfill-provider": {
+ "version": "0.6.8",
+ "resolved": "https://registry.npmmirror.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.8.tgz",
+ "integrity": "sha512-47UwBLPpQi1NoWzLuHNjRoHlYXMwIJoBf7MFou6viC/sIHWYygpvr0B6IAyh5sBdA2nr2LPIRww8lfaUVQINBA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-compilation-targets": "^7.28.6",
+ "@babel/helper-plugin-utils": "^7.28.6",
+ "debug": "^4.4.3",
+ "lodash.debounce": "^4.0.8",
+ "resolve": "^1.22.11"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"
+ }
+ },
+ "node_modules/@babel/helper-globals": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmmirror.com/@babel/helper-globals/-/helper-globals-7.29.7.tgz",
+ "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-member-expression-to-functions": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmmirror.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.29.7.tgz",
+ "integrity": "sha512-j+7JYmk1JYDtACIGj0QJqqWZjoUpMoEikQGADMaHgCMCSDqd2+P32rfcibUNrGOMWrlzK1WJBdxrB3JJQZwWtg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/traverse": "^7.29.7",
+ "@babel/types": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-module-imports": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmmirror.com/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz",
+ "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/traverse": "^7.29.7",
+ "@babel/types": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-module-transforms": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmmirror.com/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz",
+ "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-module-imports": "^7.29.7",
+ "@babel/helper-validator-identifier": "^7.29.7",
+ "@babel/traverse": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/helper-optimise-call-expression": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmmirror.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.29.7.tgz",
+ "integrity": "sha512-+kmGVjcT9RGYzoDwdwEqEvGgKe3BYq+O1iGzjFubaNgZHwYHP6lsF2Yghf4kEuv9BV7tYDZ913aBW9am6YKong==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-plugin-utils": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmmirror.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz",
+ "integrity": "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-remap-async-to-generator": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmmirror.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.29.7.tgz",
+ "integrity": "sha512-16AMiW26DbXWBbr3B8wNozKM0ydMLB892vaOaJW/fPJdnT8vJk5sdkQcU/isqUxyCE0cEoa8wZOcbgDuC4b6Og==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-annotate-as-pure": "^7.29.7",
+ "@babel/helper-wrap-function": "^7.29.7",
+ "@babel/traverse": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/helper-replace-supers": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmmirror.com/@babel/helper-replace-supers/-/helper-replace-supers-7.29.7.tgz",
+ "integrity": "sha512-atfGXWSeCiF4DnKZIfmJfQRkSw9b9gNNXR1kqKjbhG4pGYCOnkp8OcTB8E3NXjBu8NpheSnOeNKz8KT7UNFTmQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-member-expression-to-functions": "^7.29.7",
+ "@babel/helper-optimise-call-expression": "^7.29.7",
+ "@babel/traverse": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/helper-skip-transparent-expression-wrappers": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmmirror.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.29.7.tgz",
+ "integrity": "sha512-brcMGQaVzIeUb+6/bs1Av0f8YuNNjKY2JyvfRCsFuFsdKccEQ5Ges2y74D74NZ1Rz8lKJ9ksJkfqwQFJ/iNEyQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/traverse": "^7.29.7",
+ "@babel/types": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-string-parser": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmmirror.com/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz",
+ "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-identifier": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz",
+ "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-option": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmmirror.com/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz",
+ "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-wrap-function": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmmirror.com/@babel/helper-wrap-function/-/helper-wrap-function-7.29.7.tgz",
+ "integrity": "sha512-iES0Skag9ERIF68aXadpO6dbXa03mNWK3sEqJaMnLNs/eC3l0lkImdfoy6Y09/SfkpawdAB4RjQ7PVA7TcVGdw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/template": "^7.29.7",
+ "@babel/traverse": "^7.29.7",
+ "@babel/types": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helpers": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmmirror.com/@babel/helpers/-/helpers-7.29.7.tgz",
+ "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/template": "^7.29.7",
+ "@babel/types": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/parser": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmmirror.com/@babel/parser/-/parser-7.29.7.tgz",
+ "integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.29.7"
+ },
+ "bin": {
+ "parser": "bin/babel-parser.js"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmmirror.com/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.29.7.tgz",
+ "integrity": "sha512-j8SrR0zLZrRsC09DlszEx8FpMiwukKffYXMK0d5LmOglO7vGG6sz/BR/20yHqWH+Lnn31JTt2PE3hIWNgM2J6w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.29.7",
+ "@babel/traverse": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/plugin-bugfix-safari-class-field-initializer-scope": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmmirror.com/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.29.7.tgz",
+ "integrity": "sha512-r8j8escF+U2FUHo0KOhPUdMzUO+jp9fInva6+ACVAF3Y97Ev+5iNZwiqTghmzNeWwDkOPlYuTcfb1vDaoZKmAQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmmirror.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.29.7.tgz",
+ "integrity": "sha512-GE1TFSiuFeGsCxmYXZl8HwoPrVlwe4rHPFE8weieGKZqnDORK+Ar3vgWMgW+AOxQ6/2TgLSKx9p6W7O4rC6qgQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/plugin-bugfix-safari-rest-destructuring-rhs-array": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmmirror.com/@babel/plugin-bugfix-safari-rest-destructuring-rhs-array/-/plugin-bugfix-safari-rest-destructuring-rhs-array-7.29.7.tgz",
+ "integrity": "sha512-oBNVCvnO5tND+xSopWvV8WNGfpTfgP4Zr/YXXSj8zfmcPktp5Ku/aZlsIowgSD4fjmgHn6sGmB9APVsU5zOdhA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.29.7",
+ "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmmirror.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.29.7.tgz",
+ "integrity": "sha512-QQt9qKHZ2sg/kivaLr7lnQr8HVrQDdBNSfCsTjiDxRuX/K5ORyKq+Bu8Xr0cDE3Dfkv0cw28Ve0EKyKMvulkOw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.29.7",
+ "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7",
+ "@babel/plugin-transform-optional-chaining": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.13.0"
+ }
+ },
+ "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmmirror.com/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.29.7.tgz",
+ "integrity": "sha512-pn6QacGLgvCcwc+syUhKE/qSjV2D1IHDB84RNxWYSt1mW3K/SCtjinZ2p0cETJxAWBjPy3K/1lHwG5BjjPxNlw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.29.7",
+ "@babel/traverse": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/plugin-proposal-private-property-in-object": {
+ "version": "7.21.0-placeholder-for-preset-env.2",
+ "resolved": "https://registry.npmmirror.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz",
+ "integrity": "sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-import-assertions": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.29.7.tgz",
+ "integrity": "sha512-/An1OCBN93thpBAGyfsK2pcf0jvju1SAtKkL2Ny++B5Sy6sqgzXDQH1cZxWbF96Wuk+bn41MDA9bLd4VVAw6rw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-import-attributes": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.29.7.tgz",
+ "integrity": "sha512-zGYcYfq/WmZ4V+kBIXQon9dSSc8ircGZqw9ZaNhhGj9nZkeBu1jHLBDQqYYi5WA9uawvA2sIMbry2nCFhf5Djg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-unicode-sets-regex": {
+ "version": "7.18.6",
+ "resolved": "https://registry.npmmirror.com/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz",
+ "integrity": "sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-create-regexp-features-plugin": "^7.18.6",
+ "@babel/helper-plugin-utils": "^7.18.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-arrow-functions": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.29.7.tgz",
+ "integrity": "sha512-N7zArUXWzAMzm+/N0uPBeVB3Fam5lMxtUwMmDK5f/IBBS7a7p1qeUoxd/6CckXoxUdgsntq1Dh8xNW06maZbDQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-async-generator-functions": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.29.7.tgz",
+ "integrity": "sha512-d98gXZkgswvkyohMBABkhm3GeXhYj8psWfwQ2C7gtfrKGTykQa/iOIi+JJhwMjPlZ6Vm2XN+DCf3Es1EoG4ZLA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.29.7",
+ "@babel/helper-remap-async-to-generator": "^7.29.7",
+ "@babel/traverse": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-async-to-generator": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.29.7.tgz",
+ "integrity": "sha512-pcUb2SS+RMo9TWVBwKGI5ShtoG7R+zBsFmCKDa6fe8c+hPr3XJlZgoE5j6i8W7gDjhyvy+85vmYexanvXh3d1w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-module-imports": "^7.29.7",
+ "@babel/helper-plugin-utils": "^7.29.7",
+ "@babel/helper-remap-async-to-generator": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-block-scoped-functions": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.29.7.tgz",
+ "integrity": "sha512-cUSmjh72N+rN4PrkFlN1dJwNCwjVp5d38/CQrEsFggkD10UiFlBFgdH3tv5dNsLuHY+3S8db2xCHjhZcv5WgvA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-block-scoping": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.29.7.tgz",
+ "integrity": "sha512-ONyr4+AZhKh8yKWInVxU9AXA9EbsyeLcL6V0dJy6M2/62vuvpGm29zzuymbTpdc451GEpDIdAyPLP3r+P61yKQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-class-properties": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.29.7.tgz",
+ "integrity": "sha512-GtcpjFvanPfzNQi3eTitsCqtRRmmqzpy/A+yhTR1HaZo1Ly3EA8ZXxlPyHdR8/IuRMYc3E4wdGBewB2QKQjAaA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-create-class-features-plugin": "^7.29.7",
+ "@babel/helper-plugin-utils": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-class-static-block": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.29.7.tgz",
+ "integrity": "sha512-kibJgmEdX2iMwsHY2tSZNDgj8PwIlCQz7FK9KuGKO8zsuoUwSEhoNnNVp/emKWrbY4HeO6kkXfdMqRKKKXBm2A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-create-class-features-plugin": "^7.29.7",
+ "@babel/helper-plugin-utils": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.12.0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-classes": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.29.7.tgz",
+ "integrity": "sha512-qV0OGGBVacduzQHE649JyCneOFI/maT+YKsO+K4Yi3xv2wTPNjM/W2o2gdzMwEAZz7fXNTHAe0NcSg30bIN69g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-annotate-as-pure": "^7.29.7",
+ "@babel/helper-compilation-targets": "^7.29.7",
+ "@babel/helper-globals": "^7.29.7",
+ "@babel/helper-plugin-utils": "^7.29.7",
+ "@babel/helper-replace-supers": "^7.29.7",
+ "@babel/traverse": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-computed-properties": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.29.7.tgz",
+ "integrity": "sha512-RK7/IyU5phpuCdBAuig5VkzG/EnbDaui5SQGdU9BFrHdV+mV4cUjLMQ9lJDjLNtWHsqtiefpGZUXQP2BiTYMsA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.29.7",
+ "@babel/template": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-destructuring": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.29.7.tgz",
+ "integrity": "sha512-iPX8aD6H9zV5s7ZsqTdNocPN/MGQ5sSMnElKrktxjJRMnB2jN/1p2+R7GkfD6CAYoVFqy5A4XnSIUeGgJzIWpg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.29.7",
+ "@babel/traverse": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-dotall-regex": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.29.7.tgz",
+ "integrity": "sha512-3qc18hsD2RdZiyJNDNc7HQpv6xbncwh8FYtxNFFzclSyh/trPD9KkVR9BDECUjDLvb7yJVF15GfYUuC+LMkkiQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-create-regexp-features-plugin": "^7.29.7",
+ "@babel/helper-plugin-utils": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-duplicate-keys": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.29.7.tgz",
+ "integrity": "sha512-6IvRRriEMqnBwD6chtxdLpMYCHWEzN+oL5cyQtjykya19UgzbmKhxmhZgKC/LHxS2nYr9Q/qYPZ5Lr6jOL9+yQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-duplicate-named-capturing-groups-regex": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.29.7.tgz",
+ "integrity": "sha512-2wiIyo2BjtgU7HufSeDnL9L2O7zr8jmhFKuSr65VpRkUiRKRNpb0mdlk56+XPPKoIrfHqzbMuglDvZun0RISsA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-create-regexp-features-plugin": "^7.29.7",
+ "@babel/helper-plugin-utils": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-dynamic-import": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.29.7.tgz",
+ "integrity": "sha512-giOlEm/EFjfjr+te9NsdjkUo2v4f8rS/SXPumRVHAtbNcyNlvtREkU1dZzaIDclNpnaVhlCqRdFKhJBjBikzLg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-explicit-resource-management": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-explicit-resource-management/-/plugin-transform-explicit-resource-management-7.29.7.tgz",
+ "integrity": "sha512-Rstj7coNz8sE+7Ju7ihpHLI564lsK5pUpNNlvptCIC/16E/S5hbl6n3kESPKdNRmqEWlpn5xpS5Q2dvXBsySLw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.29.7",
+ "@babel/plugin-transform-destructuring": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-exponentiation-operator": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.29.7.tgz",
+ "integrity": "sha512-zFpMOTLZBdW5LfObqcSbL6kefg4R4eLdmvS0wbN9M6D5Mym/sKm9toOoWyVOa+xDjvCnuWcHls2YonXwHvH3CQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-export-namespace-from": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.29.7.tgz",
+ "integrity": "sha512-24B2nOy2TeJSMheqwPD4DDQOV/elLSIlKxjZt4i05H5AgdPdWR3n18HnNrcJ+j76WJd9gbwb9jPjNYUy6RautA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-for-of": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.29.7.tgz",
+ "integrity": "sha512-zeSIHh0+E1Um1WJRXCFlHQYu2ieJNdivLLjlBEp+dIBu3S51n+SZZmIXjxnItw6pz56Cn+KvK68BIBVsxq2JiQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.29.7",
+ "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-function-name": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.29.7.tgz",
+ "integrity": "sha512-otRWaHXE6fbAGkePvaj/kvs3HsqXfPhlnzwSOlnFgbqCPMd975dW+4wZ00WFBt+/YlBGcJwNrARQTOJOb4ZrIg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-compilation-targets": "^7.29.7",
+ "@babel/helper-plugin-utils": "^7.29.7",
+ "@babel/traverse": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-json-strings": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.29.7.tgz",
+ "integrity": "sha512-RRnE2+eon1rJAq8MnoF1b5kTpY1vU88twHcvcKMrsqP/jxIRqDVs9iJB5fqPuqyeFAW0wJo4MlUIPpQCq/aRsg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-literals": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.29.7.tgz",
+ "integrity": "sha512-DZ/oLP21ZuWx1vKqnoNv6/tvEK48AQOBRai40CX9dTjGluvT/YZCyY3rryDtyUqCEoyNroy5KKPwX2iQCiRvyw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-logical-assignment-operators": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.29.7.tgz",
+ "integrity": "sha512-A0H91hh6W8MFRkp5TqJmMr39jzGD1A1E1Ysiv2O06Sfbhkapm+XyIzxWCEh5kqwOZ1/8QZ0dY3SeQ7XBqfJd5Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-member-expression-literals": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.29.7.tgz",
+ "integrity": "sha512-hl1kwFZCCiDyfH25Xmco9jTrkPgnS9pmOzSG7W5I4SaGbLeqKv417hcU2RKmaxoPEgsoJh7ZPOrnPGq99bHoUg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-modules-amd": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.29.7.tgz",
+ "integrity": "sha512-fxtQoH3m5ywUSIfaH0FGCzWu4McsYon5bD3K4XnskC7f+OyQMj7rsOMi4NvvmJ83WwBAg4UCe+ov4VZlqEvyew==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-module-transforms": "^7.29.7",
+ "@babel/helper-plugin-utils": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-modules-commonjs": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.29.7.tgz",
+ "integrity": "sha512-j0vCldybPC5b5dwCQOJ21uKtHzt7hxLygJTg9eF1ScfaikEDNfzn94XoW5Fi+seBR0nCyL23xaBFFkq7dTM8XQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-module-transforms": "^7.29.7",
+ "@babel/helper-plugin-utils": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-modules-systemjs": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.29.7.tgz",
+ "integrity": "sha512-TM2ZcQLoG2/y4HODiStCo10DibYhWhGWAwVv+EQKmG/7GFl0N+AAmUiXOMKM+aiJ9XBJ9AHVZBvTzMnJ2sM3cQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-module-transforms": "^7.29.7",
+ "@babel/helper-plugin-utils": "^7.29.7",
+ "@babel/helper-validator-identifier": "^7.29.7",
+ "@babel/traverse": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-modules-umd": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.29.7.tgz",
+ "integrity": "sha512-B4UkaTK3QpgCwJnrxKfMPKdo92CN7OKXAlpAAnM3UPu0Q0lCCk57ylA9AJbRy2v8dDKOPAAWcoR6CMyeoHwRCA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-module-transforms": "^7.29.7",
+ "@babel/helper-plugin-utils": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-named-capturing-groups-regex": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.29.7.tgz",
+ "integrity": "sha512-vuFoLwr4qnv2xbZ16SQd6uPcH5FNrLHhk/Jzo++0XJFcaDsr4gjJVg6j398oMHiC+83k/GiBzviwF5KBJkPUtQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-create-regexp-features-plugin": "^7.29.7",
+ "@babel/helper-plugin-utils": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-new-target": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.29.7.tgz",
+ "integrity": "sha512-fEo41GmsOUhOBlw8ioo6zvjX5Xc2Lqkzlyfqbpsk3eB6TReV18uhxZ0esfEokVbY2+PVJAQHNKxER6lGrzNd3A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-nullish-coalescing-operator": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.29.7.tgz",
+ "integrity": "sha512-idmp1dFaekP9GbcMvG24Kvw2BfhFZjHnNJCkV4WuIY4PskJzwI3f1N5OdgYke38T7rftO6ERulFRn2cFeZwRkg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-numeric-separator": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.29.7.tgz",
+ "integrity": "sha512-zR7fv/z14OjgHl4AgRtkDBvBMhIzCxqV/qN/2BCRC7LjFwvuzjYe7gDWxC4Wl/SNsLM6SE1IWvRPYMgSJaUvNw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-object-rest-spread": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.29.7.tgz",
+ "integrity": "sha512-Ld98jn4c0smUywL57m7SgsHq3OpThOa6LqZJif3G6jYOovPleoFhVrBJ1WegRApSFB2wu4+RelAj9AC9G08Z4A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-compilation-targets": "^7.29.7",
+ "@babel/helper-plugin-utils": "^7.29.7",
+ "@babel/plugin-transform-destructuring": "^7.29.7",
+ "@babel/plugin-transform-parameters": "^7.29.7",
+ "@babel/traverse": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-object-super": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.29.7.tgz",
+ "integrity": "sha512-Ea/diGcw0twB5IlZPO5sgET6fJsLJqPABqTuFWIR+iMPGPZJkATEIWx0wa+aEQ5UY1CBQyP/gkAiLEqn1vBiQA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.29.7",
+ "@babel/helper-replace-supers": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-optional-catch-binding": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.29.7.tgz",
+ "integrity": "sha512-sLsyndxK2VwX6yNUOakMb7Sh553ZTe/vVM1XJ+9Z5aW1ytsc8xOIwmyk05NNjN60vkc5/KqoTH6hB4V41LJhng==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-optional-chaining": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.29.7.tgz",
+ "integrity": "sha512-6GM1dhvK3gNODkXcEcMCOLEDCLSoZ/sBbro2Ax8HURyasQ4NshagQixkRFdh5niI6E4gmA/jYI/4aT7rRos3ZQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.29.7",
+ "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-parameters": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.29.7.tgz",
+ "integrity": "sha512-ZDOBqV/qLYJI0YElr8DcENEyARsFQeESqWXH6gZlghYXuPPjvweuDhP4VyEi4BlUBlLRFZVjxoZDMjxhLW766g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-private-methods": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.29.7.tgz",
+ "integrity": "sha512-/6Rz4DK1ETDEM/bWHsPHcaEe7ZaT1EqSXjtSP/L0DijOYuaUhiRiOKcwpZ8P7zR4xXEHc2ITdiCgBm9Tpyv9ug==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-create-class-features-plugin": "^7.29.7",
+ "@babel/helper-plugin-utils": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-private-property-in-object": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.29.7.tgz",
+ "integrity": "sha512-+BNo06dnrzdNNqCm1X6YUaVv0DKk8Q+JYcoZfOkLhYWNCXzlwTSRq8zGWayT1csjcpNXV9CQTBRRbmTLZac5cA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-annotate-as-pure": "^7.29.7",
+ "@babel/helper-create-class-features-plugin": "^7.29.7",
+ "@babel/helper-plugin-utils": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-property-literals": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.29.7.tgz",
+ "integrity": "sha512-bOMRLQuI0A5ZqHq3OWJ89/rXpJ/NJrbVhXiP4zwPGMs6kpcVsuTUNjwoE30K0Qm3mf48a/TnRYYD6vPNqcg6jA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-regenerator": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.29.7.tgz",
+ "integrity": "sha512-rNNFV0DBAJp988xW2DOntfDoYn1eR8GGF5AT5vYc+rjyfaQkM242c9tZUHHPe7KYaiJizXPWhQTzzdbXySyhBw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-regexp-modifiers": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.29.7.tgz",
+ "integrity": "sha512-mB5Fs0VWrJ42ZCmc8114v60qetdaUVNkj9PmSZRmanCZM3S9hm0CFRLjRmYIsuXav14l2jvZ+4T8iiCGnhj3nQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-create-regexp-features-plugin": "^7.29.7",
+ "@babel/helper-plugin-utils": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-reserved-words": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.29.7.tgz",
+ "integrity": "sha512-5+YhdpVgmfSmwZyLMftfaiffLRMHjzIRHFHHLdibcSyJm2pasMrKHrO3Ptrt2DRshjvpgjEJJ1zVW14WPq/6QA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-shorthand-properties": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.29.7.tgz",
+ "integrity": "sha512-I+WYbGBAiCn7nA6xBrlgPH+MB7HWb4u8pv5S0Pv7OtwNvIFvCCb24YlttKEeUFVurfBCEaOTnuhlqsb7f0Z5Dg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-spread": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.29.7.tgz",
+ "integrity": "sha512-/u5K1QWada7tbYNqTjMh96718g9NTwh9tfPJMsSmVsQwGT447FskV+KcfeXkXq2GWki4EM/MuTdmBec+hOuVTQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.29.7",
+ "@babel/helper-skip-transparent-expression-wrappers": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-sticky-regex": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.29.7.tgz",
+ "integrity": "sha512-BCHzNYJGe9l7EpwwDBN/ztlL2NYFFq8hp9ddjtUEM9f2O7S7kKV/lL6Fwo7IF7NSkYhPK2vO+86nIGltA90MsA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-template-literals": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.29.7.tgz",
+ "integrity": "sha512-NCSEJ4sLFU2gqAub45HYh4fus2yQ36rr6ei6vpU7NdoJqCpxvEG8E6eJpscGyXP3VHD2Ny+fSXr04k1hoUrFqA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-typeof-symbol": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.29.7.tgz",
+ "integrity": "sha512-223mNGoTkBiTEWFoK+Q6Go3tueMRclO8vxxxxquNCYuNI4jWOofFKJRRDu6SDrB8Sgo1UEGW9T4GAQ8ZyRso1A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-unicode-escapes": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.29.7.tgz",
+ "integrity": "sha512-jCfXxSjf94lf4E0hKE0AByxF6F3/pVFqRdUUNkDJhsY0m1ZKjnN6ZYyMeHNpzflxb/0q5b7t3p+BE+SLF1WOtA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-unicode-property-regex": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.29.7.tgz",
+ "integrity": "sha512-OgZ+zoAJgZLUCunsTRQ5LAjOywDv5zzZ2/hQ5aMw1pGXyY2rtE8/chXYUmu3AlVHKpm10KEdG9aMwbI/K76ZGw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-create-regexp-features-plugin": "^7.29.7",
+ "@babel/helper-plugin-utils": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-unicode-regex": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.29.7.tgz",
+ "integrity": "sha512-7D/x/23/d/3VqZ0QA+LGbZMlGwZjztBygSWWWsfTPoQ1oQ6Q1P6Mr3d0kk42XabyUVw+fha3LqdRsFqeKqvCyA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-create-regexp-features-plugin": "^7.29.7",
+ "@babel/helper-plugin-utils": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-unicode-sets-regex": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.29.7.tgz",
+ "integrity": "sha512-BLOhLht9DOJwIxlmp91wHvkXv1lguuHS3/FwUO8HL1H0u8s4hR1gASVFyilu9iGtcTRYqjTZmlsFFeQletntEg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-create-regexp-features-plugin": "^7.29.7",
+ "@babel/helper-plugin-utils": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/preset-env": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmmirror.com/@babel/preset-env/-/preset-env-7.29.7.tgz",
+ "integrity": "sha512-GYzX36n1nsciIb0uyH0GHwxwtNwPQIcpxSeiVLDtG/B7jB5xXgchnmL1f/jCX5o+pwnaDBtO60ONSJhEBJfxYA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/compat-data": "^7.29.7",
+ "@babel/helper-compilation-targets": "^7.29.7",
+ "@babel/helper-plugin-utils": "^7.29.7",
+ "@babel/helper-validator-option": "^7.29.7",
+ "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.29.7",
+ "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.29.7",
+ "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.29.7",
+ "@babel/plugin-bugfix-safari-rest-destructuring-rhs-array": "^7.29.7",
+ "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.29.7",
+ "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.29.7",
+ "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2",
+ "@babel/plugin-syntax-import-assertions": "^7.29.7",
+ "@babel/plugin-syntax-import-attributes": "^7.29.7",
+ "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6",
+ "@babel/plugin-transform-arrow-functions": "^7.29.7",
+ "@babel/plugin-transform-async-generator-functions": "^7.29.7",
+ "@babel/plugin-transform-async-to-generator": "^7.29.7",
+ "@babel/plugin-transform-block-scoped-functions": "^7.29.7",
+ "@babel/plugin-transform-block-scoping": "^7.29.7",
+ "@babel/plugin-transform-class-properties": "^7.29.7",
+ "@babel/plugin-transform-class-static-block": "^7.29.7",
+ "@babel/plugin-transform-classes": "^7.29.7",
+ "@babel/plugin-transform-computed-properties": "^7.29.7",
+ "@babel/plugin-transform-destructuring": "^7.29.7",
+ "@babel/plugin-transform-dotall-regex": "^7.29.7",
+ "@babel/plugin-transform-duplicate-keys": "^7.29.7",
+ "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.29.7",
+ "@babel/plugin-transform-dynamic-import": "^7.29.7",
+ "@babel/plugin-transform-explicit-resource-management": "^7.29.7",
+ "@babel/plugin-transform-exponentiation-operator": "^7.29.7",
+ "@babel/plugin-transform-export-namespace-from": "^7.29.7",
+ "@babel/plugin-transform-for-of": "^7.29.7",
+ "@babel/plugin-transform-function-name": "^7.29.7",
+ "@babel/plugin-transform-json-strings": "^7.29.7",
+ "@babel/plugin-transform-literals": "^7.29.7",
+ "@babel/plugin-transform-logical-assignment-operators": "^7.29.7",
+ "@babel/plugin-transform-member-expression-literals": "^7.29.7",
+ "@babel/plugin-transform-modules-amd": "^7.29.7",
+ "@babel/plugin-transform-modules-commonjs": "^7.29.7",
+ "@babel/plugin-transform-modules-systemjs": "^7.29.7",
+ "@babel/plugin-transform-modules-umd": "^7.29.7",
+ "@babel/plugin-transform-named-capturing-groups-regex": "^7.29.7",
+ "@babel/plugin-transform-new-target": "^7.29.7",
+ "@babel/plugin-transform-nullish-coalescing-operator": "^7.29.7",
+ "@babel/plugin-transform-numeric-separator": "^7.29.7",
+ "@babel/plugin-transform-object-rest-spread": "^7.29.7",
+ "@babel/plugin-transform-object-super": "^7.29.7",
+ "@babel/plugin-transform-optional-catch-binding": "^7.29.7",
+ "@babel/plugin-transform-optional-chaining": "^7.29.7",
+ "@babel/plugin-transform-parameters": "^7.29.7",
+ "@babel/plugin-transform-private-methods": "^7.29.7",
+ "@babel/plugin-transform-private-property-in-object": "^7.29.7",
+ "@babel/plugin-transform-property-literals": "^7.29.7",
+ "@babel/plugin-transform-regenerator": "^7.29.7",
+ "@babel/plugin-transform-regexp-modifiers": "^7.29.7",
+ "@babel/plugin-transform-reserved-words": "^7.29.7",
+ "@babel/plugin-transform-shorthand-properties": "^7.29.7",
+ "@babel/plugin-transform-spread": "^7.29.7",
+ "@babel/plugin-transform-sticky-regex": "^7.29.7",
+ "@babel/plugin-transform-template-literals": "^7.29.7",
+ "@babel/plugin-transform-typeof-symbol": "^7.29.7",
+ "@babel/plugin-transform-unicode-escapes": "^7.29.7",
+ "@babel/plugin-transform-unicode-property-regex": "^7.29.7",
+ "@babel/plugin-transform-unicode-regex": "^7.29.7",
+ "@babel/plugin-transform-unicode-sets-regex": "^7.29.7",
+ "@babel/preset-modules": "0.1.6-no-external-plugins",
+ "babel-plugin-polyfill-corejs2": "^0.4.15",
+ "babel-plugin-polyfill-corejs3": "^0.14.0",
+ "babel-plugin-polyfill-regenerator": "^0.6.6",
+ "core-js-compat": "^3.48.0",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/preset-modules": {
+ "version": "0.1.6-no-external-plugins",
+ "resolved": "https://registry.npmmirror.com/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz",
+ "integrity": "sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.0.0",
+ "@babel/types": "^7.4.4",
+ "esutils": "^2.0.2"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0 || ^8.0.0-0 <8.0.0"
+ }
+ },
+ "node_modules/@babel/runtime": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmmirror.com/@babel/runtime/-/runtime-7.29.7.tgz",
+ "integrity": "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/template": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmmirror.com/@babel/template/-/template-7.29.7.tgz",
+ "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.29.7",
+ "@babel/parser": "^7.29.7",
+ "@babel/types": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/traverse": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmmirror.com/@babel/traverse/-/traverse-7.29.7.tgz",
+ "integrity": "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.29.7",
+ "@babel/generator": "^7.29.7",
+ "@babel/helper-globals": "^7.29.7",
+ "@babel/parser": "^7.29.7",
+ "@babel/template": "^7.29.7",
+ "@babel/types": "^7.29.7",
+ "debug": "^4.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/types": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmmirror.com/@babel/types/-/types-7.29.7.tgz",
+ "integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-string-parser": "^7.29.7",
+ "@babel/helper-validator-identifier": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@emnapi/runtime": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmmirror.com/@emnapi/runtime/-/runtime-1.11.1.tgz",
+ "integrity": "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@esbuild/aix-ppc64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmmirror.com/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz",
+ "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "aix"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/android-arm": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmmirror.com/@esbuild/android-arm/-/android-arm-0.21.5.tgz",
+ "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/android-arm64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmmirror.com/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz",
+ "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/android-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmmirror.com/@esbuild/android-x64/-/android-x64-0.21.5.tgz",
+ "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/darwin-arm64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmmirror.com/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz",
+ "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/darwin-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmmirror.com/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz",
+ "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/freebsd-arm64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmmirror.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz",
+ "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/freebsd-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmmirror.com/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz",
+ "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-arm": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmmirror.com/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz",
+ "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-arm64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmmirror.com/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz",
+ "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-ia32": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmmirror.com/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz",
+ "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-loong64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmmirror.com/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz",
+ "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-mips64el": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmmirror.com/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz",
+ "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==",
+ "cpu": [
+ "mips64el"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-ppc64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmmirror.com/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz",
+ "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-riscv64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmmirror.com/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz",
+ "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-s390x": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmmirror.com/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz",
+ "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/linux-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmmirror.com/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz",
+ "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/netbsd-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmmirror.com/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz",
+ "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/openbsd-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmmirror.com/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz",
+ "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/sunos-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmmirror.com/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz",
+ "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "sunos"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/win32-arm64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmmirror.com/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz",
+ "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/win32-ia32": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmmirror.com/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz",
+ "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@esbuild/win32-x64": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz",
+ "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@img/colour": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmmirror.com/@img/colour/-/colour-1.1.0.tgz",
+ "integrity": "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@img/sharp-darwin-arm64": {
+ "version": "0.35.1",
+ "resolved": "https://registry.npmmirror.com/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.35.1.tgz",
+ "integrity": "sha512-T15JRWOubQ3f5+GxnWeIvo47u5qV0M9HBgJhT+f2gE1e9e6OhR6K73Re52Hm80qWcu1DNb3GweKmpr/MnuP2Ow==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=20.9.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-darwin-arm64": "1.3.0"
+ }
+ },
+ "node_modules/@img/sharp-darwin-x64": {
+ "version": "0.35.1",
+ "resolved": "https://registry.npmmirror.com/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.35.1.tgz",
+ "integrity": "sha512-t1CPD0cr7XCHjwUj6tQ5MC0pCi866I+gUW6zbUX4aFPnKd1DFBtk0M+gWcjX8VeEzgfCNiSiNTVFZ6b7kvdbnQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=20.9.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-darwin-x64": "1.3.0"
+ }
+ },
+ "node_modules/@img/sharp-freebsd-wasm32": {
+ "version": "0.35.1",
+ "resolved": "https://registry.npmmirror.com/@img/sharp-freebsd-wasm32/-/sharp-freebsd-wasm32-0.35.1.tgz",
+ "integrity": "sha512-MBSQXqNPThW9EcZ905H6N4sEdX5EwZEYzGx5EBq9ncDCGJALMiY1xPFJxNdzuB1iBjLOpIfxajM6YxdvwmQSLA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "dependencies": {
+ "@img/sharp-wasm32": "0.35.1"
+ },
+ "engines": {
+ "node": ">=20.9.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-darwin-arm64": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmmirror.com/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.3.0.tgz",
+ "integrity": "sha512-EKbmBKtyTH+GPFDRw2TgK2oV6hyxxlJVIar4hoTYSNmIwipgMFdxPQqR392GmfdsPGWga0mCFN1cCKjRb9cljw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-darwin-x64": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmmirror.com/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.3.0.tgz",
+ "integrity": "sha512-Pl2OmOvrJ42adUllESxBsG54PfXLo1OYg9i3c5/5Ln/qJ0gZuTM9YMhQJPIbXqwidLRc/c2zuHt4RsrymmNv7A==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-arm": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmmirror.com/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.3.0.tgz",
+ "integrity": "sha512-A8UpHoUDW4DwnXoV6+q3C1s7QLRAHtPDEjWuNZjwHMyoCNZnm0GeNN8ls9f/bsEYTRQRW96C/n34XJQHJ2fT7A==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-arm64": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmmirror.com/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.3.0.tgz",
+ "integrity": "sha512-C0SqjoFKnszqa44EQ7xoaT48nnO0lOyXEULfXMWi8krrjOPGYkeK30Okzla6ATbBYsyZ0ySinK0FVkpv3DwzfQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-ppc64": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmmirror.com/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.3.0.tgz",
+ "integrity": "sha512-WOpkVxAjFd369iaIzEgNRreFD+gWdUMIGD5zplhNKNeqS6mm5dac3q2AFyCBmzYoAdouzZvRBgxy4z8QHZb4/A==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-riscv64": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmmirror.com/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.3.0.tgz",
+ "integrity": "sha512-DRWw0mOHusrCCuw2rqP87oLg6PGlkomVDFqw2hIwsSfwWpu4k3XLcBPaKKl6ct/GtL/cwNkgwjV/tc0Mqht3VA==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-s390x": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmmirror.com/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.3.0.tgz",
+ "integrity": "sha512-9APy+nFWhHS+kzLgWZfLcyrUd7YqnAQVa4BPOo4xkoHpdoktOAPG4cEr9+Jpl0TtqfVmcMJimNL5qNTyyOHZNA==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-x64": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmmirror.com/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.3.0.tgz",
+ "integrity": "sha512-y9RNUYDe2A1UAdhLyfeOodGRszQdaEoe4nfOpp/sNVPl2CWIcUyFaDoCh4vPLPxu19803j2naLqZup2WxDXCLA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linuxmusl-arm64": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmmirror.com/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.3.0.tgz",
+ "integrity": "sha512-cC1wkC0Mlucd0KSiGrLkJnB/ZqPvZCntc/Lk7ZnYO5ZSbF2euNek4Xvxafojq+wN1q/W0eprdpUIjUr/EV2PBg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linuxmusl-x64": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmmirror.com/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.3.0.tgz",
+ "integrity": "sha512-LiYMhUZicB1QG//+RvmYZpXJO8fYRENfp+MZUCnG9aw+AKvGAy9gPaCnuwsPcBFs8EV66M0NNxj9VHcNklE8zw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-linux-arm": {
+ "version": "0.35.1",
+ "resolved": "https://registry.npmmirror.com/@img/sharp-linux-arm/-/sharp-linux-arm-0.35.1.tgz",
+ "integrity": "sha512-jygmR02PpCYypt7xB7nst1vqjZp/BpRA/Kf9nK7qRponJ/KrLPaZWEG4G15z1d2FZ6XqI+T0350ha3RSnKx24A==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=20.9.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-arm": "1.3.0"
+ }
+ },
+ "node_modules/@img/sharp-linux-arm64": {
+ "version": "0.35.1",
+ "resolved": "https://registry.npmmirror.com/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.35.1.tgz",
+ "integrity": "sha512-ErCRyGU7LeoaFBZ0xW8hhLlXzhAg80sc4vxePB86qvtEvW1jEhhmbiNBP4oEzZfPMnu6HwHXfzD2W2kBU+RnCw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=20.9.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-arm64": "1.3.0"
+ }
+ },
+ "node_modules/@img/sharp-linux-ppc64": {
+ "version": "0.35.1",
+ "resolved": "https://registry.npmmirror.com/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.35.1.tgz",
+ "integrity": "sha512-LUWZ2+r2UoLCd8j0RLCwQ4gL6w47+Y7igxtVnPIDXOOEjV86LpBkAHq5VpJeg+GHbw0KN/JWlPJOdZjyZnFqFQ==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=20.9.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-ppc64": "1.3.0"
+ }
+ },
+ "node_modules/@img/sharp-linux-riscv64": {
+ "version": "0.35.1",
+ "resolved": "https://registry.npmmirror.com/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.35.1.tgz",
+ "integrity": "sha512-i7x6J3mwF4JgT0sM4V4WlAWdJ0bucPtA9rzO1bTji1n5qgBq/W5nn87RvOQPleuuxahNoLdTngByD8/vDDLArw==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=20.9.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-riscv64": "1.3.0"
+ }
+ },
+ "node_modules/@img/sharp-linux-s390x": {
+ "version": "0.35.1",
+ "resolved": "https://registry.npmmirror.com/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.35.1.tgz",
+ "integrity": "sha512-0zSaTUjTF0kIWTSYxD4EG/nvCU4jez53+3RdURtoY3HvbXtIQ98W90JnrGz/oLRFuEnfIy9+7xeq883euc0ZWw==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=20.9.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-s390x": "1.3.0"
+ }
+ },
+ "node_modules/@img/sharp-linux-x64": {
+ "version": "0.35.1",
+ "resolved": "https://registry.npmmirror.com/@img/sharp-linux-x64/-/sharp-linux-x64-0.35.1.tgz",
+ "integrity": "sha512-NbJD4mWdeyrNQKluO/tR/wBDOelcowSVGNBWxI0e3ZtlXc6F/UOVKDj1MLD4zl3oHTuvKW3s+MA9N54YTldAYw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=20.9.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-x64": "1.3.0"
+ }
+ },
+ "node_modules/@img/sharp-linuxmusl-arm64": {
+ "version": "0.35.1",
+ "resolved": "https://registry.npmmirror.com/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.35.1.tgz",
+ "integrity": "sha512-VoW2sQCWI+0YIKQEmWJ8vzaQjTg9wIyfkFpvEfAS2h43X6iHu7GTk1hhOgB4IpSzCHe8UwQZIcx7b81VTaOrJA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=20.9.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linuxmusl-arm64": "1.3.0"
+ }
+ },
+ "node_modules/@img/sharp-linuxmusl-x64": {
+ "version": "0.35.1",
+ "resolved": "https://registry.npmmirror.com/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.35.1.tgz",
+ "integrity": "sha512-LjBoSd/c5JU0/K5MwzDMlgsSRP2bPn98JQGFFQAOLQ0bU/1z4ekxUdSKY9BmlwSh/cA+OrvpgsWqfZyYfVHBRw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=20.9.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linuxmusl-x64": "1.3.0"
+ }
+ },
+ "node_modules/@img/sharp-wasm32": {
+ "version": "0.35.1",
+ "resolved": "https://registry.npmmirror.com/@img/sharp-wasm32/-/sharp-wasm32-0.35.1.tgz",
+ "integrity": "sha512-PCQUoQdZyE8tp3HpbevuihfUmgSP4qWI0FGEPWoeXqaS+cUrFfemabHQiebUmUmlUhCuNnQMxGrQ+CPqK4hnxg==",
+ "dev": true,
+ "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/runtime": "^1.11.0"
+ },
+ "engines": {
+ "node": ">=20.9.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-webcontainers-wasm32": {
+ "version": "0.35.1",
+ "resolved": "https://registry.npmmirror.com/@img/sharp-webcontainers-wasm32/-/sharp-webcontainers-wasm32-0.35.1.tgz",
+ "integrity": "sha512-xU2ml2bU2OPxYVvW2A6ae4M1g5QKyhKG06P4FAt+YEaFQQO0919Qx+XxIZEUuWTMoDViLpMws2/dQwoe/VcA6A==",
+ "cpu": [
+ "wasm32"
+ ],
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "@img/sharp-wasm32": "0.35.1"
+ },
+ "engines": {
+ "node": ">=20.9.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-win32-arm64": {
+ "version": "0.35.1",
+ "resolved": "https://registry.npmmirror.com/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.35.1.tgz",
+ "integrity": "sha512-IkmHwuFhYpd3bTsN5SAahjwhiAcyXPooBt8vEUgxY3T0IP70sSJ0nU1xiPzZY8AH/OB1XpV3j8aZSVSOSfTbdA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0 AND LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=20.9.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-win32-ia32": {
+ "version": "0.35.1",
+ "resolved": "https://registry.npmmirror.com/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.35.1.tgz",
+ "integrity": "sha512-wQahqCi9MD8Yxzg4gVM4fNrZxh+r6vD55PyIg+WJPaM5ZRUyF35iQpwJCuma3r6viU9/8Pxlc+XHV+woVa6nCQ==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "Apache-2.0 AND LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^20.9.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-win32-x64": {
+ "version": "0.35.1",
+ "resolved": "https://registry.npmmirror.com/@img/sharp-win32-x64/-/sharp-win32-x64-0.35.1.tgz",
+ "integrity": "sha512-WzBtkYtZHATLPe8XRharxZXxQ9cdLrQWHiwxt+BJ5rBsisQrKeeV86ErxPSVhcG6xCEuNhs0SqLpWr7XDa2k6w==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0 AND LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=20.9.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@isaacs/cliui": {
+ "version": "9.0.0",
+ "resolved": "https://registry.npmmirror.com/@isaacs/cliui/-/cliui-9.0.0.tgz",
+ "integrity": "sha512-AokJm4tuBHillT+FpMtxQ60n8ObyXBatq7jD2/JA9dxbDDokKQm8KMht5ibGzLVU9IJDIKK4TPKgMHEYMn3lMg==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@jridgewell/gen-mapping": {
+ "version": "0.3.13",
+ "resolved": "https://registry.npmmirror.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
+ "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/sourcemap-codec": "^1.5.0",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ }
+ },
+ "node_modules/@jridgewell/remapping": {
+ "version": "2.3.5",
+ "resolved": "https://registry.npmmirror.com/@jridgewell/remapping/-/remapping-2.3.5.tgz",
+ "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/gen-mapping": "^0.3.5",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ }
+ },
+ "node_modules/@jridgewell/resolve-uri": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmmirror.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
+ "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/source-map": {
+ "version": "0.3.11",
+ "resolved": "https://registry.npmmirror.com/@jridgewell/source-map/-/source-map-0.3.11.tgz",
+ "integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/gen-mapping": "^0.3.5",
+ "@jridgewell/trace-mapping": "^0.3.25"
+ }
+ },
+ "node_modules/@jridgewell/sourcemap-codec": {
+ "version": "1.5.5",
+ "resolved": "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
+ "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
+ "license": "MIT"
+ },
+ "node_modules/@jridgewell/trace-mapping": {
+ "version": "0.3.31",
+ "resolved": "https://registry.npmmirror.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
+ "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/resolve-uri": "^3.1.0",
+ "@jridgewell/sourcemap-codec": "^1.4.14"
+ }
+ },
+ "node_modules/@kurkle/color": {
+ "version": "0.3.4",
+ "resolved": "https://registry.npmmirror.com/@kurkle/color/-/color-0.3.4.tgz",
+ "integrity": "sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w==",
+ "license": "MIT"
+ },
+ "node_modules/@rollup/plugin-babel": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmmirror.com/@rollup/plugin-babel/-/plugin-babel-6.1.0.tgz",
+ "integrity": "sha512-dFZNuFD2YRcoomP4oYf+DvQNSUA9ih+A3vUqopQx5EdtPGo3WBnQcI/S8pwpz91UsGfL0HsMSOlaMld8HrbubA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-module-imports": "^7.18.6",
+ "@rollup/pluginutils": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0",
+ "@types/babel__core": "^7.1.9",
+ "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/babel__core": {
+ "optional": true
+ },
+ "rollup": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@rollup/plugin-node-resolve": {
+ "version": "16.0.3",
+ "resolved": "https://registry.npmmirror.com/@rollup/plugin-node-resolve/-/plugin-node-resolve-16.0.3.tgz",
+ "integrity": "sha512-lUYM3UBGuM93CnMPG1YocWu7X802BrNF3jW2zny5gQyLQgRFJhV1Sq0Zi74+dh/6NBx1DxFC4b4GXg9wUCG5Qg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@rollup/pluginutils": "^5.0.1",
+ "@types/resolve": "1.20.2",
+ "deepmerge": "^4.2.2",
+ "is-module": "^1.0.0",
+ "resolve": "^1.22.1"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "peerDependencies": {
+ "rollup": "^2.78.0||^3.0.0||^4.0.0"
+ },
+ "peerDependenciesMeta": {
+ "rollup": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@rollup/plugin-replace": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmmirror.com/@rollup/plugin-replace/-/plugin-replace-6.0.3.tgz",
+ "integrity": "sha512-J4RZarRvQAm5IF0/LwUUg+obsm+xZhYnbMXmXROyoSE1ATJe3oXSb9L5MMppdxP2ylNSjv6zFBwKYjcKMucVfA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@rollup/pluginutils": "^5.0.1",
+ "magic-string": "^0.30.3"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "peerDependencies": {
+ "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0"
+ },
+ "peerDependenciesMeta": {
+ "rollup": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@rollup/plugin-terser": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmmirror.com/@rollup/plugin-terser/-/plugin-terser-1.0.0.tgz",
+ "integrity": "sha512-FnCxhTBx6bMOYQrar6C8h3scPt8/JwIzw3+AJ2K++6guogH5fYaIFia+zZuhqv0eo1RN7W1Pz630SyvLbDjhtQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "serialize-javascript": "^7.0.3",
+ "smob": "^1.0.0",
+ "terser": "^5.17.4"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ },
+ "peerDependencies": {
+ "rollup": "^2.0.0||^3.0.0||^4.0.0"
+ },
+ "peerDependenciesMeta": {
+ "rollup": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@rollup/pluginutils": {
+ "version": "5.4.0",
+ "resolved": "https://registry.npmmirror.com/@rollup/pluginutils/-/pluginutils-5.4.0.tgz",
+ "integrity": "sha512-MfPp06CjRLfXQ3wY0R8vJDYBy/MvVcc9OulEfR0B8Iv9ko+GCNaRZ+EpJYFl27LhKsZK0o420sYCRHCjfCgeUg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "^1.0.0",
+ "estree-walker": "^2.0.2",
+ "picomatch": "^4.0.2"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "peerDependencies": {
+ "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0"
+ },
+ "peerDependenciesMeta": {
+ "rollup": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@rollup/rollup-android-arm-eabi": {
+ "version": "4.62.0",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.62.0.tgz",
+ "integrity": "sha512-IPIQ55ythEHkfEd9jMEi32OQ7SxURsGA43JI22lj01OLZNt2NUbJX8YUHxkVWyQ6daHPNn0truF5nSj3DQp6YQ==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@rollup/rollup-android-arm64": {
+ "version": "4.62.0",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.62.0.tgz",
+ "integrity": "sha512-M6s9cr10MibETyo8JsOkq+Lo1+lU6hcvb1MApnUql5qte/5hMEgzlN8/ReIKNfRV8rrqX50W1BX9zoUhC192RA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@rollup/rollup-darwin-arm64": {
+ "version": "4.62.0",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.62.0.tgz",
+ "integrity": "sha512-BqCoMoIbn0keKys+dEAdBa70EtOwV1bEsQCUgU9FdiZmmMge/Zk7LlkYGqbrdHR+Frnt0E1FOanly+rlwvvQzw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@rollup/rollup-darwin-x64": {
+ "version": "4.62.0",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.62.0.tgz",
+ "integrity": "sha512-SIMzST3VFNXDAbeIWDWiFCNM5qncUBDWaEV7NfE7oZbDt2mgfW4MvbKdbYiGOLoM32gbTv608UMd0XktEYSD7w==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@rollup/rollup-freebsd-arm64": {
+ "version": "4.62.0",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.62.0.tgz",
+ "integrity": "sha512-ezjfSQMP7ArdUsbBwbQIfwAlhE84I2iVnzQNCFSveqV42q+BmKlzVpf7mxv5EchLcoWU4y6/heFzVg1F+hodUQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-freebsd-x64": {
+ "version": "4.62.0",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.62.0.tgz",
+ "integrity": "sha512-9+qTWGW9AZRhnUgwtTwzNwcPlL87ngkeN0LA+q1bADvmY9aNvWaF2TFW8BZgnQPYxpDI7+rMVLivcd4V737TAQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
+ "version": "4.62.0",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.62.0.tgz",
+ "integrity": "sha512-T1dMEQhXA/jkJ/jyMIw9IovK8bSUq7A8kLIlvZTb/6YIVsp2zLavr4F3oyllHWo7eIVJRyE5n3tUjQJEbE1IuQ==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm-musleabihf": {
+ "version": "4.62.0",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.62.0.tgz",
+ "integrity": "sha512-2as0LgT7qQpyceQq6VUJYnumUMUrgGQCWIiDIN9DE0/tglsk6o66uCB4f3djRawAltvfCNLyZZrsqbPA6inCsA==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm64-gnu": {
+ "version": "4.62.0",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.62.0.tgz",
+ "integrity": "sha512-bVURMg+6eNN9C/yc0aVjooZcwTTtYF4YW3xta5pP0//r3o1V8gXEHXWCndj47w/HhwsFroZrFhR+6uQP5T0n0g==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm64-musl": {
+ "version": "4.62.0",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.62.0.tgz",
+ "integrity": "sha512-Ful8pM/2yYI83PViWdFdpZhdI8HJ5qsXANe5atypbHDf+KIBBDsZsbyy8hbXnULVvW9NsTh5DHwbcBftyLTfiw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-loong64-gnu": {
+ "version": "4.62.0",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.62.0.tgz",
+ "integrity": "sha512-9Gp/DgrkzfUBmNPVTyPTvay+4xEP7M/clXpj3efXBcm6uTIVIgDg4rqUpqKXvLEuFRVuEpSAOkhgNeecvaZ4Cg==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-loong64-musl": {
+ "version": "4.62.0",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.62.0.tgz",
+ "integrity": "sha512-m9tsJz54LUXkSYM8+8PG81B9IKK5r+2T0clMq4QrS16xFosufU7firBDAZEsDheDs7wTlP7h3++S7lMsU955HA==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-ppc64-gnu": {
+ "version": "4.62.0",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.62.0.tgz",
+ "integrity": "sha512-3UvJ5PNVU16aJf6M3tFI24pWzAl2/ynfbyRN3ICyQajK1lSkrnVYNnLz3v04J32qKa0FczJc22zeToc0lr2A3w==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-ppc64-musl": {
+ "version": "4.62.0",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.62.0.tgz",
+ "integrity": "sha512-vRWUAbYLGHBZS6Q8Msb2sfnf1fvJf+47t8l/TwOerM2qArzy+IeNMTHrYLHXh95h8MoatPHI5hhSZNs+mGXKPg==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-riscv64-gnu": {
+ "version": "4.62.0",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.62.0.tgz",
+ "integrity": "sha512-c00T5SYENHAt86cfW47URaP3Us5vLC/4QO7GYud1G5VNRffCwwCuBspwqYrriuJB+5m0WFzClCn9wed0FBjKvg==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-riscv64-musl": {
+ "version": "4.62.0",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.62.0.tgz",
+ "integrity": "sha512-krrCDilhXOwFkSkO3Wm9I/f9H0L92XHHwy2fwxjukxIbh0dem8gZqOW5Y8BsHrpJv5qwlRBV+Wl4ZFyRWhUpwg==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-s390x-gnu": {
+ "version": "4.62.0",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.62.0.tgz",
+ "integrity": "sha512-7pfYFSTc4/rUC/FtAI0Qp6QthDBCIi6/AuP1xYqFk5vanI6KnL5dWKP60OM/05LOsbwTmIcvr6eXC4CJuJ75IA==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-x64-gnu": {
+ "version": "4.62.0",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.62.0.tgz",
+ "integrity": "sha512-7SDIalKeIpG0Ifogbbdn58HmSotYMlf23K3dCJEmiVd9Fg36Vmni82iPQec27N3wY4Bvbxftkxz6vSx9OcouTg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-x64-musl": {
+ "version": "4.62.0",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.62.0.tgz",
+ "integrity": "sha512-eRZevouTH2i1HeAVLqJuLnt256krQkGY0TN6WsTmsIhuzbh457HuWDMakKwmi0Cjadux983CoSr8Lim2QhUIFw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-openbsd-x64": {
+ "version": "4.62.0",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.62.0.tgz",
+ "integrity": "sha512-3oVS7FLGa4U1qcvao9ylGxrjXZyUQqR8UwxEcnUEyPX53O/C/mKDZegNXTdHCP+h3e6ta/f1EN38Yif1mmZHYg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-openharmony-arm64": {
+ "version": "4.62.0",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.62.0.tgz",
+ "integrity": "sha512-yTB9TgfWj5wHe5QgktAgXTLLot1gvEjl1NiPPAUiCs4oPrIWFl5V4nC3GrkNdj9LaAU4s94nVrGbGOCqUpyWsg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-arm64-msvc": {
+ "version": "4.62.0",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.62.0.tgz",
+ "integrity": "sha512-5LOhoaesY3doG1c+ac/2JtgREpKoJr5bUHH8tKY0V8di7+uSV6BwLs2PlR0/yzefGOkR+wE7ZolZphHCsyG5Rw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-ia32-msvc": {
+ "version": "4.62.0",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.62.0.tgz",
+ "integrity": "sha512-yYkWHhmbhRTWTnWos5HC4GcPQfjlzzCNbM9e/+GXrLuaBXYA3qSDR9f0Vgufd5S8yX81U8jPKp7ZnAjZFMtRnw==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-x64-gnu": {
+ "version": "4.62.0",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.62.0.tgz",
+ "integrity": "sha512-SoTb6lPg25xZlA2ibwQ++ahCCnH+FP0qmEuafMJ4gznZKOlXioKEAeJLgCrqjM98ACziXM9V1amFjICVL4IFoA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-x64-msvc": {
+ "version": "4.62.0",
+ "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.62.0.tgz",
+ "integrity": "sha512-5L+T1fMX4RIEBoZzT0+sQ0PhTS36NULFmMXtl1TZo44TMAROIMHbZufSOjVWt/Y622BtxgxtaNOokbTDvfsrZA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@trickfilm400/rollup-plugin-off-main-thread": {
+ "version": "3.0.0-pre1",
+ "resolved": "https://registry.npmmirror.com/@trickfilm400/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-3.0.0-pre1.tgz",
+ "integrity": "sha512-/67zpWDBLV+oYAEL682s1ktXL0HgqX76f6gaVGkGnVZlBbm1zd0v4Bz8MFF2GGhoX9rvfq3KSQHubFHwa6w6/Q==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "ejs": "^3.1.10",
+ "json5": "^2.2.3",
+ "magic-string": "^0.30.21",
+ "string.prototype.matchall": "^4.0.12"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@types/estree": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmmirror.com/@types/estree/-/estree-1.0.9.tgz",
+ "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/resolve": {
+ "version": "1.20.2",
+ "resolved": "https://registry.npmmirror.com/@types/resolve/-/resolve-1.20.2.tgz",
+ "integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/trusted-types": {
+ "version": "2.0.7",
+ "resolved": "https://registry.npmmirror.com/@types/trusted-types/-/trusted-types-2.0.7.tgz",
+ "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@vitejs/plugin-vue": {
+ "version": "5.2.4",
+ "resolved": "https://registry.npmmirror.com/@vitejs/plugin-vue/-/plugin-vue-5.2.4.tgz",
+ "integrity": "sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.0.0 || >=20.0.0"
+ },
+ "peerDependencies": {
+ "vite": "^5.0.0 || ^6.0.0",
+ "vue": "^3.2.25"
+ }
+ },
+ "node_modules/@vue/compiler-core": {
+ "version": "3.5.38",
+ "resolved": "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.5.38.tgz",
+ "integrity": "sha512-s99aGxWYig9ErHbct27KXEGhrBYlRI6c4MwAgXErOAbX9xiW37/uMa+XUDO69zLz83dng8UUZ70CTOJrLrYrEQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.29.7",
+ "@vue/shared": "3.5.38",
+ "entities": "^7.0.1",
+ "estree-walker": "^2.0.2",
+ "source-map-js": "^1.2.1"
+ }
+ },
+ "node_modules/@vue/compiler-dom": {
+ "version": "3.5.38",
+ "resolved": "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.5.38.tgz",
+ "integrity": "sha512-JTqp25l8aFfJYF7/KmsXZjAxJz7T+SjmTJLoXVjHtc2BrSgSiW2n9Aem/cWq1OPe68A8JL06B3eVdhlP0H4TVw==",
+ "license": "MIT",
+ "dependencies": {
+ "@vue/compiler-core": "3.5.38",
+ "@vue/shared": "3.5.38"
+ }
+ },
+ "node_modules/@vue/compiler-sfc": {
+ "version": "3.5.38",
+ "resolved": "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.5.38.tgz",
+ "integrity": "sha512-DuA2GiZawSEW442iw/9+Fkol8hTgb4Ke5KkhmSry65QA7YuyMbIdy8p0XZRMvNwJdgRz307W8g1CSzdvS4nuNg==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.29.7",
+ "@vue/compiler-core": "3.5.38",
+ "@vue/compiler-dom": "3.5.38",
+ "@vue/compiler-ssr": "3.5.38",
+ "@vue/shared": "3.5.38",
+ "estree-walker": "^2.0.2",
+ "magic-string": "^0.30.21",
+ "postcss": "^8.5.15",
+ "source-map-js": "^1.2.1"
+ }
+ },
+ "node_modules/@vue/compiler-ssr": {
+ "version": "3.5.38",
+ "resolved": "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.5.38.tgz",
+ "integrity": "sha512-7s+W5Gc42FGxZMcuwl8H5B29T8BJPMdBT7KHFE+BbAuZ/iTEdTtv7z2XiMjiaUUw4w3ZcCEdHs36RuYJ2VA7bA==",
+ "license": "MIT",
+ "dependencies": {
+ "@vue/compiler-dom": "3.5.38",
+ "@vue/shared": "3.5.38"
+ }
+ },
+ "node_modules/@vue/devtools-api": {
+ "version": "6.6.4",
+ "resolved": "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-6.6.4.tgz",
+ "integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==",
+ "license": "MIT"
+ },
+ "node_modules/@vue/reactivity": {
+ "version": "3.5.38",
+ "resolved": "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.5.38.tgz",
+ "integrity": "sha512-pG6LV/NDNRbKizcUjFFLAfjaL8mcv4DmR9avNcUw2gDHBzZneuS2TWCmp633ynzxz9YYKNeEPK2I8Wraqy2HUQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@vue/shared": "3.5.38"
+ }
+ },
+ "node_modules/@vue/runtime-core": {
+ "version": "3.5.38",
+ "resolved": "https://registry.npmmirror.com/@vue/runtime-core/-/runtime-core-3.5.38.tgz",
+ "integrity": "sha512-iyW8WVfF1CpCXxncZY5Ei6rSd6oZr5DgEom//fUjRBRl56AXPD+s9ATvukRt77ZFTuYlnVA1bxY+dJB94tWVYw==",
+ "license": "MIT",
+ "dependencies": {
+ "@vue/reactivity": "3.5.38",
+ "@vue/shared": "3.5.38"
+ }
+ },
+ "node_modules/@vue/runtime-dom": {
+ "version": "3.5.38",
+ "resolved": "https://registry.npmmirror.com/@vue/runtime-dom/-/runtime-dom-3.5.38.tgz",
+ "integrity": "sha512-apX2wt9sdfDshS+a2xueFZLVpt0GkRJZSoPmrW/SA4yzXTznhfcMVW59gr7h4YQeY0vJhdJkk2rsIDwgfFgC5A==",
+ "license": "MIT",
+ "dependencies": {
+ "@vue/reactivity": "3.5.38",
+ "@vue/runtime-core": "3.5.38",
+ "@vue/shared": "3.5.38",
+ "csstype": "^3.2.3"
+ }
+ },
+ "node_modules/@vue/server-renderer": {
+ "version": "3.5.38",
+ "resolved": "https://registry.npmmirror.com/@vue/server-renderer/-/server-renderer-3.5.38.tgz",
+ "integrity": "sha512-vue8vbf2QlV4quHqzwmJy6dWfmRhP1J8l4wtZg60CL6VoKqcPY2oe7may3+1d9qfpedjK5PRLFqd5k3Isj9mUw==",
+ "license": "MIT",
+ "dependencies": {
+ "@vue/compiler-ssr": "3.5.38",
+ "@vue/shared": "3.5.38"
+ },
+ "peerDependencies": {
+ "vue": "3.5.38"
+ }
+ },
+ "node_modules/@vue/shared": {
+ "version": "3.5.38",
+ "resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.5.38.tgz",
+ "integrity": "sha512-FTW0AFZNaK5/mOqvGBwVfUlNLU38TiQn4+DQgIFUnrBBJQ1crMJ82yeGQLV5jyKFsO8yRukpbuP7x+nRbH6aug==",
+ "license": "MIT"
+ },
+ "node_modules/acorn": {
+ "version": "8.17.0",
+ "resolved": "https://registry.npmmirror.com/acorn/-/acorn-8.17.0.tgz",
+ "integrity": "sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/agent-base": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmmirror.com/agent-base/-/agent-base-6.0.2.tgz",
+ "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
+ "license": "MIT",
+ "dependencies": {
+ "debug": "4"
+ },
+ "engines": {
+ "node": ">= 6.0.0"
+ }
+ },
+ "node_modules/ajv": {
+ "version": "8.20.0",
+ "resolved": "https://registry.npmmirror.com/ajv/-/ajv-8.20.0.tgz",
+ "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "fast-deep-equal": "^3.1.3",
+ "fast-uri": "^3.0.1",
+ "json-schema-traverse": "^1.0.0",
+ "require-from-string": "^2.0.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/array-buffer-byte-length": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmmirror.com/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz",
+ "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "is-array-buffer": "^3.0.5"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/arraybuffer.prototype.slice": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmmirror.com/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.npmmirror.com/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.npmmirror.com/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.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz",
+ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
+ "license": "MIT"
+ },
+ "node_modules/at-least-node": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmmirror.com/at-least-node/-/at-least-node-1.0.0.tgz",
+ "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">= 4.0.0"
+ }
+ },
+ "node_modules/available-typed-arrays": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmmirror.com/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/axios": {
+ "version": "1.18.0",
+ "resolved": "https://registry.npmmirror.com/axios/-/axios-1.18.0.tgz",
+ "integrity": "sha512-E32NzpYKp++W7XRe52rHiXV2ehxmh3wbdgO7MHeFM+vqxLBYHzt0ElkiImtOBxtOmyp0yoC8C6uESVV84Y2/hw==",
+ "license": "MIT",
+ "dependencies": {
+ "follow-redirects": "^1.16.0",
+ "form-data": "^4.0.5",
+ "https-proxy-agent": "^5.0.1",
+ "proxy-from-env": "^2.1.0"
+ }
+ },
+ "node_modules/babel-plugin-polyfill-corejs2": {
+ "version": "0.4.17",
+ "resolved": "https://registry.npmmirror.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.17.tgz",
+ "integrity": "sha512-aTyf30K/rqAsNwN76zYrdtx8obu0E4KoUME29B1xj+B3WxgvWkp943vYQ+z8Mv3lw9xHXMHpvSPOBxzAkIa94w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/compat-data": "^7.28.6",
+ "@babel/helper-define-polyfill-provider": "^0.6.8",
+ "semver": "^6.3.1"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"
+ }
+ },
+ "node_modules/babel-plugin-polyfill-corejs3": {
+ "version": "0.14.2",
+ "resolved": "https://registry.npmmirror.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.14.2.tgz",
+ "integrity": "sha512-coWpDLJ410R781Npmn/SIBZEsAetR4xVi0SxLMXPaMO4lSf1MwnkGYMtkFxew0Dn8B3/CpbpYxN0JCgg8mn67g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-define-polyfill-provider": "^0.6.8",
+ "core-js-compat": "^3.48.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"
+ }
+ },
+ "node_modules/babel-plugin-polyfill-regenerator": {
+ "version": "0.6.8",
+ "resolved": "https://registry.npmmirror.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.8.tgz",
+ "integrity": "sha512-M762rNHfSF1EV3SLtnCJXFoQbbIIz0OyRwnCmV0KPC7qosSfCO0QLTSuJX3ayAebubhE6oYBAYPrBA5ljowaZg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-define-polyfill-provider": "^0.6.8"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.4.0 || ^8.0.0-0 <8.0.0"
+ }
+ },
+ "node_modules/balanced-match": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-4.0.4.tgz",
+ "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "18 || 20 || >=22"
+ }
+ },
+ "node_modules/baseline-browser-mapping": {
+ "version": "2.10.38",
+ "resolved": "https://registry.npmmirror.com/baseline-browser-mapping/-/baseline-browser-mapping-2.10.38.tgz",
+ "integrity": "sha512-31/02mVB4yuQU6adKk5SlY6m+mxDwUq5KZkyYgnLrrKl7TEm1+3PyDtDBz2kOv/wxZz41GHsvV1A/u6RmiyBvw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "baseline-browser-mapping": "dist/cli.cjs"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/brace-expansion": {
+ "version": "5.0.6",
+ "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-5.0.6.tgz",
+ "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^4.0.2"
+ },
+ "engines": {
+ "node": "18 || 20 || >=22"
+ }
+ },
+ "node_modules/browserslist": {
+ "version": "4.28.2",
+ "resolved": "https://registry.npmmirror.com/browserslist/-/browserslist-4.28.2.tgz",
+ "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "baseline-browser-mapping": "^2.10.12",
+ "caniuse-lite": "^1.0.30001782",
+ "electron-to-chromium": "^1.5.328",
+ "node-releases": "^2.0.36",
+ "update-browserslist-db": "^1.2.3"
+ },
+ "bin": {
+ "browserslist": "cli.js"
+ },
+ "engines": {
+ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
+ }
+ },
+ "node_modules/buffer-from": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmmirror.com/buffer-from/-/buffer-from-1.1.2.tgz",
+ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/call-bind": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmmirror.com/call-bind/-/call-bind-1.0.9.tgz",
+ "integrity": "sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "es-define-property": "^1.0.1",
+ "get-intrinsic": "^1.3.0",
+ "set-function-length": "^1.2.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/call-bind-apply-helpers": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmmirror.com/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.npmmirror.com/call-bound/-/call-bound-1.0.4.tgz",
+ "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "get-intrinsic": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/caniuse-lite": {
+ "version": "1.0.30001799",
+ "resolved": "https://registry.npmmirror.com/caniuse-lite/-/caniuse-lite-1.0.30001799.tgz",
+ "integrity": "sha512-hG1bReV+OUU+MOqK4t/ZWI0tZOyz3rqS9XuhOUz1cIcbwBKjOyJEJuw9ER5JuNyqxNk8u/JUVbGibBOL1yrjFw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "CC-BY-4.0"
+ },
+ "node_modules/chart.js": {
+ "version": "4.5.1",
+ "resolved": "https://registry.npmmirror.com/chart.js/-/chart.js-4.5.1.tgz",
+ "integrity": "sha512-GIjfiT9dbmHRiYi6Nl2yFCq7kkwdkp1W/lp2J99rX0yo9tgJGn3lKQATztIjb5tVtevcBtIdICNWqlq5+E8/Pw==",
+ "license": "MIT",
+ "dependencies": {
+ "@kurkle/color": "^0.3.0"
+ },
+ "engines": {
+ "pnpm": ">=8"
+ }
+ },
+ "node_modules/combined-stream": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz",
+ "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+ "license": "MIT",
+ "dependencies": {
+ "delayed-stream": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/commander": {
+ "version": "2.20.3",
+ "resolved": "https://registry.npmmirror.com/commander/-/commander-2.20.3.tgz",
+ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/common-tags": {
+ "version": "1.8.2",
+ "resolved": "https://registry.npmmirror.com/common-tags/-/common-tags-1.8.2.tgz",
+ "integrity": "sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4.0.0"
+ }
+ },
+ "node_modules/convert-source-map": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmmirror.com/convert-source-map/-/convert-source-map-2.0.0.tgz",
+ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/core-js-compat": {
+ "version": "3.49.0",
+ "resolved": "https://registry.npmmirror.com/core-js-compat/-/core-js-compat-3.49.0.tgz",
+ "integrity": "sha512-VQXt1jr9cBz03b331DFDCCP90b3fanciLkgiOoy8SBHy06gNf+vQ1A3WFLqG7I8TipYIKeYK9wxd0tUrvHcOZA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "browserslist": "^4.28.1"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/core-js"
+ }
+ },
+ "node_modules/cross-spawn": {
+ "version": "7.0.6",
+ "resolved": "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-7.0.6.tgz",
+ "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/crypto-random-string": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmmirror.com/crypto-random-string/-/crypto-random-string-2.0.0.tgz",
+ "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/csstype": {
+ "version": "3.2.3",
+ "resolved": "https://registry.npmmirror.com/csstype/-/csstype-3.2.3.tgz",
+ "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
+ "license": "MIT"
+ },
+ "node_modules/data-view-buffer": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmmirror.com/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.npmmirror.com/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.npmmirror.com/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz",
+ "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "is-data-view": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/debug": {
+ "version": "4.4.3",
+ "resolved": "https://registry.npmmirror.com/debug/-/debug-4.4.3.tgz",
+ "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/deepmerge": {
+ "version": "4.3.1",
+ "resolved": "https://registry.npmmirror.com/deepmerge/-/deepmerge-4.3.1.tgz",
+ "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/define-data-property": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmmirror.com/define-data-property/-/define-data-property-1.1.4.tgz",
+ "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-define-property": "^1.0.0",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/define-properties": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmmirror.com/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.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz",
+ "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/detect-libc": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmmirror.com/detect-libc/-/detect-libc-2.1.2.tgz",
+ "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/dunder-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmmirror.com/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/ejs": {
+ "version": "3.1.10",
+ "resolved": "https://registry.npmmirror.com/ejs/-/ejs-3.1.10.tgz",
+ "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "jake": "^10.8.5"
+ },
+ "bin": {
+ "ejs": "bin/cli.js"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/electron-to-chromium": {
+ "version": "1.5.375",
+ "resolved": "https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.5.375.tgz",
+ "integrity": "sha512-ZWP5eB4BVPW/ZYo9252hQZHZ5XavtsTgpbhcmMmRwymavC5AsLWQWBPaKMeNd2LW0KGby5HPXvj7+sr4ta5j/Q==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/entities": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmmirror.com/entities/-/entities-7.0.1.tgz",
+ "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==",
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=0.12"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/entities?sponsor=1"
+ }
+ },
+ "node_modules/es-abstract": {
+ "version": "1.24.2",
+ "resolved": "https://registry.npmmirror.com/es-abstract/-/es-abstract-1.24.2.tgz",
+ "integrity": "sha512-2FpH9Q5i2RRwyEP1AylXe6nYLR5OhaJTZwmlcP0dL/+JCbgg7yyEo/sEK6HeGZRf3dFpWwThaRHVApXSkW3xeg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "array-buffer-byte-length": "^1.0.2",
+ "arraybuffer.prototype.slice": "^1.0.4",
+ "available-typed-arrays": "^1.0.7",
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.4",
+ "data-view-buffer": "^1.0.2",
+ "data-view-byte-length": "^1.0.2",
+ "data-view-byte-offset": "^1.0.1",
+ "es-define-property": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.1.1",
+ "es-set-tostringtag": "^2.1.0",
+ "es-to-primitive": "^1.3.0",
+ "function.prototype.name": "^1.1.8",
+ "get-intrinsic": "^1.3.0",
+ "get-proto": "^1.0.1",
+ "get-symbol-description": "^1.1.0",
+ "globalthis": "^1.0.4",
+ "gopd": "^1.2.0",
+ "has-property-descriptors": "^1.0.2",
+ "has-proto": "^1.2.0",
+ "has-symbols": "^1.1.0",
+ "hasown": "^2.0.2",
+ "internal-slot": "^1.1.0",
+ "is-array-buffer": "^3.0.5",
+ "is-callable": "^1.2.7",
+ "is-data-view": "^1.0.2",
+ "is-negative-zero": "^2.0.3",
+ "is-regex": "^1.2.1",
+ "is-set": "^2.0.3",
+ "is-shared-array-buffer": "^1.0.4",
+ "is-string": "^1.1.1",
+ "is-typed-array": "^1.1.15",
+ "is-weakref": "^1.1.1",
+ "math-intrinsics": "^1.1.0",
+ "object-inspect": "^1.13.4",
+ "object-keys": "^1.1.1",
+ "object.assign": "^4.1.7",
+ "own-keys": "^1.0.1",
+ "regexp.prototype.flags": "^1.5.4",
+ "safe-array-concat": "^1.1.3",
+ "safe-push-apply": "^1.0.0",
+ "safe-regex-test": "^1.1.0",
+ "set-proto": "^1.0.0",
+ "stop-iteration-iterator": "^1.1.0",
+ "string.prototype.trim": "^1.2.10",
+ "string.prototype.trimend": "^1.0.9",
+ "string.prototype.trimstart": "^1.0.8",
+ "typed-array-buffer": "^1.0.3",
+ "typed-array-byte-length": "^1.0.3",
+ "typed-array-byte-offset": "^1.0.4",
+ "typed-array-length": "^1.0.7",
+ "unbox-primitive": "^1.1.0",
+ "which-typed-array": "^1.1.19"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/es-abstract-get": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmmirror.com/es-abstract-get/-/es-abstract-get-1.0.0.tgz",
+ "integrity": "sha512-6PMWXpdhshVvFp+FoWYs1EvG1Nj0tvk0dZM+XcK0xMEM1czRVcP6ohqPWHy6qPagSpC8j4+p89WXlT+xXJs/fg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.1.2",
+ "is-callable": "^1.2.7",
+ "object-inspect": "^1.13.4"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/es-define-property": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmmirror.com/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.npmmirror.com/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.2",
+ "resolved": "https://registry.npmmirror.com/es-object-atoms/-/es-object-atoms-1.1.2.tgz",
+ "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-set-tostringtag": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmmirror.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
+ "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.6",
+ "has-tostringtag": "^1.0.2",
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-to-primitive": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmmirror.com/es-to-primitive/-/es-to-primitive-1.3.1.tgz",
+ "integrity": "sha512-CxN9N56HYfd2m/acc/NOFrZQsN9kU4eh+2kk6A707Kz1krH8tKmfrs5RnftB8WNX80T0NS7vSQsDOlg23diR2g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-abstract-get": "^1.0.0",
+ "es-errors": "^1.3.0",
+ "is-callable": "^1.2.7",
+ "is-date-object": "^1.1.0",
+ "is-symbol": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/esbuild": {
+ "version": "0.21.5",
+ "resolved": "https://registry.npmmirror.com/esbuild/-/esbuild-0.21.5.tgz",
+ "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "optionalDependencies": {
+ "@esbuild/aix-ppc64": "0.21.5",
+ "@esbuild/android-arm": "0.21.5",
+ "@esbuild/android-arm64": "0.21.5",
+ "@esbuild/android-x64": "0.21.5",
+ "@esbuild/darwin-arm64": "0.21.5",
+ "@esbuild/darwin-x64": "0.21.5",
+ "@esbuild/freebsd-arm64": "0.21.5",
+ "@esbuild/freebsd-x64": "0.21.5",
+ "@esbuild/linux-arm": "0.21.5",
+ "@esbuild/linux-arm64": "0.21.5",
+ "@esbuild/linux-ia32": "0.21.5",
+ "@esbuild/linux-loong64": "0.21.5",
+ "@esbuild/linux-mips64el": "0.21.5",
+ "@esbuild/linux-ppc64": "0.21.5",
+ "@esbuild/linux-riscv64": "0.21.5",
+ "@esbuild/linux-s390x": "0.21.5",
+ "@esbuild/linux-x64": "0.21.5",
+ "@esbuild/netbsd-x64": "0.21.5",
+ "@esbuild/openbsd-x64": "0.21.5",
+ "@esbuild/sunos-x64": "0.21.5",
+ "@esbuild/win32-arm64": "0.21.5",
+ "@esbuild/win32-ia32": "0.21.5",
+ "@esbuild/win32-x64": "0.21.5"
+ }
+ },
+ "node_modules/escalade": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmmirror.com/escalade/-/escalade-3.2.0.tgz",
+ "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/estree-walker": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz",
+ "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
+ "license": "MIT"
+ },
+ "node_modules/esutils": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmmirror.com/esutils/-/esutils-2.0.3.tgz",
+ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/eta": {
+ "version": "4.6.0",
+ "resolved": "https://registry.npmmirror.com/eta/-/eta-4.6.0.tgz",
+ "integrity": "sha512-lW6is4T1NFOYnmqGZIfvixqj7A7sSvScF+DN8EK6K58xI5MZ5UvYe0GjopxOXQtZvUn4eDdVuZ8XSoYWTMEKwA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=20"
+ },
+ "funding": {
+ "url": "https://github.com/bgub/eta?sponsor=1"
+ }
+ },
+ "node_modules/fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmmirror.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fast-json-stable-stringify": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmmirror.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fast-uri": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmmirror.com/fast-uri/-/fast-uri-3.1.2.tgz",
+ "integrity": "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fastify"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/fastify"
+ }
+ ],
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/fdir": {
+ "version": "6.5.0",
+ "resolved": "https://registry.npmmirror.com/fdir/-/fdir-6.5.0.tgz",
+ "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "peerDependencies": {
+ "picomatch": "^3 || ^4"
+ },
+ "peerDependenciesMeta": {
+ "picomatch": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/filelist": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmmirror.com/filelist/-/filelist-1.0.6.tgz",
+ "integrity": "sha512-5giy2PkLYY1cP39p17Ech+2xlpTRL9HLspOfEgm0L6CwBXBTgsK5ou0JtzYuepxkaQ/tvhCFIJ5uXo0OrM2DxA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "minimatch": "^5.0.1"
+ }
+ },
+ "node_modules/filelist/node_modules/balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/filelist/node_modules/brace-expansion": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-2.1.1.tgz",
+ "integrity": "sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/filelist/node_modules/minimatch": {
+ "version": "5.1.9",
+ "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-5.1.9.tgz",
+ "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/follow-redirects": {
+ "version": "1.16.0",
+ "resolved": "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.16.0.tgz",
+ "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==",
+ "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.npmmirror.com/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.npmmirror.com/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.6",
+ "resolved": "https://registry.npmmirror.com/form-data/-/form-data-4.0.6.tgz",
+ "integrity": "sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==",
+ "license": "MIT",
+ "dependencies": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.8",
+ "es-set-tostringtag": "^2.1.0",
+ "hasown": "^2.0.4",
+ "mime-types": "^2.1.35"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/fs-extra": {
+ "version": "9.1.0",
+ "resolved": "https://registry.npmmirror.com/fs-extra/-/fs-extra-9.1.0.tgz",
+ "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "at-least-node": "^1.0.0",
+ "graceful-fs": "^4.2.0",
+ "jsonfile": "^6.0.1",
+ "universalify": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/fsevents": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmmirror.com/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.npmmirror.com/function-bind/-/function-bind-1.1.2.tgz",
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/function.prototype.name": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmmirror.com/function.prototype.name/-/function.prototype.name-1.2.0.tgz",
+ "integrity": "sha512-jObKIik1P2QjPHP5nz5BaOtUlfgS0fWo8IUByNXkM+o+02sJOi94em77GwJKQSJ3gfPHdgzLNrHc1uokV4P/ew==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.9",
+ "call-bound": "^1.0.4",
+ "es-define-property": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "functions-have-names": "^1.2.3",
+ "has-property-descriptors": "^1.0.2",
+ "hasown": "^2.0.4",
+ "is-callable": "^1.2.7",
+ "is-document.all": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/functions-have-names": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmmirror.com/functions-have-names/-/functions-have-names-1.2.3.tgz",
+ "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/generator-function": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmmirror.com/generator-function/-/generator-function-2.0.1.tgz",
+ "integrity": "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/gensync": {
+ "version": "1.0.0-beta.2",
+ "resolved": "https://registry.npmmirror.com/gensync/-/gensync-1.0.0-beta.2.tgz",
+ "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/get-intrinsic": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmmirror.com/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-own-enumerable-property-symbols": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmmirror.com/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz",
+ "integrity": "sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/get-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmmirror.com/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-symbol-description": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmmirror.com/get-symbol-description/-/get-symbol-description-1.1.0.tgz",
+ "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.6"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/glob": {
+ "version": "11.1.0",
+ "resolved": "https://registry.npmmirror.com/glob/-/glob-11.1.0.tgz",
+ "integrity": "sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "foreground-child": "^3.3.1",
+ "jackspeak": "^4.1.1",
+ "minimatch": "^10.1.1",
+ "minipass": "^7.1.2",
+ "package-json-from-dist": "^1.0.0",
+ "path-scurry": "^2.0.0"
+ },
+ "bin": {
+ "glob": "dist/esm/bin.mjs"
+ },
+ "engines": {
+ "node": "20 || >=22"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/globalthis": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmmirror.com/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.npmmirror.com/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.npmmirror.com/graceful-fs/-/graceful-fs-4.2.11.tgz",
+ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/has-bigints": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmmirror.com/has-bigints/-/has-bigints-1.1.0.tgz",
+ "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-property-descriptors": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmmirror.com/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.npmmirror.com/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.npmmirror.com/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.npmmirror.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
+ "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
+ "license": "MIT",
+ "dependencies": {
+ "has-symbols": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/hasown": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmmirror.com/hasown/-/hasown-2.0.4.tgz",
+ "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==",
+ "license": "MIT",
+ "dependencies": {
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/https-proxy-agent": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmmirror.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
+ "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
+ "license": "MIT",
+ "dependencies": {
+ "agent-base": "6",
+ "debug": "4"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/idb": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmmirror.com/idb/-/idb-7.1.1.tgz",
+ "integrity": "sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/internal-slot": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmmirror.com/internal-slot/-/internal-slot-1.1.0.tgz",
+ "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "hasown": "^2.0.2",
+ "side-channel": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/is-array-buffer": {
+ "version": "3.0.5",
+ "resolved": "https://registry.npmmirror.com/is-array-buffer/-/is-array-buffer-3.0.5.tgz",
+ "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.3",
+ "get-intrinsic": "^1.2.6"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-async-function": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmmirror.com/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.npmmirror.com/is-bigint/-/is-bigint-1.1.0.tgz",
+ "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-bigints": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-boolean-object": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmmirror.com/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.npmmirror.com/is-callable/-/is-callable-1.2.7.tgz",
+ "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-core-module": {
+ "version": "2.16.2",
+ "resolved": "https://registry.npmmirror.com/is-core-module/-/is-core-module-2.16.2.tgz",
+ "integrity": "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "hasown": "^2.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-data-view": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmmirror.com/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.npmmirror.com/is-date-object/-/is-date-object-1.1.0.tgz",
+ "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "has-tostringtag": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-document.all": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmmirror.com/is-document.all/-/is-document.all-1.0.0.tgz",
+ "integrity": "sha512-+XSoyS05OdBbhFuELhgTCpFNHkpBOJqtsZfUFFpe5QTw+9Sjbh8zitxhQkYAo6wV7e1Vb8cAPvpCk9jGam/82g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.4"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-finalizationregistry": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmmirror.com/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz",
+ "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-generator-function": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmmirror.com/is-generator-function/-/is-generator-function-1.1.2.tgz",
+ "integrity": "sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.4",
+ "generator-function": "^2.0.0",
+ "get-proto": "^1.0.1",
+ "has-tostringtag": "^1.0.2",
+ "safe-regex-test": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-map": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmmirror.com/is-map/-/is-map-2.0.3.tgz",
+ "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-module": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmmirror.com/is-module/-/is-module-1.0.0.tgz",
+ "integrity": "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/is-negative-zero": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmmirror.com/is-negative-zero/-/is-negative-zero-2.0.3.tgz",
+ "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-number-object": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmmirror.com/is-number-object/-/is-number-object-1.1.1.tgz",
+ "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "has-tostringtag": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-obj": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmmirror.com/is-obj/-/is-obj-1.0.1.tgz",
+ "integrity": "sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-regex": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmmirror.com/is-regex/-/is-regex-1.2.1.tgz",
+ "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "gopd": "^1.2.0",
+ "has-tostringtag": "^1.0.2",
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-regexp": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmmirror.com/is-regexp/-/is-regexp-1.0.0.tgz",
+ "integrity": "sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-set": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmmirror.com/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.npmmirror.com/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.npmmirror.com/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.npmmirror.com/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.npmmirror.com/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.npmmirror.com/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.npmmirror.com/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.npmmirror.com/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.npmmirror.com/is-weakset/-/is-weakset-2.0.4.tgz",
+ "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "get-intrinsic": "^1.2.6"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/isarray": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmmirror.com/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.npmmirror.com/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/jackspeak": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmmirror.com/jackspeak/-/jackspeak-4.2.3.tgz",
+ "integrity": "sha512-ykkVRwrYvFm1nb2AJfKKYPr0emF6IiXDYUaFx4Zn9ZuIH7MrzEZ3sD5RlqGXNRpHtvUHJyOnCEFxOlNDtGo7wg==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "@isaacs/cliui": "^9.0.0"
+ },
+ "engines": {
+ "node": "20 || >=22"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/jake": {
+ "version": "10.9.4",
+ "resolved": "https://registry.npmmirror.com/jake/-/jake-10.9.4.tgz",
+ "integrity": "sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "async": "^3.2.6",
+ "filelist": "^1.0.4",
+ "picocolors": "^1.1.1"
+ },
+ "bin": {
+ "jake": "bin/cli.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/js-tokens": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmmirror.com/js-tokens/-/js-tokens-4.0.0.tgz",
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/jsesc": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmmirror.com/jsesc/-/jsesc-3.1.0.tgz",
+ "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "jsesc": "bin/jsesc"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/json-schema-traverse": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
+ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json5": {
+ "version": "2.2.3",
+ "resolved": "https://registry.npmmirror.com/json5/-/json5-2.2.3.tgz",
+ "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "json5": "lib/cli.js"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/jsonfile": {
+ "version": "6.2.1",
+ "resolved": "https://registry.npmmirror.com/jsonfile/-/jsonfile-6.2.1.tgz",
+ "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "universalify": "^2.0.0"
+ },
+ "optionalDependencies": {
+ "graceful-fs": "^4.1.6"
+ }
+ },
+ "node_modules/jsonpointer": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmmirror.com/jsonpointer/-/jsonpointer-5.0.1.tgz",
+ "integrity": "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/leven": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmmirror.com/leven/-/leven-3.1.0.tgz",
+ "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/lodash.debounce": {
+ "version": "4.0.8",
+ "resolved": "https://registry.npmmirror.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
+ "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/lodash.sortby": {
+ "version": "4.7.0",
+ "resolved": "https://registry.npmmirror.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz",
+ "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/lru-cache": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-5.1.1.tgz",
+ "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "yallist": "^3.0.2"
+ }
+ },
+ "node_modules/magic-string": {
+ "version": "0.30.21",
+ "resolved": "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.21.tgz",
+ "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/sourcemap-codec": "^1.5.5"
+ }
+ },
+ "node_modules/math-intrinsics": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmmirror.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
+ "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/mime-db": {
+ "version": "1.52.0",
+ "resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz",
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mime-types": {
+ "version": "2.1.35",
+ "resolved": "https://registry.npmmirror.com/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/minimatch": {
+ "version": "10.2.5",
+ "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-10.2.5.tgz",
+ "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "brace-expansion": "^5.0.5"
+ },
+ "engines": {
+ "node": "18 || 20 || >=22"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/minipass": {
+ "version": "7.1.3",
+ "resolved": "https://registry.npmmirror.com/minipass/-/minipass-7.1.3.tgz",
+ "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ }
+ },
+ "node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "license": "MIT"
+ },
+ "node_modules/nanoid": {
+ "version": "3.3.12",
+ "resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.12.tgz",
+ "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "bin": {
+ "nanoid": "bin/nanoid.cjs"
+ },
+ "engines": {
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+ }
+ },
+ "node_modules/node-releases": {
+ "version": "2.0.48",
+ "resolved": "https://registry.npmmirror.com/node-releases/-/node-releases-2.0.48.tgz",
+ "integrity": "sha512-1uz8041X6LoI6ZSdZacM9lVY28vuzDlSKitnpbSNK0RfKoIJkX29NBPVEFXhnuSuEOA9Ww0xnPJ+ILWbGAv8DA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/object-inspect": {
+ "version": "1.13.4",
+ "resolved": "https://registry.npmmirror.com/object-inspect/-/object-inspect-1.13.4.tgz",
+ "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/object-keys": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmmirror.com/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.npmmirror.com/object.assign/-/object.assign-4.1.7.tgz",
+ "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.3",
+ "define-properties": "^1.2.1",
+ "es-object-atoms": "^1.0.0",
+ "has-symbols": "^1.1.0",
+ "object-keys": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/own-keys": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmmirror.com/own-keys/-/own-keys-1.0.1.tgz",
+ "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "get-intrinsic": "^1.2.6",
+ "object-keys": "^1.1.1",
+ "safe-push-apply": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/package-json-from-dist": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmmirror.com/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz",
+ "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==",
+ "dev": true,
+ "license": "BlueOak-1.0.0"
+ },
+ "node_modules/path-key": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmmirror.com/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.npmmirror.com/path-parse/-/path-parse-1.0.7.tgz",
+ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/path-scurry": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmmirror.com/path-scurry/-/path-scurry-2.0.2.tgz",
+ "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "lru-cache": "^11.0.0",
+ "minipass": "^7.1.2"
+ },
+ "engines": {
+ "node": "18 || 20 || >=22"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/path-scurry/node_modules/lru-cache": {
+ "version": "11.5.1",
+ "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-11.5.1.tgz",
+ "integrity": "sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "engines": {
+ "node": "20 || >=22"
+ }
+ },
+ "node_modules/picocolors": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz",
+ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
+ "license": "ISC"
+ },
+ "node_modules/picomatch": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.4.tgz",
+ "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/pinia": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmmirror.com/pinia/-/pinia-2.3.1.tgz",
+ "integrity": "sha512-khUlZSwt9xXCaTbbxFYBKDc/bWAGWJjOgvxETwkTN7KRm66EeT1ZdZj6i2ceh9sP2Pzqsbc704r2yngBrxBVug==",
+ "license": "MIT",
+ "dependencies": {
+ "@vue/devtools-api": "^6.6.3",
+ "vue-demi": "^0.14.10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/posva"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.4.4",
+ "vue": "^2.7.0 || ^3.5.11"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/possible-typed-array-names": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmmirror.com/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz",
+ "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/postcss": {
+ "version": "8.5.15",
+ "resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.5.15.tgz",
+ "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "nanoid": "^3.3.12",
+ "picocolors": "^1.1.1",
+ "source-map-js": "^1.2.1"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/pretty-bytes": {
+ "version": "6.1.1",
+ "resolved": "https://registry.npmmirror.com/pretty-bytes/-/pretty-bytes-6.1.1.tgz",
+ "integrity": "sha512-mQUvGU6aUFQ+rNvTIAcZuWGRT9a6f6Yrg9bHs4ImKF+HZCEK+plBvnAZYSIQztknZF2qnzNtr6F8s0+IuptdlQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/proxy-from-env": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmmirror.com/proxy-from-env/-/proxy-from-env-2.1.0.tgz",
+ "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/punycode": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmmirror.com/punycode/-/punycode-2.3.1.tgz",
+ "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/reflect.getprototypeof": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmmirror.com/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.npmmirror.com/regenerate/-/regenerate-1.4.2.tgz",
+ "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/regenerate-unicode-properties": {
+ "version": "10.2.2",
+ "resolved": "https://registry.npmmirror.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.2.tgz",
+ "integrity": "sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "regenerate": "^1.4.2"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/regexp.prototype.flags": {
+ "version": "1.5.4",
+ "resolved": "https://registry.npmmirror.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz",
+ "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "define-properties": "^1.2.1",
+ "es-errors": "^1.3.0",
+ "get-proto": "^1.0.1",
+ "gopd": "^1.2.0",
+ "set-function-name": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/regexpu-core": {
+ "version": "6.4.0",
+ "resolved": "https://registry.npmmirror.com/regexpu-core/-/regexpu-core-6.4.0.tgz",
+ "integrity": "sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "regenerate": "^1.4.2",
+ "regenerate-unicode-properties": "^10.2.2",
+ "regjsgen": "^0.8.0",
+ "regjsparser": "^0.13.0",
+ "unicode-match-property-ecmascript": "^2.0.0",
+ "unicode-match-property-value-ecmascript": "^2.2.1"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/regjsgen": {
+ "version": "0.8.0",
+ "resolved": "https://registry.npmmirror.com/regjsgen/-/regjsgen-0.8.0.tgz",
+ "integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/regjsparser": {
+ "version": "0.13.2",
+ "resolved": "https://registry.npmmirror.com/regjsparser/-/regjsparser-0.13.2.tgz",
+ "integrity": "sha512-NgRBy2Nx/bE+9F27nVHnqcN5HjyLmecqsqx2PJHu3/IEtADD4WuxuXIVExD5PoSDFVrl78dOonfcOe5O+5nbzQ==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "jsesc": "~3.1.0"
+ },
+ "bin": {
+ "regjsparser": "bin/parser"
+ }
+ },
+ "node_modules/require-from-string": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmmirror.com/require-from-string/-/require-from-string-2.0.2.tgz",
+ "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/resolve": {
+ "version": "1.22.12",
+ "resolved": "https://registry.npmmirror.com/resolve/-/resolve-1.22.12.tgz",
+ "integrity": "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "is-core-module": "^2.16.1",
+ "path-parse": "^1.0.7",
+ "supports-preserve-symlinks-flag": "^1.0.0"
+ },
+ "bin": {
+ "resolve": "bin/resolve"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/rollup": {
+ "version": "4.62.0",
+ "resolved": "https://registry.npmmirror.com/rollup/-/rollup-4.62.0.tgz",
+ "integrity": "sha512-nc72Wgq62I7rtDV4izT5/aaS0zxy3kttkinf9586ApknY3jZO9NYsmtc24fUckA0X7Q2v+ML4a15pdUlV5V/jA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "1.0.9"
+ },
+ "bin": {
+ "rollup": "dist/bin/rollup"
+ },
+ "engines": {
+ "node": ">=18.0.0",
+ "npm": ">=8.0.0"
+ },
+ "optionalDependencies": {
+ "@rollup/rollup-android-arm-eabi": "4.62.0",
+ "@rollup/rollup-android-arm64": "4.62.0",
+ "@rollup/rollup-darwin-arm64": "4.62.0",
+ "@rollup/rollup-darwin-x64": "4.62.0",
+ "@rollup/rollup-freebsd-arm64": "4.62.0",
+ "@rollup/rollup-freebsd-x64": "4.62.0",
+ "@rollup/rollup-linux-arm-gnueabihf": "4.62.0",
+ "@rollup/rollup-linux-arm-musleabihf": "4.62.0",
+ "@rollup/rollup-linux-arm64-gnu": "4.62.0",
+ "@rollup/rollup-linux-arm64-musl": "4.62.0",
+ "@rollup/rollup-linux-loong64-gnu": "4.62.0",
+ "@rollup/rollup-linux-loong64-musl": "4.62.0",
+ "@rollup/rollup-linux-ppc64-gnu": "4.62.0",
+ "@rollup/rollup-linux-ppc64-musl": "4.62.0",
+ "@rollup/rollup-linux-riscv64-gnu": "4.62.0",
+ "@rollup/rollup-linux-riscv64-musl": "4.62.0",
+ "@rollup/rollup-linux-s390x-gnu": "4.62.0",
+ "@rollup/rollup-linux-x64-gnu": "4.62.0",
+ "@rollup/rollup-linux-x64-musl": "4.62.0",
+ "@rollup/rollup-openbsd-x64": "4.62.0",
+ "@rollup/rollup-openharmony-arm64": "4.62.0",
+ "@rollup/rollup-win32-arm64-msvc": "4.62.0",
+ "@rollup/rollup-win32-ia32-msvc": "4.62.0",
+ "@rollup/rollup-win32-x64-gnu": "4.62.0",
+ "@rollup/rollup-win32-x64-msvc": "4.62.0",
+ "fsevents": "~2.3.2"
+ }
+ },
+ "node_modules/safe-array-concat": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmmirror.com/safe-array-concat/-/safe-array-concat-1.1.4.tgz",
+ "integrity": "sha512-wtZlHyOje6OZTGqAoaDKxFkgRtkF9CnHAVnCHKfuj200wAgL+bSJhdsCD2l0Qx/2ekEXjPWcyKkfGb5CPboslg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.9",
+ "call-bound": "^1.0.4",
+ "get-intrinsic": "^1.3.0",
+ "has-symbols": "^1.1.0",
+ "isarray": "^2.0.5"
+ },
+ "engines": {
+ "node": ">=0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/safe-push-apply": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmmirror.com/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.npmmirror.com/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/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmmirror.com/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/serialize-javascript": {
+ "version": "7.0.6",
+ "resolved": "https://registry.npmmirror.com/serialize-javascript/-/serialize-javascript-7.0.6.tgz",
+ "integrity": "sha512-ATTK5Q4gFVg0YDp1my2vqygyvhcklD/UV5GIlYHooGTn/NogJqIzpetkD6E5kmuVULqz/S9inUL25XcAgDRJQg==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/set-function-length": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmmirror.com/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.npmmirror.com/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.npmmirror.com/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/sharp": {
+ "version": "0.35.1",
+ "resolved": "https://registry.npmmirror.com/sharp/-/sharp-0.35.1.tgz",
+ "integrity": "sha512-lW979AMi+ESidzMv/Lnv+F9bknzLyxLqFI05Sm433vOeRcltgxQmXpnfOOFIAlKtwXU/ksupm2srQoFCkR214g==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@img/colour": "^1.1.0",
+ "detect-libc": "^2.1.2",
+ "semver": "^7.8.4"
+ },
+ "engines": {
+ "node": ">=20.9.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-darwin-arm64": "0.35.1",
+ "@img/sharp-darwin-x64": "0.35.1",
+ "@img/sharp-freebsd-wasm32": "0.35.1",
+ "@img/sharp-libvips-darwin-arm64": "1.3.0",
+ "@img/sharp-libvips-darwin-x64": "1.3.0",
+ "@img/sharp-libvips-linux-arm": "1.3.0",
+ "@img/sharp-libvips-linux-arm64": "1.3.0",
+ "@img/sharp-libvips-linux-ppc64": "1.3.0",
+ "@img/sharp-libvips-linux-riscv64": "1.3.0",
+ "@img/sharp-libvips-linux-s390x": "1.3.0",
+ "@img/sharp-libvips-linux-x64": "1.3.0",
+ "@img/sharp-libvips-linuxmusl-arm64": "1.3.0",
+ "@img/sharp-libvips-linuxmusl-x64": "1.3.0",
+ "@img/sharp-linux-arm": "0.35.1",
+ "@img/sharp-linux-arm64": "0.35.1",
+ "@img/sharp-linux-ppc64": "0.35.1",
+ "@img/sharp-linux-riscv64": "0.35.1",
+ "@img/sharp-linux-s390x": "0.35.1",
+ "@img/sharp-linux-x64": "0.35.1",
+ "@img/sharp-linuxmusl-arm64": "0.35.1",
+ "@img/sharp-linuxmusl-x64": "0.35.1",
+ "@img/sharp-webcontainers-wasm32": "0.35.1",
+ "@img/sharp-win32-arm64": "0.35.1",
+ "@img/sharp-win32-ia32": "0.35.1",
+ "@img/sharp-win32-x64": "0.35.1"
+ }
+ },
+ "node_modules/sharp/node_modules/semver": {
+ "version": "7.8.4",
+ "resolved": "https://registry.npmmirror.com/semver/-/semver-7.8.4.tgz",
+ "integrity": "sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/shebang-command": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmmirror.com/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.npmmirror.com/shebang-regex/-/shebang-regex-3.0.0.tgz",
+ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/side-channel": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmmirror.com/side-channel/-/side-channel-1.1.1.tgz",
+ "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "object-inspect": "^1.13.4",
+ "side-channel-list": "^1.0.1",
+ "side-channel-map": "^1.0.1",
+ "side-channel-weakmap": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-list": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmmirror.com/side-channel-list/-/side-channel-list-1.0.1.tgz",
+ "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "object-inspect": "^1.13.4"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-map": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmmirror.com/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.npmmirror.com/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
+ "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.5",
+ "object-inspect": "^1.13.3",
+ "side-channel-map": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/signal-exit": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmmirror.com/signal-exit/-/signal-exit-4.1.0.tgz",
+ "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/smob": {
+ "version": "1.6.2",
+ "resolved": "https://registry.npmmirror.com/smob/-/smob-1.6.2.tgz",
+ "integrity": "sha512-RQsvleCbF8cVHEv+xuDGaA4pOizFqJ0GgjtMSRo6oP8pnN7WsigHgVGey6aILRBKv4W2YOMHLqbKdnB6hpB9fw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/source-map": {
+ "version": "0.8.0-beta.0",
+ "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.8.0-beta.0.tgz",
+ "integrity": "sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==",
+ "deprecated": "The work that was done in this beta branch won't be included in future versions",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "whatwg-url": "^7.0.0"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/source-map-js": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.1.tgz",
+ "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/source-map-support": {
+ "version": "0.5.21",
+ "resolved": "https://registry.npmmirror.com/source-map-support/-/source-map-support-0.5.21.tgz",
+ "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "buffer-from": "^1.0.0",
+ "source-map": "^0.6.0"
+ }
+ },
+ "node_modules/source-map-support/node_modules/source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/stop-iteration-iterator": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmmirror.com/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz",
+ "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "internal-slot": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/string.prototype.matchall": {
+ "version": "4.0.12",
+ "resolved": "https://registry.npmmirror.com/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz",
+ "integrity": "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.3",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.6",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.0.0",
+ "get-intrinsic": "^1.2.6",
+ "gopd": "^1.2.0",
+ "has-symbols": "^1.1.0",
+ "internal-slot": "^1.1.0",
+ "regexp.prototype.flags": "^1.5.3",
+ "set-function-name": "^2.0.2",
+ "side-channel": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/string.prototype.trim": {
+ "version": "1.2.11",
+ "resolved": "https://registry.npmmirror.com/string.prototype.trim/-/string.prototype.trim-1.2.11.tgz",
+ "integrity": "sha512-PwvK7BU+CMTJGYQCTZb5RWXIML92lftJLhQz1tBzgKiqGxJaMlBAa48POXaNAC2s4y8jr3EFqrkF9+44neS46w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.9",
+ "call-bound": "^1.0.4",
+ "define-data-property": "^1.1.4",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.24.2",
+ "es-object-atoms": "^1.1.2",
+ "has-property-descriptors": "^1.0.2",
+ "safe-regex-test": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/string.prototype.trimend": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmmirror.com/string.prototype.trimend/-/string.prototype.trimend-1.0.10.tgz",
+ "integrity": "sha512-2+3aDAOmPTmuFwjDnmJG2ctEkQKVki7vOSqaxkv42Mowj1V6PnvuwFCRrR5lChUux1TBskPjfkeTOhqczDMxTw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.9",
+ "call-bound": "^1.0.4",
+ "define-properties": "^1.2.1",
+ "es-object-atoms": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/string.prototype.trimstart": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmmirror.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz",
+ "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/stringify-object": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmmirror.com/stringify-object/-/stringify-object-3.3.0.tgz",
+ "integrity": "sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "get-own-enumerable-property-symbols": "^3.0.0",
+ "is-obj": "^1.0.1",
+ "is-regexp": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/strip-comments": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmmirror.com/strip-comments/-/strip-comments-2.0.1.tgz",
+ "integrity": "sha512-ZprKx+bBLXv067WTCALv8SSz5l2+XhpYCsVtSqlMnkAXMWDq+/ekVbl1ghqP9rUHTzv6sm/DwCOiYutU/yp1fw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/supports-preserve-symlinks-flag": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmmirror.com/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/temp-dir": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmmirror.com/temp-dir/-/temp-dir-2.0.0.tgz",
+ "integrity": "sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/tempy": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmmirror.com/tempy/-/tempy-0.6.0.tgz",
+ "integrity": "sha512-G13vtMYPT/J8A4X2SjdtBTphZlrp1gKv6hZiOjw14RCWg6GbHuQBGtjlx75xLbYV/wEc0D7G5K4rxKP/cXk8Bw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-stream": "^2.0.0",
+ "temp-dir": "^2.0.0",
+ "type-fest": "^0.16.0",
+ "unique-string": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/terser": {
+ "version": "5.48.0",
+ "resolved": "https://registry.npmmirror.com/terser/-/terser-5.48.0.tgz",
+ "integrity": "sha512-J/9An6vs9Us6wKRriSFXBWdRZapREHqFzdNUKk0pmu804EMR6dr6winwo7e5JDxN4xahxQsuysyYFwlwj4XN/Q==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "peer": true,
+ "dependencies": {
+ "@jridgewell/source-map": "^0.3.3",
+ "acorn": "^8.15.0",
+ "commander": "^2.20.0",
+ "source-map-support": "~0.5.20"
+ },
+ "bin": {
+ "terser": "bin/terser"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/tinyglobby": {
+ "version": "0.2.17",
+ "resolved": "https://registry.npmmirror.com/tinyglobby/-/tinyglobby-0.2.17.tgz",
+ "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fdir": "^6.5.0",
+ "picomatch": "^4.0.4"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/SuperchupuDev"
+ }
+ },
+ "node_modules/tr46": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmmirror.com/tr46/-/tr46-1.0.1.tgz",
+ "integrity": "sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "punycode": "^2.1.0"
+ }
+ },
+ "node_modules/tslib": {
+ "version": "2.8.1",
+ "resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.8.1.tgz",
+ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
+ "dev": true,
+ "license": "0BSD",
+ "optional": true
+ },
+ "node_modules/type-fest": {
+ "version": "0.16.0",
+ "resolved": "https://registry.npmmirror.com/type-fest/-/type-fest-0.16.0.tgz",
+ "integrity": "sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg==",
+ "dev": true,
+ "license": "(MIT OR CC0-1.0)",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/typed-array-buffer": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmmirror.com/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.npmmirror.com/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.npmmirror.com/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz",
+ "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "available-typed-arrays": "^1.0.7",
+ "call-bind": "^1.0.8",
+ "for-each": "^0.3.3",
+ "gopd": "^1.2.0",
+ "has-proto": "^1.2.0",
+ "is-typed-array": "^1.1.15",
+ "reflect.getprototypeof": "^1.0.9"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/typed-array-length": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmmirror.com/typed-array-length/-/typed-array-length-1.0.8.tgz",
+ "integrity": "sha512-phPGCwqr2+Qo0fwniCE8e4pKnGu/yFb5nD5Y8bf0EEeiI5GklnACYA9GFy/DrAeRrKHXvHn+1SUsOWgJp6RO+g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.9",
+ "for-each": "^0.3.5",
+ "gopd": "^1.2.0",
+ "is-typed-array": "^1.1.15",
+ "possible-typed-array-names": "^1.1.0",
+ "reflect.getprototypeof": "^1.0.10"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/unbox-primitive": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmmirror.com/unbox-primitive/-/unbox-primitive-1.1.0.tgz",
+ "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "has-bigints": "^1.0.2",
+ "has-symbols": "^1.1.0",
+ "which-boxed-primitive": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/unicode-canonical-property-names-ecmascript": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmmirror.com/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.npmmirror.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz",
+ "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "unicode-canonical-property-names-ecmascript": "^2.0.0",
+ "unicode-property-aliases-ecmascript": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/unicode-match-property-value-ecmascript": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmmirror.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.1.tgz",
+ "integrity": "sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/unicode-property-aliases-ecmascript": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmmirror.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.2.0.tgz",
+ "integrity": "sha512-hpbDzxUY9BFwX+UeBnxv3Sh1q7HFxj48DTmXchNgRa46lO8uj3/1iEn3MiNUYTg1g9ctIqXCCERn8gYZhHC5lQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/unique-string": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmmirror.com/unique-string/-/unique-string-2.0.0.tgz",
+ "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "crypto-random-string": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/universalify": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmmirror.com/universalify/-/universalify-2.0.1.tgz",
+ "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 10.0.0"
+ }
+ },
+ "node_modules/upath": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmmirror.com/upath/-/upath-1.2.0.tgz",
+ "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4",
+ "yarn": "*"
+ }
+ },
+ "node_modules/update-browserslist-db": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmmirror.com/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz",
+ "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "escalade": "^3.2.0",
+ "picocolors": "^1.1.1"
+ },
+ "bin": {
+ "update-browserslist-db": "cli.js"
+ },
+ "peerDependencies": {
+ "browserslist": ">= 4.21.0"
+ }
+ },
+ "node_modules/vite": {
+ "version": "5.4.21",
+ "resolved": "https://registry.npmmirror.com/vite/-/vite-5.4.21.tgz",
+ "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "esbuild": "^0.21.3",
+ "postcss": "^8.4.43",
+ "rollup": "^4.20.0"
+ },
+ "bin": {
+ "vite": "bin/vite.js"
+ },
+ "engines": {
+ "node": "^18.0.0 || >=20.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/vitejs/vite?sponsor=1"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.3"
+ },
+ "peerDependencies": {
+ "@types/node": "^18.0.0 || >=20.0.0",
+ "less": "*",
+ "lightningcss": "^1.21.0",
+ "sass": "*",
+ "sass-embedded": "*",
+ "stylus": "*",
+ "sugarss": "*",
+ "terser": "^5.4.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ },
+ "less": {
+ "optional": true
+ },
+ "lightningcss": {
+ "optional": true
+ },
+ "sass": {
+ "optional": true
+ },
+ "sass-embedded": {
+ "optional": true
+ },
+ "stylus": {
+ "optional": true
+ },
+ "sugarss": {
+ "optional": true
+ },
+ "terser": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/vite-plugin-pwa": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmmirror.com/vite-plugin-pwa/-/vite-plugin-pwa-1.3.0.tgz",
+ "integrity": "sha512-c5kMgN+ITrOtHXp8PAtk2uOIEea6XjP/unCGxOWWBzQ6qa65qj/awHg0wf+QF9E/2u9vh86LqxPwzEPNbM2r5A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "debug": "^4.3.6",
+ "pretty-bytes": "^6.1.1",
+ "tinyglobby": "^0.2.10",
+ "workbox-build": "^7.4.1",
+ "workbox-window": "^7.4.1"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/antfu"
+ },
+ "peerDependencies": {
+ "@vite-pwa/assets-generator": "^1.0.0",
+ "vite": "^3.1.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0",
+ "workbox-build": "^7.4.1",
+ "workbox-window": "^7.4.1"
+ },
+ "peerDependenciesMeta": {
+ "@vite-pwa/assets-generator": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/vue": {
+ "version": "3.5.38",
+ "resolved": "https://registry.npmmirror.com/vue/-/vue-3.5.38.tgz",
+ "integrity": "sha512-vAMKHfImQlYSy0C+PBue4s3ERZ2xGKfgZg5GXAsLInq1dyh2H78ILVP5sK0KPFPVW4kv+OGCIvBEondcjpZp7A==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@vue/compiler-dom": "3.5.38",
+ "@vue/compiler-sfc": "3.5.38",
+ "@vue/runtime-dom": "3.5.38",
+ "@vue/server-renderer": "3.5.38",
+ "@vue/shared": "3.5.38"
+ },
+ "peerDependencies": {
+ "typescript": "*"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/vue-demi": {
+ "version": "0.14.10",
+ "resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.14.10.tgz",
+ "integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==",
+ "hasInstallScript": true,
+ "license": "MIT",
+ "bin": {
+ "vue-demi-fix": "bin/vue-demi-fix.js",
+ "vue-demi-switch": "bin/vue-demi-switch.js"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/antfu"
+ },
+ "peerDependencies": {
+ "@vue/composition-api": "^1.0.0-rc.1",
+ "vue": "^3.0.0-0 || ^2.6.0"
+ },
+ "peerDependenciesMeta": {
+ "@vue/composition-api": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/vue-router": {
+ "version": "4.6.4",
+ "resolved": "https://registry.npmmirror.com/vue-router/-/vue-router-4.6.4.tgz",
+ "integrity": "sha512-Hz9q5sa33Yhduglwz6g9skT8OBPii+4bFn88w6J+J4MfEo4KRRpmiNG/hHHkdbRFlLBOqxN8y8gf2Fb0MTUgVg==",
+ "license": "MIT",
+ "dependencies": {
+ "@vue/devtools-api": "^6.6.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/posva"
+ },
+ "peerDependencies": {
+ "vue": "^3.5.0"
+ }
+ },
+ "node_modules/webidl-conversions": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmmirror.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz",
+ "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==",
+ "dev": true,
+ "license": "BSD-2-Clause"
+ },
+ "node_modules/whatwg-url": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmmirror.com/whatwg-url/-/whatwg-url-7.1.0.tgz",
+ "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "lodash.sortby": "^4.7.0",
+ "tr46": "^1.0.1",
+ "webidl-conversions": "^4.0.2"
+ }
+ },
+ "node_modules/which": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmmirror.com/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.npmmirror.com/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.npmmirror.com/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.npmmirror.com/which-collection/-/which-collection-1.0.2.tgz",
+ "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-map": "^2.0.3",
+ "is-set": "^2.0.3",
+ "is-weakmap": "^2.0.2",
+ "is-weakset": "^2.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/which-typed-array": {
+ "version": "1.1.22",
+ "resolved": "https://registry.npmmirror.com/which-typed-array/-/which-typed-array-1.1.22.tgz",
+ "integrity": "sha512-fvO4ExWMFsqyhG3AiPAObMuY1lxaqgYcxbc49CNdWDDECOJNgQyvsOWVwbZc+qf3rzRtxojBK+CMEv0Ld5CYpw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "available-typed-arrays": "^1.0.7",
+ "call-bind": "^1.0.9",
+ "call-bound": "^1.0.4",
+ "for-each": "^0.3.5",
+ "get-proto": "^1.0.1",
+ "gopd": "^1.2.0",
+ "has-tostringtag": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/workbox-background-sync": {
+ "version": "7.4.1",
+ "resolved": "https://registry.npmmirror.com/workbox-background-sync/-/workbox-background-sync-7.4.1.tgz",
+ "integrity": "sha512-HhT7KE8tOWDm02wRNshXUnUPofMlhenF2DBdUnDPOubhizzPeItkYTmAB6td1Z2cjYPa98vzEiPLEuzn5hN66g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "idb": "^7.0.1",
+ "workbox-core": "7.4.1"
+ }
+ },
+ "node_modules/workbox-broadcast-update": {
+ "version": "7.4.1",
+ "resolved": "https://registry.npmmirror.com/workbox-broadcast-update/-/workbox-broadcast-update-7.4.1.tgz",
+ "integrity": "sha512-uAlgslKLvbQY+suirIdnBCSYrcgBhjp81Nj4l1lj/Jmj0MJO2CJERnCJjT0GFVwmReV0N+zs78K6gqd5gr9/+A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "workbox-core": "7.4.1"
+ }
+ },
+ "node_modules/workbox-build": {
+ "version": "7.4.1",
+ "resolved": "https://registry.npmmirror.com/workbox-build/-/workbox-build-7.4.1.tgz",
+ "integrity": "sha512-SDhxIvEAde9Gy/5w4Yo1Jh/M49Z0qE3q0oteyE8zGq0DScxFqVBcCtIXFuLtmtxRQZCMbf0prco4VyEu3KBQuw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@apideck/better-ajv-errors": "^0.3.1",
+ "@babel/core": "^7.24.4",
+ "@babel/preset-env": "^7.11.0",
+ "@babel/runtime": "^7.11.2",
+ "@rollup/plugin-babel": "^6.1.0",
+ "@rollup/plugin-node-resolve": "^16.0.3",
+ "@rollup/plugin-replace": "^6.0.3",
+ "@rollup/plugin-terser": "^1.0.0",
+ "@trickfilm400/rollup-plugin-off-main-thread": "^3.0.0-pre1",
+ "ajv": "^8.6.0",
+ "common-tags": "^1.8.0",
+ "eta": "^4.5.1",
+ "fast-json-stable-stringify": "^2.1.0",
+ "fs-extra": "^9.0.1",
+ "glob": "^11.0.1",
+ "pretty-bytes": "^5.3.0",
+ "rollup": "^4.53.3",
+ "source-map": "^0.8.0-beta.0",
+ "stringify-object": "^3.3.0",
+ "strip-comments": "^2.0.1",
+ "tempy": "^0.6.0",
+ "upath": "^1.2.0",
+ "workbox-background-sync": "7.4.1",
+ "workbox-broadcast-update": "7.4.1",
+ "workbox-cacheable-response": "7.4.1",
+ "workbox-core": "7.4.1",
+ "workbox-expiration": "7.4.1",
+ "workbox-google-analytics": "7.4.1",
+ "workbox-navigation-preload": "7.4.1",
+ "workbox-precaching": "7.4.1",
+ "workbox-range-requests": "7.4.1",
+ "workbox-recipes": "7.4.1",
+ "workbox-routing": "7.4.1",
+ "workbox-strategies": "7.4.1",
+ "workbox-streams": "7.4.1",
+ "workbox-sw": "7.4.1",
+ "workbox-window": "7.4.1"
+ },
+ "engines": {
+ "node": ">=20.0.0"
+ }
+ },
+ "node_modules/workbox-build/node_modules/pretty-bytes": {
+ "version": "5.6.0",
+ "resolved": "https://registry.npmmirror.com/pretty-bytes/-/pretty-bytes-5.6.0.tgz",
+ "integrity": "sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/workbox-cacheable-response": {
+ "version": "7.4.1",
+ "resolved": "https://registry.npmmirror.com/workbox-cacheable-response/-/workbox-cacheable-response-7.4.1.tgz",
+ "integrity": "sha512-8xaFoJdDc2OjrlbbL3gEeBO1WKcMwRqwLRupgqahYXu75yXajPLuwrbXMrIGZuWYXrQwk0xDjOxZ/ujCy/oJYw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "workbox-core": "7.4.1"
+ }
+ },
+ "node_modules/workbox-core": {
+ "version": "7.4.1",
+ "resolved": "https://registry.npmmirror.com/workbox-core/-/workbox-core-7.4.1.tgz",
+ "integrity": "sha512-DT+vu46eh/2vRsSHTY4Xmc32Z1rr9PRlQUXr1Dx30ZuXRWwOsvZgGgcwxcasubQLQmbTNYZjv44LkBAQ4tT5tQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/workbox-expiration": {
+ "version": "7.4.1",
+ "resolved": "https://registry.npmmirror.com/workbox-expiration/-/workbox-expiration-7.4.1.tgz",
+ "integrity": "sha512-lRKUF7b+OGbeXkQk1s6MHXOa3d7Xxf7Of31W6c6hCfipfIyrtdWZ89stq21AHZMaoG7VNFoHply4Ox+rU31TWg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "idb": "^7.0.1",
+ "workbox-core": "7.4.1"
+ }
+ },
+ "node_modules/workbox-google-analytics": {
+ "version": "7.4.1",
+ "resolved": "https://registry.npmmirror.com/workbox-google-analytics/-/workbox-google-analytics-7.4.1.tgz",
+ "integrity": "sha512-Mks1JwLEt++ZAkF6sS1OpSh9RtAMIsiDgRpK+codiHGIPXeaUOgi4cPc3GFadUl8V5QPeypEk8Oxgl3HlwVzHw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "workbox-background-sync": "7.4.1",
+ "workbox-core": "7.4.1",
+ "workbox-routing": "7.4.1",
+ "workbox-strategies": "7.4.1"
+ }
+ },
+ "node_modules/workbox-navigation-preload": {
+ "version": "7.4.1",
+ "resolved": "https://registry.npmmirror.com/workbox-navigation-preload/-/workbox-navigation-preload-7.4.1.tgz",
+ "integrity": "sha512-C4KVsjPcYKJOhr631AxR9XoG2rLF3QiTk5aMv36MXOjtWvm8axwNFAtKUPGsWUwLXXAMgYM1En7fsvndaXeXRQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "workbox-core": "7.4.1"
+ }
+ },
+ "node_modules/workbox-precaching": {
+ "version": "7.4.1",
+ "resolved": "https://registry.npmmirror.com/workbox-precaching/-/workbox-precaching-7.4.1.tgz",
+ "integrity": "sha512-cdr/9qByww7yzEp7zg/qI4ukUrrNjQLgN+ONQRpjy/VqGQXwkgHwr00KksGJK8v0VifwDXBb8a4cWNZH71jn3Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "workbox-core": "7.4.1",
+ "workbox-routing": "7.4.1",
+ "workbox-strategies": "7.4.1"
+ }
+ },
+ "node_modules/workbox-range-requests": {
+ "version": "7.4.1",
+ "resolved": "https://registry.npmmirror.com/workbox-range-requests/-/workbox-range-requests-7.4.1.tgz",
+ "integrity": "sha512-7i2oxAUE82gHdAJBCAQ04JzNOdRPqzuOzGfoUyJpFSmeqBNYGPrAH8GPoPjUQTfp+NycwrD2H68VtuF8qxv0vQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "workbox-core": "7.4.1"
+ }
+ },
+ "node_modules/workbox-recipes": {
+ "version": "7.4.1",
+ "resolved": "https://registry.npmmirror.com/workbox-recipes/-/workbox-recipes-7.4.1.tgz",
+ "integrity": "sha512-gnbVfmV4/TtmQaM4x9AtuXhcdstJsep3XMVeztOrQVPT+R6+6DeBjGTCQ7fFCXm+4GEHUA5VEBTyi5+4gWGeog==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "workbox-cacheable-response": "7.4.1",
+ "workbox-core": "7.4.1",
+ "workbox-expiration": "7.4.1",
+ "workbox-precaching": "7.4.1",
+ "workbox-routing": "7.4.1",
+ "workbox-strategies": "7.4.1"
+ }
+ },
+ "node_modules/workbox-routing": {
+ "version": "7.4.1",
+ "resolved": "https://registry.npmmirror.com/workbox-routing/-/workbox-routing-7.4.1.tgz",
+ "integrity": "sha512-yubJGErZOusuidAenaL5ypfhQOa7urxP/f8E0ws7FPb4039RiWXUWBAyUkmUoOL/BcQGen3h0J8872d51IYxtA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "workbox-core": "7.4.1"
+ }
+ },
+ "node_modules/workbox-strategies": {
+ "version": "7.4.1",
+ "resolved": "https://registry.npmmirror.com/workbox-strategies/-/workbox-strategies-7.4.1.tgz",
+ "integrity": "sha512-GZxpaw9NbmOelj7667uZ2kpk5BFpOGbO4X0qjwh5ls8XQ8C+Lha5LQchTiUzsTFSS+NlUpftYAyOVXvQUrcqOQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "workbox-core": "7.4.1"
+ }
+ },
+ "node_modules/workbox-streams": {
+ "version": "7.4.1",
+ "resolved": "https://registry.npmmirror.com/workbox-streams/-/workbox-streams-7.4.1.tgz",
+ "integrity": "sha512-HWWtraKUbJknd9kgqGcpQ3G114HOPYvqs8HaJMDs2ebLNAimDkVDaWfAXE6Ybl+m8U6KsCE6pWyLYuigWmnAXw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "workbox-core": "7.4.1",
+ "workbox-routing": "7.4.1"
+ }
+ },
+ "node_modules/workbox-sw": {
+ "version": "7.4.1",
+ "resolved": "https://registry.npmmirror.com/workbox-sw/-/workbox-sw-7.4.1.tgz",
+ "integrity": "sha512-fez5f2DUlDJWTFYkCWQpY10N8gtztd849NswCbVFk0QlcSM4HT5A8x4g4ii650yem4I8tHY0R7JZahwp3ltIPw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/workbox-window": {
+ "version": "7.4.1",
+ "resolved": "https://registry.npmmirror.com/workbox-window/-/workbox-window-7.4.1.tgz",
+ "integrity": "sha512-notZDH2u8VXaqyuD7xaqIfEFi6SRM4SUSd7ewe9PDsVqADuepxX2ZMY3uvuZGxzY5ZOsGC/vD3A/3smFtJt4/A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/trusted-types": "^2.0.2",
+ "workbox-core": "7.4.1"
+ }
+ },
+ "node_modules/yallist": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmmirror.com/yallist/-/yallist-3.1.1.tgz",
+ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
+ "dev": true,
+ "license": "ISC"
+ }
+ }
+}
diff --git a/client/package.json b/client/package.json
new file mode 100644
index 0000000..1224a2c
--- /dev/null
+++ b/client/package.json
@@ -0,0 +1,24 @@
+{
+ "name": "carwash-client",
+ "version": "2.0.0",
+ "private": true,
+ "type": "module",
+ "scripts": {
+ "dev": "vite",
+ "build": "vite build",
+ "preview": "vite preview"
+ },
+ "dependencies": {
+ "axios": "^1.7.7",
+ "chart.js": "^4.4.4",
+ "pinia": "^2.2.4",
+ "vue": "^3.5.12",
+ "vue-router": "^4.4.5"
+ },
+ "devDependencies": {
+ "@vitejs/plugin-vue": "^5.1.4",
+ "sharp": "^0.35.1",
+ "vite": "^5.4.10",
+ "vite-plugin-pwa": "^1.3.0"
+ }
+}
diff --git a/client/public/favicon-16x16.png b/client/public/favicon-16x16.png
new file mode 100644
index 0000000..9d5b535
Binary files /dev/null and b/client/public/favicon-16x16.png differ
diff --git a/client/public/favicon-32x32.png b/client/public/favicon-32x32.png
new file mode 100644
index 0000000..6493eeb
Binary files /dev/null and b/client/public/favicon-32x32.png differ
diff --git a/client/public/pwa-icon.svg b/client/public/pwa-icon.svg
new file mode 100644
index 0000000..8fe501a
--- /dev/null
+++ b/client/public/pwa-icon.svg
@@ -0,0 +1,23 @@
+
diff --git a/client/public/pwa/apple-touch-icon.png b/client/public/pwa/apple-touch-icon.png
new file mode 100644
index 0000000..5d86da7
Binary files /dev/null and b/client/public/pwa/apple-touch-icon.png differ
diff --git a/client/public/pwa/pwa-192x192.png b/client/public/pwa/pwa-192x192.png
new file mode 100644
index 0000000..ab8e751
Binary files /dev/null and b/client/public/pwa/pwa-192x192.png differ
diff --git a/client/public/pwa/pwa-512x512.png b/client/public/pwa/pwa-512x512.png
new file mode 100644
index 0000000..aefb992
Binary files /dev/null and b/client/public/pwa/pwa-512x512.png differ
diff --git a/client/public/pwa/pwa-maskable-512x512.png b/client/public/pwa/pwa-maskable-512x512.png
new file mode 100644
index 0000000..84769b3
Binary files /dev/null and b/client/public/pwa/pwa-maskable-512x512.png differ
diff --git a/client/scripts/check-pwa.mjs b/client/scripts/check-pwa.mjs
new file mode 100644
index 0000000..da3c11a
--- /dev/null
+++ b/client/scripts/check-pwa.mjs
@@ -0,0 +1,166 @@
+/**
+ * PWA 安装性验证脚本
+ * 检查:
+ * 1. manifest.webmanifest 合法
+ * 2. Service Worker 注册成功
+ * 3. 图标全部能加载
+ * 4. PWA 必需字段(name, icons[192/512], start_url, display, theme_color, background_color)
+ * 5. 离线 fallback(/offline 或 navigateFallback 命中)
+ */
+import puppeteer from 'puppeteer';
+import { readFile } from 'node:fs/promises';
+import path from 'node:path';
+import { fileURLToPath } from 'node:url';
+
+const __dirname = path.dirname(fileURLToPath(import.meta.url));
+const ROOT = path.resolve(__dirname, '..');
+
+const URL_TO_TEST = process.env.PWA_URL || 'http://localhost:4173/login';
+const CHROME_PATH =
+ process.env.CHROME_PATH ||
+ '/Users/yabozi/.cache/puppeteer/chrome/mac-148.0.7778.97/chrome-mac-x64/Google Chrome for Testing.app/Contents/MacOS/Google Chrome for Testing';
+
+const checks = [];
+let pass = 0;
+let fail = 0;
+function ok(name, detail) {
+ checks.push({ status: '✅', name, detail });
+ pass++;
+}
+function ko(name, detail) {
+ checks.push({ status: '❌', name, detail });
+ fail++;
+}
+
+async function fetchStatus(url) {
+ try {
+ const r = await fetch(url, { redirect: 'follow' });
+ return r.status;
+ } catch (e) {
+ return 0;
+ }
+}
+
+async function main() {
+ console.log(`🔍 PWA check: ${URL_TO_TEST}\n`);
+
+ // 1. manifest 文件可访问 + 合法 JSON
+ const manifestUrl = new URL('/manifest.webmanifest', URL_TO_TEST).toString();
+ const manifestStatus = await fetchStatus(manifestUrl);
+ if (manifestStatus === 200) {
+ ok('manifest 200', manifestUrl);
+ const r = await fetch(manifestUrl);
+ const m = await r.json();
+ const required = ['name', 'short_name', 'start_url', 'display', 'icons'];
+ const missing = required.filter((k) => !m[k]);
+ if (missing.length === 0) {
+ ok('manifest 必备字段', Object.keys(m).join(', '));
+ } else {
+ ko('manifest 必备字段缺失', missing.join(', '));
+ }
+ // 图标尺寸
+ const sizes = (m.icons || []).map((i) => i.sizes).filter(Boolean);
+ const has192 = sizes.some((s) => s.includes('192'));
+ const has512 = sizes.some((s) => s.includes('512'));
+ const hasMaskable = (m.icons || []).some((i) => i.purpose === 'maskable');
+ const hasApple = (m.icons || []).some((i) =>
+ (i.src || '').includes('apple-touch')
+ );
+ if (has192 && has512) ok('icons 192+512', sizes.join(', '));
+ else ko('icons 192/512 缺失', sizes.join(', '));
+ if (hasMaskable) ok('maskable icon', 'present');
+ else ko('maskable icon', 'missing');
+ if (hasApple) ok('apple-touch icon', 'present');
+ else ko('apple-touch icon', 'missing');
+ } else {
+ ko('manifest 不可访问', `status=${manifestStatus}, url=${manifestUrl}`);
+ }
+
+ // 2. Service Worker 注册
+ const browser = await puppeteer.launch({
+ executablePath: CHROME_PATH,
+ headless: 'new',
+ args: [
+ '--no-sandbox',
+ '--disable-setuid-sandbox',
+ '--disable-dev-shm-usage',
+ `--user-data-dir=/tmp/lh-cache-pwa-check-${Date.now()}`,
+ ],
+ });
+ const page = await browser.newPage();
+ const swLogs = [];
+ page.on('console', (m) => {
+ const t = m.text();
+ if (t.includes('[PWA]') || t.includes('ServiceWorker')) swLogs.push(t);
+ });
+
+ await page.goto(URL_TO_TEST, { waitUntil: 'networkidle0', timeout: 30000 });
+ // 等几秒给 SW 机会注册
+ await new Promise((r) => setTimeout(r, 3000));
+
+ const swInfo = await page.evaluate(async () => {
+ if (!('serviceWorker' in navigator)) return { supported: false };
+ const regs = await navigator.serviceWorker.getRegistrations();
+ return {
+ supported: true,
+ count: regs.length,
+ scopes: regs.map((r) => r.scope),
+ active: regs.map((r) => !!r.active),
+ scripts: regs.map((r) => r.active && r.active.scriptURL),
+ };
+ });
+ if (swInfo.supported && swInfo.count > 0 && swInfo.active.every(Boolean)) {
+ ok('Service Worker 已注册', `scope=${swInfo.scopes.join(',')}`);
+ } else if (swInfo.supported && swInfo.count > 0) {
+ ko('Service Worker 未激活', JSON.stringify(swInfo));
+ } else {
+ ko('Service Worker 未注册', 'getRegistrations() 为空');
+ }
+
+ // 3. 关键 meta 标签
+ const meta = await page.evaluate(() => {
+ const get = (sel) => document.querySelector(sel)?.getAttribute('content') || null;
+ return {
+ themeColor: get('meta[name="theme-color"]'),
+ appleCapable: get('meta[name="apple-mobile-web-app-capable"]'),
+ appleTitle: get('meta[name="apple-mobile-web-app-title"]'),
+ viewport: get('meta[name="viewport"]'),
+ manifestLink: !!document.querySelector('link[rel="manifest"]'),
+ appleTouchIcon: !!document.querySelector('link[rel="apple-touch-icon"]'),
+ };
+ });
+ if (meta.themeColor) ok('theme-color', meta.themeColor);
+ else ko('theme-color', 'missing');
+ if (meta.appleCapable === 'yes') ok('apple-mobile-web-app-capable', 'yes');
+ else ko('apple-mobile-web-app-capable', meta.appleCapable || 'missing');
+ if (meta.manifestLink) ok('manifest link', 'present');
+ else ko('manifest link', 'missing');
+ if (meta.appleTouchIcon) ok('apple-touch-icon link', 'present');
+ else ko('apple-touch-icon link', 'missing');
+
+ // 4. SW 日志确认 offline ready
+ const offlineReady = swLogs.some((l) => l.includes('离线缓存就绪') || l.includes('offline'));
+ if (offlineReady) ok('PWA offline log', '检测到 offline ready');
+ // 离线就绪不是强校验,标 warn 即可
+ if (!offlineReady) {
+ checks.push({
+ status: '⚠️',
+ name: 'PWA offline 日志',
+ detail: '运行后无离线 ready 日志(可能 SW 还没预缓存完,可忽略)',
+ });
+ }
+
+ await browser.close();
+
+ // 输出
+ for (const c of checks) {
+ console.log(`${c.status} ${c.name}${c.detail ? ` — ${c.detail}` : ''}`);
+ }
+ console.log(`\n总计: ✅ ${pass} ❌ ${fail}`);
+ process.exit(fail > 0 ? 1 : 0);
+}
+
+main().catch((e) => {
+ console.error('PWA check 失败:', e);
+ process.exit(2);
+});
diff --git a/client/scripts/gen-pwa-icons.mjs b/client/scripts/gen-pwa-icons.mjs
new file mode 100644
index 0000000..8821788
--- /dev/null
+++ b/client/scripts/gen-pwa-icons.mjs
@@ -0,0 +1,42 @@
+// 用 sharp 把 SVG 转为多尺寸 PNG
+import sharp from 'sharp';
+import { readFile, writeFile } from 'node:fs/promises';
+import path from 'node:path';
+import { fileURLToPath } from 'node:url';
+
+const __dirname = path.dirname(fileURLToPath(import.meta.url));
+const svg = await readFile(path.join(__dirname, '../public/pwa-icon.svg'));
+
+const targets = [
+ // 标准 PWA icons
+ { out: 'public/pwa/pwa-192x192.png', size: 192, type: 'normal' },
+ { out: 'public/pwa/pwa-512x512.png', size: 512, type: 'normal' },
+ // maskable:四周留 20% 安全区,logo 居中缩到 60%
+ { out: 'public/pwa/pwa-maskable-512x512.png', size: 512, type: 'maskable' },
+ // apple touch
+ { out: 'public/pwa/apple-touch-icon.png', size: 180, type: 'normal' },
+ // favicon
+ { out: 'public/favicon-32x32.png', size: 32, type: 'normal' },
+ { out: 'public/favicon-16x16.png', size: 16, type: 'normal' },
+];
+
+const bgColor = { r: 27, g: 110, b: 243 }; // 渐变起始色 #1B6EF3
+
+for (const { out, size, type } of targets) {
+ let buffer = await sharp(svg).resize(size, size).png().toBuffer();
+
+ if (type === 'maskable') {
+ // maskable 重新画:蓝底全填 + logo 60%
+ const inner = await sharp(svg).resize(Math.floor(size * 0.6), Math.floor(size * 0.6)).png().toBuffer();
+ buffer = await sharp({
+ create: { width: size, height: size, channels: 4, background: bgColor },
+ })
+ .composite([{ input: inner, gravity: 'center' }])
+ .png()
+ .toBuffer();
+ }
+ await writeFile(path.join(__dirname, '..', out), buffer);
+ console.log('✓', out, `${size}x${size}`, type);
+}
+
+console.log('Done.');
diff --git a/client/src/App.vue b/client/src/App.vue
new file mode 100644
index 0000000..379f6f1
--- /dev/null
+++ b/client/src/App.vue
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/client/src/api/ai.js b/client/src/api/ai.js
new file mode 100644
index 0000000..8a5ba92
--- /dev/null
+++ b/client/src/api/ai.js
@@ -0,0 +1,19 @@
+// client/src/api/ai.js — AI 截图识别
+import http from './client';
+
+export const getConfig = () => http.get('/ai/config');
+export const saveConfig = (data) => http.post('/ai/config', data);
+export const test = (data) => http.post('/ai/test', data || {});
+
+// 上传图片,返回 { image_id, url, name, size, mime }
+export const uploadImage = (file) => {
+ const fd = new FormData();
+ fd.append('file', file);
+ return http.post('/ai/upload', fd, {
+ headers: { 'Content-Type': 'multipart/form-data' },
+ });
+};
+
+// 识别图片
+// type: 'wash' | 'refuel' | 'charge' | 'maint' | 'insurance'
+export const recognize = (image_id, type) => http.post('/ai/recognize', { image_id, type });
diff --git a/client/src/api/auth.js b/client/src/api/auth.js
new file mode 100644
index 0000000..5e190fa
--- /dev/null
+++ b/client/src/api/auth.js
@@ -0,0 +1,8 @@
+// client/src/api/auth.js
+import client from './client';
+export const login = (username, password) => client.post('/auth/login', { username, password });
+export const logout = () => client.post('/auth/logout');
+export const changeAccount = (data) => client.post('/auth/account', data);
+export const me = () => client.get('/auth/me');
+export const csrf = () => client.get('/auth/csrf');
+export const health = () => client.get('/health');
diff --git a/client/src/api/chemicals.js b/client/src/api/chemicals.js
new file mode 100644
index 0000000..85ea3e4
--- /dev/null
+++ b/client/src/api/chemicals.js
@@ -0,0 +1,16 @@
+// client/src/api/chemicals.js
+import client from './client';
+export const list = (params) => client.get('/chemicals', { params });
+export const all = () => client.get('/chemicals/list');
+export const get = (id) => client.get(`/chemicals/${id}`);
+export const update = (id, data) => client.put(`/chemicals/${id}`, data);
+export const create = (data) => client.post('/chemicals', data);
+export const sync = () => client.post('/chemicals/sync');
+export const refreshIds = () => client.post('/chemicals/refresh-ids');
+export const addStock = (id, data) => client.post(`/chemicals/${id}/add`, data);
+export const consumeStock = (id, data) => client.post(`/chemicals/${id}/consume`, data);
+export const grocySearch = (q) => client.get('/chemicals/grocy-search', { params: { q } });
+export const getCategories = () => client.get('/chemicals/categories');
+export const getCategoryMappings = () => client.get('/chemicals/category-mappings');
+export const saveCategoryMappings = (mappings) => client.post('/chemicals/category-mappings', { mappings });
+export const deleteCategoryMapping = (id) => client.delete(`/chemicals/category-mappings/${id}`);
diff --git a/client/src/api/client.js b/client/src/api/client.js
new file mode 100644
index 0000000..fa9bc59
--- /dev/null
+++ b/client/src/api/client.js
@@ -0,0 +1,166 @@
+// client/src/api/client.js — axios 实例,自动带 cookie + CSRF
+import axios from 'axios';
+import { useAuthStore } from '../stores/auth';
+import { useDebugStore } from '../stores/debug';
+
+const client = axios.create({
+ baseURL: '/api',
+ withCredentials: true,
+ timeout: 15000,
+});
+
+// 特殊端点:长 timeout(同步类操作)
+const LONG_TIMEOUT_OVERRIDE = {
+ '/chemicals/sync': 90000, // Grocy 拉取最多 1.5 分钟
+ '/grocy/sync': 90000,
+ '/chemicals/grocy-search': 60000, // Grocy 全局搜索(要拉全量 products 列表)
+ '/chemicals/refresh-ids': 45000, // 后台轻量同步:只拉一次 /api/objects/products
+};
+const origRequest = client.interceptors.request;
+client.interceptors.request.use((cfg) => {
+ const path = (cfg.url || '').replace(/^\/+/, '/');
+ if (LONG_TIMEOUT_OVERRIDE[path]) {
+ cfg.timeout = LONG_TIMEOUT_OVERRIDE[path];
+ }
+ return cfg;
+});
+
+// 统一处理 server 的 {ok, data, error} 包装:
+// 成功 → 把 data 字段提到顶层(业务代码直接拿到)
+// 失败 → 抛错,err.response.data = server 的 error 对象
+client.interceptors.response.use(
+ (r) => {
+ const body = r.data;
+ if (body && typeof body === 'object' && 'ok' in body) {
+ if (body.ok) {
+ r.data = body.data; // 剥掉包装,client 拿到业务 data
+ } else {
+ // ok=false:构造一个类 axios 错误抛出去
+ const err = new Error(body.error?.message || '请求失败');
+ err.response = { status: r.status, data: body.error || body };
+ return Promise.reject(err);
+ }
+ }
+ // 调试模式:记录所有调用
+ try {
+ const debug = useDebugStore();
+ if (debug.enabled) {
+ debug.logCall({
+ method: r.config?.method?.toUpperCase(),
+ url: r.config?.url,
+ status: r.status,
+ body: summarize(r.data),
+ });
+ }
+ } catch {}
+ return r;
+ },
+ async (err) => {
+ const status = err.response?.status;
+ const body = err.response?.data;
+ // 把 server 的 error 也归一化到 err.response.data
+ if (body && typeof body === 'object' && 'ok' in body && body.ok === false) {
+ err.response.data = body.error || body;
+ }
+
+ // 调试模式:上报到 DebugPanel
+ try {
+ const debug = useDebugStore();
+ if (debug.enabled) {
+ const cfg = err.config || {};
+ // 预期错误白名单:启动时检查登录状态、未登录访问受保护资源、CSRF 缺失
+ const url = cfg.url || '';
+ const isExpected = (
+ (status === 401 && (url === '/auth/me' || url === '/auth/csrf')) ||
+ (status === 401 && location.pathname === '/login') ||
+ (status === 403 && url === '/auth/me')
+ );
+ if (!isExpected) {
+ debug.log({
+ kind: 'api',
+ title: `${(cfg.method || 'GET').toUpperCase()} ${cfg.url} → ${status || 'Network Error'}`,
+ detail: {
+ method: cfg.method?.toUpperCase(),
+ url: cfg.url,
+ baseURL: cfg.baseURL,
+ fullURL: (cfg.baseURL || '') + (cfg.url || ''),
+ status,
+ statusText: err.response?.statusText,
+ requestHeaders: cfg.headers,
+ requestBody: cfg.data ? safeParse(cfg.data) : null,
+ responseBody: err.response?.data,
+ message: err.message,
+ },
+ });
+ }
+ debug.logCall({
+ method: cfg.method?.toUpperCase(),
+ url: cfg.url,
+ status: status || 'ERR',
+ body: err.response?.data,
+ });
+ }
+ } catch {}
+
+ if (status === 401) {
+ const auth = useAuthStore();
+ auth.clear();
+ if (location.pathname !== '/login') {
+ // 把当前页所有 form 草稿强制刷盘(不丢用户填的数据)
+ try {
+ window.dispatchEvent(new CustomEvent('form-draft:flush-all'));
+ } catch {}
+ const returnTo = location.pathname + location.search;
+ location.href = '/login?redirect=' + encodeURIComponent(returnTo) + '&reason=expired';
+ }
+ }
+ // CSRF token 失效:自动刷新 + 重试原请求。retry 标记挂在 config 上防死循环。
+ if (status === 403 && (body?.code === 'CSRF' || body?.error?.code === 'CSRF') && !err.config?.__csrfRetried) {
+ try {
+ const auth = useAuthStore();
+ await auth.refreshCsrf();
+ const retryCfg = { ...err.config, __csrfRetried: true };
+ // 用新 token 重新发
+ if (auth.csrfToken && ['post', 'put', 'delete', 'patch'].includes(retryCfg.method)) {
+ retryCfg.headers = { ...(retryCfg.headers || {}), 'X-CSRF-Token': auth.csrfToken };
+ }
+ return client.request(retryCfg);
+ } catch (refreshErr) {
+ // refresh 失败就 fall through 到 reject
+ }
+ }
+ return Promise.reject(err);
+ }
+);
+
+function safeParse(s) {
+ if (typeof s !== 'string') return s;
+ try { return JSON.parse(s); } catch { return s; }
+}
+function summarize(d) {
+ if (d == null) return d;
+ if (Array.isArray(d)) return `Array(${d.length})`;
+ if (typeof d === 'object') {
+ const keys = Object.keys(d);
+ return `Object{${keys.slice(0, 6).join(',')}${keys.length > 6 ? '…' : ''}}`;
+ }
+ return d;
+}
+
+client.interceptors.request.use((cfg) => {
+ const auth = useAuthStore();
+ if (auth.csrfToken && ['post', 'put', 'delete', 'patch'].includes(cfg.method)) {
+ cfg.headers['X-CSRF-Token'] = auth.csrfToken;
+ }
+ return cfg;
+});
+
+/** 统一解包:list API 可能直接返 array,也可能返 {key: [...]}
+ * 用法:const list = asArray(r.data, 'vehicles'); */
+export function asArray(data, key) {
+ if (Array.isArray(data)) return data;
+ if (data && typeof data === 'object' && key && Array.isArray(data[key])) return data[key];
+ return [];
+}
+
+export default client;
diff --git a/client/src/api/insurance.js b/client/src/api/insurance.js
new file mode 100644
index 0000000..603f4c0
--- /dev/null
+++ b/client/src/api/insurance.js
@@ -0,0 +1,19 @@
+// client/src/api/insurance.js — 保险记录 CRUD + 附件上传
+import http from './client';
+
+export const list = (params) => http.get('/insurances', { params });
+export const get = (id) => http.get(`/insurances/${id}`);
+export const create = (data) => http.post('/insurances', data);
+export const update = (id, data) => http.put(`/insurances/${id}`, data);
+export const remove = (id) => http.delete(`/insurances/${id}`);
+
+// 上传保单附件(图片或 PDF)
+export const upload = (id, file) => {
+ const fd = new FormData();
+ fd.append('file', file);
+ return http.post(`/insurances/${id}/upload`, fd, {
+ headers: { 'Content-Type': 'multipart/form-data' },
+ });
+};
+
+export const deleteAttachment = (id) => http.delete(`/insurances/${id}/attachment`);
diff --git a/client/src/api/logs.js b/client/src/api/logs.js
new file mode 100644
index 0000000..c1ec4b2
--- /dev/null
+++ b/client/src/api/logs.js
@@ -0,0 +1,22 @@
+// client/src/api/logs.js — 保养 / 加油 / 充电三个领域共用
+import http from './client';
+
+const RES = {
+ maintenances: '/maintenances',
+ refuels: '/refuels',
+ chargings: '/chargings',
+};
+
+function factory(base) {
+ return {
+ list: (params) => http.get(base, { params }),
+ get: (id) => http.get(`${base}/${id}`),
+ create: (data) => http.post(base, data),
+ update: (id, data) => http.put(`${base}/${id}`, data),
+ remove: (id) => http.delete(`${base}/${id}`),
+ };
+}
+
+export const maintApi = factory(RES.maintenances);
+export const refuelApi = factory(RES.refuels);
+export const chargingApi = factory(RES.chargings);
diff --git a/client/src/api/operationLogs.js b/client/src/api/operationLogs.js
new file mode 100644
index 0000000..a1e1a2c
--- /dev/null
+++ b/client/src/api/operationLogs.js
@@ -0,0 +1,6 @@
+// client/src/api/operationLogs.js
+import client from './client';
+export const list = (params) => client.get('/operation-logs', { params });
+export const get = (id) => client.get(`/operation-logs/${id}`);
+export const options = () => client.get('/operation-logs/options');
+export const recover = (id) => client.post(`/operation-logs/${id}/recover`);
diff --git a/client/src/api/settings.js b/client/src/api/settings.js
new file mode 100644
index 0000000..2915869
--- /dev/null
+++ b/client/src/api/settings.js
@@ -0,0 +1,16 @@
+// client/src/api/settings.js
+import client from './client';
+export const get = () => client.get('/settings');
+export const update = (data) => client.post('/settings', data);
+export const overview = () => client.get('/stats/overview');
+export const dashboardExtra = () => client.get('/dashboard/extra');
+export const getCity = () => client.get('/settings/city');
+export const grocyLogs = (limit) => client.get('/settings/grocy-logs', { params: { limit } });
+export const getWeather = () => client.get('/settings/weather');
+export const resetAll = (confirm_token, seed = true) =>
+ client.post('/settings/reset', { confirm_token, seed });
+
+// 月度报表
+export const reportMonths = (limit = 12) => client.get('/reports/monthly/list', { params: { limit } });
+export const reportExcelUrl = (month) => `/api/reports/monthly/excel?month=${encodeURIComponent(month)}`;
+export const reportPdfUrl = (month) => `/api/reports/monthly/pdf?month=${encodeURIComponent(month)}`;
diff --git a/client/src/api/vehicles.js b/client/src/api/vehicles.js
new file mode 100644
index 0000000..78979e7
--- /dev/null
+++ b/client/src/api/vehicles.js
@@ -0,0 +1,9 @@
+// client/src/api/vehicles.js
+import client from './client';
+export const list = (params) => client.get('/vehicles', { params });
+export const get = (id) => client.get(`/vehicles/${id}`);
+export const create = (data) => client.post('/vehicles', data);
+export const update = (id, data) => client.put(`/vehicles/${id}`, data);
+export const remove = (id) => client.delete(`/vehicles/${id}`);
+export const stats = () => client.get('/vehicles/stats');
+export const health = (id) => client.get(`/vehicles/${id}/health`);
diff --git a/client/src/api/washes.js b/client/src/api/washes.js
new file mode 100644
index 0000000..d8c50de
--- /dev/null
+++ b/client/src/api/washes.js
@@ -0,0 +1,17 @@
+// client/src/api/washes.js
+import client from './client';
+export const list = (params) => client.get('/washes', { params });
+export const get = (id) => client.get(`/washes/${id}`);
+export const create = (data) => client.post('/washes', data);
+export const remove = (id) => client.delete(`/washes/${id}`);
+export const batchDelete = (ids, challenge) => client.post('/washes/batch-delete', { ids, challenge });
+export const types = () => client.get('/washes/types');
+
+// 对比照
+export const listPhotos = (id) => client.get(`/washes/${id}/photos`);
+export const uploadPhoto = (id, formData) => client.post(`/washes/${id}/photos`, formData, {
+ headers: { 'Content-Type': 'multipart/form-data' }
+});
+export const deletePhoto = (id, photoId) => client.delete(`/washes/${id}/photos/${photoId}`);
+export const comparePhotos = (id, type1 = 'before', type2 = 'after') =>
+ client.get(`/washes/${id}/photos/compare`, { params: { type1, type2 } });
diff --git a/client/src/components/AiFallbackModal.vue b/client/src/components/AiFallbackModal.vue
new file mode 100644
index 0000000..bc6631d
--- /dev/null
+++ b/client/src/components/AiFallbackModal.vue
@@ -0,0 +1,64 @@
+
+
+
+
+
+
+
+
![待识别图片]()
+
图片不可预览
+
📌 对照右栏填表,遇到看不清的字段直接留空
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/client/src/components/AppHeader.vue b/client/src/components/AppHeader.vue
new file mode 100644
index 0000000..a08f2a7
--- /dev/null
+++ b/client/src/components/AppHeader.vue
@@ -0,0 +1,290 @@
+
+
+
+
+
+
+
diff --git a/client/src/components/AppLayout.vue b/client/src/components/AppLayout.vue
new file mode 100644
index 0000000..924eeb7
--- /dev/null
+++ b/client/src/components/AppLayout.vue
@@ -0,0 +1,59 @@
+
+
+
+
+
+
+
diff --git a/client/src/components/ChartBlock.vue b/client/src/components/ChartBlock.vue
new file mode 100644
index 0000000..f4180ba
--- /dev/null
+++ b/client/src/components/ChartBlock.vue
@@ -0,0 +1,90 @@
+
+
+
+
+
+
diff --git a/client/src/components/ChemPicker.vue b/client/src/components/ChemPicker.vue
new file mode 100644
index 0000000..aa4f68c
--- /dev/null
+++ b/client/src/components/ChemPicker.vue
@@ -0,0 +1,189 @@
+
+
+
+
+
+
+ {{ selected[0].name }}
+
+
+
+
+
+
+
+ {{ ch.name }}
+
+ {{ ch.category || '—' }}
+ 库存 {{ ch.current_amount }} {{ ch.unit || '' }}
+
+
+
+
+ 无匹配「{{ query }}」
+
+
+
+
+
+
+
+
diff --git a/client/src/components/ConfirmDangerDialog.vue b/client/src/components/ConfirmDangerDialog.vue
new file mode 100644
index 0000000..e414a82
--- /dev/null
+++ b/client/src/components/ConfirmDangerDialog.vue
@@ -0,0 +1,256 @@
+
+
+
+
+
+ {{ titleIcon }}
+ {{ title }}
+
+
+
{{ message }}
+
+
+
+
+
+ 请计算:
+ {{ challenge.a }} {{ challenge.op }} {{ challenge.b }} = ?
+
+
+
+
+
+
+
+ 请输入 {{ confirmWord }} 以确认
+
+
+
+
+
+
+
+
+
{{ error }}
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/client/src/components/DebugPanel.vue b/client/src/components/DebugPanel.vue
new file mode 100644
index 0000000..390b49f
--- /dev/null
+++ b/client/src/components/DebugPanel.vue
@@ -0,0 +1,240 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ fmtTime(e.ts) }}
+ {{ kindLabel(e.kind) }}
+ {{ e.title }}
+
+
+
+ {{ fmtDetail(e.detail) }}
+
+
暂无错误 · 一切正常 ✨
+
+
+
+
+
+ {{ fmtTime(c.ts) }}
+ {{ c.status }}
+ {{ c.method }}
+ {{ c.url }}
+
+
+ {{ fmtBody(c.body) }}
+
+
暂无 API 调用记录
+
+
+
+
+
+
+
+
+
+
+
diff --git a/client/src/components/MobileCardList.vue b/client/src/components/MobileCardList.vue
new file mode 100644
index 0000000..931e8c7
--- /dev/null
+++ b/client/src/components/MobileCardList.vue
@@ -0,0 +1,241 @@
+
+
+
+
+
+ |
+
+ {{ col.label }}
+ |
+ |
+
+
+
+
+ |
+
+ |
+
+
+ {{ col.formatter ? col.formatter(row[col.key], row) : row[col.key] }}
+
+ |
+
+
+ |
+
+
+ |
+ {{ emptyText }}
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ col.label }}
+
+
+ {{ col.formatter ? col.formatter(row[col.key], row) : row[col.key] }}
+
+
+
+
+
+
+
+
+
+ {{ emptyText }}
+
+
+
+
+
+
+
diff --git a/client/src/components/PwaToasts.vue b/client/src/components/PwaToasts.vue
new file mode 100644
index 0000000..6ad6021
--- /dev/null
+++ b/client/src/components/PwaToasts.vue
@@ -0,0 +1,218 @@
+
+
+
+
+
+
🔄
+
+ 新版本可用
+ 点击刷新以加载最新内容
+
+
+
+
+
+
+
+
+
+
📦
+
+ 已可离线使用
+ 无网络时仍能打开
+
+
+
+
+
+
+
+
+
⬇️
+
+ 安装 CarLog
+ 添加到主屏幕,像 App 一样使用
+
+
+
+
+
+
+
+
+
+
📱
+
+ 添加到主屏幕
+ 点击底部分享 ⬆,选「添加到主屏幕」
+
+
+
+
+
+
+
+
+
+
diff --git a/client/src/components/StatCard.vue b/client/src/components/StatCard.vue
new file mode 100644
index 0000000..f51367f
--- /dev/null
+++ b/client/src/components/StatCard.vue
@@ -0,0 +1,29 @@
+
+
+
+ {{ title }}
+ {{ icon }}
+
+
{{ value }}
+
{{ hint }}
+
+
+
+
+
+
diff --git a/client/src/composables/useAiRecognize.js b/client/src/composables/useAiRecognize.js
new file mode 100644
index 0000000..94feca3
--- /dev/null
+++ b/client/src/composables/useAiRecognize.js
@@ -0,0 +1,125 @@
+// client/src/composables/useAiRecognize.js
+// 通用 AI 截图识别 composable — 5 个表单复用
+// 用法:
+// const ai = useAiRecognize();
+//
+//
+//
+//
+//
+// 调用流程:
+// open(type, onSuccess) — 成功 → onSuccess(data)
+// — 失败 → 打开 AiFallbackModal(不再 alert)
+//
+// 兜底数据:fallback.value = { image_id, preview_url, type, error }
+// 调用方在 @confirm 里读 fallback.value 并把它当作成功数据使用(手动填的字段)即可。
+
+import { ref } from 'vue';
+import * as aiApi from '../api/ai';
+
+export function useAiRecognize() {
+ const busy = ref(false);
+ const error = ref('');
+ const showFallback = ref(false);
+ const fallback = ref(null); // { image_id, preview_url, type, error }
+
+ function pickFile() {
+ return new Promise((resolve) => {
+ const input = document.createElement('input');
+ input.type = 'file';
+ input.accept = 'image/*';
+ input.onchange = (e) => resolve(e.target.files[0] || null);
+ input.click();
+ });
+ }
+
+ function previewUrlFor(imageId) {
+ // 上传目录固定在 /api/uploads/ai/
+ return `/api/uploads/ai/${imageId}`;
+ }
+
+ async function open(type, onSuccess) {
+ error.value = '';
+ fallback.value = null;
+ showFallback.value = false;
+ const file = await pickFile();
+ if (!file) return;
+ busy.value = true;
+ let uploadedId = null;
+ let uploadedUrl = null;
+ try {
+ // 1) 上传
+ const upR = await aiApi.uploadImage(file);
+ uploadedId = upR.data.image_id;
+ uploadedUrl = upR.data.url || previewUrlFor(uploadedId);
+ // 2) 识别
+ const recR = await aiApi.recognize(uploadedId, type);
+ const data = recR.data.data || {};
+ // 3) 回调
+ onSuccess?.(data, recR.data);
+ } catch (e) {
+ const msg = e.response?.data?.error?.message || e.message;
+ error.value = msg;
+ if (uploadedId) {
+ // 至少上传成功了,弹出兜底 modal 让用户对着图填
+ fallback.value = {
+ image_id: uploadedId,
+ preview_url: uploadedUrl,
+ type,
+ error: msg,
+ };
+ showFallback.value = true;
+ } else {
+ // 上传就挂了 — 只能 alert
+ const isConfig = msg.includes('未配置 AI API key') || msg.includes('请先');
+ alert(
+ 'AI 识别失败:' + msg + (isConfig ? '' :
+ '\n\n可能原因:\n1. AI API key 未配置或无效(设置 → AI 截图识别)\n2. 网络无法访问 AI provider\n3. 文件过大或格式不支持'),
+ );
+ }
+ } finally {
+ busy.value = false;
+ }
+ }
+
+ async function recognizeFromFile(file, type, onSuccess) {
+ error.value = '';
+ fallback.value = null;
+ showFallback.value = false;
+ if (!file) return;
+ busy.value = true;
+ let uploadedId = null;
+ let uploadedUrl = null;
+ try {
+ const upR = await aiApi.uploadImage(file);
+ uploadedId = upR.data.image_id;
+ uploadedUrl = upR.data.url || previewUrlFor(uploadedId);
+ const recR = await aiApi.recognize(uploadedId, type);
+ const data = recR.data.data || {};
+ onSuccess?.(data, recR.data);
+ } catch (e) {
+ const msg = e.response?.data?.error?.message || e.message;
+ error.value = msg;
+ if (uploadedId) {
+ fallback.value = { image_id: uploadedId, preview_url: uploadedUrl, type, error: msg };
+ showFallback.value = true;
+ } else {
+ alert('AI 识别失败:' + msg);
+ }
+ } finally {
+ busy.value = false;
+ }
+ }
+
+ function cancelFallback() {
+ showFallback.value = false;
+ fallback.value = null;
+ }
+
+ return { open, recognizeFromFile, busy, error, showFallback, fallback, cancelFallback };
+}
diff --git a/client/src/main.js b/client/src/main.js
new file mode 100644
index 0000000..4c7d1b1
--- /dev/null
+++ b/client/src/main.js
@@ -0,0 +1,83 @@
+// client/src/main.js — 入口
+import { createApp } from 'vue';
+import { createPinia } from 'pinia';
+import App from './App.vue';
+import router from './router';
+import { useDebugStore } from './stores/debug';
+import { usePwaStore } from './stores/pwa';
+import { registerSW } from 'virtual:pwa-register';
+import './style.css';
+
+const app = createApp(App);
+app.use(createPinia());
+app.use(router);
+
+// PWA Service Worker 注册
+if ('serviceWorker' in navigator) {
+ const pwa = usePwaStore();
+ const updateSW = registerSW({
+ immediate: true,
+ onNeedRefresh() {
+ console.info('[PWA] 新版本可用');
+ pwa.triggerNeedRefresh();
+ },
+ onOfflineReady() {
+ console.info('[PWA] 离线缓存就绪');
+ pwa.triggerOfflineReady();
+ },
+ onRegisterError(err) {
+ console.warn('[PWA] SW 注册失败', err);
+ },
+ });
+ pwa.bindRegisterSw(updateSW);
+ // 暴露到 window 方便调试 / 强制更新
+ window.__pwaUpdate = () => updateSW(true);
+}
+
+// 全局错误捕获 → 调试面板
+app.config.errorHandler = (err, instance, info) => {
+ const debug = useDebugStore();
+ debug.log({
+ kind: 'vue',
+ title: `[${info}] ${err?.message || err}`,
+ detail: {
+ message: err?.message,
+ stack: err?.stack,
+ info,
+ component: instance?.$options?.name || instance?.$options?.__name || '',
+ },
+ });
+ console.error('[vue error]', err, info);
+};
+
+window.addEventListener('unhandledrejection', (e) => {
+ const debug = useDebugStore();
+ debug.log({
+ kind: 'promise',
+ title: `未捕获的 Promise 异常: ${e.reason?.message || e.reason}`,
+ detail: {
+ message: e.reason?.message || String(e.reason),
+ stack: e.reason?.stack,
+ },
+ });
+ console.error('[unhandledrejection]', e.reason);
+});
+
+window.addEventListener('error', (e) => {
+ const debug = useDebugStore();
+ if (e.error) {
+ debug.log({
+ kind: 'runtime',
+ title: `全局错误: ${e.message}`,
+ detail: {
+ message: e.message,
+ filename: e.filename,
+ lineno: e.lineno,
+ colno: e.colno,
+ stack: e.error?.stack,
+ },
+ });
+ }
+});
+
+app.mount('#app');
diff --git a/client/src/router/index.js b/client/src/router/index.js
new file mode 100644
index 0000000..7474971
--- /dev/null
+++ b/client/src/router/index.js
@@ -0,0 +1,69 @@
+// client/src/router/index.js — 路由 + 守卫(路由级 code-split)
+import { createRouter, createWebHistory } from 'vue-router';
+import { useAuthStore } from '../stores/auth';
+
+// 路由级别懒加载 → 每个 view 独立 chunk,首屏只下载 Home + Login
+const Login = () => import(/* webpackChunkName: "v-login" */ '../views/Login.vue');
+const Home = () => import(/* webpackChunkName: "v-home" */ '../views/Home.vue');
+const Offline = () => import(/* webpackChunkName: "v-offline" */ '../views/Offline.vue');
+const WashesList = () => import('../views/WashesList.vue');
+const WashNew = () => import('../views/WashNew.vue');
+const WashShow = () => import('../views/WashShow.vue');
+const ChemicalsList = () => import('../views/ChemicalsList.vue');
+const ChemicalNew = () => import('../views/ChemicalNew.vue');
+const BatchPurchase = () => import('../views/BatchPurchase.vue');
+const ChemicalDetail = () => import('../views/ChemicalDetail.vue');
+const VehiclesList = () => import('../views/VehiclesList.vue');
+const VehicleForm = () => import('../views/VehicleForm.vue');
+const VehicleDetail = () => import('../views/VehicleDetail.vue');
+const MaintenanceList = () => import('../views/MaintenanceList.vue');
+const RefuelList = () => import('../views/RefuelList.vue');
+const ChargingList = () => import('../views/ChargingList.vue');
+const InsuranceList = () => import('../views/InsuranceList.vue');
+const Stats = () => import('../views/Stats.vue');
+const Settings = () => import('../views/Settings.vue');
+const OperationLogs = () => import('../views/OperationLogs.vue');
+
+const routes = [
+ { path: '/login', name: 'login', component: Login, meta: { public: true } },
+ { path: '/', name: 'home', component: Home },
+ { path: '/washes', name: 'washes', component: WashesList },
+ { path: '/washes/new', name: 'wash-new', component: WashNew },
+ { path: '/washes/:id', name: 'wash-show', component: WashShow },
+ { path: '/chemicals', name: 'chemicals', component: ChemicalsList },
+ { path: '/chemicals/new', name: 'chemical-new', component: ChemicalNew },
+ { path: '/chemicals/purchase', name: 'chemical-purchase', component: BatchPurchase },
+ { path: '/chemicals/:id', name: 'chemical-show', component: ChemicalDetail },
+ { path: '/vehicles', name: 'vehicles', component: VehiclesList },
+ { path: '/vehicles/new', name: 'vehicle-new', component: VehicleForm },
+ { path: '/vehicles/:id', name: 'vehicle-show', component: VehicleDetail },
+ { path: '/vehicles/:id/edit', name: 'vehicle-edit', component: VehicleForm },
+ { path: '/maintenances', name: 'maintenances', component: MaintenanceList },
+ { path: '/refuels', name: 'refuels', component: RefuelList },
+ { path: '/chargings', name: 'chargings', component: ChargingList },
+ { path: '/insurances', name: 'insurances', component: InsuranceList },
+ { path: '/stats', name: 'stats', component: Stats },
+ { path: '/settings', name: 'settings', component: Settings },
+ { path: '/operation-logs', name: 'operation-logs', component: OperationLogs },
+ { path: '/offline', name: 'offline', component: Offline, meta: { public: true } },
+ { path: '/:pathMatch(.*)*', redirect: '/' },
+];
+
+const router = createRouter({
+ history: createWebHistory(),
+ routes,
+ scrollBehavior: () => ({ top: 0 }),
+});
+
+router.beforeEach(async (to, from) => {
+ const auth = useAuthStore();
+ if (!auth.bootstrapped) await auth.refresh();
+ if (!to.meta.public && !auth.user) {
+ return { name: 'login', query: { redirect: to.fullPath } };
+ }
+ if (to.name === 'login' && auth.user) {
+ return { name: 'home' };
+ }
+});
+
+export default router;
diff --git a/client/src/stores/auth.js b/client/src/stores/auth.js
new file mode 100644
index 0000000..633cacb
--- /dev/null
+++ b/client/src/stores/auth.js
@@ -0,0 +1,44 @@
+// client/src/stores/auth.js
+import { defineStore } from 'pinia';
+import * as authApi from '../api/auth';
+
+export const useAuthStore = defineStore('auth', {
+ state: () => ({
+ user: null,
+ csrfToken: '',
+ bootstrapped: false,
+ }),
+ actions: {
+ async refresh() {
+ try {
+ const me = await authApi.me();
+ this.user = me.data?.user || me.data;
+ if (!this.user) throw new Error('no user');
+ } catch {
+ this.user = null;
+ }
+ await this.refreshCsrf();
+ this.bootstrapped = true;
+ },
+ async refreshCsrf() {
+ try {
+ const r = await authApi.csrf();
+ this.csrfToken = r.data?.csrf_token || '';
+ } catch {
+ this.csrfToken = '';
+ }
+ },
+ async login(username, password) {
+ const r = await authApi.login(username, password);
+ this.user = r.data?.user || null;
+ await this.refreshCsrf();
+ return r.data;
+ },
+ async logout() {
+ try { await authApi.logout(); } catch {}
+ this.user = null;
+ this.csrfToken = '';
+ },
+ clear() { this.user = null; },
+ },
+});
diff --git a/client/src/stores/debug.js b/client/src/stores/debug.js
new file mode 100644
index 0000000..bc09e57
--- /dev/null
+++ b/client/src/stores/debug.js
@@ -0,0 +1,59 @@
+// client/src/stores/debug.js — 调试模式 + 错误/调用日志
+import { defineStore } from 'pinia';
+
+const LS_KEY = 'carwash:debug';
+
+function loadLS() {
+ try { return localStorage.getItem(LS_KEY) === '1'; } catch { return false; }
+}
+function saveLS(v) { try { localStorage.setItem(LS_KEY, v ? '1' : '0'); } catch {} }
+
+export const useDebugStore = defineStore('debug', {
+ state: () => ({
+ enabled: loadLS(),
+ errors: [], // { id, ts, kind, title, detail }
+ calls: [], // { id, ts, method, url, status, ms, body }
+ collapsed: false,
+ tab: 'errors', // 'errors' | 'calls'
+ }),
+ getters: {
+ count: (s) => s.errors.length,
+ callCount: (s) => s.calls.length,
+ latest: (s) => s.errors[s.errors.length - 1],
+ },
+ actions: {
+ toggle() {
+ this.enabled = !this.enabled;
+ saveLS(this.enabled);
+ },
+ set(v) {
+ this.enabled = !!v;
+ saveLS(this.enabled);
+ },
+ log(err) {
+ if (!this.enabled) return;
+ const id = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
+ this.errors.push({
+ id,
+ ts: new Date().toISOString(),
+ kind: err.kind || 'error',
+ title: err.title || '未知错误',
+ detail: err.detail || {},
+ });
+ if (this.errors.length > 50) this.errors = this.errors.slice(-50);
+ this.collapsed = false;
+ },
+ logCall(call) {
+ if (!this.enabled) return;
+ this.calls.push({
+ id: `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
+ ts: new Date().toISOString(),
+ ...call,
+ });
+ if (this.calls.length > 80) this.calls = this.calls.slice(-80);
+ },
+ clear() { this.errors = []; this.calls = []; },
+ setTab(t) { this.tab = t; },
+ togglePanel() { this.collapsed = !this.collapsed; },
+ },
+});
diff --git a/client/src/stores/pwa.js b/client/src/stores/pwa.js
new file mode 100644
index 0000000..58698cd
--- /dev/null
+++ b/client/src/stores/pwa.js
@@ -0,0 +1,89 @@
+// client/src/stores/pwa.js — PWA 状态:更新提示 / 安装提示 / 离线
+import { defineStore } from 'pinia';
+import { ref, computed } from 'vue';
+
+export const usePwaStore = defineStore('pwa', () => {
+ /** true = 已有新版本 SW 等激活,需要刷新 */
+ const needRefresh = ref(false);
+ /** true = 资源已缓存完,可离线用 */
+ const offlineReady = ref(false);
+ /** 浏览器/桌面触发安装的事件(Android/Desktop Chrome) */
+ const installPromptEvent = ref(null);
+ /** 已安装(standalone 模式运行) */
+ const isInstalled = computed(() => {
+ if (typeof window === 'undefined') return false;
+ return (
+ window.matchMedia('(display-mode: standalone)').matches ||
+ window.navigator.standalone === true // iOS Safari
+ );
+ });
+ /** iOS 设备 + Safari + 未安装 → 引导走"分享 → 添加到主屏幕" */
+ const isIosSafari = computed(() => {
+ if (typeof window === 'undefined') return false;
+ const ua = window.navigator.userAgent;
+ return /iPad|iPhone|iPod/.test(ua) && /Safari/.test(ua) && !/CriOS|FxiOS|EdgiOS/.test(ua);
+ });
+
+ let updateFn = null;
+
+ function bindRegisterSw(registerFn) {
+ updateFn = registerFn;
+ }
+
+ function triggerNeedRefresh() {
+ needRefresh.value = true;
+ }
+ function triggerOfflineReady() {
+ offlineReady.value = true;
+ // 5s 后自动收起
+ setTimeout(() => (offlineReady.value = false), 5000);
+ }
+ async function applyUpdate() {
+ if (updateFn) {
+ await updateFn(true);
+ needRefresh.value = false;
+ } else {
+ window.location.reload();
+ }
+ }
+ function dismissNeedRefresh() {
+ needRefresh.value = false;
+ }
+ function dismissOfflineReady() {
+ offlineReady.value = false;
+ }
+ async function promptInstall() {
+ const e = installPromptEvent.value;
+ if (!e) return false;
+ e.prompt();
+ const choice = await e.userChoice;
+ installPromptEvent.value = null;
+ return choice.outcome === 'accepted';
+ }
+ function captureInstallPrompt(e) {
+ e.preventDefault();
+ installPromptEvent.value = e;
+ }
+ // 全局监听 beforeinstallprompt
+ if (typeof window !== 'undefined') {
+ window.addEventListener('beforeinstallprompt', captureInstallPrompt);
+ window.addEventListener('appinstalled', () => {
+ installPromptEvent.value = null;
+ });
+ }
+
+ return {
+ needRefresh,
+ offlineReady,
+ installPromptEvent,
+ isInstalled,
+ isIosSafari,
+ bindRegisterSw,
+ triggerNeedRefresh,
+ triggerOfflineReady,
+ applyUpdate,
+ dismissNeedRefresh,
+ dismissOfflineReady,
+ promptInstall,
+ };
+});
diff --git a/client/src/style.css b/client/src/style.css
new file mode 100644
index 0000000..e0624df
--- /dev/null
+++ b/client/src/style.css
@@ -0,0 +1,156 @@
+/* client/src/style.css — EstateHub 设计令牌 + 全局样式 */
+:root {
+ --bg: #E8F4F9;
+ --bg-soft: #F2F8FB;
+ --card: #FFFFFF;
+ --card-shadow: 0 2px 8px rgba(40, 80, 110, 0.06);
+ --card-shadow-hover: 0 4px 16px rgba(40, 80, 110, 0.10);
+ --text: #0F2233;
+ --text-soft: #5A6F80;
+ --text-mute: #8A9CAB;
+ --line: #E1ECF2;
+ --accent: #0F2233;
+ --accent-soft: #1A3A55;
+ --brand: #1E5B8A;
+ --brand-soft: #2C7AB0;
+ --green: #4DBA9A;
+ --green-soft: #7CD0B5;
+ --warn: #E8A33D;
+ --danger: #D9695C;
+ --info: #5AA8D8;
+ --radius: 14px;
+ --radius-sm: 8px;
+ --radius-lg: 22px;
+ --pill: 999px;
+ --font: 'Outfit', system-ui, -apple-system, 'PingFang SC', 'Microsoft YaHei', sans-serif;
+
+ /* === 响应式断点(4 档)===
+ * --bp-sm: 小屏手机 (< 480px) → 极简布局
+ * --bp-md: 大屏手机/小平板 (480~767) → 紧凑布局
+ * --bp-lg: 平板 (768~1023) → 双列/抽屉式
+ * --bp-xl: 桌面 (1024~1439) → 标准布局
+ * --bp-2xl: 大屏桌面 (≥1440) → 宽屏布局
+ */
+ --bp-sm: 480px;
+ --bp-md: 768px;
+ --bp-lg: 1024px;
+ --bp-xl: 1440px;
+
+ /* iOS 安全区 + Android 导航条适配 */
+ --safe-top: env(safe-area-inset-top, 0px);
+ --safe-bottom: env(safe-area-inset-bottom, 0px);
+ --safe-left: env(safe-area-inset-left, 0px);
+ --safe-right: env(safe-area-inset-right, 0px);
+}
+
+* { box-sizing: border-box; }
+html, body, #app { height: 100%; }
+body {
+ margin: 0;
+ font-family: var(--font);
+ background: var(--bg);
+ color: var(--text);
+ -webkit-font-smoothing: antialiased;
+ font-feature-settings: 'ss01', 'cv11';
+}
+a { color: inherit; text-decoration: none; }
+button { font-family: inherit; cursor: pointer; }
+
+/* === 工具类 === */
+.btn {
+ display: inline-flex; align-items: center; gap: 6px;
+ padding: 8px 16px; border-radius: var(--radius-sm);
+ font-size: 14px; font-weight: 500; border: 0; transition: all .15s;
+}
+.btn-primary { background: var(--accent); color: #fff; }
+.btn-primary:hover { background: var(--accent-soft); }
+.btn-ghost { background: transparent; color: var(--text); border: 1px solid var(--line); }
+.btn-ghost:hover { background: var(--bg-soft); }
+.btn-danger { background: var(--danger); color: #fff; }
+.btn-sm { padding: 4px 10px; font-size: 12px; }
+
+.card {
+ background: var(--card);
+ border-radius: var(--radius);
+ box-shadow: var(--card-shadow);
+ transition: box-shadow .2s;
+}
+.card:hover { box-shadow: var(--card-shadow-hover); }
+.card-pad { padding: 24px; }
+
+.input, .select, .textarea {
+ width: 100%; padding: 10px 14px;
+ border: 1px solid var(--line); border-radius: var(--radius-sm);
+ font-size: 14px; font-family: inherit; background: #fff; color: var(--text);
+ transition: border .15s;
+}
+.input:focus, .select:focus, .textarea:focus { outline: 0; border-color: var(--brand-soft); }
+.textarea { resize: vertical; min-height: 80px; }
+.label { display: block; font-size: 13px; color: var(--text-soft); margin-bottom: 6px; font-weight: 500; }
+
+.pill {
+ display: inline-flex; align-items: center; gap: 4px;
+ padding: 3px 10px; border-radius: var(--pill);
+ font-size: 12px; font-weight: 500;
+}
+.pill-blue { background: #E0F0FA; color: var(--brand); }
+.pill-green { background: #DEF4EC; color: #2E8A6B; }
+.pill-warn { background: #FBEED9; color: #8B6510; }
+.pill-danger { background: #FBE3DF; color: #A33B30; }
+.pill-gray { background: #EEF2F5; color: var(--text-soft); }
+
+.text-soft { color: var(--text-soft); }
+.text-mute { color: var(--text-mute); }
+.text-green { color: var(--green); }
+.text-danger { color: var(--danger); }
+.text-brand { color: var(--brand); }
+
+.flex { display: flex; }
+.flex-col { display: flex; flex-direction: column; }
+.items-center { align-items: center; }
+.justify-between { justify-content: space-between; }
+.justify-center { justify-content: center; }
+.gap-2 { gap: 8px; }
+.gap-3 { gap: 12px; }
+.gap-4 { gap: 16px; }
+.gap-6 { gap: 24px; }
+.mt-2 { margin-top: 8px; } .mt-3 { margin-top: 12px; } .mt-4 { margin-top: 16px; } .mt-6 { margin-top: 24px; }
+.mb-2 { margin-bottom: 8px; } .mb-3 { margin-bottom: 12px; } .mb-4 { margin-bottom: 16px; } .mb-6 { margin-bottom: 24px; }
+
+table.data { width: 100%; border-collapse: collapse; font-size: 14px; }
+table.data th, table.data td { text-align: left; padding: 10px 12px; border-bottom: 1px solid var(--line); }
+table.data th { font-weight: 500; color: var(--text-soft); font-size: 12px; text-transform: uppercase; letter-spacing: .04em; }
+table.data tr:hover td { background: var(--bg-soft); }
+table.data tr:last-child td { border-bottom: 0; }
+
+/* 渐入动画 */
+.fade-enter-active, .fade-leave-active { transition: opacity .2s; }
+.fade-enter-from, .fade-leave-to { opacity: 0; }
+
+/* === 移动端全局响应式工具 === */
+.mobile-only { display: none; }
+.desktop-only { display: inline-flex; }
+@media (max-width: 767px) {
+ .mobile-only { display: inline-flex; }
+ .desktop-only { display: none; }
+}
+
+/* === 移动端表格兜底:未转 MobileCardList 的 横向滚动 === */
+@media (max-width: 767px) {
+ .card > table.data,
+ .card-pad > table.data {
+ display: block;
+ overflow-x: auto;
+ -webkit-overflow-scrolling: touch;
+ white-space: nowrap;
+ }
+ table.data th, table.data td { font-size: 13px; padding: 8px 10px; }
+ /* 标记移动端可滚动提示 */
+ .card:has(> table.data[role="scroll"]) { position: relative; }
+}
+
+/* === 移动端全局输入优化(防 iOS 缩放) === */
+@media (max-width: 767px) {
+ input, select, textarea { font-size: 16px; }
+ .input, .select, .textarea { padding: 10px 12px; }
+}
diff --git a/client/src/utils/formDraft.js b/client/src/utils/formDraft.js
new file mode 100644
index 0000000..cdb6be1
--- /dev/null
+++ b/client/src/utils/formDraft.js
@@ -0,0 +1,77 @@
+// client/src/utils/formDraft.js — 表单草稿暂存(sessionStorage 版)
+// 用途:session 过期被 401 拦截器打回登录页时,把用户填的表单数据存住,
+// 登录成功回到原页后能恢复,不丢工。
+//
+// 用法(推荐,5 行接入):
+// const draft = useFormDraft('washes/new');
+// const restored = draft.load();
+// if (restored) Object.assign(form, restored); // 恢复草稿
+// watch(form, (v) => draft.save(v), { deep: true }); // 自动保存
+// await onSubmit();
+// draft.clear(); // 提交成功清掉
+//
+// 401 触发:window.dispatchEvent(new CustomEvent('form-draft:flush-all'))
+// ↓ 监听器把每个已注册草稿的 latest 立即写盘
+// ↓ 然后 axios 拦截器跳 /login
+// ↓ 登录成功回到原页 → 组件 mount → load() 恢复
+
+const PREFIX = 'formDraft:';
+const DEBOUNCE_MS = 250;
+
+function key(name) { return PREFIX + name; }
+
+export function useFormDraft(name) {
+ let latest = null;
+ let timer = null;
+
+ function load() {
+ try {
+ const raw = sessionStorage.getItem(key(name));
+ if (!raw) return null;
+ return JSON.parse(raw);
+ } catch { return null; }
+ }
+
+ function saveNow(value) {
+ try { sessionStorage.setItem(key(name), JSON.stringify(value)); } catch {}
+ }
+
+ function save(value) {
+ latest = value;
+ if (timer) clearTimeout(timer);
+ timer = setTimeout(() => saveNow(value), DEBOUNCE_MS);
+ }
+
+ function flush() {
+ if (timer) { clearTimeout(timer); timer = null; }
+ if (latest !== null) saveNow(latest);
+ }
+
+ function clear() {
+ if (timer) { clearTimeout(timer); timer = null; }
+ latest = null;
+ try { sessionStorage.removeItem(key(name)); } catch {}
+ }
+
+ return { load, save, flush, clear };
+}
+
+/* ----- 全局 flush 注册(401 用)----- */
+const registered = new Set();
+let listenerInstalled = false;
+function installListener() {
+ if (listenerInstalled) return;
+ listenerInstalled = true;
+ window.addEventListener('form-draft:flush-all', () => {
+ for (const fn of registered) {
+ try { fn(); } catch {}
+ }
+ });
+}
+
+/** 让一个草稿实例参与全局 flush:返回 unregister 函数 */
+export function registerDraftForFlush(flushFn) {
+ installListener();
+ registered.add(flushFn);
+ return () => registered.delete(flushFn);
+}
diff --git a/client/src/views/BatchPurchase.vue b/client/src/views/BatchPurchase.vue
new file mode 100644
index 0000000..81a54a4
--- /dev/null
+++ b/client/src/views/BatchPurchase.vue
@@ -0,0 +1,246 @@
+
+
+
+
+
批量采购入库
+
一次性往 Grocy 入库多个产品(采购/自制/盘点)
+
+
← 返回
+
+
+
+
+
+ {{ filteredProducts.length }} 个产品可选
+
+
+
+
+
产品列表
+
+
+
没匹配的产品
+
+
+
+
+
采购清单 ({{ cart.length }} 项)
+
左侧点击产品加入清单
+
+
+
+ {{ item.name }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 共 {{ cart.length }} 项 · 总额 ¥{{ totalCost.toFixed(2) }}
+
+
+
+
+
+
执行结果
+
+ | 产品 | 数量 | Grocy | 本地 |
+
+
+ | {{ r.name }} |
+ {{ r.amount }} {{ r.unit || '' }} |
+
+ ✓ 已扣减
+ ✗ {{ r.grocy_error }}
+ |
+
+ 排队中
+ 已同步
+ 失败
+ |
+
+
+
+
+
+
+
+
+
+
diff --git a/client/src/views/ChargingList.vue b/client/src/views/ChargingList.vue
new file mode 100644
index 0000000..9a6b356
--- /dev/null
+++ b/client/src/views/ChargingList.vue
@@ -0,0 +1,358 @@
+
+
+
+
+
充电记录
+
家充 / 快充 / 慢充,每次电量 + 电耗自动计算
+
+
+
+
+
+
+
+
至
+
+
+ {{ data.total || 0 }} 条
+ ¥{{ (data.stats?.total_cost || 0).toFixed(2) }} 总花费
+ {{ avgKwh }} kWh/100km
+
+
+
+
+
加载中…
+
还没有充电记录
+
+ {{ row.charge_date }}
+
+ {{ row.vehicle_name }}
+ {{ row.vehicle_plate }}
+
+
+ {{ CHARGE_LABEL[row.charge_type] || row.charge_type || '—' }}
+
+ {{ row.odometer_km ? row.odometer_km + ' km' : '—' }}
+ {{ row.kwh }} kWh
+
+ {{ row.start_soc }}% → {{ row.end_soc }}%
+ —
+
+ ¥{{ row.price_per_kwh || 0 }}
+
+ ¥{{ (row.total_cost || 0).toFixed(2) }}
+
+
+ {{ row.kwh_per_100km.toFixed(2) }} kWh/100km
+ {{ row.consumption_skip_reason || '需里程' }}
+
+ {{ row.station || '—' }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{{ form.id ? '编辑充电' : '新建充电' }}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/client/src/views/ChemicalDetail.vue b/client/src/views/ChemicalDetail.vue
new file mode 100644
index 0000000..79af82a
--- /dev/null
+++ b/client/src/views/ChemicalDetail.vue
@@ -0,0 +1,432 @@
+
+
+ 加载中…
+ {{ error }}
+
+
+
+
+ {{ data.name }}
+ Grocy
+ 演示
+ 本地
+
+
+ ← 返回汽车用品
+
+
+
+
+
+
+
+
+
+
+
+
采购入库 → Grocy
+
这会直接调用 Grocy POST /api/stock/products/{{ data.grocy_product_id }}/add
+
+
+
+
+
+
+
+
基本信息
+
+
+ | 用品 ID | {{ data.grocy_product_id }} |
+ | 分类 | {{ data.category_display || '—' }} |
+ | 单位 | {{ data.unit || '—' }} |
+ | 位置 | {{ data.location || '—' }} |
+ | 描述 | {{ data.description }} |
+ | 最低库存 | {{ data.min_stock_amount || 0 }} {{ data.unit || '' }} |
+ | 最佳赏味期 | {{ data.best_before_date || '—' }} |
+ | 最后同步 | {{ data.last_synced_at || '—' }} |
+
+
+
+
+
+
当前库存
+
{{ data.current_amount }} {{ data.unit || '' }}
+
价值
+
¥{{ (data.current_value || 0).toFixed(2) }}
+
+ ⚠ 低于最低库存
+ 库存正常
+ 库存为空
+
+
+
+
+
+
+
本系统使用统计
+
+
+
累计使用次数
+
{{ data.usage_count || 0 }}
+
+
+
累计使用量
+
{{ (data.total_amount || 0).toFixed(2) }} {{ data.unit || '' }}
+
+
+
+
+
+
+
洗车扣减换算 — 例:1 {{ data.consume_unit_name || '加仑' }} = {{ data.qu_factor }} {{ data.unit }}
+
本系统会把「洗车页面输入的量」乘以此系数,转换成 Grocy 库存单位({{ data.unit || '—' }})后再扣库存。Grocy 端扣多少、库存就减多少。
+
+
+
+
+
+
+
+
+
+
+
预览:洗车页面输入 1 {{ convPreview.unitName }} → Grocy 扣 {{ convPreview.grams }} {{ data.unit }}
+
{{ convError }}
+
{{ convOk }}
+
+
+
+
+
+
+
+
+
Grocy 详细数据
+
+
+ | Grocy 库存价值 | ¥{{ (data.grocy_details.stock_value || 0).toFixed(2) }} |
+ | 最近采购 | {{ data.grocy_details.last_purchased || '—' }} |
+ | 最近使用 | {{ data.grocy_details.last_used || '—' }} |
+ | 最近进价 | ¥{{ formatGrocyPrice(data.grocy_details.last_price) }} |
+ | 平均价 | ¥{{ formatGrocyPrice(data.grocy_details.avg_price) }} |
+ | 下次到期 | {{ data.grocy_details.next_due_date || '—' }} |
+ | 已开库存 | {{ data.grocy_details.stock_amount_opened || 0 }} {{ data.unit }} |
+ | 未开库存 | {{ (data.current_amount - (data.grocy_details.stock_amount_opened || 0)) }} {{ data.unit }} |
+ | 平均保质期 | {{ data.grocy_details.average_shelf_life_days }} 天 |
+
+
+
+
+
+
+
+
进销存记录
+
+
+
+
+
+
+
+
+
+
+ | 日期 |
+ 类型 |
+ 用量 |
+ 关联洗车 |
+ 位置 |
+ 同步状态 |
+
+
+
+
+ | {{ u.usage_date }} |
+ 洗车扣减 |
+ {{ u.amount }} {{ data.unit }} |
+
+
+ #{{ u.wash_id }} {{ washTypeLabel(u.wash_type) }}
+
+ —
+ |
+ {{ u.location || '—' }} |
+
+
+ {{ u.sync_status }}
+
+ |
+
+
+ | 本系统暂无使用记录 |
+
+
+
+
+
+
+
+
+ | 入库日期 |
+ 批次 |
+ 数量 |
+ 价格 |
+ 最佳赏味期 |
+ 状态 |
+ 备注 |
+
+
+
+
+ | 拉取失败:{{ data.grocy_error }} |
+
+
+ | {{ g.purchased_date || g.row_created_timestamp }} |
+ #{{ g.id }} |
+ {{ g.amount }} {{ data.unit }} |
+ ¥{{ formatGrocyPrice(g.price) }} |
+ {{ g.best_before_date || '—' }} |
+
+ 已开
+ 未开
+ |
+ {{ g.note || '—' }} |
+
+
+ | Grocy 暂无入库批次 |
+
+
+
+
+
+
+
+
+
+
+
diff --git a/client/src/views/ChemicalNew.vue b/client/src/views/ChemicalNew.vue
new file mode 100644
index 0000000..5894bfe
--- /dev/null
+++ b/client/src/views/ChemicalNew.vue
@@ -0,0 +1,168 @@
+
+
+
+
新建汽车用品 → Grocy
+ ← 返回
+
+
+
+
+
+
+
+
+
diff --git a/client/src/views/ChemicalsList.vue b/client/src/views/ChemicalsList.vue
new file mode 100644
index 0000000..52428cf
--- /dev/null
+++ b/client/src/views/ChemicalsList.vue
@@ -0,0 +1,386 @@
+
+
+
+
+
汽车用品
+
+ 共 {{ total }} 项 ·
+ ¥{{ totalValue.toFixed(2) }} 库存价值
+
+ {{ grocyCount }} 来自 Grocy
+
+ {{ lowCount }} 低库存
+
+
+
+
+
+ + 批量采购
+ + 新建
+
+
+
+
+
+ 🔍
+
+
+
+ 本地匹配 {{ filteredRows.length }} / {{ rows.length }}
+
+
+
+
+
+
+
+ Grocy 全局搜索 ·
+ 在 Grocy 库里搜(不依赖本地缓存)
+
+
+
{{ grocyResults.error }}
+
+
Grocy 端匹配 {{ grocyResults.items.length }} 条
+
+
+ | ID | 名称 | 分类 | 本地缓存 | 操作 |
+
+
+
+ {{ g.id }} |
+ {{ g.name }} · {{ g.description }} |
+ {{ groupName(g.product_group_id) }} |
+
+ 已同步
+ 本地无
+ |
+
+
+
+ |
+
+
+
+
+
+
+ {{ msg }}
+
+
+
+
+
+ {{ row.name }}
+ {{ row.description }}
+
+
+
+ Grocy
+ 演示
+ 本地
+
+
+ {{ row.category_display || '—' }}
+
+
+ {{ row.current_amount }}
+ {{ row.unit || '' }}
+
+
+ {{ formatValue(row.current_value) }}
+
+
+ {{ row.min_stock_amount }} {{ row.unit || '' }}
+ —
+
+
+ 低库存
+ 正常
+ 空
+
+
+
+
+
+
+
+
+
diff --git a/client/src/views/Home.vue b/client/src/views/Home.vue
new file mode 100644
index 0000000..87460f2
--- /dev/null
+++ b/client/src/views/Home.vue
@@ -0,0 +1,364 @@
+
+
+
+
+
+
加载中…
+
{{ error }}
+
+
+
+
+ ⚠️
+ 低库存预警
+ {{ lowStock.length }} 个 Grocy 产品低于最低库存
+ 查看全部 →
+
+
+
+ {{ p.name }}
+
+ 当前 {{ p.current_amount }} / 最低 {{ p.min_stock_amount }} {{ p.unit || '' }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
今日天气 · {{ weather.city || cfg.app.city || 'Beijing' }}
+ {{ weather.provider || '—' }}
+
+
+ 尚未拉取。配置天气 API key 后在「设置」页点「拉取今日天气」或运行 npm run weather。
+
+
+
+
{{ weather.weather_desc }}
天气
+
{{ weather.humidity }}%
湿度
+
{{ weather.wind_kph }}
风速 km/h
+
+
+
+
+
+
+
最近洗车
+ 查看全部 →
+
+
+
+ | 日期 | 类型 | 车辆 | 位置 | 花费 | 天气 |
+
+
+
+ | {{ r.wash_date }} |
+ {{ washTypeLabel(r.wash_type) }} |
+ {{ r.vehicle_name || '—' }} |
+ {{ r.location || '—' }} |
+ ¥ {{ r.cost }} |
+ {{ r.weather_desc || '—' }} · {{ r.temp_c ?? '—' }}℃ |
+
+ | 暂无记录 |
+
+
+
+
+
+
+
+
最近用车记录
+ 保养 · 加油 · 充电
+
+
+
+ | 日期 | 类型 | 车辆 | 概要 | 花费 |
+
+
+
+ | {{ r.date }} |
+ {{ logTypeLabel(r.type) }} |
+ {{ r.vehicle_name || '—' }} |
+ {{ r.summary }} |
+ ¥{{ (r.cost || 0).toFixed(2) }} |
+
+ | 还没记录,去 保养 / 加油 / 充电 添加 |
+
+
+
+
+
+
+
+
+
+
+
diff --git a/client/src/views/InsuranceList.vue b/client/src/views/InsuranceList.vue
new file mode 100644
index 0000000..e1a1757
--- /dev/null
+++ b/client/src/views/InsuranceList.vue
@@ -0,0 +1,452 @@
+
+
+
+
+
保险记录
+
交强 / 商业 / 三责 / 车损… 保单附件图片/PDF 直接在线看
+
+
+
+
+
+
+
+
总保单
+
{{ data.stats?.total || 0 }}
+
+
+
有效中
+
{{ data.stats?.active_count || 0 }}
+
+
+
30 天内到期
+
{{ data.stats?.expiring_count || 0 }}
+
+
+
累计保费
+
¥{{ (data.stats?.total_premium || 0).toFixed(0) }}
+
+
+
+
+
+
+
+
+ 展示 {{ data.rows?.length || 0 }} 条
+
+
+
+
+
+
加载中…
+
还没有保单记录
+
+
+
+ {{ statusLabel(row.status) }}
+ · {{ row.days_to_expire }}d
+ · {{ -row.days_to_expire }}d
+
+
+ {{ row.insurance_type }}
+
+ {{ row.vehicle_name }}
+ {{ row.vehicle_plate }}
+
+ {{ row.company || '—' }}
+ {{ row.policy_no || '—' }}
+
+ {{ row.start_date }}
+ → {{ row.end_date }}
+
+
+ ¥{{ (row.premium || 0).toFixed(0) }}
+
+
+
+ 查看
+
+ 无
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{{ form.id ? '编辑保单' : '新建保单' }}
+
+
+
+
+
+
+
+
+
+
上传保单附件
+
支持图片 (jpg/png/webp/heic) 和 PDF,最大 10MB
+
+
+
+
+
+
+
+
+
diff --git a/client/src/views/Login.vue b/client/src/views/Login.vue
new file mode 100644
index 0000000..3ee36b4
--- /dev/null
+++ b/client/src/views/Login.vue
@@ -0,0 +1,199 @@
+
+
+
+
+ CL
+ CarLog
+ 车记
+
+
欢迎回来
+
{{ subtitleText }}
+
+
+
+
首次使用?默认账号 admin / 密码 carwash2026,登录后请到「设置 → 账户」修改。
+
+
+
+
+
+
+
+
diff --git a/client/src/views/MaintenanceList.vue b/client/src/views/MaintenanceList.vue
new file mode 100644
index 0000000..c0605c0
--- /dev/null
+++ b/client/src/views/MaintenanceList.vue
@@ -0,0 +1,369 @@
+
+
+
+
+
保养记录
+
机油、机滤、刹车油、轮胎… 每次保养 + 下次保养里程
+
+
+
+
+
+
+
+
+
+
+
至
+
+
+ {{ data.total || 0 }} 条
+ ¥{{ (data.stats?.total_cost || 0).toFixed(2) }} 总花费
+
+
+
+
+
+
加载中…
+
还没有保养记录,点右上「+ 新建保养」开始
+
+ {{ row.maint_date }}
+
+ {{ row.vehicle_name || '—' }}
+ {{ row.vehicle_plate }}
+
+
+ {{ it.name }}
+
+ {{ row.odometer_km ? row.odometer_km + ' km' : '—' }}
+
+ EV {{ row.ev_km }}
+ HEV {{ row.hev_km }}
+ —
+
+ {{ row.shop || '—' }}
+ {{ row.next_due_km ? row.next_due_km + ' km' : '—' }}
+
+ ¥{{ (row.total_cost || 0).toFixed(2) }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{{ form.id ? '编辑保养' : '新建保养' }}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/client/src/views/Offline.vue b/client/src/views/Offline.vue
new file mode 100644
index 0000000..71fa595
--- /dev/null
+++ b/client/src/views/Offline.vue
@@ -0,0 +1,62 @@
+
+
+
📡
+
暂时无法连接到网络
+
离线模式下,已缓存的页面仍可继续浏览。恢复网络后可使用完整功能。
+
+
+
+
+
+
+
diff --git a/client/src/views/OperationLogs.vue b/client/src/views/OperationLogs.vue
new file mode 100644
index 0000000..e3d674e
--- /dev/null
+++ b/client/src/views/OperationLogs.vue
@@ -0,0 +1,275 @@
+
+
+
+
+
操作日志
+
记录所有"会改变数据"的操作 · 共 {{ total }} 条
+
+
+
+
+
+
+
{{ s.c }}
+
{{ actionLabel(s.action) }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ row.created_at }}
+
+ {{ row.username || '—' }}
+
+ {{ row.action_label }}
+
+ {{ row.target_label }}
+
+ {{ row.target_summary || '—' }}
+
+
+
+ IP: {{ row.ip || '—' }}
+ UA: {{ row.user_agent || '—' }}
+
+
{{ formatDetail(row.detail) }}
+
+
+
+
+ 已恢复
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/client/src/views/RefuelList.vue b/client/src/views/RefuelList.vue
new file mode 100644
index 0000000..f6d8210
--- /dev/null
+++ b/client/src/views/RefuelList.vue
@@ -0,0 +1,374 @@
+
+
+
+
+
加油记录
+
每次加油 + 油耗自动计算(需勾「加满」两次)
+
+
+
+
+
+
+
+
至
+
+
+ {{ data.total || 0 }} 条
+ ¥{{ (data.stats?.total_cost || 0).toFixed(2) }} 总花费
+ {{ avgConsumption }} L/100km
+
+
+
+
+
加载中…
+
还没有加油记录
+
+
+ {{ row.refuel_date }}
+
+
+ {{ row.vehicle_name }}
+ {{ row.vehicle_plate }}
+
+
+ {{ row.fuel_type || '—' }}
+ 加满
+
+
+ {{ row.odometer_km ? row.odometer_km + ' km' : '—' }}
+
+
+ {{ row.liters }} L
+
+
+ ¥{{ row.price_per_liter || 0 }}
+
+
+ ¥{{ (row.total_cost || 0).toFixed(2) }}
+
+
+ {{ row.consumption_100km.toFixed(2) }} L/100km
+ {{ row.consumption_skip_reason || '需加满+里程' }}
+
+
+ {{ row.station || '—' }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{{ form.id ? '编辑加油' : '新建加油' }}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/client/src/views/Settings.vue b/client/src/views/Settings.vue
new file mode 100644
index 0000000..c2bd97b
--- /dev/null
+++ b/client/src/views/Settings.vue
@@ -0,0 +1,663 @@
+
+
+ 设置
+ 配置账户、天气、Grocy、CSV 导出
+
+ 加载中…
+ {{ error }}
+
+
+
+
+
+
+ 天气
+ 基于 wttr.in,每天最多请求一次(当天缓存优先)
+
+
+
+
+
+ Grocy
+ 化学品同步到 Grocy 库存(同步在「化学品」页面操作)
+
+
+
+
+
同步历史
+
加载中…
+
+
+ | 时间 | 操作 | 状态 | 结果 |
+
+
+
+ | {{ log.started_at.replace('T',' ').slice(0,16) }} |
+ {{ log.action === 'pull_products' ? '拉取产品' : log.action }} |
+
+
+ {{ log.status === 'success' ? '✓ 成功' : log.status === 'failed' ? '✗ 失败' : '…' }}
+
+ |
+
+
+ +{{ log.detail.inserted }} / ~{{ log.detail.updated }} / -{{ log.detail.deactivated }}
+
+ {{ log.detail.error }}
+ —
+ |
+
+
+
+
+
+
+
+
+ 📷 AI 截图识别
+ 上传小票/订单截图,自动提取日期/金额/油号/度数/保单号等填入表单。
+
+
+
+
+
+ 系统信息
+
+
+ | 当前账号 | {{ auth.user?.username }} |
+ | 登录时间 | {{ auth.user?.last_login_at || '—' }} |
+ | 登录 IP | {{ auth.user?.last_login_ip || '—' }} |
+ | 服务地址 | http://{{ host }} |
+ | 数据目录 | MySQL: carlog |
+
+
+
+
+
+
+ Grocy 分类映射
+
+ 你 Grocy 4.5.x 没开放分类 API,化学品列表暂时显示 group-ID。给每个 ID 配个真实名字就能正常显示。
+
+ {{ msgs.category }}
+
+
+
+
+
+
+
+
+ ⚠️ 危险操作
+
+ 一键清空所有业务数据(车辆、洗车、保养、加油、充电、保险),并可选择重新灌入演示数据。
+ 管理员账户不会被删除。
+
+
+
+
+
+
+
+ {{ resetMsg }}
+
+
+
+
+
+
+
+
⚠️ 确认要{{ resetMode === 'reset' ? '重置所有数据' : '清空所有数据' }}吗?
+
+
+ 这将删除所有车辆、洗车记录、保养、加油、充电、保险数据,并重新灌入演示数据。
+
+
+ 这将删除所有车辆、洗车记录、保养、加油、充电、保险数据(不可恢复)。
+
+
管理员账户 admin2 会保留。
+
+
请在下框输入 RESET-ALL-DATA 确认:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/client/src/views/Stats.vue b/client/src/views/Stats.vue
new file mode 100644
index 0000000..61b27a4
--- /dev/null
+++ b/client/src/views/Stats.vue
@@ -0,0 +1,414 @@
+
+
+ 统计
+ 历史数据汇总
+
+
+
+
+
+
月度报表
+
按月生成 Excel / PDF 报表:洗车、加油、充电、保养、保险 + 化学品 Top
+
+
+
+
+
+ 加载中…
+
+
+
+
+
+
+
+
+
+
+
+
+
车辆花费 Top
+
+ | 车辆 | 次数 | 花费 | 占比 |
+
+
+ | {{ r.name }} {{ r.plate }} |
+ {{ r.count }} |
+ ¥ {{ (r.cost || 0).toFixed(2) }} |
+
+
+
+ {{ (r.pct || 0).toFixed(1) }}%
+
+ |
+
+ | 暂无 |
+
+
+
+
+
化学品 Top 5
+
+ | 名称 | 累计用量 | 次数 |
+
+
+ | {{ c.name }} |
+ {{ c.total_amount }} {{ c.unit || '' }} |
+ {{ c.count }} |
+
+ | 暂无 |
+
+
+
+
+
+
+
+
+
油价趋势 (按月)
+
+
看你是越加越贵还是赶上了降价;连续 3 个月 +5% 就要考虑改加油时机
+
+
+
年均养护成本 (按车辆)
+
+
洗车+加油+充电+保养+保险 / 持有天数 × 365,看哪台车最费钱
+
+
+
+
+
+
洗车频率 vs 季节 (按月)
+
+
看你什么时候最勤快:雨季前扎堆 vs 冬天摆烂?
+
+
+
各车成本明细
+
+ | 车辆 | 持有 | 终身 | 年化 |
+
+
+ | {{ r.name }} {{ r.plate }} |
+ {{ Math.round(r.days_owned) }} 天 |
+ ¥{{ Number(r.lifetime_cost).toFixed(0) }} |
+ ¥{{ Number(r.annual_cost).toFixed(0) }} |
+
+ | 暂无 |
+
+
+
+
+
+
+
+
+
用车成本构成(按车辆)
+
+ | 车辆 | 洗车 | 保养 | 加油 | 充电 | 合计 |
+
+
+ | {{ r.name }} {{ r.plate }} |
+ ¥{{ r.wash.toFixed(0) }} |
+ ¥{{ r.maint.toFixed(0) }} |
+ ¥{{ r.refuel.toFixed(0) }} |
+ ¥{{ r.charge.toFixed(0) }} |
+ ¥{{ r.total.toFixed(0) }} |
+
+ | 暂无 |
+
+
+
+
+
油耗/电耗(按车辆)
+
+ | 车辆 | 加油 | 充电 |
+
+
+ | {{ r.name }} {{ r.plate }} |
+
+ 平均 {{ r.l_per_100km }} L/100km 总 {{ r.total_liters }}L
+ 需加满+里程
+ |
+
+ 平均 {{ r.kwh_per_100km }} kWh/100km 总 {{ r.total_kwh }} kWh
+ 需里程
+ |
+
+ | 暂无 |
+
+
+
+
+
+
+
+
+
+
+
diff --git a/client/src/views/VehicleDetail.vue b/client/src/views/VehicleDetail.vue
new file mode 100644
index 0000000..d8c9de0
--- /dev/null
+++ b/client/src/views/VehicleDetail.vue
@@ -0,0 +1,379 @@
+
+
+ 加载中…
+ {{ error }}
+
+
+
+
+ {{ vehicle.name }}
+ 停用
+
+
+ ← 返回车辆列表
+
+
+
+ 编辑车辆
+
+
+
+
+
+
+
车辆信息
+
+
+ | 车牌 | {{ vehicle.plate }} |
+ | 类型 | {{ typeLabel(vehicle.type) }} |
+
+ | 动力 |
+
+
+ {{ powertrainIcon(vehicle.powertrain) }} {{ powertrainLabel(vehicle.powertrain) }}
+
+ |
+
+ | 颜色 | {{ vehicle.color }} |
+ | 备注 | {{ vehicle.notes }} |
+
+ | 当前里程 |
+ {{ health.current_km.toLocaleString() }} km |
+
+
+
+
+
+
累计数据
+
+
洗车{{ health?.totals?.wash_count || 0 }}次 · ¥{{ (health?.totals?.wash_cost || 0).toFixed(0) }}
+
保养{{ health?.totals?.maint_count || 0 }}次 · ¥{{ (health?.totals?.maint_cost || 0).toFixed(0) }}
+
加油{{ (health?.totals?.refuel_liters || 0).toFixed(0) }}L¥{{ (health?.totals?.refuel_cost || 0).toFixed(0) }}
+
充电{{ (health?.totals?.charge_kwh || 0).toFixed(0) }}kWh¥{{ (health?.totals?.charge_cost || 0).toFixed(0) }}
+
+
+ 持有总成本
+ ¥{{ (health?.totals?.grand || 0).toFixed(2) }}
+
+
+
+
+
+
+
+
能耗 / 效率
+
+
+
平均油耗
+
{{ health.avg_consumption.l_per_100km }} L/100km
+
+
+
平均电耗
+
{{ health.avg_consumption.kwh_per_100km }} kWh/100km
+
+
+
需要至少一次「加满+里程」的加油或充电记录才能计算
+
+
+
洗车新鲜度
+
+
{{ health.wash_recency.last_date }}
+
+ {{ health.wash_recency.days_since }} 天没洗
+ · 该洗了
+
+
+
暂无洗车记录
+
+
+
下次保养预测
+
+
+ ⚠️ 已超过保养里程,建议尽快保养
+
+
距 {{ health.next_maintenance.next_due_km }} km
+
+
还剩 {{ health.next_maintenance.km_remaining }} km · 上次 {{ health.next_maintenance.last_date }}
+
+
尚未设置下次保养里程(在保养记录里设置「下次里程」即可启用预测)
+
+
+
+
+
+
+
+
+
+
所有记录
+
+
+
+
+
+
+
+
+
+
+ | 日期 | 类型 | 花费 | 用品 | 位置 |
+
+
+ | {{ w.wash_date }} |
+ {{ washTypeLabel(w.wash_type) }} |
+ ¥{{ (w.cost || 0).toFixed(2) }} |
+
+ {{ c.chemical_name }} {{ c.amount }}{{ c.unit || '' }}
+ |
+ {{ w.location || '—' }} |
+
+ | 暂无洗车记录 |
+
+
+
+
+ | 日期 | 项目 | 总里程 | 下次里程 | 店名 | 花费 |
+
+
+ | {{ r.maint_date }} |
+ {{ it.name }} |
+ {{ r.odometer_km || '—' }} km |
+ {{ r.next_due_km || '—' }} km |
+ {{ r.shop || '—' }} |
+ ¥{{ (r.total_cost || 0).toFixed(2) }} |
+
+ | 暂无 |
+
+
+
+
+ | 日期 | 油号 | 里程 | 升数 | 单价 | 花费 | 油耗 | 加油站 |
+
+
+ | {{ r.refuel_date }} |
+ {{ r.fuel_type || '—' }} |
+ {{ r.odometer_km || '—' }} km |
+ {{ r.liters }}L |
+ ¥{{ r.price_per_liter || 0 }} |
+ ¥{{ (r.total_cost || 0).toFixed(2) }} |
+ {{ r.consumption_100km.toFixed(2) }} L/100km{{ r.consumption_skip_reason || '需加满+里程' }} |
+ {{ r.station || '—' }} |
+
+ | 暂无 |
+
+
+
+
+ | 日期 | 类型 | 里程 | 度数 | SOC | 单价 | 花费 | 电耗 | 地点 |
+
+
+ | {{ r.charge_date }} |
+ {{ CHARGE_LABEL[r.charge_type] || r.charge_type || '—' }} |
+ {{ r.odometer_km || '—' }} km |
+ {{ r.kwh }} kWh |
+ {{ r.start_soc }}→{{ r.end_soc }}%— |
+ ¥{{ r.price_per_kwh || 0 }} |
+ ¥{{ (r.total_cost || 0).toFixed(2) }} |
+ {{ r.kwh_per_100km.toFixed(2) }} kWh/100km{{ r.consumption_skip_reason || '需里程' }} |
+ {{ r.station || '—' }} |
+
+ | 暂无 |
+
+
+
+
+ | 状态 | 险种 | 公司 | 保单号 | 生效日 | 到期日 | 保费 | 附件 |
+
+
+ |
+
+ {{ insStatusLabel(r.status) }}
+ · {{ r.days_to_expire }}d
+
+ |
+ {{ r.insurance_type }} |
+ {{ r.company || '—' }} |
+ {{ r.policy_no || '—' }} |
+ {{ r.start_date }} |
+ {{ r.end_date }} |
+ ¥{{ (r.premium || 0).toFixed(0) }} |
+
+ 查看
+ 无
+ |
+
+ | 暂无 |
+
+
+
+
+
+
+
+
+
+
diff --git a/client/src/views/VehicleForm.vue b/client/src/views/VehicleForm.vue
new file mode 100644
index 0000000..ed5792c
--- /dev/null
+++ b/client/src/views/VehicleForm.vue
@@ -0,0 +1,136 @@
+
+
+
+
{{ isEdit ? '编辑车辆' : '新建车辆' }}
+ ← 返回
+
+
+
+
+
+
+
+
+
diff --git a/client/src/views/VehiclesList.vue b/client/src/views/VehiclesList.vue
new file mode 100644
index 0000000..d6e6cb2
--- /dev/null
+++ b/client/src/views/VehiclesList.vue
@@ -0,0 +1,167 @@
+
+
+
+
+
车辆
+
共 {{ vehicles.length }} 辆
+
+
+ + 新建
+
+
+
+ 加载中…
+
+
+
+
{{ typeIcon(v.type) }}
+
+
{{ v.name }}
+
{{ v.plate }}
+
+ {{ typeLabel(v.type) }}
+ {{ powertrainIcon(v.powertrain) }} {{ powertrainLabel(v.powertrain) }}
+ {{ v.color }}
+ 停用
+
+
+
洗车次数{{ v.wash_count || 0 }}
+
累计花费¥ {{ (v.total_cost || 0).toFixed(2) }}
+
最近{{ v.last_wash_date || '—' }}
+
+
{{ v.notes }}
+
+
+ 详情
+ 编辑
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/client/src/views/WashNew.vue b/client/src/views/WashNew.vue
new file mode 100644
index 0000000..76debe1
--- /dev/null
+++ b/client/src/views/WashNew.vue
@@ -0,0 +1,326 @@
+
+
+
+
新建洗车记录
+
+
+ ← 返回
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/client/src/views/WashShow.vue b/client/src/views/WashShow.vue
new file mode 100644
index 0000000..237c8bb
--- /dev/null
+++ b/client/src/views/WashShow.vue
@@ -0,0 +1,352 @@
+
+
+
+
洗车详情
+
+ ← 返回
+
+
+
+
+ 加载中…
+ {{ error }}
+
+
+
+
+
+
洗车日期
+
{{ data.wash_date }}
+
+
+ {{ typeLabel(data.wash_type) }}
+
+
+
+
车辆
{{ data.vehicle_name || '—' }} · {{ data.vehicle_plate }}
+
位置
{{ data.location || '—' }}
+
花费
¥ {{ Number(data.cost).toFixed(2) }}
+
耗时
{{ data.duration_min ? data.duration_min + ' 分钟' : '—' }}
+
天气
{{ data.weather_desc || '—' }} · {{ data.temp_c }}℃
+
湿度
{{ data.humidity ? data.humidity + '%' : '—' }}
+
+
+
备注
+
{{ data.notes }}
+
+
+
+
+
化学品使用
+
未记录
+
+
+
+
{{ c.chemical_name || c.chemical_id }}
+
{{ c.chemical_id }}
+
+
{{ c.amount }} {{ c.unit || '' }}
+
+
+
+
+
+
+
+
+
对比照
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ photoTypeLabel(p.photo_type) }}
+ {{ p.caption }}
+
+
+
+
+
+
+
+
+
+ 还没上传 before/after 照片。
去上传
+
+
+
+
洗前 (before)
+
+
![]()
+
{{ compareData.before.caption }}
+
+
未上传
+
+
→
+
+
洗后 (after)
+
+
![]()
+
{{ compareData.after.caption }}
+
+
未上传
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/client/src/views/WashesList.vue b/client/src/views/WashesList.vue
new file mode 100644
index 0000000..78c32c9
--- /dev/null
+++ b/client/src/views/WashesList.vue
@@ -0,0 +1,351 @@
+
+
+
+
+
洗车记录
+
共 {{ total }} 条记录{{ selectedCount ? ` · 已选 ${selectedCount} 条` : '' }}
+
+
+
+ + 新建
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
goTo(row.id)"
+ >
+
+
+
+
+ {{ row.wash_date }}
+
+
+ {{ typeLabel(row.wash_type) }}
+
+
+ {{ row.vehicle_name || '—' }}
+
+
+ {{ row.location || '—' }}
+
+
+ ¥ {{ Number(row.cost).toFixed(2) }}
+
+
+ {{ row.duration_min ? row.duration_min + ' 分钟' : '—' }}
+
+
+ {{ row.weather_desc || '—' }}
+
+
+
+ 查看 →
+
+
+ 暂无记录
+ + 新建第一条
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/client/vite.config.js b/client/vite.config.js
new file mode 100644
index 0000000..f93bd17
--- /dev/null
+++ b/client/vite.config.js
@@ -0,0 +1,158 @@
+import { defineConfig } from 'vite';
+import vue from '@vitejs/plugin-vue';
+import { VitePWA } from 'vite-plugin-pwa';
+import path from 'node:path';
+
+export default defineConfig({
+ plugins: [
+ vue(),
+ VitePWA({
+ registerType: 'autoUpdate',
+ injectRegister: 'auto', // 自动注入 register 脚本
+ includeAssets: ['favicon-16x16.png', 'favicon-32x32.png'],
+ manifest: {
+ id: '/',
+ name: 'CarLog 洗车管理系统',
+ short_name: 'CarLog',
+ description: '记录洗车/加油/充电/保养/保险/车品的全能车辆账本',
+ lang: 'zh-CN',
+ dir: 'ltr',
+ start_url: '/',
+ scope: '/',
+ display: 'standalone',
+ orientation: 'portrait',
+ background_color: '#F5F8FC',
+ theme_color: '#1B6EF3',
+ categories: ['productivity', 'lifestyle', 'utilities'],
+ icons: [
+ { src: '/pwa/pwa-192x192.png', sizes: '192x192', type: 'image/png', purpose: 'any' },
+ { src: '/pwa/pwa-512x512.png', sizes: '512x512', type: 'image/png', purpose: 'any' },
+ {
+ src: '/pwa/pwa-maskable-512x512.png',
+ sizes: '512x512',
+ type: 'image/png',
+ purpose: 'maskable',
+ },
+ {
+ src: '/pwa/apple-touch-icon.png',
+ sizes: '180x180',
+ type: 'image/png',
+ purpose: 'any',
+ },
+ ],
+ shortcuts: [
+ {
+ name: '拍照录入',
+ short_name: '拍照',
+ url: '/washes/new?capture=1',
+ description: '打开相机快速拍照记录',
+ },
+ {
+ name: '新建洗车',
+ short_name: '洗车',
+ url: '/washes/new',
+ description: '快速记录一次洗车',
+ },
+ {
+ name: '新建加油',
+ short_name: '加油',
+ url: '/refuel/new',
+ description: '快速记录一次加油',
+ },
+ {
+ name: '新建保养',
+ short_name: '保养',
+ url: '/maintenance/new',
+ description: '快速记录一次保养',
+ },
+ ],
+ // 拍照快捷方式行为
+ capture_links: ['/washes/new?capture=1'],
+ launch_handler: { client_mode: 'auto' },
+ },
+ workbox: {
+ globPatterns: ['**/*.{js,css,html,ico,png,svg,woff2,webmanifest}'],
+ navigateFallback: '/index.html',
+ navigateFallbackDenylist: [/^\/api/, /^\/uploads/],
+ runtimeCaching: [
+ {
+ urlPattern: ({ url }) => url.pathname.startsWith('/api/') && url.pathname.includes('/static'),
+ handler: 'CacheFirst',
+ options: {
+ cacheName: 'api-static',
+ expiration: { maxEntries: 200, maxAgeSeconds: 60 * 60 * 24 * 30 },
+ },
+ },
+ {
+ urlPattern: ({ url }) => url.pathname.startsWith('/uploads/'),
+ handler: 'CacheFirst',
+ options: {
+ cacheName: 'uploads',
+ expiration: { maxEntries: 200, maxAgeSeconds: 60 * 60 * 24 * 30 },
+ },
+ },
+ {
+ urlPattern: ({ request }) => request.destination === 'image',
+ handler: 'StaleWhileRevalidate',
+ options: { cacheName: 'images' },
+ },
+ {
+ urlPattern: ({ request }) => request.destination === 'font',
+ handler: 'CacheFirst',
+ options: { cacheName: 'fonts' },
+ },
+ ],
+ },
+ devOptions: {
+ enabled: false, // dev 不开 SW(避免 HMR 干扰)
+ },
+ }),
+ ],
+ resolve: {
+ alias: {
+ '@': path.resolve(__dirname, './src'),
+ },
+ },
+ server: {
+ port: 5173,
+ proxy: {
+ '/api': {
+ target: 'http://127.0.0.1:8787',
+ changeOrigin: true,
+ },
+ },
+ },
+ optimizeDeps: {
+ include: ['vue', 'pinia', 'axios', 'chart.js/auto'],
+ },
+ build: {
+ outDir: 'dist',
+ emptyOutDir: true,
+ cssCodeSplit: true,
+ target: 'es2018',
+ rollupOptions: {
+ output: {
+ manualChunks(id) {
+ if (id.includes('node_modules')) {
+ if (id.includes('chart.js') || id.includes('vue-chartjs')) return 'chart';
+ if (id.includes('echarts') || id.includes('zrender')) return 'chart';
+ if (id.includes('workbox') || id.includes('vite-plugin-pwa')) return 'pwa';
+ if (id.includes('vue') || id.includes('pinia') || id.includes('@vue')) return 'vue';
+ if (
+ id.includes('axios') ||
+ id.includes('dayjs') ||
+ id.includes('dompurify')
+ )
+ return 'utils';
+ return 'vendor';
+ }
+ },
+ // 文件名带 hash,长期缓存友好
+ entryFileNames: 'assets/[name]-[hash].js',
+ chunkFileNames: 'assets/[name]-[hash].js',
+ assetFileNames: 'assets/[name]-[hash][extname]',
+ },
+ },
+ chunkSizeWarningLimit: 600,
+ },
+});
diff --git a/docs/DEV-PLAN.md b/docs/DEV-PLAN.md
new file mode 100644
index 0000000..ed4addd
--- /dev/null
+++ b/docs/DEV-PLAN.md
@@ -0,0 +1,1134 @@
+# 开发计划:i 平台基座 + CarLog 子系统化
+
+> **目标读者**:Trae(另一个 AI IDE 工具),按本文档逐步实施,最终提交代码由 Mavis(我)做 code review + 跑测试。
+>
+> **完成定义**:所有 Phase 1 + Phase 2 任务通过验收测试,端到端流程跑通。
+
+---
+
+## 0. 背景
+
+i 是一个生活操作系统平台,**单 Vue SPA + 单 Express 进程 + 单 MySQL**。CarLog(v2.8)作为第一个子系统。
+
+完整架构见 `docs/ARCHITECTURE.md`。本计划是它的实施分解。
+
+### 0.1 当前仓库状态
+
+- **仓库**: https://gitea.img2img.com/wsh5485/i.git
+- **本地**: `/Users/yabozi/wzpstudio/i`
+- **当前 commit**: `77adc8e`(README + ARCHITECTURE.md + .gitignore)
+- **CarLog v2.8 代码已经拷到 i 仓库**:
+ - `server/src/` (13 个 route 文件 + middleware + services)
+ - `server/migrations/` (0001~0018 共 18 个迁移)
+ - `client/src/` (20 个 view + components + stores + api)
+ - 配置文件 (.editorconfig, .eslintrc.json, .prettierrc.json, vitest.config.js, package.json, etc.)
+- **MySQL**: `162.14.110.130:33306 / carlog`(密码 `ZeMRBwXP8JC6B3rF`,`.env` 里读)
+- **node_modules**: 没装,需要 `npm install` 装 server 和 client
+
+### 0.2 关键决策
+
+| 决策 | 选定 | 理由 |
+|---|---|---|
+| 子系统隔离 | 物理目录 + 表前缀 | 单用户场景,分进程/分库是过度工程 |
+| 平台层和子系统共享一个进程 | 是 | 一个 Express server |
+| 路由前缀 | **保持现状** `/api/*` | 改 URL 路径影响前端所有 link/api call,工作量大且无收益 |
+| CarLog 代码目录 | 移到 `server/src/subsystems/carlog/` | 物理隔离,加子系统不会乱碰 |
+| CarLog 前端目录 | 移到 `client/src/views/subsystems/carlog/` | 同上 |
+| CarLog 路由 path | 不变(`/washes`、`/vehicles`) | 用户体验一致 |
+| CarLog 表前缀 | **Phase 2 不做**(留给 Phase 3) | 当前阶段数据库里只有 CarLog 的表,没必要急着加前缀;加第二个子系统前再做 |
+| 平台层路由前缀 | `/api/platform/*` | 平台层独立路径空间 |
+| 平台层 UI 路径 | `/settings/global`, `/settings/:subsystem`, `/admin/subsystems` | 用户能直接 URL 进入 |
+| 元数据驱动 | `subsystems` 表的 `settings_schema` + `nav_items` JSON 字段 | 加新子系统不用改平台前端代码 |
+
+---
+
+## Phase 1: 平台基座
+
+### Task 1.1: 数据库迁移(subsystems + platform_settings 表)
+
+**新增文件**: `server/migrations/019_platform.sql`
+
+**内容**:
+
+```sql
+-- ============================================================
+-- 019_platform.sql — i 平台基座 (subsystems + platform_settings)
+-- ============================================================
+-- 可重复执行(先 DROP 再 CREATE)
+
+DROP TABLE IF EXISTS subsystems;
+CREATE TABLE subsystems (
+ id VARCHAR(50) PRIMARY KEY, -- 'carlog' / 'fitness' / 'reading'
+ name VARCHAR(100) NOT NULL, -- 显示名: '洗车管理系统'
+ description TEXT,
+ icon VARCHAR(20), -- emoji: '🚗'
+ color VARCHAR(20), -- '#1B6EF3'
+ category VARCHAR(50) NOT NULL, -- 'vehicle' / 'fitness' / 'reading'
+ version VARCHAR(20),
+ enabled TINYINT(1) NOT NULL DEFAULT 1,
+ sort_order INT NOT NULL DEFAULT 0,
+ settings_schema JSON, -- JSON Schema 描述设置项
+ nav_items JSON, -- [{label, icon, path, sort}]
+ created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+ INDEX idx_category (category, enabled, sort_order)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
+DROP TABLE IF EXISTS platform_settings;
+CREATE TABLE platform_settings (
+ `key` VARCHAR(200) PRIMARY KEY, -- 总设置无前缀 'ui.theme' / 子系统设置 'carlog.ai.provider'
+ value JSON NOT NULL,
+ type VARCHAR(20) NOT NULL, -- 'string' / 'number' / 'boolean' / 'json'
+ description TEXT,
+ updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
+
+-- Seed: CarLog 注册到 subsystems 表
+INSERT INTO subsystems (id, name, description, icon, color, category, version, enabled, sort_order, settings_schema, nav_items) VALUES
+('carlog', '洗车管理系统', '个人 detailer 洗车管理系统', '🚗', '#1B6EF3', 'vehicle', '2.8.0', 1, 10,
+'{
+ "fields": [
+ {"key": "weather.default_city", "label": "默认城市", "type": "select", "options": ["Beijing", "Shanghai", "Korla", "Shenzhen"], "default": "Korla"},
+ {"key": "ai.provider", "label": "AI 识别 provider", "type": "select", "options": ["minimax_vl", "openai_compat"], "default": "minimax_vl"},
+ {"key": "grocy.url", "label": "Grocy URL", "type": "string", "default": ""},
+ {"key": "grocy.api_key", "label": "Grocy API Key", "type": "password", "default": ""},
+ {"key": "ui.compact_mode", "label": "紧凑模式", "type": "boolean", "default": false}
+ ]
+}',
+'[
+ {"label": "概览", "path": "/", "icon": "🏠", "sort": 0},
+ {"label": "车辆", "path": "/vehicles", "icon": "🚙", "sort": 10},
+ {"label": "洗车记录", "path": "/washes", "icon": "🧽", "sort": 20},
+ {"label": "加油", "path": "/refuels", "icon": "⛽", "sort": 30},
+ {"label": "充电", "path": "/chargings", "icon": "🔌", "sort": 40},
+ {"label": "保养", "path": "/maintenances", "icon": "🔧", "sort": 50},
+ {"label": "保险", "path": "/insurances", "icon": "🛡️", "sort": 60},
+ {"label": "药剂", "path": "/chemicals", "icon": "🧴", "sort": 70},
+ {"label": "统计", "path": "/stats", "icon": "📊", "sort": 80},
+ {"label": "设置", "path": "/settings", "icon": "⚙️", "sort": 90}
+]
+');
+
+-- Seed: 几个总设置默认值
+INSERT INTO platform_settings (`key`, value, type, description) VALUES
+('ui.theme', '"auto"', 'string', 'UI 主题: auto/light/dark'),
+('ui.language', '"zh-CN"', 'string', '界面语言: zh-CN/en'),
+('dashboard.layout', '"default"', 'string', 'Dashboard 布局: default/compact'),
+('backup.enabled', 'false', 'boolean', '自动备份开关'),
+('backup.path', '""', 'string', '备份路径');
+```
+
+**验证**:
+```bash
+mysql -h 162.14.110.130 -P 33306 -u carlog -p carlog < server/migrations/019_platform.sql
+mysql -h 162.14.110.130 -P 33306 -u carlog -p carlog -e "SHOW TABLES;" | grep -E "subsystems|platform_settings"
+mysql -h 162.14.110.130 -P 33306 -u carlog -p carlog -e "SELECT id, name, category FROM subsystems;"
+# 期望输出: carlog | 洗车管理系统 | vehicle
+mysql -h 162.14.110.130 -P 33306 -u carlog -p carlog -e "SELECT \`key\`, type FROM platform_settings;"
+# 期望输出 5 行: ui.theme / ui.language / dashboard.layout / backup.enabled / backup.path
+```
+
+**幂等性**: 文件用 `DROP TABLE IF EXISTS` + `CREATE TABLE`,可重复执行。
+
+---
+
+### Task 1.2: 平台路由 — subsystems
+
+**新增文件**: `server/src/routes/platform/subsystems.js`
+
+**内容**:
+```js
+import express from 'express';
+import { db } from '../../db.js';
+import { requireAuth } from '../../middleware/auth.js';
+
+const router = express.Router();
+
+// GET /api/platform/subsystems — 列出所有子系统(按 category 分组,按 sort_order 排序)
+router.get('/', requireAuth, async (req, res, next) => {
+ try {
+ const [rows] = await db().execute(
+ `SELECT id, name, description, icon, color, category, version, enabled, sort_order, settings_schema, nav_items, created_at, updated_at
+ FROM subsystems
+ WHERE enabled = 1
+ ORDER BY category, sort_order`
+ );
+ // JSON 字段需要手动 parse
+ const subs = rows.map(r => ({
+ ...r,
+ settings_schema: typeof r.settings_schema === 'string' ? JSON.parse(r.settings_schema) : r.settings_schema,
+ nav_items: typeof r.nav_items === 'string' ? JSON.parse(r.nav_items) : r.nav_items,
+ enabled: !!r.enabled,
+ }));
+ res.json({ ok: true, data: subs });
+ } catch (err) {
+ next(err);
+ }
+});
+
+// GET /api/platform/subsystems/:id — 单个详情
+router.get('/:id', requireAuth, async (req, res, next) => {
+ try {
+ const [rows] = await db().execute(
+ `SELECT * FROM subsystems WHERE id = ? LIMIT 1`,
+ [req.params.id]
+ );
+ if (!rows.length) return res.status(404).json({ ok: false, error: 'subsystem not found' });
+ const sub = rows[0];
+ sub.settings_schema = typeof sub.settings_schema === 'string' ? JSON.parse(sub.settings_schema) : sub.settings_schema;
+ sub.nav_items = typeof sub.nav_items === 'string' ? JSON.parse(sub.nav_items) : sub.nav_items;
+ sub.enabled = !!sub.enabled;
+ res.json({ ok: true, data: sub });
+ } catch (err) {
+ next(err);
+ }
+});
+
+// PATCH /api/platform/subsystems/:id — 启停
+router.patch('/:id', requireAuth, async (req, res, next) => {
+ try {
+ const { enabled } = req.body;
+ if (typeof enabled !== 'boolean') {
+ return res.status(400).json({ ok: false, error: 'enabled must be boolean' });
+ }
+ await db().execute(
+ `UPDATE subsystems SET enabled = ? WHERE id = ?`,
+ [enabled ? 1 : 0, req.params.id]
+ );
+ res.json({ ok: true, data: { id: req.params.id, enabled } });
+ } catch (err) {
+ next(err);
+ }
+});
+
+export default router;
+```
+
+**注意**:
+- 路由前缀 `/api/platform/subsystems`,在 `index.js` mount
+- 响应统一用 `{ok: true, data: ...}` 包装(与其他平台路由一致)
+- 已有 `requireAuth` middleware 直接复用(`server/src/middleware/auth.js`)
+- mysql2 返回的 JSON 字段可能是字符串,需要手动 parse
+- enabled 是 TINYINT(1),要转 boolean
+
+**验证**:
+```bash
+# 启动 server (如果还没起)
+cd server && npm install && npm run dev &
+
+# 拿 token
+TOKEN=$(curl -s -X POST http://localhost:8787/api/auth/login -H 'Content-Type: application/json' -d '{"username":"admin","password":"carwash2026"}' | python3 -c "import json,sys; print(json.load(sys.stdin)['data']['token'])")
+
+# 列表
+curl -s -H "Authorization: Bearer $TOKEN" http://localhost:8787/api/platform/subsystems | python3 -m json.tool | head -20
+# 期望: data 数组里有 carlog 那条
+
+# 单个
+curl -s -H "Authorization: Bearer $TOKEN" http://localhost:8787/api/platform/subsystems/carlog | python3 -m json.tool
+# 期望: data.settings_schema 是对象, data.nav_items 是数组
+
+# 启停
+curl -s -X PATCH -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
+ -d '{"enabled": false}' http://localhost:8787/api/platform/subsystems/carlog
+# 期望: {ok: true, data: {id: 'carlog', enabled: false}}
+# 然后立即恢复
+curl -s -X PATCH -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
+ -d '{"enabled": true}' http://localhost:8787/api/platform/subsystems/carlog
+```
+
+---
+
+### Task 1.3: 平台路由 — settings
+
+**新增文件**: `server/src/routes/platform/settings.js`
+
+**内容**:
+```js
+import express from 'express';
+import { db } from '../../db.js';
+import { requireAuth } from '../../middleware/auth.js';
+
+const router = express.Router();
+
+// GET /api/platform/settings?prefix=carlog. — 获取设置(支持 prefix 过滤)
+router.get('/', requireAuth, async (req, res, next) => {
+ try {
+ const { prefix } = req.query;
+ let rows;
+ if (prefix) {
+ [rows] = await db().execute(
+ `SELECT \`key\`, value, type, description, updated_at
+ FROM platform_settings
+ WHERE \`key\` LIKE ?
+ ORDER BY \`key\``,
+ [prefix + '%']
+ );
+ } else {
+ [rows] = await db().execute(
+ `SELECT \`key\`, value, type, description, updated_at
+ FROM platform_settings
+ ORDER BY \`key\``
+ );
+ }
+ const settings = rows.map(r => ({
+ ...r,
+ value: typeof r.value === 'string' ? JSON.parse(r.value) : r.value,
+ }));
+ res.json({ ok: true, data: settings });
+ } catch (err) {
+ next(err);
+ }
+});
+
+// GET /api/platform/settings/:key — 单个
+router.get('/:key(*)', requireAuth, async (req, res, next) => {
+ try {
+ const key = req.params.key;
+ const [rows] = await db().execute(
+ `SELECT \`key\`, value, type, description, updated_at
+ FROM platform_settings
+ WHERE \`key\` = ? LIMIT 1`,
+ [key]
+ );
+ if (!rows.length) return res.status(404).json({ ok: false, error: 'setting not found' });
+ const setting = rows[0];
+ setting.value = typeof setting.value === 'string' ? JSON.parse(setting.value) : setting.value;
+ res.json({ ok: true, data: setting });
+ } catch (err) {
+ next(err);
+ }
+});
+
+// PUT /api/platform/settings/:key — 设置单个
+router.put('/:key(*)', requireAuth, async (req, res, next) => {
+ try {
+ const key = req.params.key;
+ const { value, type, description } = req.body;
+ if (value === undefined) {
+ return res.status(400).json({ ok: false, error: 'value required' });
+ }
+ const inferredType = type || (typeof value === 'boolean' ? 'boolean' : typeof value === 'number' ? 'number' : typeof value === 'object' ? 'json' : 'string');
+ await db().execute(
+ `INSERT INTO platform_settings (\`key\`, value, type, description)
+ VALUES (?, ?, ?, ?)
+ ON DUPLICATE KEY UPDATE value = VALUES(value), type = VALUES(type), description = VALUES(description)`,
+ [key, JSON.stringify(value), inferredType, description || null]
+ );
+ res.json({ ok: true, data: { key, value, type: inferredType } });
+ } catch (err) {
+ next(err);
+ }
+});
+
+// POST /api/platform/settings/batch — 批量设置
+router.post('/batch', requireAuth, async (req, res, next) => {
+ try {
+ const { settings } = req.body;
+ if (!Array.isArray(settings)) {
+ return res.status(400).json({ ok: false, error: 'settings must be array' });
+ }
+ for (const s of settings) {
+ if (!s.key || s.value === undefined) continue;
+ const type = s.type || (typeof s.value === 'boolean' ? 'boolean' : typeof s.value === 'number' ? 'number' : typeof s.value === 'object' ? 'json' : 'string');
+ await db().execute(
+ `INSERT INTO platform_settings (\`key\`, value, type, description)
+ VALUES (?, ?, ?, ?)
+ ON DUPLICATE KEY UPDATE value = VALUES(value), type = VALUES(type), description = VALUES(description)`,
+ [s.key, JSON.stringify(s.value), type, s.description || null]
+ );
+ }
+ res.json({ ok: true, data: { updated: settings.length } });
+ } catch (err) {
+ next(err);
+ }
+});
+
+export default router;
+```
+
+**注意**:
+- Express 路由参数 `:key(*)` 让 key 可以包含 `.`(如 `carlog.weather.default_city`)
+- key 是 MySQL 保留字,必须用反引号转义
+- `INSERT ... ON DUPLICATE KEY UPDATE` 做 upsert
+- 批量接口 settings 数组每个元素 `{key, value, type?, description?}`
+
+**验证**:
+```bash
+# 列表(无 prefix)
+curl -s -H "Authorization: Bearer $TOKEN" http://localhost:8787/api/platform/settings | python3 -m json.tool | head -30
+# 期望: 5 行总设置
+
+# 列表(带 prefix)
+curl -s -H "Authorization: Bearer $TOKEN" 'http://localhost:8787/api/platform/settings?prefix=carlog.' | python3 -m json.tool
+# 期望: 空数组(carlog 子系统设置还没有)
+
+# 单个
+curl -s -H "Authorization: Bearer $TOKEN" http://localhost:8787/api/platform/settings/ui.theme | python3 -m json.tool
+# 期望: value='auto'
+
+# 设置单个
+curl -s -X PUT -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
+ -d '{"value": "dark"}' http://localhost:8787/api/platform/settings/ui.theme
+# 期望: {ok: true, data: {key: 'ui.theme', value: 'dark', type: 'string'}}
+
+# 批量
+curl -s -X POST -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
+ -d '{"settings":[{"key":"carlog.ai.provider","value":"openai_compat","description":"AI provider"},{"key":"carlog.ui.compact_mode","value":true,"description":"紧凑模式"}]}' \
+ http://localhost:8787/api/platform/settings/batch
+# 期望: {ok: true, data: {updated: 2}}
+
+# 再查 prefix
+curl -s -H "Authorization: Bearer $TOKEN" 'http://localhost:8787/api/platform/settings?prefix=carlog.' | python3 -m json.tool
+# 期望: 2 条 (ai.provider 和 ui.compact_mode)
+```
+
+---
+
+### Task 1.4: 平台路由 — dashboard
+
+**新增文件**: `server/src/routes/platform/dashboard.js`
+
+**内容**:
+```js
+import express from 'express';
+import { db } from '../../db.js';
+import { requireAuth } from '../../middleware/auth.js';
+
+const router = express.Router();
+
+// GET /api/platform/dashboard — 跨子系统聚合数据
+router.get('/', requireAuth, async (req, res, next) => {
+ try {
+ // Phase 2 只聚合 CarLog 的关键 stats
+ const [vehicles] = await db().execute(`SELECT COUNT(*) AS n FROM vehicles WHERE deleted_at IS NULL`);
+ const [washesThisMonth] = await db().execute(
+ `SELECT COUNT(*) AS n, COALESCE(SUM(total_cost), 0) AS total
+ FROM wash_records
+ WHERE deleted_at IS NULL
+ AND DATE_FORMAT(wash_date, '%Y-%m') = DATE_FORMAT(UTC_DATE(), '%Y-%m')`
+ );
+ const [refuelsThisMonth] = await db().execute(
+ `SELECT COUNT(*) AS n, COALESCE(SUM(total_cost), 0) AS total, COALESCE(SUM(liters), 0) AS liters
+ FROM refuel_records
+ WHERE deleted_at IS NULL
+ AND DATE_FORMAT(refuel_date, '%Y-%m') = DATE_FORMAT(UTC_DATE(), '%Y-%m')`
+ );
+
+ res.json({
+ ok: true,
+ data: {
+ carlog: {
+ vehicles_count: vehicles[0].n,
+ this_month: {
+ washes: washesThisMonth[0].n,
+ wash_cost: Number(washesThisMonth[0].total),
+ refuels: refuelsThisMonth[0].n,
+ refuel_cost: Number(refuelsThisMonth[0].total),
+ refuel_liters: Number(refuelsThisMonth[0].liters),
+ },
+ },
+ // fitness: {...}, // Phase 3 加
+ // reading: {...}, // Phase 3 加
+ },
+ });
+ } catch (err) {
+ next(err);
+ }
+});
+
+export default router;
+```
+
+**注意**:
+- 现在只聚 CarLog;Fitness / Reading 子系统加进来后再扩
+- 用 UTC_DATE()(mysql2 timezone='Z' 配置已经设为 UTC)
+- 总数 / 总额用 `Number()` 转 JS number(mysql2 返回 DECIMAL 是字符串)
+- 后续扩字段时按需加
+
+**验证**:
+```bash
+curl -s -H "Authorization: Bearer $TOKEN" http://localhost:8787/api/platform/dashboard | python3 -m json.tool
+# 期望: {ok: true, data: {carlog: {vehicles_count: N, this_month: {washes: N, wash_cost: X, ...}}}}
+```
+
+---
+
+### Task 1.5: 路由挂载到 index.js
+
+**修改文件**: `server/src/index.js`
+
+**改动**:
+1. 加 3 个 import
+2. mount 3 个 router
+
+**找到现有的 import 和 mount 位置(参考 CarLog 仓库的 `server/src/index.js` 已有模式),在合适位置加**:
+```js
+// 在 routes 引入区加
+import platformSubsystemsRouter from './routes/platform/subsystems.js';
+import platformSettingsRouter from './routes/platform/settings.js';
+import platformDashboardRouter from './routes/platform/dashboard.js';
+
+// 在 app.use mount 区加(参考现有 app.use('/api/vehicles', ...) 的位置)
+app.use('/api/platform/subsystems', platformSubsystemsRouter);
+app.use('/api/platform/settings', platformSettingsRouter);
+app.use('/api/platform/dashboard', platformDashboardRouter);
+```
+
+**注意**:
+- 保持现有所有 `app.use('/api/*', ...)` 不变
+- 平台路由 mount 在最后(不冲突就行)
+- mount 顺序不重要(路径都不重叠)
+
+**验证**:
+```bash
+cd server && npm run dev
+# 等 server 起来后跑上面 1.2/1.3/1.4 的所有 curl
+```
+
+---
+
+### Task 1.6: 单元测试(平台路由)
+
+**新增文件**:
+- `server/test/routes/platform.subsystems.test.js`
+- `server/test/routes/platform.settings.test.js`
+- `server/test/routes/platform.dashboard.test.js`
+
+**测试范围(每个文件 4-8 个 case)**:
+- auth 拦截(不带 token 返 401)
+- list / get / patch / put / post 各种 HTTP 方法
+- JSON 字段 parse 正确
+- 不存在的 key 返 404
+- prefix 过滤正确
+- 批量 upsert 正确
+
+**参考现有 `server/test/routes/*.test.js`** 的写法(vitest + supertest + mock db)。
+
+**验证**:
+```bash
+cd server && npm test
+# 期望: 所有测试通过, 旧测试 101 个不破 + 新增 12-20 个平台测试
+```
+
+---
+
+## Phase 2: CarLog 子系统化
+
+### Task 2.1: 后端代码目录迁移
+
+**目标**: 把 `server/src/routes/*.js`(13 个 CarLog 路由文件)移到 `server/src/subsystems/carlog/routes/*.js`
+
+**操作**(mv 不是 cp):
+```bash
+mkdir -p server/src/subsystems/carlog/routes
+mv server/src/routes/*.js server/src/subsystems/carlog/routes/
+
+ls server/src/routes/ # 期望: 空目录(保留着,里面加个 .gitkeep)
+touch server/src/routes/.gitkeep
+```
+
+**修改每个迁移文件中的 import 路径**:
+- `server/src/subsystems/carlog/routes/vehicles.js` 里原本是 `import { db } from '../../db.js'`
+- 现在路径变了: `import { db } from '../../../db.js'`(深一层)
+- middleware 同理:`'../../middleware/auth.js'` → `'../../../middleware/auth.js'`
+
+**13 个文件都要改**,用 `sed -i '' "s|from '../../|from '../../../|g" server/src/subsystems/carlog/routes/*.js`
+
+**新建文件**: `server/src/subsystems/carlog/index.js`
+
+```js
+// 聚合导出 CarLog 子系统的所有路由
+import vehicles from './routes/vehicles.js';
+import washes from './routes/washes.js';
+import refuels from './routes/washes.js'; // 注意: refuels 文件名是 refuels.js, 不是 washes
+// ... 其他 10 个
+
+// 注意: 上面的 refuels 注释错了,应该是 import refuels from './routes/refuels.js'
+// 实际写时按文件名一个个 import
+
+import vehiclesRouter from './routes/vehicles.js';
+import washesRouter from './routes/washes.js';
+import refuelsRouter from './routes/refuels.js'; // refuels.js 实际叫 refuels.js
+import chargingRouter from './routes/charging.js'; // 如果有的话, 实际看 routes/ 下的文件名
+import maintenanceRouter from './routes/maintenance.js'; // 同上
+import insuranceRouter from './routes/insurance.js';
+import chemicalsRouter from './routes/chemicals.js';
+import aiRouter from './routes/ai.js';
+import authRouter from './routes/auth.js';
+import settingsRouter from './routes/settings.js';
+import logsRouter from './routes/logs.js';
+import operationLogsRouter from './routes/operationLogs.js';
+import extraRouter from './routes/extra.js';
+import tagsRouter from './routes/tags.js';
+import notificationsRouter from './routes/notifications.js';
+import achievementsRouter from './routes/achievements.js';
+
+export {
+ vehiclesRouter,
+ washesRouter,
+ refuelsRouter,
+ insuranceRouter,
+ chemicalsRouter,
+ aiRouter,
+ authRouter,
+ settingsRouter,
+ logsRouter,
+ operationLogsRouter,
+ extraRouter,
+ tagsRouter,
+ notificationsRouter,
+ achievementsRouter,
+};
+```
+
+**重要**: 先 `ls server/src/routes/` 看清楚 13 个文件实际叫什么名字, 别凭印象写。如果某个路由文件叫 `refuels.js`, import 时就是 `from './routes/refuels.js'`, 不要瞎猜。
+
+**验证**:
+```bash
+ls server/src/subsystems/carlog/routes/ # 期望: 13 个 .js 文件
+ls server/src/routes/ # 期望: 只有 .gitkeep
+
+cd server && node -e "import('./src/subsystems/carlog/index.js').then(m => console.log(Object.keys(m)))"
+# 期望: 所有 router 名字列出
+```
+
+---
+
+### Task 2.2: 后端路由挂载从新目录
+
+**修改文件**: `server/src/index.js`
+
+**改动**: 把现有 mount 改用新 index.js 导出的 router
+```js
+// 原来的:
+// import vehiclesRouter from './routes/vehicles.js';
+// app.use('/api/vehicles', vehiclesRouter);
+
+// 改成:
+import {
+ vehiclesRouter, washesRouter, refuelsRouter,
+ insuranceRouter, chemicalsRouter, aiRouter,
+ authRouter, settingsRouter, logsRouter,
+ operationLogsRouter, extraRouter, tagsRouter,
+ notificationsRouter, achievementsRouter,
+} from './subsystems/carlog/index.js';
+
+app.use('/api/vehicles', vehiclesRouter);
+app.use('/api/washes', washesRouter);
+app.use('/api/refuels', refuelsRouter); // 注意: 如果原文件名是 refuels.js 就这样, 看实际
+// ... 其他 11 个
+app.use('/api/insurance', insuranceRouter);
+app.use('/api/chemicals', chemicalsRouter);
+app.use('/api/ai', aiRouter);
+app.use('/api/auth', authRouter);
+app.use('/api/settings', settingsRouter);
+app.use('/api/logs', logsRouter);
+app.use('/api/operation-logs', operationLogsRouter);
+app.use('/api/extra', extraRouter);
+app.use('/api/tags', tagsRouter);
+app.use('/api/notifications', notificationsRouter);
+app.use('/api/achievements', achievementsRouter);
+```
+
+**重要**:
+- 路由 path 不要变(保持 `/api/vehicles` 而不是 `/api/carlog/vehicles`)
+- 文件名要按 `ls server/src/subsystems/carlog/routes/` 实际看到的写
+- 一定要先 cd 到 server/src/subsystems/carlog/routes 看看实际文件名, 常见陷阱:
+ - `refuels.js` vs `refuel.js`
+ - `washes.js` vs `wash.js` (中文习惯复数)
+ - `chemicals.js` vs `chemical.js`
+- mount 顺序保持不变(先 auth 后业务,因为有些 middleware 依赖 auth)
+
+**验证**:
+```bash
+cd server && npm run dev
+# 登录 + 列车辆 + 列洗车 + 列加油 + 列成就 + 列通知 + 全 OK
+TOKEN=$(curl -s -X POST http://localhost:8787/api/auth/login -H 'Content-Type: application/json' -d '{"username":"admin","password":"carwash2026"}' | python3 -c "import json,sys; print(json.load(sys.stdin)['data']['token'])")
+for ep in vehicles washes refuels insurance chemicals tags notifications achievements; do
+ echo "Testing /api/$ep ..."
+ curl -s -H "Authorization: Bearer $TOKEN" "http://localhost:8787/api/$ep" | head -1
+done
+# 期望: 每行返回 JSON 不报错
+```
+
+---
+
+### Task 2.3: 前端代码目录迁移
+
+**目标**: 把 `client/src/views/*.vue`(20 个 view)和相关 component / composable / store 移到子系统目录
+
+**操作**:
+```bash
+mkdir -p client/src/views/subsystems/carlog
+mkdir -p client/src/components/subsystems/carlog
+mkdir -p client/src/stores/subsystems/carlog
+mkdir -p client/src/composables/subsystems/carlog
+
+# 20 个 view 全部移过去
+mv client/src/views/*.vue client/src/views/subsystems/carlog/
+
+# Home.vue 不动(i 平台的 Dashboard 走 views/Platform/Dashboard.vue,CarLog 用 views/subsystems/carlog/Home.vue 作为子系统主页)
+# Login.vue 不动(i 平台统一登录走 views/Login.vue)
+
+# 加 .gitkeep 在原 views/ 目录
+touch client/src/views/.gitkeep
+```
+
+**修改 router path**: `client/src/router/index.js` 里所有 CarLog 路由的 import 路径改成相对新位置(`'../views/subsystems/carlog/WashesList.vue'` 等),router path 本身**保持不变**(`/washes`、`/vehicles` 等),用户 URL 不变。
+
+**关键 import 修改**:
+```js
+// 原来的:
+// import WashesList from '../views/WashesList.vue';
+
+// 改成:
+// import WashesList from '../views/subsystems/carlog/WashesList.vue';
+
+// 其他 19 个 view 同理
+```
+
+**验证**:
+```bash
+ls client/src/views/subsystems/carlog/ # 期望: 20 个 .vue 文件
+ls client/src/views/ # 期望: 只有 .gitkeep + Login.vue + Offline.vue 等非子系统文件
+
+cd client && npm run dev
+# 浏览器打开 http://localhost:5173/
+# 登录后能正常进 /vehicles /washes /refuels /stats 等所有页面
+```
+
+---
+
+### Task 2.4: 前端平台层 — 总设置 UI
+
+**新增文件**: `client/src/views/Platform/GlobalSettings.vue`
+
+**内容**: 总设置页面
+- 表单: 主题(auto/light/dark select)、语言(zh-CN/en select)、Dashboard 布局(default/compact select)、备份开关(boolean checkbox)、备份路径(string input)
+- 加载数据: `GET /api/platform/settings` 过滤 platform 层 key(不带 prefix)
+- 保存: `POST /api/platform/settings/batch`
+- 用现有 `\n' +
+'\n' +
+'\n' +
+'\n' +
+'
首次安装向导
\n' +
+'
配置你的洗车管理系统
\n' +
+'
填好以下信息,大约 1 分钟完成初始化
\n' +
+'\n' +
+'
① 数据库
\n' +
+'
\n' +
+'\n' +
+'\n' +
+'
\n' +
+'
\n' +
+'
\n' +
+'
\n' +
+'
\n' +
+'\n' +
+'
\n' +
+'\n' +
+'
② 管理员账号
\n' +
+'
\n' +
+'\n' +
+'
\n' +
+'\n' +
+'
③ Grocy (可选,跳过可在设置里补充)
\n' +
+'
\n' +
+'
\n' +
+'\n' +
+'
\n' +
+'\n' +
+'
\n' +
+'\n' +
+'\n' +
+'
\n' +
+'
2 台车 + 过去 30 天洗车记录 + 5 个化学品,用于熟悉系统
\n' +
+'\n' +
+'
\n' +
+'
正在初始化数据库…
\n' +
+'\n' +
+'
\n' +
+'\n' +
+'
\n' +
+'
\n' +
+'\n' +
+'\n' +
+'\n' +
+'