From c0fe5b332102f145dc0535614a905ae7a26e8ded Mon Sep 17 00:00:00 2001 From: Ethanfly Date: Mon, 29 Dec 2025 13:50:23 +0800 Subject: [PATCH] Implement SFTP functionality and enhance UI/UX - Added SFTP file management capabilities including list, upload, download, delete, and directory operations. - Integrated SFTP progress callbacks to provide real-time feedback during file transfers. - Updated the UI to include a dedicated SFTP browser and host information panel. - Enhanced the sidebar and title bar with improved styling and animations for a cyberpunk theme. - Refactored host management to support editing and connecting to hosts with a more intuitive interface. - Updated package dependencies to support new features and improve performance. --- README.md | 320 ++--- capacitor.config.ts | 39 + main.js | 62 +- package-lock.json | 2183 ++++++++++++++++++++++++++++++- package.json | 74 +- preload.js | 23 + public/icon-256.png | Bin 0 -> 14715 bytes public/icon-512.png | Bin 0 -> 34626 bytes public/icon.ico | Bin 0 -> 361102 bytes public/icon.png | Bin 0 -> 14715 bytes public/icon.svg | 47 + public/index.html | 184 ++- public/logo.svg | 108 ++ scripts/generate-icons.js | 80 ++ server/index.js | 471 +++++++ server/package.json | 20 + src/App.js | 336 +++-- src/components/HostEditPanel.js | 411 ++++++ src/components/HostInfoPanel.js | 388 ++++++ src/components/HostManager.js | 116 +- src/components/SFTPBrowser.js | 1321 +++++++++++++++++++ src/components/ServerConfig.js | 207 +++ src/components/Sidebar.js | 385 ++++-- src/components/Terminal.js | 196 ++- src/components/TitleBar.js | 112 +- src/index.css | 688 ++++++++-- src/services/api.js | 398 ++++++ src/services/database.js | 18 +- src/services/sftp.js | 497 +++++++ tailwind.config.js | 98 +- 30 files changed, 8112 insertions(+), 670 deletions(-) create mode 100644 capacitor.config.ts create mode 100644 public/icon-256.png create mode 100644 public/icon-512.png create mode 100644 public/icon.ico create mode 100644 public/icon.png create mode 100644 public/icon.svg create mode 100644 public/logo.svg create mode 100644 scripts/generate-icons.js create mode 100644 server/index.js create mode 100644 server/package.json create mode 100644 src/components/HostEditPanel.js create mode 100644 src/components/HostInfoPanel.js create mode 100644 src/components/SFTPBrowser.js create mode 100644 src/components/ServerConfig.js create mode 100644 src/services/api.js create mode 100644 src/services/sftp.js diff --git a/README.md b/README.md index 07beef5..9128b33 100644 --- a/README.md +++ b/README.md @@ -1,178 +1,179 @@ -# 🚀 EasyShell +# EasyShell 🚀 -高颜值远程 Shell 管理终端 - 一款现代化的 SSH 连接管理工具 +> 赛博朋克风格跨平台远程 Shell 管理终端 -![Platform](https://img.shields.io/badge/platform-Windows%20%7C%20macOS%20%7C%20Linux-blue) -![Electron](https://img.shields.io/badge/Electron-28.0.0-47848F?logo=electron) -![React](https://img.shields.io/badge/React-18.2.0-61DAFB?logo=react) -![License](https://img.shields.io/badge/license-MIT-green) +支持 **Windows / macOS / Linux / Android** 多平台运行。 -## ✨ 特性 +![EasyShell](https://img.shields.io/badge/version-1.0.0-blue) ![Electron](https://img.shields.io/badge/Electron-28-green) ![Capacitor](https://img.shields.io/badge/Capacitor-5.6-orange) -- 🎨 **高颜值界面** - 现代化深色主题,精心设计的 UI/UX -- 🔐 **SSH 连接管理** - 支持密码和私钥认证方式 -- 💾 **双模式存储** - 本地 SQLite + 远程 MySQL 同步 -- 📝 **智能命令提示** - 内置常用命令,支持搜索和使用频率排序 -- 🔄 **数据同步** - 自动建库建表,一键上传/下载数据 -- 📑 **多标签终端** - 同时管理多个 SSH 会话 -- ⌨️ **快捷键支持** - Ctrl+K 打开命令面板 +## ✨ 功能特点 -## 📸 界面预览 +- 🎨 **赛博朋克 UI** - 霓虹色调、玻璃拟态、动态特效 +- 🖥️ **SSH 终端** - 完整的 xterm.js 终端模拟 +- 📁 **SFTP 文件管理** - 远程文件浏览、上传、下载 +- 📊 **主机信息面板** - 实时系统状态监控 +- ☁️ **云端同步** - MySQL 数据库同步支持 +- 📱 **跨平台** - 桌面端和移动端统一体验 -应用采用深色主题设计,包含: -- 可折叠侧边栏(主机列表分组显示) -- 多标签终端区域 -- 命令面板(Ctrl+K 快速调用) -- 主机管理弹窗 -- 数据库连接设置 - -## 🛠️ 技术栈 - -- **Electron** - 跨平台桌面应用框架 -- **React** - 用户界面库 -- **TailwindCSS** - 原子化 CSS 框架 -- **Framer Motion** - 动画库 -- **XTerm.js** - 终端模拟器 -- **SSH2** - SSH 连接库 -- **better-sqlite3** - 本地数据库 -- **mysql2** - MySQL 连接库 - -## 📦 安装 - -### 环境要求 - -- Node.js >= 18.0.0 -- npm >= 9.0.0 -- Python 3.x(用于编译原生模块) -- Visual Studio Build Tools(Windows)/ Xcode(macOS) - -### 安装步骤 - -```bash -# 克隆项目 -git clone https://github.com/your-username/easyshell.git -cd easyshell - -# 安装依赖 -npm install - -# 重新编译原生模块(如果遇到问题) -npm rebuild better-sqlite3 --build-from-source -npm rebuild ssh2 --build-from-source - -# 启动开发模式 -npm start -``` - -### 构建发布版本 - -```bash -# 构建生产版本 -npm run dist -``` - -## 🚀 使用说明 - -### 本地模式 - -应用默认使用本地 SQLite 数据库存储数据,无需任何配置即可使用。 - -### 远程同步模式 - -1. 点击左下角「本地模式」或设置图标 -2. 输入 MySQL 服务器信息: - - 主机地址 - - 端口(默认 3306) - - 用户名 - - 密码 - - 数据库名(默认 easyshell) -3. 点击「连接数据库」 -4. 系统将自动创建数据库和所需的表结构 - -### 添加主机 - -1. 点击侧边栏的 ➕ 按钮或「添加主机」 -2. 填写主机信息: - - 名称(显示名) - - 分组(用于分类) - - 主机地址 - - 端口(默认 22) - - 用户名 - - 密码或 SSH 私钥 -3. 可选择标识颜色 -4. 点击「测试连接」验证配置 -5. 点击「添加主机」保存 - -### 命令提示 - -- 按 `Ctrl+K` 打开命令面板 -- 搜索或浏览预设命令 -- 使用方向键选择,回车执行 -- 命令会直接发送到当前终端 - -## 📁 项目结构 +## 🏗️ 项目结构 ``` easyshell/ -├── main.js # Electron 主进程 -├── preload.js # 预加载脚本(IPC 桥接) -├── package.json # 项目配置 -├── tailwind.config.js # TailwindCSS 配置 -├── public/ -│ └── index.html # HTML 模板 -└── src/ - ├── index.js # React 入口 - ├── index.css # 全局样式 - ├── App.js # 主应用组件 - ├── components/ - │ ├── TitleBar.js # 标题栏 - │ ├── Sidebar.js # 侧边栏 - │ ├── Terminal.js # 终端组件 - │ ├── HostManager.js # 主机管理 - │ ├── Settings.js # 设置面板 - │ └── CommandPalette.js # 命令面板 - └── services/ - ├── database.js # 数据库服务 - └── ssh.js # SSH 服务 +├── src/ # React 前端源码 +│ ├── components/ # UI 组件 +│ ├── services/ # 服务层 +│ │ ├── api.js # 跨平台 API 适配层 +│ │ ├── database.js # 数据库服务 +│ │ ├── ssh.js # SSH 服务 (Electron) +│ │ └── sftp.js # SFTP 服务 (Electron) +│ └── ... +├── server/ # 后端服务器 (移动端需要) +│ ├── index.js # Express + Socket.IO 服务 +│ └── package.json +├── android/ # Android 原生项目 (Capacitor) +├── main.js # Electron 主进程 +├── preload.js # Electron 预加载脚本 +└── capacitor.config.ts # Capacitor 配置 ``` -## ⌨️ 快捷键 +## 🚀 快速开始 -| 快捷键 | 功能 | -|--------|------| -| `Ctrl+K` | 打开命令面板 | -| `Esc` | 关闭弹窗 | -| `↑/↓` | 命令面板中导航 | -| `Enter` | 执行选中命令 | +### 安装依赖 -## 🔧 数据库结构 +```bash +# 安装前端依赖 +npm install -### hosts 表(主机信息) -| 字段 | 类型 | 说明 | -|------|------|------| -| id | INT | 主键 | -| name | VARCHAR | 主机名称 | -| host | VARCHAR | 主机地址 | -| port | INT | SSH 端口 | -| username | VARCHAR | 用户名 | -| password | TEXT | 密码(加密存储建议) | -| private_key | TEXT | SSH 私钥 | -| group_name | VARCHAR | 分组名 | -| color | VARCHAR | 标识颜色 | -| description | TEXT | 备注 | +# 安装服务器依赖 +cd server && npm install && cd .. +``` -### commands 表(命令提示) -| 字段 | 类型 | 说明 | -|------|------|------| -| id | INT | 主键 | -| command | TEXT | 命令内容 | -| description | TEXT | 命令描述 | -| category | VARCHAR | 分类 | -| usage_count | INT | 使用次数 | +### 桌面端开发 -## 🤝 贡献 +```bash +# 启动 Electron 开发模式 +npm start +``` -欢迎提交 Issue 和 Pull Request! +### 移动端开发 + +#### 1. 启动后端服务器 + +```bash +# 在电脑上启动服务器 +npm run server + +# 或开发模式 (自动重启) +npm run server:dev +``` + +服务器将在 `http://0.0.0.0:3001` 启动。 + +#### 2. 构建并运行安卓应用 + +```bash +# 首次使用需要初始化 +npm run cap:add:android + +# 构建并打开 Android Studio +npm run android + +# 或直接运行到设备 +npm run android:run +``` + +#### 3. 在手机上配置 + +1. 确保手机和电脑在同一局域网 +2. 打开 EasyShell 应用 +3. 点击右上角设置图标 +4. 输入电脑 IP 地址,如 `http://192.168.1.100:3001` +5. 点击测试连接,确认成功后保存 + +## 📦 构建发布 + +### 桌面端 + +```bash +# Windows +npm run dist + +# macOS (需要在 Mac 上构建) +npm run dist + +# Linux +npm run dist +``` + +### 安卓端 + +```bash +# 构建 Release APK +npm run build +npx cap sync android +cd android && ./gradlew assembleRelease +``` + +APK 位于 `android/app/build/outputs/apk/release/` + +## 🔧 配置说明 + +### 服务器配置 + +服务器默认端口 `3001`,可通过环境变量修改: + +```bash +PORT=8080 npm run server +``` + +### Capacitor 配置 + +编辑 `capacitor.config.ts` 可以修改: + +- 应用 ID +- 状态栏样式 +- 背景颜色 +- 等等 + +## 🛡️ 安全说明 + +- SSH 密码和私钥存储在本地 (桌面端使用 electron-store,移动端使用 localStorage) +- 移动端通过 WebSocket 与后端服务器通信 +- 建议在受信任的网络环境中使用 +- 生产环境建议配置 HTTPS/WSS + +## 📱 移动端限制 + +由于浏览器安全限制,移动端有以下差异: + +| 功能 | 桌面端 | 移动端 | +|------|--------|--------| +| SSH 连接 | 直连 | 通过服务器代理 | +| SFTP 上传 | ✅ | ⚠️ 需要文件选择器 | +| MySQL 同步 | ✅ | ❌ 暂不支持 | +| 窗口控制 | ✅ | ❌ 不需要 | + +## 🤝 技术栈 + +**前端:** +- React 18 +- Tailwind CSS +- Framer Motion +- xterm.js + +**桌面端:** +- Electron 28 +- electron-store +- ssh2 + +**移动端:** +- Capacitor 5 +- Socket.IO + +**后端:** +- Express +- Socket.IO +- ssh2 ## 📄 许可证 @@ -180,5 +181,4 @@ MIT License --- -Made with ❤️ by EasyShell Team - +Made with ❤️ and ⚡ by EasyShell Team diff --git a/capacitor.config.ts b/capacitor.config.ts new file mode 100644 index 0000000..3d36c11 --- /dev/null +++ b/capacitor.config.ts @@ -0,0 +1,39 @@ +import { CapacitorConfig } from '@capacitor/cli'; + +const config: CapacitorConfig = { + appId: 'com.easyshell.app', + appName: 'EasyShell', + webDir: 'build', + server: { + // 开发时可以指向本地服务器 + // url: 'http://192.168.1.100:3000', + // cleartext: true, + androidScheme: 'https', + }, + plugins: { + StatusBar: { + style: 'DARK', + backgroundColor: '#050810', + }, + Keyboard: { + resize: 'body', + resizeOnFullScreen: true, + }, + App: { + // 防止返回键直接退出 + }, + }, + android: { + allowMixedContent: true, + backgroundColor: '#050810', + // 全屏沉浸模式 + // useLegacyBridge: true, + }, + ios: { + backgroundColor: '#050810', + contentInset: 'automatic', + }, +}; + +export default config; + diff --git a/main.js b/main.js index af53e19..cd9edf8 100644 --- a/main.js +++ b/main.js @@ -6,9 +6,10 @@ const path = require('path'); const Store = require('electron-store'); const databaseService = require('./src/services/database'); const sshService = require('./src/services/ssh'); +const sftpService = require('./src/services/sftp'); let mainWindow; -const isDev = process.env.NODE_ENV !== 'production' || !app.isPackaged; +const isDev = !app.isPackaged; // 只根据是否打包来判断开发模式 // 配置存储 const configStore = new Store({ @@ -182,8 +183,8 @@ ipcMain.handle('hosts:update', (event, { id, host }) => { return databaseService.updateHost(id, host); }); -ipcMain.handle('hosts:delete', (event, id) => { - return databaseService.deleteHost(id); +ipcMain.handle('hosts:delete', async (event, id) => { + return await databaseService.deleteHost(id); }); // 命令 @@ -276,3 +277,58 @@ ipcMain.handle('ssh:exec', async (event, { hostConfig, command }) => { return await sshService.exec(hostConfig, command); }); +// ========== SFTP IPC ========== + +// 设置进度回调 +sftpService.setProgressCallback((progress) => { + mainWindow?.webContents.send('sftp:progress', progress); +}); + +ipcMain.handle('sftp:list', async (event, { hostConfig, remotePath }) => { + return await sftpService.list(hostConfig, remotePath); +}); + +ipcMain.handle('sftp:download', async (event, { hostConfig, remotePath }) => { + return await sftpService.download(hostConfig, remotePath, mainWindow); +}); + +ipcMain.handle('sftp:upload', async (event, { hostConfig, localPath, remotePath }) => { + return await sftpService.upload(hostConfig, localPath, remotePath); +}); + +ipcMain.handle('sftp:delete', async (event, { hostConfig, remotePath }) => { + return await sftpService.delete(hostConfig, remotePath); +}); + +ipcMain.handle('sftp:mkdir', async (event, { hostConfig, remotePath }) => { + return await sftpService.mkdir(hostConfig, remotePath); +}); + +ipcMain.handle('sftp:rmdir', async (event, { hostConfig, remotePath }) => { + return await sftpService.rmdir(hostConfig, remotePath); +}); + +ipcMain.handle('sftp:rename', async (event, { hostConfig, oldPath, newPath }) => { + return await sftpService.rename(hostConfig, oldPath, newPath); +}); + +ipcMain.handle('sftp:writeFile', async (event, { hostConfig, remotePath, content }) => { + return await sftpService.writeFile(hostConfig, remotePath, content); +}); + +ipcMain.handle('sftp:readFile', async (event, { hostConfig, remotePath }) => { + return await sftpService.readFile(hostConfig, remotePath); +}); + +ipcMain.handle('sftp:stat', async (event, { hostConfig, remotePath }) => { + return await sftpService.stat(hostConfig, remotePath); +}); + +ipcMain.handle('sftp:chmod', async (event, { hostConfig, remotePath, mode }) => { + return await sftpService.chmod(hostConfig, remotePath, mode); +}); + +ipcMain.handle('sftp:chown', async (event, { hostConfig, remotePath, uid, gid }) => { + return await sftpService.chown(hostConfig, remotePath, uid, gid); +}); + diff --git a/package-lock.json b/package-lock.json index fa5bcc7..825690b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,12 @@ "name": "easyshell", "version": "1.0.0", "dependencies": { + "@capacitor/android": "^5.6.0", + "@capacitor/app": "^5.0.6", + "@capacitor/core": "^5.6.0", + "@capacitor/haptics": "^5.0.6", + "@capacitor/keyboard": "^5.0.6", + "@capacitor/status-bar": "^5.0.6", "@xterm/addon-fit": "^0.10.0", "@xterm/addon-web-links": "^0.11.0", "@xterm/xterm": "^5.5.0", @@ -18,17 +24,22 @@ "react-dom": "^18.2.0", "react-icons": "^4.12.0", "react-scripts": "5.0.1", + "socket.io-client": "^4.6.1", "sql.js": "^1.10.0", "ssh2": "^1.15.0" }, "devDependencies": { + "@capacitor/cli": "^5.6.0", "autoprefixer": "^10.4.16", "concurrently": "^8.2.2", "cross-env": "^10.1.0", "electron": "^28.0.0", "electron-builder": "^24.9.1", + "png-to-ico": "^3.0.1", "postcss": "^8.4.32", + "sharp": "^0.34.5", "tailwindcss": "^3.4.0", + "to-ico": "^1.1.5", "wait-on": "^7.2.0" } }, @@ -2043,6 +2054,191 @@ "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "license": "MIT" }, + "node_modules/@capacitor/android": { + "version": "5.7.8", + "resolved": "https://registry.npmjs.org/@capacitor/android/-/android-5.7.8.tgz", + "integrity": "sha512-ooWclwcuW0dy3YfqgoozkHkjatX8H2fb2/RwRsJa3cew1P1lUXIXri3Dquuy4LdqFAJA7UHcJ19Bl/6UKdsZYA==", + "license": "MIT", + "peerDependencies": { + "@capacitor/core": "^5.7.0" + } + }, + "node_modules/@capacitor/app": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/@capacitor/app/-/app-5.0.8.tgz", + "integrity": "sha512-ClUPJG6Awkf5HncVCZQwLrnuugjU8TnACSJ1dKJb6QNCHv2jQzmXvB3KvTvxTZyWbh5EVvlla0qlobYyU1lb6A==", + "license": "MIT", + "peerDependencies": { + "@capacitor/core": "^5.0.0" + } + }, + "node_modules/@capacitor/cli": { + "version": "5.7.8", + "resolved": "https://registry.npmjs.org/@capacitor/cli/-/cli-5.7.8.tgz", + "integrity": "sha512-qN8LDlREMhrYhOvVXahoJVNkP8LP55/YPRJrzTAFrMqlNJC18L3CzgWYIblFPnuwfbH/RxbfoZT/ydkwgVpMrw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ionic/cli-framework-output": "^2.2.5", + "@ionic/utils-fs": "^3.1.6", + "@ionic/utils-subprocess": "^2.1.11", + "@ionic/utils-terminal": "^2.3.3", + "commander": "^9.3.0", + "debug": "^4.3.4", + "env-paths": "^2.2.0", + "kleur": "^4.1.4", + "native-run": "^2.0.0", + "open": "^8.4.0", + "plist": "^3.0.5", + "prompts": "^2.4.2", + "rimraf": "^4.4.1", + "semver": "^7.3.7", + "tar": "^6.1.11", + "tslib": "^2.4.0", + "xml2js": "^0.5.0" + }, + "bin": { + "cap": "bin/capacitor", + "capacitor": "bin/capacitor" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@capacitor/cli/node_modules/commander": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", + "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || >=14" + } + }, + "node_modules/@capacitor/cli/node_modules/glob": { + "version": "9.3.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-9.3.5.tgz", + "integrity": "sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q==", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "minimatch": "^8.0.2", + "minipass": "^4.2.4", + "path-scurry": "^1.6.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@capacitor/cli/node_modules/kleur": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/@capacitor/cli/node_modules/minimatch": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-8.0.4.tgz", + "integrity": "sha512-W0Wvr9HyFXZRGIDgCicunpQ299OKXs9RgZfaukz4qAW/pJhcpUfupc9c+OObPOFueNy8VSrZgEmDtk6Kh4WzDA==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@capacitor/cli/node_modules/minipass": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.2.8.tgz", + "integrity": "sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=8" + } + }, + "node_modules/@capacitor/cli/node_modules/rimraf": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-4.4.1.tgz", + "integrity": "sha512-Gk8NlF062+T9CqNGn6h4tls3k6T1+/nXdOcSZVikNVtlRdYpA7wRJJMoXmuvOnLW844rPjdQ7JgXCYM6PPC/og==", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^9.2.0" + }, + "bin": { + "rimraf": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@capacitor/cli/node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@capacitor/core": { + "version": "5.7.8", + "resolved": "https://registry.npmjs.org/@capacitor/core/-/core-5.7.8.tgz", + "integrity": "sha512-rrZcm/2vJM0WdWRQup1TUidbjQV9PfIadSkV4rAGLD7R6PuzZSMPGT0gmoZzCRlXkqrazrWWDkurei3ozU02FA==", + "license": "MIT", + "peer": true, + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/@capacitor/haptics": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/@capacitor/haptics/-/haptics-5.0.8.tgz", + "integrity": "sha512-PkmBlaApk0vQ/ddm875H0VPmnmUUAQ+lxEtGgvJAaXV83acDdo6zH+pJLCodgIR7UPiscfdcqt6xajeYw2Ld1w==", + "license": "MIT", + "peerDependencies": { + "@capacitor/core": "^5.0.0" + } + }, + "node_modules/@capacitor/keyboard": { + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/@capacitor/keyboard/-/keyboard-5.0.9.tgz", + "integrity": "sha512-JFiIHR251LrC6NGdstO3T5H5XfOcVEDEm0zWbHfROO1xyJwXjH3QHu6F22VXu5cGEmIOFII32Tt+cXGsGzJpcQ==", + "license": "MIT", + "peerDependencies": { + "@capacitor/core": "^5.0.0" + } + }, + "node_modules/@capacitor/status-bar": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/@capacitor/status-bar/-/status-bar-5.0.8.tgz", + "integrity": "sha512-ES/4ik3/6SV6RVXVVoMm5i7zanwZ+CBtNX6glhBHDHS0nR2m3Dmdxuk/BaFEZWT5bDn4xzg0LlnMsS8mlbUf+A==", + "license": "MIT", + "peerDependencies": { + "@capacitor/core": "^5.0.0" + } + }, "node_modules/@csstools/normalize.css": { "version": "12.1.1", "resolved": "https://registry.npmjs.org/@csstools/normalize.css/-/normalize.css-12.1.1.tgz", @@ -2620,6 +2816,17 @@ "node": ">= 10.0.0" } }, + "node_modules/@emnapi/runtime": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.7.1.tgz", + "integrity": "sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@emotion/is-prop-valid": { "version": "0.8.8", "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-0.8.8.tgz", @@ -2799,6 +3006,773 @@ "deprecated": "Use @eslint/object-schema instead", "license": "BSD-3-Clause" }, + "node_modules/@img/colour": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.0.0.tgz", + "integrity": "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", + "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", + "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", + "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", + "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.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", + "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", + "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.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", + "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", + "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.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", + "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", + "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.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", + "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", + "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.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", + "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", + "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.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", + "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", + "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.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", + "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", + "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.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", + "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", + "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.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", + "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", + "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.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", + "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", + "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-ppc64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", + "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-ppc64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-riscv64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", + "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-riscv64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", + "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", + "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", + "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", + "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", + "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.7.0" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", + "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", + "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", + "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@ionic/cli-framework-output": { + "version": "2.2.8", + "resolved": "https://registry.npmjs.org/@ionic/cli-framework-output/-/cli-framework-output-2.2.8.tgz", + "integrity": "sha512-TshtaFQsovB4NWRBydbNFawql6yul7d5bMiW1WYYf17hd99V6xdDdk3vtF51bw6sLkxON3bDQpWsnUc9/hVo3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ionic/utils-terminal": "2.3.5", + "debug": "^4.0.0", + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@ionic/utils-array": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@ionic/utils-array/-/utils-array-2.1.6.tgz", + "integrity": "sha512-0JZ1Zkp3wURnv8oq6Qt7fMPo5MpjbLoUoa9Bu2Q4PJuSDWM8H8gwF3dQO7VTeUj3/0o1IB1wGkFWZZYgUXZMUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.0.0", + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@ionic/utils-fs": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@ionic/utils-fs/-/utils-fs-3.1.7.tgz", + "integrity": "sha512-2EknRvMVfhnyhL1VhFkSLa5gOcycK91VnjfrTB0kbqkTFCOXyXgVLI5whzq7SLrgD9t1aqos3lMMQyVzaQ5gVA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/fs-extra": "^8.0.0", + "debug": "^4.0.0", + "fs-extra": "^9.0.0", + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@ionic/utils-fs/node_modules/@types/fs-extra": { + "version": "8.1.5", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-8.1.5.tgz", + "integrity": "sha512-0dzKcwO+S8s2kuF5Z9oUWatQJj5Uq/iqphEtE3GQJVRRYm/tD1LglU2UnXi2A8jLq5umkGouOXOR9y0n613ZwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@ionic/utils-fs/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@ionic/utils-fs/node_modules/jsonfile": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", + "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/@ionic/utils-fs/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@ionic/utils-object": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@ionic/utils-object/-/utils-object-2.1.6.tgz", + "integrity": "sha512-vCl7sl6JjBHFw99CuAqHljYJpcE88YaH2ZW4ELiC/Zwxl5tiwn4kbdP/gxi2OT3MQb1vOtgAmSNRtusvgxI8ww==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.0.0", + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@ionic/utils-process": { + "version": "2.1.11", + "resolved": "https://registry.npmjs.org/@ionic/utils-process/-/utils-process-2.1.11.tgz", + "integrity": "sha512-Uavxn+x8j3rDlZEk1X7YnaN6wCgbCwYQOeIjv/m94i1dzslqWhqIHEqxEyeE8HsT5Negboagg7GtQiABy+BLbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ionic/utils-object": "2.1.6", + "@ionic/utils-terminal": "2.3.4", + "debug": "^4.0.0", + "signal-exit": "^3.0.3", + "tree-kill": "^1.2.2", + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@ionic/utils-process/node_modules/@ionic/utils-terminal": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/@ionic/utils-terminal/-/utils-terminal-2.3.4.tgz", + "integrity": "sha512-cEiMFl3jklE0sW60r8JHH3ijFTwh/jkdEKWbylSyExQwZ8pPuwoXz7gpkWoJRLuoRHHSvg+wzNYyPJazIHfoJA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/slice-ansi": "^4.0.0", + "debug": "^4.0.0", + "signal-exit": "^3.0.3", + "slice-ansi": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "tslib": "^2.0.1", + "untildify": "^4.0.0", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@ionic/utils-process/node_modules/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/@ionic/utils-stream": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/@ionic/utils-stream/-/utils-stream-3.1.6.tgz", + "integrity": "sha512-4+Kitey1lTA1yGtnigeYNhV/0tggI3lWBMjC7tBs1K9GXa/q7q4CtOISppdh8QgtOhrhAXS2Igp8rbko/Cj+lA==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.0.0", + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@ionic/utils-subprocess": { + "version": "2.1.14", + "resolved": "https://registry.npmjs.org/@ionic/utils-subprocess/-/utils-subprocess-2.1.14.tgz", + "integrity": "sha512-nGYvyGVjU0kjPUcSRFr4ROTraT3w/7r502f5QJEsMRKTqa4eEzCshtwRk+/mpASm0kgBN5rrjYA5A/OZg8ahqg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ionic/utils-array": "2.1.6", + "@ionic/utils-fs": "3.1.7", + "@ionic/utils-process": "2.1.11", + "@ionic/utils-stream": "3.1.6", + "@ionic/utils-terminal": "2.3.4", + "cross-spawn": "^7.0.3", + "debug": "^4.0.0", + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@ionic/utils-subprocess/node_modules/@ionic/utils-terminal": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/@ionic/utils-terminal/-/utils-terminal-2.3.4.tgz", + "integrity": "sha512-cEiMFl3jklE0sW60r8JHH3ijFTwh/jkdEKWbylSyExQwZ8pPuwoXz7gpkWoJRLuoRHHSvg+wzNYyPJazIHfoJA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/slice-ansi": "^4.0.0", + "debug": "^4.0.0", + "signal-exit": "^3.0.3", + "slice-ansi": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "tslib": "^2.0.1", + "untildify": "^4.0.0", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@ionic/utils-subprocess/node_modules/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/@ionic/utils-terminal": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@ionic/utils-terminal/-/utils-terminal-2.3.5.tgz", + "integrity": "sha512-3cKScz9Jx2/Pr9ijj1OzGlBDfcmx7OMVBt4+P1uRR0SSW4cm1/y3Mo4OY3lfkuaYifMNBW8Wz6lQHbs1bihr7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/slice-ansi": "^4.0.0", + "debug": "^4.0.0", + "signal-exit": "^3.0.3", + "slice-ansi": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "tslib": "^2.0.1", + "untildify": "^4.0.0", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@ionic/utils-terminal/node_modules/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", @@ -3704,6 +4678,12 @@ "@sinonjs/commons": "^1.7.0" } }, + "node_modules/@socket.io/component-emitter": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", + "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==", + "license": "MIT" + }, "node_modules/@surma/rollup-plugin-off-main-thread": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/@surma/rollup-plugin-off-main-thread/-/rollup-plugin-off-main-thread-2.2.3.tgz", @@ -4369,6 +5349,13 @@ "@types/node": "*" } }, + "node_modules/@types/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-+OpjSaq85gvlZAYINyzKpLeiFkSC4EsC6IIiT6v6TLSU5k5U83fHGj9Lel8oKEXM0HqgrMVCjXPDPVICtxF7EQ==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/sockjs": { "version": "0.3.36", "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.36.tgz", @@ -5584,6 +6571,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/asap": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", @@ -5605,7 +6602,6 @@ "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", "dev": true, "license": "MIT", - "optional": true, "engines": { "node": ">=0.8" } @@ -5622,7 +6618,6 @@ "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", "dev": true, "license": "MIT", - "optional": true, "engines": { "node": ">=8" } @@ -5727,6 +6722,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "*" + } + }, "node_modules/aws-ssl-profiles": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/aws-ssl-profiles/-/aws-ssl-profiles-1.1.2.tgz", @@ -5736,6 +6741,13 @@ "node": ">= 6.0.0" } }, + "node_modules/aws4": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.13.2.tgz", + "integrity": "sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw==", + "dev": true, + "license": "MIT" + }, "node_modules/axe-core": { "version": "4.11.0", "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.11.0.tgz", @@ -6078,6 +7090,16 @@ "node": ">= 8.0.0" } }, + "node_modules/big-integer": { + "version": "1.6.52", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.52.tgz", + "integrity": "sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==", + "dev": true, + "license": "Unlicense", + "engines": { + "node": ">=0.6" + } + }, "node_modules/big.js": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", @@ -6087,6 +7109,16 @@ "node": "*" } }, + "node_modules/bignumber.js": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-2.4.0.tgz", + "integrity": "sha512-uw4ra6Cv483Op/ebM0GBKKfxZlSmn6NgFRby5L3yGTlunLj53KQgndDlqy2WVFOwgvurocApYkSud0aO+mvrpQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, "node_modules/binary-extensions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", @@ -6127,6 +7159,13 @@ "bluebird": "^3.5.5" } }, + "node_modules/bmp-js": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/bmp-js/-/bmp-js-0.0.1.tgz", + "integrity": "sha512-OS74Rlt0Aynu2mTPmY9RZOUOXlqWecFIILFXr70vv16/xCZnFxvri9IKkF1IGxQ8r9dOE62qGNpKxXx8Lko8bg==", + "dev": true, + "license": "MIT" + }, "node_modules/body-parser": { "version": "1.20.4", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.4.tgz", @@ -6203,6 +7242,19 @@ "license": "MIT", "optional": true }, + "node_modules/bplist-parser": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.3.2.tgz", + "integrity": "sha512-apC2+fspHGI3mMKj+dGevkGo/tCqVB8jMb6i+OX+E29p0Iposz07fABkRIfVUPNd5A5VbuOz1bZbnmkKLYF+wQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "big-integer": "1.6.x" + }, + "engines": { + "node": ">= 5.10.0" + } + }, "node_modules/brace-expansion": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", @@ -6298,6 +7350,24 @@ "ieee754": "^1.1.13" } }, + "node_modules/buffer-alloc": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", + "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-alloc-unsafe": "^1.1.0", + "buffer-fill": "^1.0.0" + } + }, + "node_modules/buffer-alloc-unsafe": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz", + "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==", + "dev": true, + "license": "MIT" + }, "node_modules/buffer-crc32": { "version": "0.2.13", "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", @@ -6321,6 +7391,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/buffer-fill": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", + "integrity": "sha512-T7zexNBwiiaCOGDg9xNX9PBmjrubblRkENuptryuI64URkXDFum9il/JGL8Lm8wYfAXpredVXXZz7eMHilimiQ==", + "dev": true, + "license": "MIT" + }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -6591,6 +7668,23 @@ "node": ">=4" } }, + "node_modules/caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/centra": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/centra/-/centra-2.7.0.tgz", + "integrity": "sha512-PbFMgMSrmgx6uxCdm57RUos9Tc3fclMvhLSATYN39XsDV29B89zZ3KA89jmY0vwSGazyU+uerqwa6t+KaodPcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6" + } + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -7781,6 +8875,19 @@ "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==", "license": "BSD-2-Clause" }, + "node_modules/dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", + "dev": true, + "license": "MIT", + "dependencies": { + "assert-plus": "^1.0.0" + }, + "engines": { + "node": ">=0.10" + } + }, "node_modules/data-urls": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz", @@ -8053,6 +9160,16 @@ "npm": "1.2.8000 || >= 1.4.16" } }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, "node_modules/detect-newline": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", @@ -8299,6 +9416,12 @@ "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" } }, + "node_modules/dom-walk": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.2.tgz", + "integrity": "sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w==", + "dev": true + }, "node_modules/domelementtype": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", @@ -8429,6 +9552,17 @@ "dev": true, "license": "MIT" }, + "node_modules/ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -8658,6 +9792,26 @@ "integrity": "sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==", "license": "ISC" }, + "node_modules/elementtree": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/elementtree/-/elementtree-0.1.7.tgz", + "integrity": "sha512-wkgGT6kugeQk/P6VZ/f4T+4HB41BVgNBq5CDIZVbQ02nvTVqAiVTbskxxu3eA/X96lMlfYOwnLQpN2v5E1zDEg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "sax": "1.1.4" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/elementtree/node_modules/sax": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.1.4.tgz", + "integrity": "sha512-5f3k2PbGGp+YtKJjOItpg3P99IMD84E4HOvcfleTb5joCHNXYLsR9yWFPOYGgaeMPDubQILTCMdsFb2OMeOjtg==", + "dev": true, + "license": "ISC" + }, "node_modules/emittery": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.8.1.tgz", @@ -8704,6 +9858,66 @@ "once": "^1.4.0" } }, + "node_modules/engine.io-client": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.4.0.tgz", + "integrity": "sha512-GyKPDyoEha+XZ7iEqam49vz6auPnNJ9ZBfy89f+rMMas8AuiMWOZ9PVzu8xb9ZC6rafUqiGHSCfu22ih66E+1g==", + "license": "MIT", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1", + "engine.io-parser": "~5.0.3", + "ws": "~8.11.0", + "xmlhttprequest-ssl": "~2.0.0" + } + }, + "node_modules/engine.io-client/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/engine.io-client/node_modules/ws": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", + "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/engine.io-parser": { + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.7.tgz", + "integrity": "sha512-P+jDFbvK6lE3n1OL+q9KuzdOFWkkZ/cMV9gol/SbVfpyqfvrfrFTOFJ6fQm2VC3PZHlU3QPhVwmbsCnauHF2MQ==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/enhanced-resolve": { "version": "5.18.4", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.4.tgz", @@ -8949,6 +10163,13 @@ "license": "MIT", "optional": true }, + "node_modules/es6-promise": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.3.1.tgz", + "integrity": "sha512-SOp9Phqvqn7jtEUxPWdWfWoLmyt2VaJ6MpvP9Comy1MceMXqE6bxvaTu4iaxpYYPzhny28Lc+M87/c2cPK6lDg==", + "dev": true, + "license": "MIT" + }, "node_modules/escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", @@ -9713,6 +10934,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/exif-parser": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/exif-parser/-/exif-parser-0.1.12.tgz", + "integrity": "sha512-c2bQfLNbMzLPmzQuOr8fy0csy84WmwnER81W88DzTp9CYNPJ6yzOj2EZAh9pywYpqHnshVLHQJ8WzldAyfY+Iw==", + "dev": true + }, "node_modules/exit": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", @@ -9797,6 +11024,13 @@ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "license": "MIT" }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true, + "license": "MIT" + }, "node_modules/extract-zip": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", @@ -9981,6 +11215,16 @@ "url": "https://opencollective.com/webpack" } }, + "node_modules/file-type": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz", + "integrity": "sha512-RLoqTXE8/vPmMuTI88DAzhMYC99I8BWv7zYP4A1puo5HIjEJ5EX48ighy4ZyKMG9EDXxBgW6e++cn7d1xuFghA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/filelist": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", @@ -10158,6 +11402,16 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "*" + } + }, "node_modules/fork-ts-checker-webpack-plugin": { "version": "6.5.3", "resolved": "https://registry.npmjs.org/fork-ts-checker-webpack-plugin/-/fork-ts-checker-webpack-plugin-6.5.3.tgz", @@ -10615,6 +11869,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", + "dev": true, + "license": "MIT", + "dependencies": { + "assert-plus": "^1.0.0" + } + }, "node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -10676,6 +11940,17 @@ "node": "*" } }, + "node_modules/global": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/global/-/global-4.4.0.tgz", + "integrity": "sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "min-document": "^2.19.0", + "process": "^0.11.10" + } + }, "node_modules/global-agent": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/global-agent/-/global-agent-3.0.0.tgz", @@ -10881,6 +12156,31 @@ "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", "license": "MIT" }, + "node_modules/har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=4" + } + }, + "node_modules/har-validator": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", + "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", + "deprecated": "this library is no longer supported", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.3", + "har-schema": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/harmony-reflect": { "version": "1.6.2", "resolved": "https://registry.npmjs.org/harmony-reflect/-/harmony-reflect-1.6.2.tgz", @@ -11260,6 +12560,22 @@ } } }, + "node_modules/http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + }, + "engines": { + "node": ">=0.8", + "npm": ">=1.3.7" + } + }, "node_modules/http2-wrapper": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", @@ -11386,6 +12702,19 @@ "node": ">= 4" } }, + "node_modules/image-size": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz", + "integrity": "sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==", + "dev": true, + "license": "MIT", + "bin": { + "image-size": "bin/image-size.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/immer": { "version": "9.0.21", "resolved": "https://registry.npmjs.org/immer/-/immer-9.0.21.tgz", @@ -11486,6 +12815,16 @@ "node": ">= 0.4" } }, + "node_modules/ip-regex": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-1.0.3.tgz", + "integrity": "sha512-HjpCHTuxbR/6jWJroc/VN+npo5j0T4Vv2TAI5qdEHQx7hsL767MeccGFSsLtF694EiZKTSEqgoeU6DtGFCcuqQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/ipaddr.js": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.3.0.tgz", @@ -11701,6 +13040,13 @@ "node": ">=8" } }, + "node_modules/is-function": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-function/-/is-function-1.0.2.tgz", + "integrity": "sha512-lw7DUp0aWXYg+CBCN+JKkcE0Q2RayZnSvnZBlwgxHBQhqt5pZNVy4Ri7H9GmmXkdu7LUthszM+Tor1u/2iBcpQ==", + "dev": true, + "license": "MIT" + }, "node_modules/is-generator-fn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", @@ -12047,6 +13393,13 @@ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "license": "ISC" }, + "node_modules/isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==", + "dev": true, + "license": "MIT" + }, "node_modules/istanbul-lib-coverage": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", @@ -13144,6 +14497,89 @@ "node": ">= 10.13.0" } }, + "node_modules/jimp": { + "version": "0.2.28", + "resolved": "https://registry.npmjs.org/jimp/-/jimp-0.2.28.tgz", + "integrity": "sha512-9HT7DA279xkTlry2oG30s6AtOUglNiY2UdyYpj0yNI4/NBv8PmdNC0gcldgMU4HqvbUlrM3+v+6GaHnTkH23JQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "bignumber.js": "^2.1.0", + "bmp-js": "0.0.3", + "es6-promise": "^3.0.2", + "exif-parser": "^0.1.9", + "file-type": "^3.1.0", + "jpeg-js": "^0.2.0", + "load-bmfont": "^1.2.3", + "mime": "^1.3.4", + "mkdirp": "0.5.1", + "pixelmatch": "^4.0.0", + "pngjs": "^3.0.0", + "read-chunk": "^1.0.1", + "request": "^2.65.0", + "stream-to-buffer": "^0.1.0", + "tinycolor2": "^1.1.2", + "url-regex": "^3.0.0" + } + }, + "node_modules/jimp/node_modules/bmp-js": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/bmp-js/-/bmp-js-0.0.3.tgz", + "integrity": "sha512-epsm3Z92j5xwek9p97pVw3KbsNc0F4QnbYh+N93SpbJYuHFQQ/UAh6K+bKFGyLePH3Hudtl/Sa95Quqp0gX8IQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/jimp/node_modules/jpeg-js": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/jpeg-js/-/jpeg-js-0.2.0.tgz", + "integrity": "sha512-Ni9PffhJtYtdD7VwxH6V2MnievekGfUefosGCHadog0/jAevRu6HPjYeMHbUemn0IPE8d4wGa8UsOGsX+iKy2g==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/jimp/node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true, + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/jimp/node_modules/minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha512-miQKw5Hv4NS1Psg2517mV4e4dYNaO3++hjAvLOAzKqZ61rH8NS1SK+vbfBWZ5PY/Me/bEWhUwqMghEW5Fb9T7Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/jimp/node_modules/mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha512-SknJC52obPfGQPnjIkXbmA6+5H15E+fR+E4iR2oQ3zzCLbd7/ONua69R/Gw7AgkTLsRG+r5fzksYwWe1AgTyWA==", + "deprecated": "Legacy versions of mkdirp are no longer supported. Please update to mkdirp 1.x. (Note that the API surface has changed to use Promises in 1.x.)", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "0.0.8" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/jimp/node_modules/pngjs": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-3.4.0.tgz", + "integrity": "sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, "node_modules/jiti": { "version": "1.21.7", "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", @@ -13168,6 +14604,13 @@ "@sideway/pinpoint": "^2.0.0" } }, + "node_modules/jpeg-js": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/jpeg-js/-/jpeg-js-0.1.2.tgz", + "integrity": "sha512-CiRVjMKBUp6VYtGwzRjrdnro41yMwKGOWdP9ATXqmixdz2n7KHNwdqthTYSSbOKj/Ha79Gct1sA8ZQpse55TYA==", + "dev": true, + "license": "BSD-3-Clause" + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -13186,6 +14629,13 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", + "dev": true, + "license": "MIT" + }, "node_modules/jsdom": { "version": "16.7.0", "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.7.0.tgz", @@ -13324,8 +14774,7 @@ "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", "dev": true, - "license": "ISC", - "optional": true + "license": "ISC" }, "node_modules/json5": { "version": "2.2.3", @@ -13381,6 +14830,47 @@ "node": ">=0.10.0" } }, + "node_modules/jsprim": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", + "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.4.0", + "verror": "1.10.0" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/jsprim/node_modules/extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==", + "dev": true, + "engines": [ + "node >=0.6.0" + ], + "license": "MIT" + }, + "node_modules/jsprim/node_modules/verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", + "dev": true, + "engines": [ + "node >=0.6.0" + ], + "license": "MIT", + "dependencies": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, "node_modules/jsx-ast-utils": { "version": "3.3.5", "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", @@ -13557,6 +15047,46 @@ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", "license": "MIT" }, + "node_modules/load-bmfont": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/load-bmfont/-/load-bmfont-1.4.2.tgz", + "integrity": "sha512-qElWkmjW9Oq1F9EI5Gt7aD9zcdHb9spJCW1L/dmPf7KzCCEJxq8nhHz5eCgI9aMf7vrG/wyaCqdsI+Iy9ZTlog==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-equal": "0.0.1", + "mime": "^1.3.4", + "parse-bmfont-ascii": "^1.0.3", + "parse-bmfont-binary": "^1.0.5", + "parse-bmfont-xml": "^1.1.4", + "phin": "^3.7.1", + "xhr": "^2.0.1", + "xtend": "^4.0.0" + } + }, + "node_modules/load-bmfont/node_modules/buffer-equal": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-0.0.1.tgz", + "integrity": "sha512-RgSV6InVQ9ODPdLWJ5UAqBqJBOg370Nz6ZQtRzpt6nUjc8v0St97uJ4PYC6NztqIScrAXafKM3mZPMygSe1ggA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/load-bmfont/node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true, + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/loader-runner": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.1.tgz", @@ -13915,6 +15445,16 @@ "node": ">=4" } }, + "node_modules/min-document": { + "version": "2.19.2", + "resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.2.tgz", + "integrity": "sha512-8S5I8db/uZN8r9HSLFVWPdJCvYOejMcEC82VIzNUc6Zkklf/d1gg2psfE79/vyhWOj4+J8MtwmoOz3TmvaGu5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "dom-walk": "^0.1.0" + } + }, "node_modules/mini-css-extract-plugin": { "version": "2.9.4", "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.9.4.tgz", @@ -14114,6 +15654,42 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/native-run": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/native-run/-/native-run-2.0.1.tgz", + "integrity": "sha512-XfG1FBZLM50J10xH9361whJRC9SHZ0Bub4iNRhhI61C8Jv0e1ud19muex6sNKB51ibQNUJNuYn25MuYET/rE6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ionic/utils-fs": "^3.1.7", + "@ionic/utils-terminal": "^2.3.4", + "bplist-parser": "^0.3.2", + "debug": "^4.3.4", + "elementtree": "^0.1.7", + "ini": "^4.1.1", + "plist": "^3.1.0", + "split2": "^4.2.0", + "through2": "^4.0.2", + "tslib": "^2.6.2", + "yauzl": "^2.10.0" + }, + "bin": { + "native-run": "bin/native-run" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/native-run/node_modules/ini": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.3.tgz", + "integrity": "sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -14231,6 +15807,16 @@ "integrity": "sha512-7wfH4sLbt4M0gCDzGE6vzQBo0bfTKjU7Sfpqy/7gs1qBfYz2vEJH6vXcBKpO3+6Yu1telwd0t9HpyOoLEQQbIQ==", "license": "MIT" }, + "node_modules/oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "*" + } + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -14584,6 +16170,38 @@ "node": ">=6" } }, + "node_modules/parse-bmfont-ascii": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/parse-bmfont-ascii/-/parse-bmfont-ascii-1.0.6.tgz", + "integrity": "sha512-U4RrVsUFCleIOBsIGYOMKjn9PavsGOXxbvYGtMOEfnId0SVNsgehXh1DxUdVPLoxd5mvcEtvmKs2Mmf0Mpa1ZA==", + "dev": true, + "license": "MIT" + }, + "node_modules/parse-bmfont-binary": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/parse-bmfont-binary/-/parse-bmfont-binary-1.0.6.tgz", + "integrity": "sha512-GxmsRea0wdGdYthjuUeWTMWPqm2+FAd4GI8vCvhgJsFnoGhTrLhXDDupwTo7rXVAgaLIGoVHDZS9p/5XbSqeWA==", + "dev": true, + "license": "MIT" + }, + "node_modules/parse-bmfont-xml": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/parse-bmfont-xml/-/parse-bmfont-xml-1.1.6.tgz", + "integrity": "sha512-0cEliVMZEhrFDwMh4SxIyVJpqYoOWDJ9P895tFuS+XuNzI5UBmBk5U5O4KuJdTnZpSBI4LFA2+ZiJaiwfSwlMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "xml-parse-from-string": "^1.0.0", + "xml2js": "^0.5.0" + } + }, + "node_modules/parse-headers": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/parse-headers/-/parse-headers-2.0.6.tgz", + "integrity": "sha512-Tz11t3uKztEW5FEVZnj1ox8GKblWn+PvHY9TmJV5Mll2uHEwRdR/5Li1OlXoECjLYkApdhWy44ocONwXLiKO5A==", + "dev": true, + "license": "MIT" + }, "node_modules/parse-json": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", @@ -14602,6 +16220,29 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/parse-png": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/parse-png/-/parse-png-1.1.2.tgz", + "integrity": "sha512-Ge6gDV9T5zhkWHmjvnNiyhPTCIoY7W+FC7qWPtuL2lIGZAFxxqTRG/ouEXsH9qkw+HzYiPEU/tFcxOCEDTP1Yw==", + "dev": true, + "license": "MIT", + "dependencies": { + "pngjs": "^3.2.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/parse-png/node_modules/pngjs": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-3.4.0.tgz", + "integrity": "sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, "node_modules/parse5": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", @@ -14712,6 +16353,20 @@ "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==", "license": "MIT" }, + "node_modules/phin": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/phin/-/phin-3.7.1.tgz", + "integrity": "sha512-GEazpTWwTZaEQ9RhL7Nyz0WwqilbqgLahDM3D0hxWwmVDI52nXEybHqiN6/elwpkJBhcuj+WbBu+QfT0uhPGfQ==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", + "dev": true, + "license": "MIT", + "dependencies": { + "centra": "^2.7.0" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -14739,6 +16394,29 @@ "node": ">=0.10.0" } }, + "node_modules/pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "pinkie": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/pirates": { "version": "4.0.7", "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", @@ -14748,6 +16426,29 @@ "node": ">= 6" } }, + "node_modules/pixelmatch": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/pixelmatch/-/pixelmatch-4.0.2.tgz", + "integrity": "sha512-J8B6xqiO37sU/gkcMglv6h5Jbd9xNER7aHzpfRdNmV4IbQBzBpe4l9XmbG+xPF/znacgu2jfEw+wHffaq/YkXA==", + "dev": true, + "license": "ISC", + "dependencies": { + "pngjs": "^3.0.0" + }, + "bin": { + "pixelmatch": "bin/pixelmatch" + } + }, + "node_modules/pixelmatch/node_modules/pngjs": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-3.4.0.tgz", + "integrity": "sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, "node_modules/pkg-dir": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", @@ -14833,6 +16534,51 @@ "node": ">=10.4.0" } }, + "node_modules/png-to-ico": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/png-to-ico/-/png-to-ico-3.0.1.tgz", + "integrity": "sha512-S8BOAoaGd9gT5uaemQ62arIY3Jzco7Uc7LwUTqRyqJDTsKqOAiyfyN4dSdT0D+Zf8XvgztgpRbM5wnQd7EgYwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "^22.10.3", + "minimist": "^1.2.8", + "pngjs": "^7.0.0" + }, + "bin": { + "png-to-ico": "bin/cli.js" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/png-to-ico/node_modules/@types/node": { + "version": "22.19.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.3.tgz", + "integrity": "sha512-1N9SBnWYOJTrNZCdh/yJE+t910Y128BoyY+zBLWhL3r0TYzlTmFdXrPwHL9DyFZmlEXNQQolTZh3KHV31QDhyA==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/png-to-ico/node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/pngjs": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-7.0.0.tgz", + "integrity": "sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.19.0" + } + }, "node_modules/possible-typed-array-names": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", @@ -16169,6 +17915,16 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, "node_modules/process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", @@ -16736,6 +18492,16 @@ "pify": "^2.3.0" } }, + "node_modules/read-chunk": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/read-chunk/-/read-chunk-1.0.1.tgz", + "integrity": "sha512-5NLTTdX45dKFtG8CX5pKmvS9V5u9wBE+gkklN7xhDuhq3pA2I4O7ALfKxosCMcLHOhkxj6GNacZhfXtp5nlCdg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/read-config-file": { "version": "6.3.2", "resolved": "https://registry.npmjs.org/read-config-file/-/read-config-file-6.3.2.tgz", @@ -16963,6 +18729,89 @@ "strip-ansi": "^6.0.1" } }, + "node_modules/request": { + "version": "2.88.2", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", + "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", + "deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.5.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/request/node_modules/form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 0.12" + } + }, + "node_modules/request/node_modules/qs": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", + "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/request/node_modules/tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/request/node_modules/uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", + "dev": true, + "license": "MIT", + "bin": { + "uuid": "bin/uuid" + } + }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -16987,6 +18836,38 @@ "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", "license": "MIT" }, + "node_modules/resize-img": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/resize-img/-/resize-img-1.1.2.tgz", + "integrity": "sha512-/4nKUmuNPuM6gYTWad136ica81baOVjpesgv8FGaIvP0KWcbCWahOWBKaM4tFoM+aVcSA+qQDg28pcnIzFRpJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "bmp-js": "0.0.1", + "file-type": "^3.8.0", + "get-stream": "^2.0.0", + "jimp": "^0.2.21", + "jpeg-js": "^0.1.1", + "parse-png": "^1.1.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/resize-img/node_modules/get-stream": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-2.3.1.tgz", + "integrity": "sha512-AUGhbbemXxrZJRD5cDvKtQxLuYaIbNtDTK8YqupCI393Q2KSTreEsLUN3ZxAWFGiKTzL6nKuzfcIvieflUX9qA==", + "dev": true, + "license": "MIT", + "dependencies": { + "object-assign": "^4.0.1", + "pinkie-promise": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/resolve": { "version": "1.22.11", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", @@ -17769,6 +19650,64 @@ "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", "license": "ISC" }, + "node_modules/sharp": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz", + "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", + "dev": true, + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@img/colour": "^1.0.0", + "detect-libc": "^2.1.2", + "semver": "^7.7.3" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.34.5", + "@img/sharp-darwin-x64": "0.34.5", + "@img/sharp-libvips-darwin-arm64": "1.2.4", + "@img/sharp-libvips-darwin-x64": "1.2.4", + "@img/sharp-libvips-linux-arm": "1.2.4", + "@img/sharp-libvips-linux-arm64": "1.2.4", + "@img/sharp-libvips-linux-ppc64": "1.2.4", + "@img/sharp-libvips-linux-riscv64": "1.2.4", + "@img/sharp-libvips-linux-s390x": "1.2.4", + "@img/sharp-libvips-linux-x64": "1.2.4", + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", + "@img/sharp-libvips-linuxmusl-x64": "1.2.4", + "@img/sharp-linux-arm": "0.34.5", + "@img/sharp-linux-arm64": "0.34.5", + "@img/sharp-linux-ppc64": "0.34.5", + "@img/sharp-linux-riscv64": "0.34.5", + "@img/sharp-linux-s390x": "0.34.5", + "@img/sharp-linux-x64": "0.34.5", + "@img/sharp-linuxmusl-arm64": "0.34.5", + "@img/sharp-linuxmusl-x64": "0.34.5", + "@img/sharp-wasm32": "0.34.5", + "@img/sharp-win32-arm64": "0.34.5", + "@img/sharp-win32-ia32": "0.34.5", + "@img/sharp-win32-x64": "0.34.5" + } + }, + "node_modules/sharp/node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -17949,6 +19888,51 @@ "npm": ">= 3.0.0" } }, + "node_modules/socket.io-client": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.6.1.tgz", + "integrity": "sha512-5UswCV6hpaRsNg5kkEHVcbBIXEYoVbMQaHJBXJCyEQ+CiFPV1NIOY0XOFWG4XR4GZcB8Kn6AsRs/9cy9TbqVMQ==", + "license": "MIT", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.2", + "engine.io-client": "~6.4.0", + "socket.io-parser": "~4.2.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-client/node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/socket.io-parser": { + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.5.tgz", + "integrity": "sha512-bPMmpy/5WWKHea5Y/jYAP6k74A+hvmRCQaJuJB6I/ML5JZq/KfNieUVo/3Mh7SAqn7TyFdIo6wqYHInG1MU1bQ==", + "license": "MIT", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.4.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/sockjs": { "version": "0.3.24", "resolved": "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz", @@ -18067,6 +20051,16 @@ "wbuf": "^1.7.3" } }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 10.x" + } + }, "node_modules/sprintf-js": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", @@ -18107,6 +20101,32 @@ "nan": "^2.23.0" } }, + "node_modules/sshpk": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.18.0.tgz", + "integrity": "sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + }, + "bin": { + "sshpk-conv": "bin/sshpk-conv", + "sshpk-sign": "bin/sshpk-sign", + "sshpk-verify": "bin/sshpk-verify" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/stable": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/stable/-/stable-0.1.8.tgz", @@ -18273,6 +20293,29 @@ "node": ">= 0.4" } }, + "node_modules/stream-to": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/stream-to/-/stream-to-0.2.2.tgz", + "integrity": "sha512-Kg1BSDTwgGiVMtTCJNlo7kk/xzL33ZuZveEBRt6rXw+f1WLK/8kmz2NVCT/Qnv0JkV85JOHcLhD82mnXsR3kPw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/stream-to-buffer": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/stream-to-buffer/-/stream-to-buffer-0.1.0.tgz", + "integrity": "sha512-Da4WoKaZyu3nf+bIdIifh7IPkFjARBnBK+pYqn0EUJqksjV9afojjaCCHUemH30Jmu7T2qcKvlZm2ykN38uzaw==", + "dev": true, + "license": "MIT", + "dependencies": { + "stream-to": "~0.2.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/string_decoder": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", @@ -19254,12 +21297,29 @@ "integrity": "sha512-WKexMoJj3vEuK0yFEapj8y64V0A6xcuPuK9Gt1d0R+dzCSJc0lHqQytAbSB4cDAK0dWh4T0E2ETkoLE2WZ41OQ==", "license": "MIT" }, + "node_modules/through2": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz", + "integrity": "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "readable-stream": "3" + } + }, "node_modules/thunky": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", "license": "MIT" }, + "node_modules/tinycolor2": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.6.0.tgz", + "integrity": "sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==", + "dev": true, + "license": "MIT" + }, "node_modules/tinyglobby": { "version": "0.2.15", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", @@ -19332,6 +21392,23 @@ "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", "license": "BSD-3-Clause" }, + "node_modules/to-ico": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/to-ico/-/to-ico-1.1.5.tgz", + "integrity": "sha512-5kIh7m7bkIlqIESEZkL8gAMMzucXKfPe3hX2FoDY5HEAfD9OJU+Qh9b6Enp74w0qRcxVT5ejss66PHKqc3AVkg==", + "dev": true, + "license": "MIT", + "dependencies": { + "arrify": "^1.0.1", + "buffer-alloc": "^1.1.0", + "image-size": "^0.5.0", + "parse-png": "^1.0.0", + "resize-img": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -19481,6 +21558,19 @@ "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", "license": "0BSD" }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, "node_modules/tweetnacl": { "version": "0.14.5", "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", @@ -19737,6 +21827,16 @@ "integrity": "sha512-vRCqFv6UhXpWxZPyGDh/F3ZpNv8/qo7w6iufLpQg9aKnQ71qM4B5KiI7Mia9COcjEhrO9LueHpMYjYzsWH3OIg==", "license": "MIT" }, + "node_modules/untildify": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", + "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/upath": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", @@ -19796,6 +21896,19 @@ "requires-port": "^1.0.0" } }, + "node_modules/url-regex": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/url-regex/-/url-regex-3.2.0.tgz", + "integrity": "sha512-dQ9cJzMou5OKr6ZzfvwJkCq3rC72PNXhqz0v3EIhF4a3Np+ujr100AhUx2cKx5ei3iymoJpJrPB3sVSEMdqAeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ip-regex": "^1.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/utf8-byte-length": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/utf8-byte-length/-/utf8-byte-length-1.0.5.tgz", @@ -20797,12 +22910,56 @@ } } }, + "node_modules/xhr": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/xhr/-/xhr-2.6.0.tgz", + "integrity": "sha512-/eCGLb5rxjx5e3mF1A7s+pLlR6CGyqWN91fv1JgER5mVWg1MZmlhBvy9kjcsOdRk8RrIujotWyJamfyrp+WIcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "global": "~4.4.0", + "is-function": "^1.0.1", + "parse-headers": "^2.0.0", + "xtend": "^4.0.0" + } + }, "node_modules/xml-name-validator": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", "license": "Apache-2.0" }, + "node_modules/xml-parse-from-string": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/xml-parse-from-string/-/xml-parse-from-string-1.0.1.tgz", + "integrity": "sha512-ErcKwJTF54uRzzNMXq2X5sMIy88zJvfN2DmdoQvy7PAFJ+tPRU6ydWuOKNMyfmOjdyBQTFREi60s0Y0SyI0G0g==", + "dev": true, + "license": "MIT" + }, + "node_modules/xml2js": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", + "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/xml2js/node_modules/xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4.0" + } + }, "node_modules/xmlbuilder": { "version": "15.1.1", "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz", @@ -20819,6 +22976,24 @@ "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", "license": "MIT" }, + "node_modules/xmlhttprequest-ssl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz", + "integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4" + } + }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", diff --git a/package.json b/package.json index 1c89b12..13a5e0b 100644 --- a/package.json +++ b/package.json @@ -1,15 +1,28 @@ { "name": "easyshell", "version": "1.0.0", - "description": "高颜值远程Shell管理终端", + "description": "跨平台远程Shell管理终端 - 支持 Windows/Mac/Linux/Android", + "author": "EasyShell Team", "main": "main.js", + "homepage": "./", "scripts": { "start": "concurrently \"npm run react\" \"wait-on http://localhost:3000 && electron .\"", "react": "cross-env BROWSER=none react-scripts start", "build": "react-scripts build", "electron": "electron .", "pack": "electron-builder --dir", - "dist": "npm run build && electron-builder" + "dist": "npm run icons && npm run build && electron-builder", + "icons": "node scripts/generate-icons.js", + "server": "cd server && npm start", + "server:dev": "cd server && npm run dev", + "android": "npm run build && npx cap sync android && npx cap open android", + "android:run": "npm run build && npx cap sync android && npx cap run android", + "ios": "npm run build && npx cap sync ios && npx cap open ios", + "cap:init": "npx cap init EasyShell com.easyshell.app --web-dir=build", + "cap:add:android": "npx cap add android", + "cap:add:ios": "npx cap add ios", + "cap:sync": "npx cap sync", + "full:android": "npm run build && npm run cap:sync && npx cap open android" }, "build": { "appId": "com.easyshell.app", @@ -21,14 +34,49 @@ "build/**/*", "main.js", "preload.js", - "src/services/**/*" + "src/services/database.js", + "src/services/ssh.js", + "src/services/sftp.js", + "node_modules/**/*", + "!node_modules/.cache/**/*" + ], + "extraMetadata": { + "main": "main.js" + }, + "asar": true, + "asarUnpack": [ + "node_modules/ssh2/**/*", + "node_modules/cpu-features/**/*" ], "win": { "target": "nsis", "icon": "public/icon.ico" + }, + "mac": { + "target": "dmg", + "icon": "public/icon.icns" + }, + "linux": { + "target": "AppImage", + "icon": "public/icon.png" + }, + "nsis": { + "oneClick": false, + "allowToChangeInstallationDirectory": true, + "createDesktopShortcut": true, + "createStartMenuShortcut": true } }, "dependencies": { + "@capacitor/android": "^5.6.0", + "@capacitor/app": "^5.0.6", + "@capacitor/core": "^5.6.0", + "@capacitor/haptics": "^5.0.6", + "@capacitor/keyboard": "^5.0.6", + "@capacitor/status-bar": "^5.0.6", + "@xterm/addon-fit": "^0.10.0", + "@xterm/addon-web-links": "^0.11.0", + "@xterm/xterm": "^5.5.0", "electron-store": "^8.1.0", "framer-motion": "^10.16.16", "mysql2": "^3.6.5", @@ -36,28 +84,34 @@ "react-dom": "^18.2.0", "react-icons": "^4.12.0", "react-scripts": "5.0.1", + "socket.io-client": "^4.6.1", "sql.js": "^1.10.0", - "ssh2": "^1.15.0", - "@xterm/xterm": "^5.5.0", - "@xterm/addon-fit": "^0.10.0", - "@xterm/addon-web-links": "^0.11.0" + "ssh2": "^1.15.0" }, "devDependencies": { + "@capacitor/cli": "^5.6.0", "autoprefixer": "^10.4.16", "concurrently": "^8.2.2", "cross-env": "^10.1.0", "electron": "^28.0.0", "electron-builder": "^24.9.1", + "png-to-ico": "^3.0.1", "postcss": "^8.4.32", + "sharp": "^0.34.5", "tailwindcss": "^3.4.0", + "to-ico": "^1.1.5", "wait-on": "^7.2.0" }, "browserslist": { "production": [ - "last 1 electron version" + ">0.2%", + "not dead", + "not op_mini all" ], "development": [ - "last 1 electron version" + "last 1 chrome version", + "last 1 firefox version", + "last 1 safari version" ] } -} \ No newline at end of file +} diff --git a/preload.js b/preload.js index 53bf37c..7e8c7aa 100644 --- a/preload.js +++ b/preload.js @@ -75,5 +75,28 @@ contextBridge.exposeInMainWorld('electronAPI', { return () => ipcRenderer.removeAllListeners(channel); }, }, + + // SFTP 文件操作 + sftp: { + list: (hostConfig, remotePath) => ipcRenderer.invoke('sftp:list', { hostConfig, remotePath }), + download: (hostConfig, remotePath) => ipcRenderer.invoke('sftp:download', { hostConfig, remotePath }), + upload: (hostConfig, localPath, remotePath) => ipcRenderer.invoke('sftp:upload', { hostConfig, localPath, remotePath }), + delete: (hostConfig, remotePath) => ipcRenderer.invoke('sftp:delete', { hostConfig, remotePath }), + mkdir: (hostConfig, remotePath) => ipcRenderer.invoke('sftp:mkdir', { hostConfig, remotePath }), + rmdir: (hostConfig, remotePath) => ipcRenderer.invoke('sftp:rmdir', { hostConfig, remotePath }), + rename: (hostConfig, oldPath, newPath) => ipcRenderer.invoke('sftp:rename', { hostConfig, oldPath, newPath }), + writeFile: (hostConfig, remotePath, content) => ipcRenderer.invoke('sftp:writeFile', { hostConfig, remotePath, content }), + readFile: (hostConfig, remotePath) => ipcRenderer.invoke('sftp:readFile', { hostConfig, remotePath }), + stat: (hostConfig, remotePath) => ipcRenderer.invoke('sftp:stat', { hostConfig, remotePath }), + chmod: (hostConfig, remotePath, mode) => ipcRenderer.invoke('sftp:chmod', { hostConfig, remotePath, mode }), + chown: (hostConfig, remotePath, uid, gid) => ipcRenderer.invoke('sftp:chown', { hostConfig, remotePath, uid, gid }), + + // 传输进度事件 + onProgress: (callback) => { + const channel = 'sftp:progress'; + ipcRenderer.on(channel, (event, progress) => callback(progress)); + return () => ipcRenderer.removeAllListeners(channel); + }, + }, }); diff --git a/public/icon-256.png b/public/icon-256.png new file mode 100644 index 0000000000000000000000000000000000000000..78a856d6660996680b5b0927c76425b973811a5b GIT binary patch literal 14715 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 Cfa`Gp literal 0 HcmV?d00001 diff --git a/public/icon-512.png b/public/icon-512.png new file mode 100644 index 0000000000000000000000000000000000000000..2c8863411cea2e6c8faa03ac792172aaeba84fa2 GIT binary patch literal 34626 zcmcfpcRbbq`v;Cc&N=ovviGR0%%W_^NM%KcP-rSdDtnwGWEEN{nPo+0Mj1y&NTTfQ zQAjxU!5QD@(RlTGy+6O-?e_iW`$spS=kxJ+T-W`&U-#>}9;ZmN<42hp_!s~H%m&8} zn*#uc{t5?lG|-n-pYAQ_3;nfYHa7rZWT*UrfhSLS01yENhjlD{p8iOty(!Q$K)R@K z*~u$R_KJ1Q#)yB#Uc39zu0{(rllJ=;lAXK!n_1zZd-pyJT@==(3s!owO1*L z>X>A?K7rJcJW%F!RPmY3mCVPW1yGYJ+a5&W-E`*x zvJMeE4vQ&8fAENL>KFL%?c5=W($~Dj5wWHyjm3zGN}RzRUEdKBykpSx`_-VIPvEqL zk*g}!NVjR<=38T9Eqfb9wd{k~WUX8d=kefU5^!;6Gs0-cU_NyYnvUpb7|cKN{0;R9 z)>N(36VHy^{pQ=eJLZN)>&m)3?S)q-iZ8UwNz|4_=SM5wySlNfFtf>_r5mvujN~%V`ses zA9bvrbKq1UmKQQS4(hVjH^wgHol6M%n#-0DG>~WRUDKI&z*}=PZ+RL!+avFHQncqr z#=gw0*E#!gQ#en`_H-QXIkt8u$5xfN%Kzd)SJ$Xn&C{S|=7TLcOXSwPS@P7u+q1c~ z&X=}49Lf}kIfb^5Q-0(Xn+EB%kCNr@`r_Gjm^Z48$||alKeE;`6{`}?zQw#@zMj$W zP~$8@-`Y46H+8+_b|Vu9P3@FQGrOy&h?3Gxjtqq~6iZSMpH{-OV61;te#fnmFJ%$= z>Vyw4LX8l@^SKsD7t5#uX1_2xu(!zgvmf z*d|V_yoea}QeBd-*6C$;@(aT?r=Zpe#nZ8xXee!poRC;(*Qa}K9?4?%HOcoI7^6);gDu$T1OIufOGiKr+KQ z72r^LNA(Y$Uh#kbq&W6S^VFKPG>)@l(7pJS?&PjU8uF=^_jY{@3j8`&q0fdYNWwph z-9;XsEqxn{J6eW%K|C=Z>x^CrCsQfkC65@qP{k`6@|A|>i+*xexb}YFcmyZxS)g3y z*e!(=@6*rt2q&&I7FQ6hNd^Roy$s{AR;X87={?AoK`>qtdt$A;^TdXK&RUGOl*^XZ zo4S;&H@oXRV)BmM<>P$ey)e$sp=2(zV4)hNocFL&+FfFyM&2(3ol{Tlm*7}lU2OGQ zJkN1`M!e+Z(D8B`?)#oYN+t0J9~^uZiqE;JW92^q{B1<q|@DblO_vfFJQP01)IArzG9%nMZo8Gi&Mx>g*ElE1bNNdB* z+dQCL#H7nQha2~8R+%o2U3M_{;$5oHG#)yt+4!0kD_>83Mn2KP*xcf^5^})~n}9DK zZftg%BZ>SZNrfufBl;IDjPzmF3CPS=BR%F8;Kn&;mo!@x)@+`kzXhc zclaP{5%&G@7-}k}MJ+NhipD@*eBt(X#5V2~D?y8%jPz^S>W;oJ=zq{g;)A%B+L?`T zP|s6`zyS>rKg>&^#=Z|()%w;J!}mtbn>S2Z_7NmrU#Zt~FmHX~FDX-CcC}jg>=`Ez zEK0cc^t@X0fN9J9Q->Lr5{8Zq(Pn?7A*a=o+tK&td&67RZcs1n=k@7S7^tVbeM~}# zhx49J>r3ez*EYTG^_P92`n;k+H5Z zWb{{+ZE|bSK>4wWZ-t@@9m4Dz@_eW9wSZbtnYvjhITCYt#6%k zbVlL?y=CRZAwKWbXRt7Ic9Pn{W@1U!57rVoL4Bco6<&E9jU#-g7Hil9g z$AmBA(W}fhSh8ZtaXVeYfqG_t*)Z1pr>Vr=`S z(!75Fb~QyeK4Em0Imo?%T;a}!CG4JPn8GUjPHJO5B72pB4~Sn)k53v|-ACy7uCA%H za2soW+8`TFrDBhyr|;-iw3# z2!`&5YzZYm6`>;AJyN-6MGrb`zpx%hHRZMx}p0`!NbM5C|vIqK@Ne{l^ zyshU>7MdSEQu8$XQkSnGO#&;!)fTs(ezvuea;5K2Wk4d4pVU2>m&Sel zU-{6MUPzOR5MStI*2VdAl7yd%lW>BKN3$l;&Y%q{NFq= zSLFrt)G*k=*HPC`pE_+Og3j-4(Ma*G?sMw2U}#xhH5v&gPj;}U+C|a|AvJdPg~OO? zI-2HY+z#n*VL|I4*E$nJuo<0+0gp$!NCoX&@$4KC!ZD2U59Su94f$J_zif|$kq1-R zv0mKq+e>xlKZjFn?>M93csH%Pw?}C_W%5Zqwk?;wg7_e##BM4If%;k*@ghxo>y*qo z4U}C*oD|fi?|)r%@Zl!KYM2C=3kmsYto|~0!$fdrKm5h$%Bw7#ckaqahh*{f_#wkO z^vhn{w=``$uKHQ>aPE)Y{ zrM$CW@xzXf&Sw#L6W;59ws#&nmk6h1Vr)|?(4Qm-8}?t%;75sp6Z4TiGfSNf6psKT zPnJcRbJvDa7}iLPu^=G?Qz*mbe?IQ9mH@X+>v7%ta;ew)MxJRBP~Sy^h;GB!%fBK5 zXw(xNmg6a^Eo9EK%QfJzu(|0{H-Wz$h=($8=ewp8V1@(tW;O zFFc%uLL?CQJB1?TlMRzM0zSLrt1Qb`W3;fe&8noY8?v`1L{aPB+k{EE*E;@qGD&lS z(b0|lCd)PP{0 zqxTmacK?c1AT&m+KqH#o+Wl&gn|K>*WHVnPdke+Hz@R44;-Ce|@e)(K=~I*VSpQdH z-W*R@+_sGNtE9t1U{Uim_lkb3QBpE?>cDUd+5*$_UtTYTg7 z=ZX^jZF)3POP?@Pn;@aRBj70yqgOFkEoFKf2+y7IitRpW;cphV9%dX!dX8=8jk$4? zmUok#jV)zhI>N!LxT84s6?StE!BbiKH_MFQpu7BK^OZBc10SFBk)M1#{Zq=(p;9Zq zBv~2tmPFz3yORRV8l+Fp&b2IeyF48 zO5*7CM^xa4M8A&zk;|Lgs91FW%&8KM4MXxcdHl+R9mG)$dbB?|iZxE?nB;86>`|;0 z6FT7wvgUAk91lH}O3-r`{7Uut^i|=;GhU_TcEsNK&w;qVKsm`XWikS7xHNHLF8#A= z-|A+Wl?z_rO#SGk=<*v+iIqq3%BG@ik>o3tpEvw{W^kQve@0*(qL=Q)C@!!E=k&yP zm7|IK`2Bo%ziGegiI}sr03zzCvm-dFl4^n%wB36C%Nq@PvEf2L!wAFOJa(&S?~`j^ z=c2|JKR51iXLuRPMpx-x*?@jJ^cjsECzA{&jQEx^C*ST69|e#2k^K?IFEg)r6Rr0X z9$y;m)4N!Du>$@iT$+6J`cvY`SgiIY&xDrROCgeRUd1HiAN~f&79SDpjRXiwT3_@^ z7f4Ss6q7Qkg9}Stg_c*RRkrk0%m*YWAd4x(T1 zkmSl1(Z!}I*H1UT7iA_xm0y^lKWQ^F336uzihF^ojyvY}8G+C>$rzneH?=%R*CWs2 zs~+}LA9=%oD`b?b6me|~J zO`)Ex3Fr2`}mA&khAjZ!Nll%9k5 zsdh!Wq?yx5)UiWk67{LxV?9=5orD}shdlE!&U4E=VBBYBoTAYv)E0z0ANQ$9cR%6G ziznXCnhVEOwwrZyJcclnsnhe)8l>HyW*ciZ!u;@9+?#7^B6pCJzp_>xf+h78_3`Q{ z&L&){w<)sy&6))tXNve4>QjdtDVuH+I$ZoU%@3cdq6g_NeEpw%2T7#GcOo@j#pm#h zZ>qu>B<3HH-i$IU5E|Mvg7%bWli#CGLgyS*URsBu?2lI#w{0=4)})hAz`QB&JBs(c ze-d-95C(y-!k-}i;MfFvIkkhDP{6Ol`CvAPJX2-vCm~1PH*#Vs7gPBB-Xo?% zGw4Bhia9e{dTtvT-JB3ZEBrH-p&S#4qT#-CJZ)AvD4&P4vbd1`Z7tT9zz3-iE)I@% zml$;3!E-6i!cU)?5{_!VA4e0IY&Hx#_TK(OxHycwrrG{&+a`!h{I{(+_#RI#xyW#B z8@B_fbd%5^bw;z~hLewuvZmTFJZqTw10B@O^Q`wy9a&jWljVn2W;?0k5qpz=?1afU z%DFq|)8`xHO^50%0g~}3nYZ@ZAN_*^sMYhGsyA@4SJzK|>)bW`SE z=!RBeJ+Lq4(kMY5T5aEghR7Le{8`je>gBTubUuU(?h!K<^g31<&F`QhL4z_4lq#P15({1Bn3n0VNn7s~^?EVf6S-gn>b_h5W?de2?3@`?gUNYr%xh znpmpZ3&tu9SwK*q6WKwOKt2DO;a_m?b7$J;bS9gJ6b-%mrAv_H=D~FbqX|^Zq;K;T zB=8~bX~H4TN`pzKe-p^oT^;A9(-Z`Gu5EvIxDl1SIcWuWtj3qvOWs>Hpr#t!BVv#L zJ5tv8Lq5eY+4?j~4dA?6cUjAvp_cTtBUQ++_-rrQ=R-j4zy|+|Rp(GNXA)}Dq20Nm zOlBXgJjY(Tw@o6i34x-OqU-ol^7c1cPcp6EAKm3sr(@D4YzZBGSt`MxdDeLYfk{~d z7{D&lEni>h@7f83^dbz#K*q!BV2lRE>th*VZ8&u;9ZBwZ-15F z6xO_EcFHmZ;#t}1HR%%H6gnSqNy#V365Bm18Y86Awv!FyTjx_9>z4iz7u6o`92eG_ zFE%)IUd@);UE4T0J?MIN;0FuFKnVM(-|Z{TiM;(F!IK=W``6JuyqL0n5Nn{7#J-xw z-$|$m;RFRMr(mZ_Y#`LRZX7e^y!8+3fWk?hJ7-oZ&*9}?&ntaB^l}ZqDKLGiodFc$ zPI2|$cm$EHGzfPK{dD%`pTJc9$kY9y_5}30eN_c<07E=b4!>4G3r>4m?k9wCVos{x z{GUBVsd-7>Mp-o6s>+lG-bEGwJp%(9nvhqy1AK0~D#oAhTpIhMtDvKceA<-_KI{l}yp7x0jb+%!$t<^8PG>`_j3pm~UV zzGbDEqXErWLg&OoSbZkf(4=cQaS&lTP`E#&w%D(@+7tUQ!^&CxR%_%P%@%&o|Wry9)yw` zNUZ!B7(VVQEZ`dqpozx&3Dh9MxaWTk!6TRg9m05}P*n~>71n0gn6(hVEm%j5qrQhI zUSN)HPBRkHFgh!njJ+g%xB9DO^7e!2D~lU<#-jh6Y^=s_+v+>QZ*sn18Ie` zV>UC^chc`!j~>}Gvc-x#B<{U~fZGFzJ*0Km-RkdFXXV}*VnXuEB_WRXYrA55y{qrD z$Ch{wQa>k@e3=FWSO2(hH*G9;W#(xr`C~)GbhYa&pbQm?NL%;sIC&b z;~Sfsfk0c9fZzo(Ws@^cxr0VsDmPGK3%$=cdJpmbz323Ki-@Xut+R4k>hdV^0hH7WvY2T5;_e_OhdrPG8;h?UFBAkE_2|{@U<8Q&7I{vLoN#!$ zcAYD%%(!!S>^BTOk)dVBpWkrsx0B%@j9HW@-9^fPN_{Ot84P2Hr)2rqMo)P3$e zuG5U=xO0B9cSCab{xjZ+ze%-vRa;lh!#mI}6k}S;W#x&0fq1D<@^x8|#zneB3ygve z-o@h--zp3ZBPW6zo-rO5P7v#d<&~QB6jo=Oi0bRddHlJ zjfi~zxgl$^_=hFkXh#u9+o$}rBWgAE8x`=nMl~lWxHNFZa+`nbY}gL%jkzF0?2flM#l5%HyZF5*GB{PjSF8o0*a12)f|t-u3H&vB>L<$Bo%UL@%#&{74Q`Q)`!hB zd12(`Bqcb;9G|oc^be43iH?wtshlu$5Bbf(x80=2o_eFWTx!JI4+sM&5EyJ zY5nkV(72iCvjZrF3R+ixIZlVIl)w`kgr&0XU*;JAb>;&B>*s`@LSic9XJ^zgN3+h# z?+iDc&3nFfX5coyC-(ys;44SlUyOx)EENw+Ms+E43>cRHnDyPJ#tbxi5u+w zX^~M^pfK?gNzFcH=s*-c_Olt(@HMzICU;_7+nv<)6o{~sz5s182qb5r+{)1%(MDbj zGT!Bpn;4+5xzkf%l@y9F%Dge6E?NM%Egv&d5ZiQ@j}n6IRA9PrVBZ2PaC$$nea6!D z^gda6sm!B;wzGv@YO`&=!?*fl2heu*=Hi5>qT4I%=9hh~X*B%|Scq%E8e7ZFn?HX) z@<>rP%m5J4mx=^>7CwBlz zF?AS%8d&3#=)sYZ089L`o|i!9aD=f@l+o-kuSEpcN3$MvMe(kQ3G-3%rp1Ao;qaBn z6e;M3$gf=!t-M;~mC3BHC$`uD&S1#Y@t?AB>EeaLQtL;r;@|o0p zGkve{ITr~52OBtLcJLIU;4pdg>W8p5PZZST9=Tm{o>9hUzImM+oDopO$10d=SaJGc zk2}uDb0uCTDr)5|Ph+ zTFi=D^KPdH_i-BxV5)x965vSKF!|9Gi;TGvvUbC0W>}M0i0wMm;;(vDq!?({MnW*7 z6V6p#_e?z|0!?_v&_YGikD>rB7X26r#i4^DkPO2T7r((UX{M(14z+uvEVx&S&uNvX zqD}!H_jgk;PMVV6-!6_<2XoShXEAJVhEM}5WlX&`QNjW;rMu9zy-xJJtgDL_-{2q& zhsDt_Q+Ol`4%VB{25%n6p4>(rBAl_cGXF+Q9X*JzKqthTtkp`!{rq`UQcQ++&CaW0 zh3Kw-!(5@D)|!O_oE}F4T=G&-XYY4KfVa1c-awNwRkaY9#WVLO*HTljdxmuYkRWkoRpUo`+*Su#p~DH(L4RHE zF!guEwUOnDZ@^bmejAGJfI6rQ!}b9Y=AX|tRbSCUfOUUg;xxHM8A_=^^6q59ooT*6 z8t`M|v;S^Nw+%z~6={LkjUHVHwB-me@Qw;Y{xILm2}mm!YHI;*#8mt$r zKpZ`~9i(xPVm8dEW~3*HSN*a(z*sxX6-JmJtQ~~=tk@y?;rO#J7t4WA6BlNLT`F`x z?kJM#@h&J<9oRum7z)&RzdP@=z0Ck>)iJxZy~?qLV5};<8pRrsJil1FyKavpyxV^_ zM3m z_b2Kx7etA4pu4lHK&J_&wvr|B%&dRcw;!-!s+puYjD@7f(b+mTc%x_C8`Ct_cXg@I z3(P?rBie+5T7XGc++EdUYGeYoRYPpntqCjYT?c!pic%F5FQUbY?TD|sp>IO?!CI<{ z=$&9*$dS5~m3#V_S$7c2W2*=SjaM;JeK2)xy|m$0B84f>_mB`$_%6b<#B)0C?V?hm zjXO$RCL)rUNL03(N2Mi^t0kE({P#EODQxb{SRL7ri5}FHJvhT4T*mtGU&Z+Sy}^r=P50R={(reaK7GvJ8>D)C zZD)s9c6J!Vcn16*J3RA}8d%oJPPOXx`Ap!m}T zLOcp+qbGk06#xQI6ten7Jc&LEOFKCp%5)0b40URdEMfwNqk66aq?__am9|9gvtRDfX|JKh2*VG0`mWs!M? zx|8pJ$4eUak7xy;R+z#i)$z7)D$=1MAOW z`>7Qt$+r$^*ZhqSLy*QwN-9`o1J9xOtotoXVdxFa6>GpE9sb)^#J`^+0!Zl`L_oq? z!jTamg~T0pD1tDAxa0uq-^)&V@|#$=|3fUw5EC5u?@Bh-;81QT)(e=?kwa0>7`5I1 z!hJOxkPIb%lZF6d_yXp8i)l0RKAI zPPEun!=WnUZyLk;*X2utq?7dy>d;=$6Z>z%X0D$8J)s2{Yl<$~DNFv>gau|SWZ49N z*f0M3E5=5V5S{OcH1y|U^6YVy+WQZ|lJ;5ioqKyC3L1{g&D0n6f10yA=a)I3D;H6x{Zq98V9!jEhWwEJ{+rw(oMhRpmuJKc2D4ou{%S^oc+v*!OcC-*OpQ_|MoD;=cBrmG;0jnrEB zjG6Txa1HKk(O8R6VgH>Pv_Q$}U;J1t0G;WlO5n%`V{aR#ewVd%ucSeOR5(@~nC&FV zzgOFd>}W{982lGoy_}er`+in#|6PfXQqAq4^EH>3zZ^ZikGYTx<)2qHJ9bFb=urbYiR=Hz9?^?1ZMOeXmM8VtxnD*1fAO*7rU$NrV>NKmX509Olo^dis}r^w|8va(68kEt)TON+XAh}pEcwAv0I?tQ~G!EdYC zNJ=Wpip+n+d_gu3`YopouLNxYA#_5N(nbH*=UFGAcVRLWRMj z(L6%)wr>41sl75UtZ8@0U$Tfy?G%e5Rt6qeniStPI;0aYd8(xM(1Ws!l>E$|8j-k^qvhQ)C1RK2WVIf- z?l<6Id!#LT&z$ZW0*1JQ_(}6Gg%s8p>O?nxhrhxILOc}ZKj-95ke!T7Rd`gHTN;_9 z6cx4XRQ>ze*7_T!CPsf zhMm$Aqd$kz+6Wu%@~s##ivKznk%Tp-x>YdHX!>0|=yZ0`b;p;ydc7sKC0y|v1vJku z+>;eecJ+`Vy3_MGYEWuPfTTJAT&?NIg1;oNSzgxtwcl_{L#lAOf_42ZC(5mSvPtBze%p8=o|$fWFjXh z&kNn@<3XXnBraV31GQXby3S89Pu~9F-coW!-NwpI#O+hqgcod(&#?^QYKP%ui|>sn zgB8dIKPP>MpO%K5V(uK|rsx#_T)i3p04&M!h}LpwA2g#^Ev(glTZO!LabEv|&70em zuZkCF7 zf7l5`u)v0E0L6Kr&g)NdP!I-mthiR|nqh_Kw06L>QgLQn4|S1T z@PuK>S1x}p+6+ByZzQG7>UeEM+&zmEg7c_{_8zFFN7GmVV|s9l)TOrvyTU{5P#rGg zQXY442^tI4F-;7rHQhk;!*mNKw7ssyU9$&jRIBTN0#^Vu5a1DRG?#iOqd1-ej$0E< zcLToLFpQL^%FLBVdBXCI9N)_-i z0ZbhLSvO*8j!p|}Ot^C1lxDdwO-&~VZ?6fbNrz4@ywhL?MS763LO?1C0pPSCbfON6 z)}3|;RHSxz6)lr@h^^vU*tN!@YkrP%R3*I?8G3clz#A&m>&CvBjJQfesj-k5qFmHu z>}3SgUcf3c)|hZ}rIhui$hS&1(hc3 zfb|*>nOW&lT22=>!!`Lg>4n7Svn{eYO8ny@NP{Xu36gx~SG2$wK*2;^D#`o@>nXIy zVr#IFAp8g35klEKJ%yuul--wUPStLNL8X8rrF8_^g9fsPeZZqz8>e@t?7#<$ruo9N*?->MQ~Vxth2gPHA^+K;*_%7qp2R zEi;9bC8QYYd>$~nG)=yl?)2S2?H2f|{zIg0fz@Zqqk<*4Cs3X0#kFwEh!&++q7B(3 z=qqj%DuO8L#j)BTRIt6xZ+XC@W_s99o;uLWHlYWk>p7}Z;JL%5aBkq08~VGve3x`O z-&3}+!ka;2wYXw&1vz?bj}|FbjupcKBNBgwmfU092H%c!RT!D7FPP!9f~jTG2X4ln zh!xVRY(a9@$TOT@~9Kl z>{JR<=bLC3YaV{|U6VZ{0=(&F78xn7pi%%o=J)TQWHirFlCZygxd6|!<|E|}vi zhjSIOi8?mJ)HV{Zebn$J1FR6@wusOpksoTO9Fk`anvxEla&WMc|$PE+@k*DC4 zjurIJ@sjLenA1>T-%fhrF4$7byn~fNsaY& zF;eHE$A&WNIcwvY2PJ(rMAbI>(4ExqPEkiN3cUkBk_hHl7=U+yL;VKGux9r>mRUu* zsVt+1VJ4G}UGVG=yWKvXE0H;)7w>5&xbGBTUGZ6;jVt9|5?H$*7RD72FjU3Us`p+|pO zaF_=VHM8WYik@H|8kb~FN3xn(>hwnWK_V$qSp7t`MoW)4lwOEfy&ZnYqlMFh^#xv$ zo#v2ZUF1FuTNwOvOZ-#O6!tn6#(gr}$C)@{m@i$u^`lM%+Btb==Z^iFtdgL}y92;> zTA9$k>ocZAMZ_armvS!{y%({wWf>@8(5GWnqk57o2tNaPZ+klgf+EC_8xR-$DqR+g zX5b2YL5r?-H66@Dj;@7E6f-PXr$*)6DVnzJO-o6EXLd@N8h;wP9qEYMLl?m+DzaDY z9;7)g7<~}2b3UX-)vq#345Zjp+Zvh33)c6Al52+%RM3O~0SI*P#4T-1sR^qfd<*UH zi|cBs|LR+siipU(;ilrDK;ty$*~z!>nhqXM_rqX5cu(0^?Mgme-p^6elpSfOoS*ri z=i2k>2ePGcuBuHJ+GRyuf_QBh_qEubGU(0NSHfkjesWJ?2JA(re;6l|`R2a&{LinK zwWdE8xcgoxarf7UJ+TKKS>R+&e?tF{sKTohYY%8VzJl^c^^=v-n#&%LsY;QU7>7hbxFyO=QAxY$KH zy|{73mHeV7htD^xHTMhSN#Ars>XR*P>Cdn8K4Lb@%*dFsJfO{4@qXX!++$T4jE`-p znz&w>83!pgO&vU8?6#+As@vkGm8|}y?g+Cwj7VnUkH|2NlsIXO_9-SruqB`1EDOw| zhLx3XzK@jx8~2T0un`Z2w3CWxUEo1S9xV63;@;OAFH>uloH!!hc8o0B%p~+ZD09BU zv%q|{QTeV!t?%xom&uK>$;JVL9NAfHqaVA)UNo4@vz19nKfQ7q#h0h^l9;1^q`fA} z@-q*!^ZgUg4mByYsB#cOpxEx7)jO%Ch(RR%;o~KlzsB#_5@R`NgfQ%2$=KyB~|^<#d@m z*!pz%#C|ywvrqV-?&8FW;U2jYb|}8SyuAnSdRok#GyIgn#d}ab>(#fWhhM5A%$rqu zUd5U7bDKFgXc*=u=^2!jO6K~}h@JGbIOi)N_Ru)35>A6?=h%bMF00%fXSuh4rty+_ zA{!L|Ejg6#)=mdzB`ExmHZdd+lODnXOJ}{$d6*&W-Y!E=m>$!G1tT(3V}A-(n$We} zsF&1lhraT%s9K&1<=-Yes(*S~X8#W-IZ?Nd*GoP)7{rIUw3PS=+Qb(~w0&lV1ob_c zwnM6A0~SV_oC}}XvbkJ3cV}}MmKk*4ve$p3kRwzFCxwazxP7em=d*KbE`qy z_dFF(4%o^+zw_WHYn-~9KJ&pg>R2ADGgbZM3Kboz zHN4+TyZVB39m*Q^p&DLfDoWT`( zUCzzv7Gtpe(VY?UaG@ZKcFQ?9$2e<~;YnZKkJtO2bma{VxIOur8{plpp}ncNu3i?q zoW1z0dZFgwXj#&7)uWb`6H5I#X4x!<6Exr(*10S5bAK|VJ98C6xGqmr5) zm%sV-f~@Cll-+`p5z#dg-D}P;;>nx5J?YRHp^VB%o6MG)QDj?_IziWkP2NhdNLBH? zB{_xXJt3^VcF#gsZ7tYhQGKmLk>l+uGMlu}?SH&xu3Pb`Q2&MOtwq$IOWrvPJUl=f3X^-7D_$nW@c3zXycbm-$}m*HI-$y@eFlXF`I7O|_H z7qS*r)+nFC8I$HiZ=-UrES6fTOp0VG%cj42qx#T%z=Xbu{V7FVyqCPiN&D`i#i^TH z11~Y9c_7G^=`z$d3WVB5KSwo=ToB+ebg zPVOWbLe=zxi=X(^Hk#$lI>S^X4yme(VwpW3*+oQ(FKaY?joIY_H#cm#7bovVEm;8PaSTF$INRU)mJU$%p(3Xx z=xwl+Y8E=XzTOcU#4*ImtMcmmL@3zp|G}^=M z&<7eKkH)BqI?9P9n5Ku`IuzCJmX@<>SS;Cjo>OF`FdBw9OYKN5fE189e)<`mnfK(D zQe3B~$n9(S9}F?_&4XoY*UQ&$B{#=S3~hJZs3rDA1rne<;Q3M9?VCBn<|jrNIV-kv zLvA(Ok9SLPf&1W`)`@oY)XWd>#2(rnjJ|Iq9%%>5*H_4VD6r&vP8O)|ZGkfB76;rC~u?0)L+e|bSOJEwA6dsl73QXqGS3kX^~BPgggwuVV09 z9`#Z|r#IjwGN+A1o3(Uj+Nx(0@Hi7}@0k16gYf;U(~84AdW?(T(|P*{A=mA!ERM_v z7|JFYE>WMCI#e3J`^+Htk?80osJ(aygYJPPdO{~P{5{*Ff>Ti)3)j9uYvYpCYHOtj z)jtwc7uIw!rp+fmlO%qS6%+Td+L7P$N4U?3xDjqnT#^xM`)tCo9Qz<>Gb?qMb~>u} za5@%uJge&!E~ZO~950{i_`~mgft_yd*Dk3O{WLk*Io$`Mu6ZFsOVmJ>zaS(xl_C26 zJ&3@7@D_P%=>F5s{}%u#deRyH;7SX78;#rWeo0kihv2y1s zm+JSTMqPPA2XhsxD-*2yE^uEloC}0BuZbz94*XogU4-?#(8eW|vQ&;|}2kP!hyyuhhaqfmY_{!4LV>|91sfj&p=HO;Vs;pond2c4;oxK4*D{TaoV zl?Ijrp~IH_CGpptxy}4$Ee$`oZ~~D7ulwo2tQ#0ZzrLN4!X^ zOo+1WwKzV!+faT!qp`Y}Bf(zpUaQztM4Kl9kX@;nIDpE`uUkMc5X=J8`p$al^$9Xd zFr`&Pw1zgPdsf6{mq4Es7j?W9QV)_AB6ctrQFwJv?Nz=oPkGLQA!q_0`!(=% zx#HCH!uGyZZWf)H!Z-Ha-{*M1k7pC#?mo1xO8jh8B_-u8&~&le5Y!DY&;gM>aA+n6 z&01blN?4IQcNt)$FQV5r%XHgztIHhAp>?!_Jz~~l1oZh{y9>F&_b*b_SN-IHtBT`$ z{`EV43tGAkOo2LmQ7@iU#v0lkq6cv{2kzd#F-Qd>xPVA(eTJeW2hcu34K1)UYIcZ_ z#ejrZHL5pvxLL%cXEv*@80-*oFoIck7=M;X7Oo>O3s#VOCgJ5iy;O1(7OEIr2o zzVVikv3c6dQ?Z`kS@G53AFEb^V1}V5G5hDWZi_>0AXPAvrho)0JVlV*jAp#~l~%2p zcCS9}br5@TsXl0*%ZTyjK_ki13uHM=z2nj?C?%>7nI_F|N z$3ZQ=%}(U%EGr)BkP;RiOc(@DiqF!5=yJ2EHz6?2tu-2Cpo4w6VnClaX8ez1Z9F8hmJ%4GfH6BP|DJ114!OqPysP{m320PE%@?)OWkrk@Izb9Y4mE7ewNX1L#Iwfr>+Kzl(Vc7xDMM!@mA zdDQ8mu-$zEjm-Wuj6@q6B+*lgj`%VmB_mp1Q-4rSRiF9lc5aCWJLgHc^!$w3hflhW zM_%#dcyP*aSMf(#aP+|4a`g@vU6YP>wGQPXjQl6cxXWSH1seGZzakBaj=^9HgA|s5 z?#?X$ugK2o%n8qWJ8#DDwW2`+G=ph6JI~5v1#))5MDD}t2(lcnw?qnh4sKpl51}WP z>2Yis4_V?uhCf1@LX3Fc`!h-YF)7}_p$Xq_h?VA}GK!^9{~Fd__~=CAy~p`UjXFtGX_hi-?nX`Q=gG-M*KQd@RM zCc|7gF@hY8F--O~HZpV?H$y*SNzjM#Qo?73vK`XW(5VM#!G%ZWr|!{#nD|)^RU0Zy z|4CYphLqHyqKcNo=^d~9u{rOGw{ySHgV$kQQp3z3qljAjHZ?SZ_}#VDF4{UDoi=0z z=9nDkgLyYEw99kGGF_sEf-N$fs`vqXAQ{HJm_DW#K1*)hkxF%Yw)i;gv( zQJnXF@mik>^SMOqp|wKt0Uq%D=PNUDSj(T+J?g;Q@5s+L#kDb9FlTCjLV06lF>-^v zQOd-2wNTPP(v;WJjQDt>rClBXpM0n4xB%~oUz~@yZ^ zNnY91!7>rGD_xxwlz&Ktk;FqnYdiH8PV2hymZpApRz6`f%l5t_={PSh03!}NP^$3( z6eS_a|DsT?3J z-H-hw9ZAZ?x0R3VZwS^guPJ89_E>v|h1KcG#(+XJ2#{Oj*% z%x3u4N|K`wro5XrnGs&qnGqhjc|XQg&A|GqhQU?l?c8E#_LCg)>AEoR1%T?MOfJ$F z%Jj&Fvt)#~-C_Rnu%B7fmU$G0aFU4Iv_bfpz^vR%sv#1+f(aPjv zFYHbVPvz}pM1TCgBBZwXaXYLbyuYlGkhnfhi#Z|zj7N=gqmfjHp*;-|)8Cx!uuexi zM(C!_I%j{G+n@(#tw4kpN=EF^WgOIsE}3#y1Yooilpm2k-XKBO25fEhdY|mqEm{_| zXe=cS7@*twB6~oI&#Ld9=yuzrt)G(?FIE8m7OpqDn+?F=(Y|gNnU;X z|5Wzf;Z%qJ-=9N95z2}ZC1mfB(Gj6+j+MQ)5VGQsk&#q(_TDRd9x}6%Eo4PD*@xqt z=W{f^-{0@KuIIX*{_scV+~fVa-}`>Q@8dgNMh$u#v#-+O6XAna^-qiQ-h2byJ`G-= zdn>1?(mDFQj>4~BOcZpBSZ? z83wNC`Rms0-d%1NF)rUd;$9vZaE;t_BGM)-yowU}wVCRmO-u>Awgku$mD3;BJq38) zEYQ}oHalEoa(Bq8M4p~)=^C`@S2JEb8wJg-k9<1!a3*(Pv)!{NYUquBJX^DxXrHGPJzK z-*wr$?ZxDquZ|PfyIaNEXikJnuDT8PM2)Cby!y~Hrl#3?o2@5B{)vqlD^q#!f?2pX zq$MTL1@KsefC&(zlHWI;WNGuzczO8vs_%w9Tx%8n%TRQ?Rqh~vAM-x&^ny+esM5LM>eKF7QA@4#C}a02Y6F!2t4Wo{fFG(4EauV{TU7U8x3#Y=M-gE-t$&; z+q(aBVozzsXufR_@ql?h;py{&CR=L3*pl=f6rM)3V9-Z(J=S~WpwYee6d-#Q;ODW3 zbuqBF=yA<{Tz-C+F|8UW6`4Nc4%u@Krw4`m7?nt;7#>`*=M!&tmbn8lt_ymdL^5F; z+g})h9n6kz0;V8T%t`*XH8 z0mX+DAiRLg8-f@3vz@8Dcnn?>mzEtEKWRz;?;|hiT}5TtNcrHft5cxSo=gk!vd@+W zA(9U>2!V@}<|>@G<(>=5Pzu8ANAJkr;E#LHT$ItbI)&ofkMIJIQX0b-5O{ysK|v;> za6Z2#D{n62?GS1~e6qGTXfTV}VZ7|<(4O>d~r${uacRU77O`A1@ zSxXGcknSyrFm0uu+Do0_42H6wn87h9brYtx*!N5n0wfoKiDZB*Sw8*O6LS@=JKkDM zGL!;p_Q;3w)=4stH`(ttt>2z5vl{As9(;ShbWd$%wt4@~Ol#$jJ5{j`IVYbiG-COq zc86G0W!1@>BEg-T=#l;13e6p^IYuM+MrMPp<(J@$~?MzpPxY zs`U%z$*4=+DQdE6o(B|qV-K9&hFP6AuF7xftC-OvZB-plnnfZ=g*T9g!4gJ+=qm7l zreThU4BhhwrZHwSI zcC$Zql&|&^l+AnG#HW?)D^G83*mB*;IT*_olK5hE5E#+Re|d{;+aAul^{;%ZoEPi9C6@uiw@ zVaV?+3+PsT0_#b^%!XfR+pKF0`ZS0!_zC2;*pF9~cXl_ULLF@x?cAbt%I;F5zlPpE zK7R4KLm7YoNy)(c#Q~|7BHJ)F`}B#h$I*j4;p&~7nI9BV>8b~X*DS=&wEwz`>({X2$iBKZA5XpTyJ7K_y`-1Bz#9T{N z!w|P4wh|_#B+OL9k=0oD%6h?Fh34cVUFK|A&CFanyoyvBGxi`D-K4mO$npN^^BC59 z2vgd6!PdW$F&!-=uVS5+Rr@R~g3D5t*9Tsx5y*M~(dL7767q5Znh#9|gp-l6o%C6)}KM8;e z>tme{I(Y55p%0Ig#;l-9SDQUEs%tQTYtN`*PsSA46v=1NFml^Fy%<)C<#J3U7{T1d z+Wo#nu)_h{io>kRpn0P5OzUL4Cafb&hSI#3^=kOFC%la8w$UIcT+$GI9H3VDf@02B zUi<3|i5s6~DBe9`Ey8K1s8N$TppdsslD63_%sV(S%c(VSbO2_`+}lBPG_IZ;iMeI% zQQF?sc@Q&MkuPG#x5ONoNj$%ac)5^C#LC8}r-OFi#8lM4@qvY1Xf@yw0Qp-U9S6lk zY=I8aW7^yG1Ql04%~r;st3DwD&b;kc@74w2U1^m9%ySLvhH zhI}kwWkw{JDs+YGK_d3JDt}UoYF0Lu&3pa8LDR9a6feO|ZDrsak zbUqfS);5dRl^E?C87icT>kP4tu`YVuDd<@ck)N);sd%7xdcm!>@F{&noHY(CA}{o+ zQbDX*cHTh6H-#v+!=d)w8G-T_ED_u@%5>R!`NbamRaC{9ym*Cqx7=Ar&A(8yp%wU; zi5$`QI8fxaL7xzTD^g)^`m(zTt7_l?;7A7cM4y);?;05swQRRR-?E>%5y4r2N7@j5 zPXDhqpR^=Bqzt()n2)O~qOXOq(#ri>in^O#5dPlQN)3CvY-gxw!`XY+Y$GW8YxM^{ zu`zYoE?w}3QErlMoz)wK-?KF#Ik_mTUefSwDdwko;={#?$Ptzgmq~LN>0mUeT8Y3(y z_d^z2l(tKLy{^3YXpcr?if(xPn3~HkU0W_)X|qdOFj%X+Er*WsL7hULkmD^)i!a%` z#8fC)&`W6b&(xTShc*`lt=gc>Y}z8na(O8CYV?86DFLt}i~F}lm=xfVO!GeI43&pZ z%EPJ9B{6Ty<_Y0Jrjpf{TB>mZUrMrEsMW&JQIfP2#MVD3KByqXvDZt>hW?76loOLZEf z+IU==hnlxwGDeiVG3Um2UIJO(#$P|;8pobg{>V`VA{yK1Hol!3T*xJ;k$ znB*3!BO5+Gg+NeY5`2i}F2(Fv9kBgTg}%2nPV5Tz>Bn~B^zc@QASU)=47cP&nB(cL zQoJ+d@{@FZE`hE4uz$ebLHQ>0c!2~$xX{b2hAk|JRmsT;W6fa8P0^H>iEHdxiusn> zbHX;0t?p^T6O)%$Tgr#0M5*~B-`<__Q1gujUQbWIw$@EpO5^TQ^kO~^4W(saVRtr% z3MD(Mv_>ebg9bJ&@I=Eam+WwZGZoL**S3M|#6BM^5y@p4QM1(-Wa@}flXAV%A@dXF zYW}8d1AMUojXHF+1tv0X{4TX)FTXMBws+3IpEV` z1z6deB^CCFl}hWvZ9X*T%w_|!OPMq$kwr}Y|OmuEZJ35-3 zEg!nn%|2-`dBz^Zs?abL&v&9-ud(VAlb%&+Xi>nbsWB!Q!x2WgT^G%iH;G)dytTnk zP8%fbQ{DAN3VN?Z%43l_0O4nv;Q|@}g_R@sG6}f%(BzJA+YmerXo>mPwlT+z-MT{z z+DplJYc|T2iQ%!xoktT_jSC^pPZq)eJ4$n&mTCfZg@+7}xT(`5fq>U+VW#YOG>U?_ z+FqrmC$JCorrGibTB8~24?9RDFDiaEWXu0>l^`wa!gkC?iHAkDwMIeW1Sa{0_L4?6 z*`d9okOaRDeH zT;pV2f8)GVD-&#W0i3eI9sq3pJOOb0_&9Tje!jGZ1#E`&moWAMCZ>gd=PR|v!Qv7G zAv|fd)GQl{Eirt(1%ctVSR!|iCC+amC;+D%1nAIXOWllJN#m&iA+V|FyqQzJ z2)L$_x33Ky1{XlcfeW;X8$Sm`IR5ny;YZsi(huy9BZla)eMGVcehHAdGVt2<7cJN+ z4+?l%*8G<@jTX7H^NR2@#C2>z+_fQGOFX}}f2|uWe(!^!1N&XRi0>(ZJoxzM6@xIa zVvq#vA2UL_xI(TegApZ@;YySdn_+<7@E!f|UU$V?PuJ|{>-C>IlvuP0UHa}5GH^CZ z?^gFY(t!TO_u`{V;}`Y7`@Rxt%W>k0^OW3fer28{J*em2@{-w>@~*so@kwi4*yCtucCk6@Q!>* zN|!#TB-Z`$Srfnn=8D=?R<0Tcoi!C(RGD*fu;>X9VQze6vJ#0rkjX}THv~%pEr0n< zLgqri?zuk|lwf`56fY5`@v8GA7<;T=SYT0NeA@Y9fy&@f>}X=((&64yF*Xjw`c*V{ zvs9}$*R9@8hfhKmrryleTarV4!^)IQifI_pXV?}H%}3Rh;CeJ?is$vv$C(6u#DE;d zCk$KZ@A7l-E>Q3G7fxn7n<}!5Pl@oK0wyZSo5gx#_UUusM}wZE_vOLt*BBG(%h@V7 zwtDkp5h9*LVVq3Z{h~9>^2+S}gyS5ck6OZ^=ds*%>AU9U^O5{#Zb#ICygcA?mjF5N zF$}TaK>-B9(D3MPY=xF!7#`ey(ufeFY5v6f3ZCPX93y<67|+Mq^0*BHl@Qo{*Ts6g z(DZ4xfai1s@1X2>GyS*FbD|;y$h}ilLR{2^UV5?cp z{_^GlM?LVGIen$HQJD}{oiuQW`Vx$JgX_9gicag*gR-3+9G$<#h(iVl^}Ip|o!`fT z9TgNNtBoZx)~V#WXH+F*ZSd`E*MUOERdRIR{`d^oG%<&%ac7=W(^x%mWROZ7P7b`I zIY}sX%jC)Na19@n{Hg)w6h%|-v@NRMqGQ9bh&G5cE!P{B|AnipQQFPua`CUtZoy`5}bd{is3F!%O&@{qx*n$Jf=n4nc8m_&u>HjlIJhY#lwB* ziyPi2rw{@%TL7O7c)k*kaEC~Cp{@X^($9fO9$#x?&|-&XDsZSVAa%LL&yhIZ3*R`% zYH!vvDh?vqiy|<=Z`mdhkfu64J5bobE-L{p>mR(DIL(2b$la41*zco86PN2=b_&nB-y7;dBuzkyXdm?7TK`ct5 znK7udI&(1o!ljrg)jSH7bTwxRS{iR9UsbX56aMUHgWIC?Z5c83mqAFMN#}K>-cQqV zpH>>9Ec`4AuQN+DiPf9)m@b>}nh>(SGkHStOgwBNWjb-gpgyW8pcf^!k_f-hH*0Ol zFv2iF!(#_bWSgOz34jyV2OaNkx6x=)F1Uz`) z7XXZy6Kx*~UX=5;D>YlIrkHVkNYgyIDPG6-V)^6Y@}XJMW-Bb25#z#jG`M~}s3uZ? zb~zHPgHXzQKy{j2CnOCh@@`i+Z zcgjT6M)Ld5-?`pxI8cnAb|fG~)~UnV6A)kRB|#1%90JN3tC8N+fNLLo2R<((2^UK% znL=TlV|Aa-NRP!7ey#@#2|9cyVWE+}Cr|cSyxq$uN(m;#nBO%SP`mHB zC+@yNUhl`l61)AE2WEO3&w1bi1Z3}mw=5~8HArF13ja@2Xn9>^?RO)ApWjTOUGkWB z<_gyXW6-`k66b8)NY3&!7DzKgdpBMLynd7YC@K4uIP&E-`vwp@O z5}B-1G2wVY>*TccHw(_6pJTBZ`hWU#bf{;={j6k{+UjfpjqbZRuzt2G{W(_D&t3vnTDt-$caX{>-5PTcgFBl14Ik`iQr~Fy^zFom-S8Qpy@`m5DtJr%%7k$jCoJoBZ z!!5LTB`1}40C@pgo?Ob~TV5VHG5W=AZ1&#dy`|EI3VKiloy}m+N)nh`1JX>;-mzZf zSRT5I<2Os^bjUSxV|=yaz{@$c*-S{1V~(63r;TsshsZF!z$fI2;gxIZ`1<_l65+A? z>!o&9(2Xst<0Dc~f3&#cD~jQkDNdn|!1=H`59r75$0x>0&2W4N&~=#SHO*IOOR~Km+{XzlXfEz&D8mKq`-p!aE$?opWOw3bB|kZLk1Y+ z2i1(gO@W)|B&G2ikJ0$~h%OtI0%-!^CyT8?s<_yXz@Onc;sa{?W`6={LjQ9J83#XL z5x!0Xh+{3we=jqB1sGZ0b^`|aFjp}C4S>^JI`ZW?;)#%oFF-VBiPi57{P!~BgrGe@ zz||yh{dz&$IOs9xaSo~ffZr1#M$s=9KG2G!2uV448)dCY-1={mXoiDE^jRg=sH%c$}dysIWdDR3I%;*sliO5ZS_i zP$(uuL&E$=ENSEXwbpBD572Kvo+{G(;}|vOO8{rw^cqR4qfx8>t7RgP39s?o!@b4b zv^;jI$E9mg56pc=HC+OcfUeanUj)Vs1Ex!HL8F&mzDfLw{EsL`v`gxH+a7K!Y{kJH z6@||skv7Bw==MMlgv|DEb$pAV??dE2MAY$hKI0sbBM@+lT2jHR{XWbf$_wnNMkaCs zf%ec9Uz#~QeSDr1;J0MZhC`h5wI}x+CwyD>{{spqpmQ4OI!m_ji5oQjIF|`86Q1ME zhi@Oie?!7>alWkdgGWC*YwxnWzy85Q3NNZnjNX$~-_ zOFoEFq4&C=P;{B>3kex%Tnp@i2@VhT=s&#muRUy+rxrar)OOe|)Zei>v_tFDf!6k1 zXJf!;SV&};Rl7?Vs}3(ZDI{2Nt9OT&su#K?hW5wHRQpPIe@|x*>b#T2+H&n&;T~8p zFt>hHkWry*(&XqS@m#IajojCq{kdH``lY?_td1J1ZO>1EJ{poHI|j+jL#}VzNE7_g z%F;B}=u>y4hR>u&xBfwIh35=7<&U<%)FmZ*`CDK4`FCR0rbgIUW}V=3_zGW8y@EI` zluM+1sSQOoXelA@qIvou`a3rnX+EAY2%i2h6508RI)_ZDPqG35ml56&F|L<8+4bsq z8a^tBX>;K!FmmbmROk8_*`~(AZT$g0=AzyGy}o#!eq^@Y>vCFbxRzQs`ZT`-j`IKD zvtPGI@^UZkD}^!fxz#w~b3%X2j1xND;kW%Yv`(S^JT-GX4DXxlkQq zi)g7=gzMK&@u;TVS2U5S!vgU1!MuTBxav9oVXYDmgHiDVjrE60d)%oo8Sij}8`Rli z_a2ei5X};)ocIe}aGp zdkbfx0A*4$wE1C~d&I?;ChQF^DHc-zzXdo-rKKSLmJ9L*_+a+p;So(#bqI~$QV;#} zKEr(Qe|^Tq&5jBFIxe74?vB!O^m@sHFv%Y36kGy!ua@I8=poyV(RCn9hm-6A9!IBl zMM=cZrw0d2!)?oM9r-BjQLl~qW~>PgiG=r44a1Oad2h}@vhMDsSN!jjZvCE+E7*@P zeNI^wGjp_4K9%{NlS<*W5>i^vTHHdyXEGBR>C>6NQ@eFnoaFw$g^p?4We!FJF}v@H z%xk(>jrPJju6A*n@-QcBHBXkcrCNVLyscF<-X{qDdxiim-@&H15=e}Y%2{`@^6Sq= zVtunTzz^+c_=J0Z1qfKQfCJrs118*ze=mO>JoQIS1c2Glb_qT(uq4p~RZOV^=joDg z2~v0TY~LZp#_s2w3*ejUzWo)FgVc`J`Wju?5Bj-uVE{wFCfcN7z5;u&$7tC90PL97 zen2c61b@{aY^0P`O&=#@Ef)piWdkC-waPBAWiA->+2Ar}@Q)z?voC#qH@?R7SZUjW zG@g(E1KH7qkQUR&#!0HQ>?Gi0*n<0VCd|L?`GHm z(=bhnn-Ok+EFAe~6rnPH2IKn`>s2O@apZ0JSjiAV67kiED;9w(h z(87QqFye;u-vouyj7hUnAae{~lHNlAuCfk@TFqwIpvjfBhT{poyq747n3wq{`-IH+ zpEexwidOW%-Sw=L_+BWhRN@BASuxKZN;xX+xwD$g5w{UxlWONYK&D8%zpb8Uho)qT zdGqQ5tC3lSOvDH=K&23;m83u&&3&!<2p20M4Z9})a~KCLG7sFeL(*{xC^~razeFJ| zDmWOa8iv%={vTdnsFMy))JDU*(~CDf)5h) z!D)d{rb&-sfZW)=~x66%X;Ssb-w`DkQ zr&Y1;BMJi8c%DX~L~?cP85(dxr-K=bx&EW^_qSTK<# zNP0GL>38$Vnx!$zV`4yv11?Qevs$yswkLsLKio7zG$P5$qm^Ogk7nv?X({^=RKE+Pi~q!IDuh{*aatfV@rBA4H7s#|}`q{hOk-yuxF?8e*Z@efx2Zb=P~+cA_citW&H z;qbKTXJMkiMuor#_`7r8oVhevG5#Y=M>ZcYS+oZUFiQ`}t-sXbyz_q&K(YdovYIAs zyH{$?!Ph$UR@$C94JCJ*+zWQdNR2f}Ki`GlaicZ%xbmNYq5cEq0jn#0^ltr_NAsRx z;T!qBn1&N|d?=yG`)sP=!}9jfpd0t1x*vTD-XD#HF_%1Ebufc# z=ORn5W8^+MA7s1PG-qFwCF;x~jlnp%4)OGO?!V7LitXzNx+U)0wGsU@cr9mT_>(?k z;D)=x;2BxRSDu!dDV~JyEawmcREeCTu(R+Zu9-|bH0)+!&SO%;toW??U}~Dbo|xaM z+GWu$l5|Fy*>!N!uYIRRO3ykh!)Z-ydRpUAWxH{N4l}W`+h-4lQ3kY4>(OSv(!O^5 zGwE}_ssSNoZz=_k7Df9p-&yHl9@a0X>}aC4Y6K3*rijC22zrd}+;Y3EI!7@0M+PN@D)GUroDvPox&Y=hj9azz`Qqp@eC%mt45uvdz*52*>2R(&C}}F5 zl^>qb7w2q6jYGr*=zn`5gtqw1*+r$psK_rxSt#G5F|XoLwk5sSH(o#HW`*DK*16s@ zanf9ZBvSIsd;cwGiPrGNUzvbBl?2^GNNq37)zr}`k_b7I3F|Ak=HB4;MKLL zW=*uIdY5ms3(6Z*9ySYSKkw$O^W1nLa{Sv{WLT*_kx+H2SKfoF@6frueyp8hbjda{%#MA&(JMV01jSlf-+7Eb8z5i_5oRVrlZC*+x|8|8aD0m)2;s@(+ z6Aren1(g67WXOs1p@EaeI4XDSu1l{s1?|@>1oVG>FCIZ$Q0J_cgl>CUpeyjVj&^RU zCv;$BUq|pFqj#yRUUd$KL^!T}Kq%0-H_4b>**c~D=Y+%u`Hz`wTKCt^dRw5^yAY2} z4uzhUS}C4jy%#F@4Ng7z(AS@5+2JO?i2W|UY911Vzq(nHbH&b(*}L}iKdu~@3;ZNC zr9ZP=!?ib?d~~lv_BlxYi)Kysy6}!v0y8!84#7)H+&2@&-!p&5G?gHKj#{rca5G#u zh7|n~wYS&%P>wgxlC+oMS#umroul7)kElB{a&>S_K#5bMPBSf4masPb{}_Xo&EL;zSDyW`sh`(zjJ>GMiri2$5bYZv#9T-aAxu}sku&hFZJ*( z>`CHmZb)s4$g$Io|GE1v*)p@{9v4@57W51z-htz-z(7(i$NO_Aca1bgy|FzTQhAFj62M)d%d~q%&oqU6Wz9KXHON^;~ zaQU9(ls?3eW%xj4yeIhMbd2Kt7w$wh1?~F6p5!7MqTM%e4*H%1DBxK8S^J&)a?&}d ziqoR(-3hkivgpsL%|sJqQn{iQ~CsWsM^Hc7#!Ltf5} z*YxTu^?~o)O?(*CtVoRej~fSlXu(N)UTeS_`0ZH^T3elEdY5Dc$XLDL!!!*yXJs9K ztEu2;PBr6Tp1Jy3dir*E-F{I0-FLr))t7@Yhdrp6p87?luq_cX0w{^^Z?$EuiVv#X z95Lp`$44<5C+2AP*WOKI0Y0`lXHfqBV&4P0=iW)}b^DgZS(LPLKH-NR4rn~7u0os6 za)oHaXYrph>(d}@`4Zj@n^<(x(4DYI$%G>JAWa-H%=&#SHpfFnjn<-3YikV|}+1Tdbg~1EwEb(AK z@}Z75?bNAurNbHv$zo|Fa#n1~Y;E*uW7iR=57V%k>}xZvUnF_#kyrg4vTyapE<$q? zOsRc{m1esSTF-+|s5f#}4sC*Q34fb${~QafpWU_4{PB}x2`!>@*+^|abFm02F7Tii zMNc@uuS87!FL)MLxpdWQ46&KoJ=xP-R{WpO zILvk(C4BR%t>tgI>kw^)cVp(0%kB5xzonl)(XIe+*&FqgEj`Cjd(#?!ID>4>!HCL^`ds&eEhLr@*b&VV}EdREnhM zMElDV(B;&2r+6LsO4-{IWIoLRLm!Kf(HLE$km$Ek(#uduJUMQIIOG@6wUYOlP}pN| zr5V>S**He#cE z(2WUOptt!xXcoomI1C5dlhdjfRHu%!`ucfsiM zfTaWX5F0zB{K6#i+5~l&+CcV%Jpy07s<8x91}qDWARdlEL7FO@A(J-#_XDV5t+x>@NR-V%17Q@-i4RW?+| zUfe7wUXl{CTW5}YUTYVmFf~&(l()aQTxK-4hHkhE<{-DbPotlm6lHgpDT}5n*0;o03f{hbd)j)oP4#kv zgcg+%$|Q^Olg*FX$_NF2w9f{vcxv{I zei$8pDEtmglW7cfhHejKMIVf4DHprU;DI9E`9n-vdNI?uF$b(H5r}j_8aPtX$vkw= z%xbN$X@4n~?=(xJzt<%Q|MQ2GXAi2gPdAfvfD^G*r|$lM-hEswNx_9PmaF*nbI#YY z*PeTuyO%3V7A@@K6DCT$xms_bmio)?uFEQaG-8pR+m=1|*h4H^=Ps9oMV0(; zN5Ie5&l!nv<;=~^LRh_lD9Il&oETNQ7pVCC zbDe%qT{N2){{~50+Pju^I{YMsMVhEMK4wQt#rtde=64YT@%!cRZ%h?Md*fe}G}~SN zo#k-hWjMRzm9Y{bD6z}7EHCy4KV-@J^wytnq7DAI&eiAkfVabmHz<-Zr LlpmHz8Nd8L>*lCz literal 0 HcmV?d00001 diff --git a/public/icon.ico b/public/icon.ico new file mode 100644 index 0000000000000000000000000000000000000000..69d6375abe57810c9e443f1968cef31ecdd735b1 GIT binary patch literal 361102 zcmeEP2Y6LQ_75tMmjZpJ4EU~S zKoE*zgHaR{io)nH6h=m%Fd`C#;ZZ0IjX_~ZEDD1LpfGUYcS|EjT%f{wT|X-1LZFi) zTyL_vL=uq-nB{Dc(m@ATt?iA^Kez_bVGfr4J~#^b zsV?T<^E+oxGvLxcdmv??J^ppqa9lYz5x*Q=fM2$)$ImPGAU`Bts;l(=&uMe+5WQYU){){93@1;Qe-xGuI{rw~G-RVN)L13_MHjS_#C`!7E_2nAUVk}QM=xFIHLBtkN>5s~4AAaDA8BNXQEBK;;mHUJZH z?5vjGqWHicR?ab82ynG;5q4Pi8FP$V#_x(!!+jG++J8SG+W`}XJ7U7Hz9`DrXFqI6dj4R%9Ong@zfJW-VF{oQiHE&rDF1gRn$Qa6%*qQu6AbW%fqKJ;WmUhF+< zb7LYwni`4Hlqi%YN27F5EJ~B&P?|UZrSSt%Ixrrk0}@dhmxR*TK`4z$Mrm{k zN+Z*-AZlp!>H)czmPTclahd!sPPb2p{d91UTZ{XmG2N5x6M-9IM}Nd+Z!8GAH!aAu z&B_+<vad>DmFKzFC$RP%X;MNVG-H5W7}UMv}qY7fWKYs+W!Nm-4~x z2GTut7U8%loTkLvBgjpD$CB6#EF0@9l^-2oKx{Mmv8i&!GcJS6;yzgtHx$dp`P3`_ zAMaj+13P1)lZF=eDLL*yE>5 zJ@C$}vRV?Vg)h!1FN`rvo|_~7A4ynjzT-akDA?;p)Z!JvTp}) zSH7iXHT1nY<)#9%ZueXD(kmkA6F#A&q7pWCL-o-Lv)ch!f9M= ztel4a&t(YxwlpIK%S#;0`Z+bu0fS=twu-n6q3@SwMpv(2?4a5BvToFXOPiM)&Zot+ zYz{B#*Hp^mInNn9ZEWmztTTMpmha&4oaYQ;4#Dr>cC1_AaB0bO`|7z995J`BFXk5X z!`yr)%o*PwbH+Ji&RAE>8RLdIdG44q$^&z9JuzpbH|FH{U{1C#7UlX^uN@z9iRTQW zY@R0t5M>eZI!pTF1yO z+p>pX+wh^-HY@|%vNEwPGYi{>4ksFcZRy$AHaG{{(sHpaWfZn0=V9BRG1!(g76(!$ zlpjl-{;%nQNxb^&rL0D|us-~3Ll z11(p3-#jDktBf#tJWzgLtIt_pVNxBqA4<~w_N>ecHDgDscdEgo_TSU)Ey>F0*v1t@&MChrKC<*k2lE{vX#djlS2=_}Lc!<2oq&VQ*Ri_Rk41zds>@`aDj% z_jB3&4$=R62am`8xxwbV`u&fb@7e6$L`(Xm-uu(XVgLM~M&DoYUmN`LT@Q>H(l&ik z@BKr^V*i4GM&HkK=^wx8fvTTuu&C6w)pAkq{TZWiV4=K@VxFk`pZhk%&q3Osskv+e zp1+FzzOW`0-`zd|-yWTYZx77Hw>uZ(+fA$R?W#@qX4zJJGw%StnQ;`02aGiTM*aSM znRz&{*ssy{i+6cCJL0b|Tr17_51Q)m@q+_V^{1gU=g&puug0PBp2?`VW0pMcKe!U* zyEdTw#_c#eZd&8-A2tdHmdNX{#@f$y7({aW->-Y1n&$4j-^Ks?eFgB-lox6U8EuMb( zIy`+J(LEk`>P{a#b$ckDIu?Pa4kh5JJ%jM%_8}M@@7MVIN95qZGOtGafAvyZsm=fS zvIpX$EZZv+JZmVe$N%hX9JpRy=QY%RUgLf6#x?NsXnKFMp?5X(zk2T%*KT^NHv2v{af4%pQ%EE!w@;b7i{s0Z6v3466 zJ3(6Cg^rZdsJy24c*XCUOY6LU++ZA7FRx?y{X91a;=N{HKa9?Q8KJ>ph`#-0goOJc z?OZT|0~_CCZtlJOocFO~0u}AgPs4!?b?5t8sr{rnSW;fgzT=9l)Y`U6{a=uRn>RbO z%lwn)wERvb@jC0a-F9F2_OJVY<#)9D?ANakj?r3gR(|ijtLL`=`+2R$>rGyN9ou31 zRc1o_)q&S~H&2cKir>v^z+f+1U#`QdId;&J7rf5mwceqUxUalWZF}s# zV42*x(g8bH^uf;S`(o$ve%QIRKXxv0#*W1<*s;)+$Q?W8dtk>rPwbfMg&lKzuw%9_ zcFgj_j+p`2Q4)w9GlH;VdN6iO3k9#UZk-?V#f)^H-J;$6Ufw6*vy|CH=6wSCf+c@m zeeJWSCx<+8Q_-MHcN7n;I5RT~XG(_Q%#7hUGd)|TX*oDEB^PHVk0Q!L*~HN(n=poG zEXoSUp{!s$%JK_PHlAo)AuQqWqFfPHfjpWa;Ktf`AFWh_g4!nCw@|88CBRgHa)ool8I|_HE&owIfuwqom-%rkq@AP#SZ8=4J zSR66@9p-^&AN!n__6|i*|GML?_-o`$--_Re!%xK2v> zhjzB_S?OLCC)R6rA{bhNdvy%3F=a4NzTgZi_Wi(pbr%@8|vI-_hPGw}I)g2daW3!;bB<7tES_n`hC zI}I1M1tTG-?*2c$nK{Qm3L1@ zWf|H2oScJ-Tgd+B&~mZ?T7&YPbQZ8>E6O+Qf^qc$7?&TxU#D!A_@;bcqxBy*6&JP# zSyg{7pY4_S42RqB>D$*x{l@kG-QfsS@!0{}F8v`(wqJV0)D}9=knEUF&X&(K4s%;p z%jX(fx8U_TOD(LwC+-!tWoc#liz;WxFV@|L;$c?G>MayiR8zuf)mc zAl#NeD%$b@*<*>eoDywWhSyimw7mZLlW}2}e~atS=LYY*ChH>Z`#W##1y7SM;`%>u zvL|Z3bVJQ&L?8N~`W;1EUSPhY%5C{Ao$EZ3he~eCy^|!qJhx$tW%c)@{x6t_3%mVV zRR6dL2VDBRha@xAboRD>rER_U1$sMRc7_q2^Q3U-i%tT{_sT)B*wJO*{>ySZn*v)xMxBkF6{HRsQ#>9Sk^y!>ly@j zwf@Y`l6s5pG+O_O1-P)^%cA;^7;2B5n|im~oI%u`(+2DBMg2cH9~TaIQvK`hoirx@ z$}-#YbK~_Nj|(?>Ac1Tcm1~2pQGa*bGj$v;9CYup^}l!8SX{W7Y&g4U|KB@pG%g%+ z>6-QTcE`QNdAM-c8401aHmYL%m7dfG-Y)&%5tSq-BpBt?I%#pZoOTT*c=e?p}swGMj`I08bofn2cc2! z$a*ClNqYjN@3o{ZE&VRnrQZIP56RE099%fwm+D{l-WKm44v26z*DEGs7()Dmqr_Ynx&E`Wss4^g2&-Fv{$9~2YD0K!8(R9#w)#HT zy)d_K-MN18WOsk>96Ap@hmjceu^NQ#xkvocHWm=GFZ!crY~B6MVLN=tjG5 zr;Tp7{^GlxG3B$}1BHXWompu2_=&w;ul{^KU7Ql~DA^^9eB}u{y2DXl#yVrabDj8n zn$OtTPJquKSI;xNed<8hssHKH6kIB5*!dF%@DXOyL2=RPr!w(Bq=G#2}yORKav7#iO)r+(b+DaPxBf3irmn5 z*~Ua&Z*`tAT~c8880*%ugANtv4m;x9p*}cwb6=c0=!A1O^~bpbMEhNEZl5d8?RCSs zJ?=QS+XLrzdE(qoFPz)qjdR<5ac&#YRzIBE5`c4?1BrrgZc{MMZU`X?#o6^?IJ+(! zXV*r`v^olBSHia0$38?GIdA1YEBHB)5wiDuiwBWD5!QkU= zZ&)@y*8hd;v!egKWlW;+$fUtIGBF)TCJd3OC<8|dnKE&tAPYzGnTFxW_~AG*j%e%% zqHG))Lo|A%OnJFDGHR4exp_D;aPoF4G zss7i^6Y=iuB0R8gIBuPmj_qR-iA;B`_`X#h-M8X9SEK08m2~ea)pYktzJIknJ6*nm zHNtccOWeg$-pArQSsBAj_p(fPvxd;!EWV$WPWQ8c$K*q}C3QwkytB`PE_QZweXaRFHwQ!h*8EuGrCTT9))~nd zy=+*!x1#_E;q9)RO%#rSIKt=KeR+!gABu}$?rsB_;n4_-gG=G7e|5%1fu?L=em z>dgO7bo0Z@W4Gf%=8~Ea$NsAt>H`l)yU`D>NUQnv>LKvyTX)~7Gq=ssy7@Zx!=ocs zNPXaJFpN|70qF?u?GvMnukIb*E!!^oi2H!$U{jog1+zX#_UnKC(wWhS_p7z-7r*Il zDP!T^Q;s9a#qTbb0Vi8q-xKo()Eu8Xpw2qByR}={9@dG+Q)g70#`oo)-mz=Tn~y99Avxz2*u4W+lxJaJ3H*eiUWdjG75M!%azWt~3S zQ?gr}G|moPf!N0K^s%1cx;NBz{vY4v-kVZ{{jmEY$+e)46&eXxISNFARFS55oVsr@E@ z4%`RIc8kwV@t8&heqi_753L?U(&&a33h!Ej~BZezsX+Vjm0a1JAdO?) zEWG{7HIjea_Oc&uRr|@8%HE-4uzz7d9iJ|u?alF@-_5vZJ5OON%>D4yCp{#;5lzh* zy`!v^2=cj1?G)b=X@Hvp9$GmBuWTEGi(AIw;-&&zykQb9u9x^rvhrj+1j7x5Talr|ES$s46 zoSo|RGuN%j_GgWP@aMyQplrAJYzhBGA28I?P>dW8Cd{R8czzF;3g+ioO3LDU!PsV~+X zz@;U7t!qE|652<7aDZ%;ls=6_+grkaWgmq2I^fNV@|_^=2i6VZIm^km`DC|#F+sjZ z@Mp4DXP-z+fTln_E8_OKPjb8Dbe%DDi49y8nyRhJx5^9>Q-&$!hdd0FyWosDtKN)ZNKYOPibu= z>Y+^eH{4$Vo(_$Z2R#$yPM;;}M`?ZXAwRjw@|B;PY(M$(+Gp}7rt~RhY5Tby10o&p z`FnNs{kHYA7O)`3fwG=jeQvh>?9XhuXC0qvmbO1T!%p%sC-`1YYr$DlJJ(vU$@b@r zz&?{dH|_&vyT#|G^nV5UxD##Xdp8*=6!U`gn^wHErrajmPrl^#Q6I2BJEc!M(RNOo zYQJbZ&v#p@TzJ*`%_%yTF0m3Rnz{wOzboHljJ^7wp)B|O8>Q$ zjA~g@)dO2evWAyQ6hxnc{Japku}mWV z9OOqnD7Obl#Gl)$4Xr7s(e{r|qxQ@Ggt-rt?G~Ta{Evyup*xe1C?+x+AwzNy_cSC5 zAzO8hYnSzExJ1pxxNA+DTJqaQ+fTlX_fa1lSl_3PPi4_|PSav-5gABvcuaRjndies zoJH1%Gcfai(^=v_V+ek%5v{5f+Ui2o~7 zcLzI6FX~g<{&uc^Yx{3{VV_3rpZ1Y2Y3_pq8y)NUv{v>(TAUr7Z}q7w2c5hhq9|?= zx1H;*Y>V>qzCvn0^}){7_QqRxHEr+TE$ttt(!CtI&$O!4?wbcsHQjz?zjc?-4=x;r z#VIx7ZeVhx;l3X}G9W&*aeZm+yE@zN_+A^|?MwD` zxmVnQ;(pk@&QSi^9}V#C*M0kd@3ryWzHJ3@M!Ey$CECxar-PyJ(X+NSkDcqDeZXz! zyM27Wk?(ja+b%wbdf2Xg^C89h za5TmHP{jP;xF5@$ZAInG#yQYHZ+FQ27m7M_HHh=wo_1^X>7pPCU)R)w! zQr~hPQ(tqRYrsm2i}Z4Ibf$VOqr3Mn2lww=Q=A-vTc)Pr_bYPo-p)z5bZ7=HA1@`_ z$9Xc{HXjwYE|BT?LfIaYeFGm`jEbXV^Tl*zDJs}Tl8Jo=-+aAH2Uke;acnzTabOkE zYRR8)#Xj;Wyl<^+Pq~Ne2>0B8^4;sno{))b3U_Wq`3|N{DBr$WCblzVyGthaMSSBn znKo~iZ86y%bK_3QM=|>;E?>V}vPonc&GL17iT0s@^8#^1jc1YWm6jjpjG#XKYpCxp5)YR4>C>mb#f)p^7unwgVdk)RAIHm! zGQ#oXnvpP`Dv|9OPm^C^_APvh_|Cqc*`|-}J#`sql?)i?oQ84E9Wa(M-3jCDGF+Z{ z2Yxql4dz8;z}vCUW#Y*pB9B(Gx2d0BNBU=lPalV$HjjOso^vyD6qx@{D~bQMs>AP71&$2gj2wH`%G4EykNW z$71!!7|`6i(f!D_?hm~@c^w}{GO;=?7q92U;csexW0vr_ zq3^gE3<&aac~p@MEVPv0%J0!UZ$YBJ3qCnKT8g#s@u4xu3agC~-Cg^!tG6r5 zD-0hL-6YAt2ZaZb;OfWggn3HdHusryeqn@rzp4)pk3!}7iFj>y4k)jW@nk#D7372G zi!YAZVwM4u90e3s+g*WvOPcZ?+dkHeJIhW!Ru@-F>5opX)AXGXkN{2A>P^LYXD zT*v^)!}3MhffT=UEZ#VjrSI!?BkT)2@;>3;@^?rwz-LCp^Cf2f+u6PP^v=Oj{MJ=t z8ul3{%F)x#iEm5z!si8iX2j=)e0I$IceA&54|VNRV|+LdzdVqK5SNDU3|LaW{+-S( z8+>NO=Z1WC9Nf2m4fCFCNv6%r^aK0l`OBWc`g*S$>V73I`RthO2Y5a}yq`?ZjvZeR zOTLIkVSll{=2P}ncl^AM><8F}t{$rAXAHKVfd^SEl z5@D|T+^@UK0-p)6J>l2IM?t#(J8EzF{~jGdK08OY+FsAs*$y)bdt=L*Ubtm{Po3JZ z54cZ@3&;l_^>J%tl=gmLWV_;7Cm8=mF01Od*~I@GtIOqk#9P<)!uS8`f#?2sElwWk ziCvp}>(qvQzRzqdqm%5*Nd|-*N&F`n5c!s*%==&6eH~u;%e6>LwC{Aj z^0f3r)e&V6ratDre(>CNR?C36Um*Bg^*hr4R`Q>5|MH)&?UZo>I)Xp#@lN(h%rd}w zK=807skmbzepB^(n*Wguh`gn#pNpNOk96F(zmHJX13VU3GanH5j0B%0{*w%dyrn76 z^`8C1wYttHpQP=9d-XebKEQKAOZg?fC-I-|nu&bX6T`qVAo7-`+t&2NiNifRr0wdy zxEk{(kM@%0-Inr8e9vgQZz%GTKYEe@k+(FxX?sr`+|^6xe+S{8`<{7VDZj+`qigA_N5V+?iHcr#+Y z-PuyB@o(}+B=}tQbb7n-|M3T}lVXZ;jO8aEyABh_*]UWzHlF-LV7uw18D&3}_m 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%0D~!8+i7C1-G3+AQ#1gXx{Gy7%rK*uk)u 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 literal 0 HcmV?d00001 diff --git a/public/icon.png b/public/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..78a856d6660996680b5b0927c76425b973811a5b GIT binary patch literal 14715 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 Cfa`Gp literal 0 HcmV?d00001 diff --git a/public/icon.svg b/public/icon.svg new file mode 100644 index 0000000..2d93369 --- /dev/null +++ b/public/icon.svg @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/index.html b/public/index.html index 461e0e4..6b905de 100644 --- a/public/index.html +++ b/public/index.html @@ -1,43 +1,147 @@ - - - - - - EasyShell - - - - - - - -
- - + + + + + + EasyShell + + + + + + + + + + + +
+ EasyShell +
+
INITIALIZING
+
+ +
+ + + + \ No newline at end of file diff --git a/public/logo.svg b/public/logo.svg new file mode 100644 index 0000000..228aa61 --- /dev/null +++ b/public/logo.svg @@ -0,0 +1,108 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/scripts/generate-icons.js b/scripts/generate-icons.js new file mode 100644 index 0000000..25791d1 --- /dev/null +++ b/scripts/generate-icons.js @@ -0,0 +1,80 @@ +/** + * 图标生成脚本 + * 将 SVG 转换为各平台所需的图标格式 + */ +const sharp = require('sharp'); +const toIco = require('to-ico'); +const fs = require('fs'); +const path = require('path'); + +const publicDir = path.join(__dirname, '../public'); +const svgPath = path.join(publicDir, 'icon.svg'); + +// 需要生成的 PNG 尺寸 +const sizes = [16, 24, 32, 48, 64, 128, 256, 512]; + +async function generateIcons() { + console.log('🎨 开始生成图标...\n'); + + // 读取 SVG 文件 + const svgBuffer = fs.readFileSync(svgPath); + + // 生成各尺寸 PNG + const pngBuffers = {}; + + for (const size of sizes) { + const outputPath = path.join(publicDir, `icon-${size}.png`); + + const pngBuffer = await sharp(svgBuffer) + .resize(size, size) + .png() + .toBuffer(); + + fs.writeFileSync(outputPath, pngBuffer); + pngBuffers[size] = pngBuffer; + console.log(`✅ 生成 icon-${size}.png`); + } + + // 生成主 PNG 图标 (256x256,用于任务栏) + const mainPngPath = path.join(publicDir, 'icon.png'); + fs.writeFileSync(mainPngPath, pngBuffers[256]); + console.log('✅ 生成 icon.png (256x256)'); + + // 生成 Windows ICO 文件 (包含多尺寸) + try { + const icoSizes = [16, 24, 32, 48, 64, 128, 256]; + const icoPngBuffers = icoSizes.map(s => pngBuffers[s]); + + const icoBuffer = await toIco(icoPngBuffers); + fs.writeFileSync(path.join(publicDir, 'icon.ico'), icoBuffer); + console.log('✅ 生成 icon.ico (Windows 图标)'); + } catch (err) { + console.error('❌ 生成 ICO 失败:', err.message); + } + + // 生成 macOS ICNS 说明 + console.log('\n📝 macOS 图标说明:'); + console.log(' 使用 icon-512.png 通过 iconutil 生成 .icns 文件'); + console.log(' 或使用在线工具: https://cloudconvert.com/png-to-icns\n'); + + // 清理临时文件(保留常用尺寸) + const keepSizes = [256, 512]; + for (const size of sizes) { + if (!keepSizes.includes(size)) { + const tempPath = path.join(publicDir, `icon-${size}.png`); + if (fs.existsSync(tempPath)) { + fs.unlinkSync(tempPath); + } + } + } + + console.log('🎉 图标生成完成!\n'); + console.log('生成的文件:'); + console.log(' - public/icon.png (任务栏/窗口图标)'); + console.log(' - public/icon.ico (Windows 安装包/桌面图标)'); + console.log(' - public/icon.svg (Web/高清图标)'); + console.log(' - public/icon-256.png (备用)'); + console.log(' - public/icon-512.png (macOS 用)'); +} + +generateIcons().catch(console.error); diff --git a/server/index.js b/server/index.js new file mode 100644 index 0000000..0a8bd42 --- /dev/null +++ b/server/index.js @@ -0,0 +1,471 @@ +/** + * EasyShell - 后端服务器 + * 提供 SSH 代理、SFTP 服务和 WebSocket 通信 + */ +const express = require('express'); +const http = require('http'); +const { Server } = require('socket.io'); +const cors = require('cors'); +const { Client } = require('ssh2'); +const path = require('path'); +const fs = require('fs'); + +const app = express(); +const server = http.createServer(app); + +// Socket.IO 配置 +const io = new Server(server, { + cors: { + origin: '*', + methods: ['GET', 'POST'], + }, + pingTimeout: 60000, + pingInterval: 25000, +}); + +// 中间件 +app.use(cors()); +app.use(express.json()); + +// 存储活动的 SSH 连接 +const sshConnections = new Map(); +const sftpConnections = new Map(); + +// 健康检查 +app.get('/health', (req, res) => { + res.json({ status: 'ok', timestamp: Date.now() }); +}); + +// 获取服务器信息 +app.get('/info', (req, res) => { + res.json({ + name: 'EasyShell Server', + version: '1.0.0', + connections: sshConnections.size, + }); +}); + +// ========== Socket.IO 事件处理 ========== + +io.on('connection', (socket) => { + console.log(`✅ 客户端连接: ${socket.id}`); + + // SSH 连接 + socket.on('ssh:connect', async (hostConfig, callback) => { + const connectionId = `${socket.id}-${Date.now()}`; + + try { + const conn = new Client(); + + conn.on('ready', () => { + console.log(`✅ SSH 连接成功: ${hostConfig.host}`); + + // 创建 shell + conn.shell({ term: 'xterm-256color' }, (err, stream) => { + if (err) { + callback({ success: false, error: err.message }); + return; + } + + // 存储连接 + sshConnections.set(connectionId, { conn, stream, hostConfig }); + + // 数据传输 + stream.on('data', (data) => { + socket.emit(`ssh:data:${connectionId}`, data.toString()); + }); + + stream.stderr.on('data', (data) => { + socket.emit(`ssh:data:${connectionId}`, data.toString()); + }); + + stream.on('close', () => { + console.log(`📤 SSH 会话关闭: ${hostConfig.host}`); + socket.emit(`ssh:close:${connectionId}`); + sshConnections.delete(connectionId); + }); + + callback({ success: true, connectionId }); + }); + }); + + conn.on('error', (err) => { + console.error(`❌ SSH 连接错误: ${err.message}`); + socket.emit(`ssh:error:${connectionId}`, err.message); + callback({ success: false, error: err.message }); + }); + + // 连接配置 + const connectConfig = { + host: hostConfig.host, + port: hostConfig.port || 22, + username: hostConfig.username, + readyTimeout: 20000, + keepaliveInterval: 10000, + }; + + if (hostConfig.privateKey && hostConfig.privateKey.trim()) { + connectConfig.privateKey = hostConfig.privateKey; + } + if (hostConfig.password && hostConfig.password.trim()) { + connectConfig.password = hostConfig.password; + } + + conn.connect(connectConfig); + } catch (error) { + callback({ success: false, error: error.message }); + } + }); + + // SSH 写入数据 + socket.on('ssh:write', ({ connectionId, data }) => { + const connection = sshConnections.get(connectionId); + if (connection?.stream) { + connection.stream.write(data); + } + }); + + // SSH 调整窗口大小 + socket.on('ssh:resize', ({ connectionId, cols, rows }) => { + const connection = sshConnections.get(connectionId); + if (connection?.stream) { + connection.stream.setWindow(rows, cols, 0, 0); + } + }); + + // SSH 断开连接 + socket.on('ssh:disconnect', (connectionId) => { + const connection = sshConnections.get(connectionId); + if (connection) { + connection.conn.end(); + sshConnections.delete(connectionId); + console.log(`📤 SSH 连接已断开: ${connectionId}`); + } + }); + + // SSH 执行命令 + socket.on('ssh:exec', async ({ hostConfig, command }, callback) => { + const conn = new Client(); + let output = ''; + let errorOutput = ''; + + conn.on('ready', () => { + conn.exec(command, (err, stream) => { + if (err) { + conn.end(); + callback({ success: false, error: err.message }); + return; + } + + stream.on('close', (code) => { + conn.end(); + callback({ + success: true, + code, + stdout: output, + stderr: errorOutput, + }); + }); + + stream.on('data', (data) => { + output += data.toString(); + }); + + stream.stderr.on('data', (data) => { + errorOutput += data.toString(); + }); + }); + }); + + conn.on('error', (err) => { + callback({ success: false, error: err.message }); + }); + + const connectConfig = { + host: hostConfig.host, + port: hostConfig.port || 22, + username: hostConfig.username, + }; + + if (hostConfig.privateKey) { + connectConfig.privateKey = hostConfig.privateKey; + } else if (hostConfig.password) { + connectConfig.password = hostConfig.password; + } + + conn.connect(connectConfig); + }); + + // ========== SFTP 操作 ========== + + // SFTP 列出目录 + socket.on('sftp:list', async ({ hostConfig, remotePath }, callback) => { + try { + const result = await sftpOperation(hostConfig, async (sftp) => { + return new Promise((resolve, reject) => { + sftp.readdir(remotePath, (err, list) => { + if (err) { + reject(err); + return; + } + + const files = list.map(item => ({ + filename: item.filename, + longname: item.longname, + attrs: { + size: item.attrs.size, + mtime: item.attrs.mtime, + atime: item.attrs.atime, + mode: item.attrs.mode, + isDirectory: (item.attrs.mode & 0o40000) === 0o40000, + isFile: (item.attrs.mode & 0o100000) === 0o100000, + } + })); + + resolve({ success: true, files }); + }); + }); + }); + callback(result); + } catch (error) { + callback({ success: false, error: error.message }); + } + }); + + // SFTP 创建目录 + socket.on('sftp:mkdir', async ({ hostConfig, remotePath }, callback) => { + try { + const result = await sftpOperation(hostConfig, async (sftp) => { + return new Promise((resolve, reject) => { + sftp.mkdir(remotePath, (err) => { + if (err) reject(err); + else resolve({ success: true }); + }); + }); + }); + callback(result); + } catch (error) { + callback({ success: false, error: error.message }); + } + }); + + // SFTP 删除文件 + socket.on('sftp:delete', async ({ hostConfig, remotePath }, callback) => { + try { + const result = await sftpOperation(hostConfig, async (sftp) => { + return new Promise((resolve, reject) => { + sftp.unlink(remotePath, (err) => { + if (err) reject(err); + else resolve({ success: true }); + }); + }); + }); + callback(result); + } catch (error) { + callback({ success: false, error: error.message }); + } + }); + + // SFTP 删除目录(递归) + socket.on('sftp:rmdir', async ({ hostConfig, remotePath }, callback) => { + try { + const result = await sftpOperation(hostConfig, async (sftp) => { + const deleteRecursive = async (dirPath) => { + return new Promise((resolve, reject) => { + sftp.readdir(dirPath, async (err, list) => { + if (err) { + reject(err); + return; + } + + try { + for (const item of list) { + const itemPath = `${dirPath}/${item.filename}`; + const isDir = (item.attrs.mode & 0o40000) === 0o40000; + + if (isDir) { + await deleteRecursive(itemPath); + } else { + await new Promise((res, rej) => { + sftp.unlink(itemPath, (err) => { + if (err) rej(err); + else res(); + }); + }); + } + } + + sftp.rmdir(dirPath, (err) => { + if (err) reject(err); + else resolve(); + }); + } catch (e) { + reject(e); + } + }); + }); + }; + + await deleteRecursive(remotePath); + return { success: true }; + }); + callback(result); + } catch (error) { + callback({ success: false, error: error.message }); + } + }); + + // SFTP 重命名 + socket.on('sftp:rename', async ({ hostConfig, oldPath, newPath }, callback) => { + try { + const result = await sftpOperation(hostConfig, async (sftp) => { + return new Promise((resolve, reject) => { + sftp.rename(oldPath, newPath, (err) => { + if (err) reject(err); + else resolve({ success: true }); + }); + }); + }); + callback(result); + } catch (error) { + callback({ success: false, error: error.message }); + } + }); + + // SFTP 读取文件 + socket.on('sftp:readFile', async ({ hostConfig, remotePath }, callback) => { + try { + const result = await sftpOperation(hostConfig, async (sftp) => { + return new Promise((resolve, reject) => { + let content = ''; + const readStream = sftp.createReadStream(remotePath); + + readStream.on('data', (chunk) => { + content += chunk.toString(); + }); + + readStream.on('error', reject); + + readStream.on('end', () => { + resolve({ success: true, content }); + }); + }); + }); + callback(result); + } catch (error) { + callback({ success: false, error: error.message }); + } + }); + + // SFTP 写入文件 + socket.on('sftp:writeFile', async ({ hostConfig, remotePath, content }, callback) => { + try { + const result = await sftpOperation(hostConfig, async (sftp) => { + return new Promise((resolve, reject) => { + const writeStream = sftp.createWriteStream(remotePath); + + writeStream.on('error', reject); + writeStream.on('close', () => resolve({ success: true })); + + writeStream.end(content); + }); + }); + callback(result); + } catch (error) { + callback({ success: false, error: error.message }); + } + }); + + // 客户端断开连接时清理 + socket.on('disconnect', () => { + console.log(`📤 客户端断开: ${socket.id}`); + + // 清理该客户端的所有 SSH 连接 + for (const [id, connection] of sshConnections.entries()) { + if (id.startsWith(socket.id)) { + connection.conn.end(); + sshConnections.delete(id); + } + } + }); +}); + +// SFTP 操作辅助函数 +async function sftpOperation(hostConfig, operation) { + return new Promise((resolve, reject) => { + const conn = new Client(); + + conn.on('ready', () => { + conn.sftp(async (err, sftp) => { + if (err) { + conn.end(); + reject(err); + return; + } + + try { + const result = await operation(sftp); + conn.end(); + resolve(result); + } catch (error) { + conn.end(); + reject(error); + } + }); + }); + + conn.on('error', reject); + + const connectConfig = { + host: hostConfig.host, + port: hostConfig.port || 22, + username: hostConfig.username, + readyTimeout: 20000, + }; + + if (hostConfig.privateKey && hostConfig.privateKey.trim()) { + connectConfig.privateKey = hostConfig.privateKey; + } + if (hostConfig.password && hostConfig.password.trim()) { + connectConfig.password = hostConfig.password; + } + + conn.connect(connectConfig); + }); +} + +// 启动服务器 +const PORT = process.env.PORT || 3001; + +server.listen(PORT, '0.0.0.0', () => { + console.log(` +╔═══════════════════════════════════════════════════╗ +║ ║ +║ ⚡ EasyShell Server v1.0.0 ║ +║ ║ +║ 🌐 HTTP: http://0.0.0.0:${PORT} ║ +║ 🔌 Socket: ws://0.0.0.0:${PORT} ║ +║ ║ +║ Ready for connections... ║ +║ ║ +╚═══════════════════════════════════════════════════╝ + `); +}); + +// 优雅关闭 +process.on('SIGTERM', () => { + console.log('正在关闭服务器...'); + + // 关闭所有 SSH 连接 + for (const [id, connection] of sshConnections.entries()) { + connection.conn.end(); + } + + server.close(() => { + console.log('服务器已关闭'); + process.exit(0); + }); +}); + +module.exports = { app, server, io }; + diff --git a/server/package.json b/server/package.json new file mode 100644 index 0000000..0fa2094 --- /dev/null +++ b/server/package.json @@ -0,0 +1,20 @@ +{ + "name": "easyshell-server", + "version": "1.0.0", + "description": "EasyShell 后端服务器 - SSH/SFTP 代理", + "main": "index.js", + "scripts": { + "start": "node index.js", + "dev": "nodemon index.js" + }, + "dependencies": { + "cors": "^2.8.5", + "express": "^4.18.2", + "socket.io": "^4.6.1", + "ssh2": "^1.15.0" + }, + "devDependencies": { + "nodemon": "^3.0.2" + } +} + diff --git a/src/App.js b/src/App.js index 162db47..d2e7994 100644 --- a/src/App.js +++ b/src/App.js @@ -6,6 +6,11 @@ import Terminal from './components/Terminal'; import HostManager from './components/HostManager'; import Settings from './components/Settings'; import CommandPalette from './components/CommandPalette'; +import HostInfoPanel from './components/HostInfoPanel'; +import SFTPBrowser from './components/SFTPBrowser'; +import ServerConfig from './components/ServerConfig'; +import HostEditPanel from './components/HostEditPanel'; +import { getAPI, platform } from './services/api'; function App() { const [hosts, setHosts] = useState([]); @@ -17,26 +22,32 @@ function App() { const [showCommandPalette, setShowCommandPalette] = useState(false); const [isRemoteConnected, setIsRemoteConnected] = useState(false); const [sidebarCollapsed, setSidebarCollapsed] = useState(false); + const [showInfoPanel, setShowInfoPanel] = useState(false); + const [showSFTP, setShowSFTP] = useState(false); + const [showServerConfig, setShowServerConfig] = useState(false); + const [selectedHost, setSelectedHost] = useState(null); // 选中的主机(用于右侧编辑面板) + + // 获取跨平台 API + const api = useMemo(() => getAPI(), []); + + // 检测是否是移动端 + const isMobile = platform.isMobile(); // 加载主机列表 const loadHosts = useCallback(async () => { - if (window.electronAPI) { - const hostList = await window.electronAPI.hosts.getAll(); - setHosts(hostList); - } - }, []); + const hostList = await api.hosts.getAll(); + setHosts(hostList || []); + }, [api]); // 检查远程连接状态 const checkRemoteStatus = useCallback(async () => { - if (window.electronAPI) { - const connected = await window.electronAPI.db.isRemoteConnected(); - setIsRemoteConnected(connected); - // 如果已连接,刷新主机列表(因为启动时可能已自动同步) - if (connected) { - loadHosts(); - } + const connected = await api.db.isRemoteConnected(); + setIsRemoteConnected(connected); + // 如果已连接,刷新主机列表(因为启动时可能已自动同步) + if (connected) { + loadHosts(); } - }, [loadHosts]); + }, [api, loadHosts]); useEffect(() => { loadHosts(); @@ -105,12 +116,22 @@ function App() { loadHosts(); }, [loadHosts]); - // 编辑主机 + // 编辑主机 - 打开模态框 const handleEditHost = useCallback((host) => { setEditingHost(host); setShowHostManager(true); }, []); + // 选中主机 - 右侧面板编辑 + const handleSelectHost = useCallback((host) => { + setSelectedHost(host); + }, []); + + // 新增主机 - 右侧面板 + const handleAddNewHost = useCallback(() => { + setSelectedHost({}); // 空对象表示新建 + }, []); + const openHostManager = useCallback(() => { setEditingHost(null); setShowHostManager(true); @@ -126,112 +147,252 @@ function App() { return (
- + {/* 桌面端显示标题栏 */} + {!isMobile && } + + {/* 移动端顶部栏 */} + {isMobile && ( +
+
+ EasyShell + EASYSHELL +
+ +
+ )}
setSidebarCollapsed(!sidebarCollapsed)} + isMobile={isMobile} + onOpenServerConfig={() => setShowServerConfig(true)} />
{/* 标签栏 */} {activeTabs.length > 0 && ( -
- {activeTabs.map((tab) => ( -
+ {/* 背景装饰 */} +
+ + {activeTabs.map((tab, index) => ( + setActiveTabId(tab.id)} > - - + {/* 活动指示器 */} + {activeTabId === tab.id && ( + + )} + + {/* 连接状态 */} + + + {/* 标签名 */} + {tab.title} - -
+ + ))}
)} {/* 终端内容 - 所有终端都渲染,通过显示/隐藏切换 */} -
- {activeTabs.length === 0 ? ( -
-
-
🚀
-

- 欢迎使用 EasyShell -

-

- 高颜值远程 Shell 管理终端 -

-
- - + + Ctrl + K + 打开命令面板 + + + {/* 装饰性扫描线 */} +
+ {[...Array(5)].map((_, i) => ( + + ))} +
+ +
+ ) : ( + activeTabs.map((tab) => ( +
+ handleConnectionChange(tab.id, connected)} + onShowCommandPalette={openCommandPalette} + onToggleInfoPanel={() => setShowInfoPanel(!showInfoPanel)} + onOpenSFTP={() => setShowSFTP(true)} + showInfoPanel={showInfoPanel} + />
-

- 按 Ctrl+K 打开命令面板 -

-
-
- ) : ( - activeTabs.map((tab) => ( -
- handleConnectionChange(tab.id, connected)} - onShowCommandPalette={openCommandPalette} - /> -
- )) - )} + )) + )} +
+ + {/* 右侧主机信息面板 */} + + {showInfoPanel && activeTabId && activeTabs.find(t => t.id === activeTabId) && ( + t.id === activeTabId)?.hostId} + connectionId={activeTabId} + isConnected={activeTabs.find(t => t.id === activeTabId)?.connected} + onOpenSFTP={() => setShowSFTP(true)} + onClose={() => setShowInfoPanel(false)} + /> + )} + + + {/* 右侧主机编辑面板 */} + + {selectedHost && ( + setSelectedHost(null)} + onConnect={(host) => { + connectHost(host); + setSelectedHost(null); + }} + onUpdate={() => { + loadHosts(); + }} + onDelete={() => { + loadHosts(); + setSelectedHost(null); + }} + /> + )} +
@@ -279,6 +440,23 @@ function App() { /> )} + + {/* SFTP 文件浏览器 */} + + {showSFTP && activeTabId && activeTabs.find(t => t.id === activeTabId) && ( + t.id === activeTabId)?.hostId} + isConnected={activeTabs.find(t => t.id === activeTabId)?.connected} + onClose={() => setShowSFTP(false)} + /> + )} + + + {/* 服务器配置 (移动端) */} + setShowServerConfig(false)} + />
); } diff --git a/src/components/HostEditPanel.js b/src/components/HostEditPanel.js new file mode 100644 index 0000000..c0a20cf --- /dev/null +++ b/src/components/HostEditPanel.js @@ -0,0 +1,411 @@ +import React, { useState, useEffect, useMemo } from 'react'; +import { motion } from 'framer-motion'; +import { + FiX, + FiServer, + FiCheck, + FiLoader, + FiKey, + FiEye, + FiEyeOff, + FiTrash2, + FiPlay, + FiSave, +} from 'react-icons/fi'; +import { getAPI } from '../services/api'; + +const colors = [ + '#00d4ff', '#3fb950', '#d29922', '#f85149', '#bc8cff', + '#56d4dd', '#ffa657', '#ff7b72', '#d2a8ff', '#ff2d95', +]; + +function HostEditPanel({ host, onClose, onConnect, onUpdate, onDelete }) { + const api = useMemo(() => getAPI(), []); + const [showPassword, setShowPassword] = useState(false); + const [testing, setTesting] = useState(false); + const [testResult, setTestResult] = useState(null); + const [saving, setSaving] = useState(false); + const [formData, setFormData] = useState({ + name: '', + host: '', + port: 22, + username: '', + password: '', + privateKey: '', + groupName: '默认分组', + color: '#00d4ff', + description: '', + }); + + // 当 host 变化时更新表单 + useEffect(() => { + if (host) { + setFormData({ + name: host.name || '', + host: host.host || '', + port: host.port || 22, + username: host.username || '', + password: host.password || '', + privateKey: host.private_key || '', + groupName: host.group_name || '默认分组', + color: host.color || '#00d4ff', + description: host.description || '', + }); + setTestResult(null); + } + }, [host]); + + const handleSubmit = async (e) => { + e.preventDefault(); + setSaving(true); + + try { + if (host?.id) { + await api.hosts.update(host.id, formData); + } else { + await api.hosts.add(formData); + } + onUpdate && onUpdate(); + setTestResult({ success: true, message: '保存成功!' }); + } catch (error) { + console.error('保存失败:', error); + setTestResult({ success: false, message: '保存失败: ' + error.message }); + } finally { + setSaving(false); + } + }; + + const handleTest = async () => { + setTesting(true); + setTestResult(null); + + try { + const result = await api.ssh.test({ + host: formData.host, + port: formData.port, + username: formData.username, + password: formData.password, + privateKey: formData.privateKey, + }); + setTestResult(result); + } catch (error) { + setTestResult({ success: false, message: error.message }); + } finally { + setTesting(false); + } + }; + + const handleDelete = async () => { + if (window.confirm('确定要删除这个主机吗?')) { + try { + await api.hosts.delete(host.id); + onDelete && onDelete(); + onClose && onClose(); + } catch (error) { + console.error('删除失败:', error); + } + } + }; + + const handleConnect = () => { + if (host && onConnect) { + onConnect(host); + } + }; + + return ( + + {/* 背景装饰 */} +
+
+ + {/* 头部 */} +
+
+
+ +
+
+

+ {host?.id ? '编辑主机' : '新建主机'} +

+

+ {formData.host || 'hostname'} +

+
+
+ +
+ + {/* 快捷操作 */} + {host?.id && ( +
+ + + 连接终端 + + + + +
+ )} + + {/* 表单 */} +
+ {/* 名称 */} +
+ + setFormData({ ...formData, name: e.target.value })} + className="w-full px-3 py-2 bg-shell-bg border border-shell-border rounded-lg + text-shell-text text-sm placeholder-shell-text-dim/50 + focus:border-shell-accent focus:ring-1 focus:ring-shell-accent/50 transition-all" + placeholder="生产服务器" + /> +
+ + {/* 主机地址和端口 */} +
+
+ + setFormData({ ...formData, host: e.target.value })} + className="w-full px-3 py-2 bg-shell-bg border border-shell-border rounded-lg + text-shell-text text-sm font-mono placeholder-shell-text-dim/50 + focus:border-shell-accent focus:ring-1 focus:ring-shell-accent/50 transition-all" + placeholder="192.168.1.100" + /> +
+
+ + setFormData({ ...formData, port: parseInt(e.target.value) || 22 })} + className="w-full px-3 py-2 bg-shell-bg border border-shell-border rounded-lg + text-shell-text text-sm font-mono placeholder-shell-text-dim/50 + focus:border-shell-accent focus:ring-1 focus:ring-shell-accent/50 transition-all" + /> +
+
+ + {/* 用户名 */} +
+ + setFormData({ ...formData, username: e.target.value })} + className="w-full px-3 py-2 bg-shell-bg border border-shell-border rounded-lg + text-shell-text text-sm font-mono placeholder-shell-text-dim/50 + focus:border-shell-accent focus:ring-1 focus:ring-shell-accent/50 transition-all" + placeholder="root" + /> +
+ + {/* 密码 */} +
+ +
+ setFormData({ ...formData, password: e.target.value })} + className="w-full px-3 py-2 pr-10 bg-shell-bg border border-shell-border rounded-lg + text-shell-text text-sm font-mono placeholder-shell-text-dim/50 + focus:border-shell-accent focus:ring-1 focus:ring-shell-accent/50 transition-all" + placeholder="••••••••" + /> + +
+
+ + {/* 私钥 */} +
+ +