├── .gitignore ├── README.md ├── doc ├── img.png ├── img_1.png └── img_2.png ├── package-lock.json ├── package.json ├── phpMyAdmin ├── .gitkeep └── config.inc.php ├── pnpm-lock.yaml ├── src ├── login.html ├── main.js └── networking │ ├── inbound-tcp-to-ws-proxy.js │ ├── outbound-ws-to-tcp-proxy.js │ ├── php.ini │ ├── utils.js │ └── with-networking.js └── tests ├── fetch-hook.js ├── http-hook.js ├── ip-hook.js └── ws-hook.js /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | node_modules 3 | phpMyAdmin 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PMAtron (phpmyadmin现代风格) 2 | 3 | ![GitHub stars](https://img.shields.io/github/stars/maskerprc/pmatron?style=social) 4 | ![License](https://img.shields.io/badge/license-MIT-blue.svg) 5 | ![Downloads](https://img.shields.io/github/downloads/maskerprc/pmatron/total) 6 | ![Build Status](https://img.shields.io/github/workflow/status/maskerprc/pmatron/CI) 7 | 8 | PMAtron 是一款基于 Electron 的创新桌面应用,能在本地为你呈现完整的 phpMyAdmin 功能体验。它巧妙运用了 PHP-WASM 技术,让你无需传统 PHP 服务器环境即可直接享有 phpMyAdmin 的全部功能。 9 | 10 | #### 登录 11 | img_1.png 12 | 13 | #### MySQL GUI 14 | img_2.png 15 | 16 | ## ✨ 功能特色 17 | 18 | PMAtron 为你重新定义使用 phpMyAdmin 的方式,带来: 19 | 20 | - **零配置体验**:无需搭建任何 Web 服务器或 PHP 环境,即刻启动、使用 phpMyAdmin。 21 | - **原生桌面体验**:以桌面应用形式运行,提供更顺畅、更直观的操作。 22 | - **增强安全性**:自定义协议的实现,为传统浏览器访问方式提供更多安全保护层。 23 | - **跨平台支持**:可在 Windows、macOS 和 Linux 上无缝运行。 24 | - **离线能力**:即使在无网络环境下,也能对本地数据库进行访问与管理。 25 | - **现代架构**:基于 Electron 与 PHP-WASM,提供高性能与高可靠性的稳定体验。 26 | 27 | ## 加入我们(利用休息时间维护,开发不易,帮忙点个Star) 28 | 加入我们的技术交流微信群! 29 | 30 | 如果你对 PMAtron 项目有兴趣,想了解更多技术细节、交流使用经验或参与社区贡献,欢迎扫码加入我们的技术交流微信群。 31 | 32 | img.png 33 | 34 | 入群你将获得: 35 | 36 | - 最新开发进度与版本更新信息 37 | - 社区开发者的经验分享与技术答疑 38 | - 使用技巧、BUG 反馈、功能建议的讨论平台 39 | - 第一时间参与社区活动与新特性测试 40 | 41 | 请在加群时备注你的 GitHub 用户名或关注的方向,以便我们更好地为你提供帮助。 42 | 43 | ## 🚀 快速开始 44 | 45 | 运行 PMAtron 非常简单: 46 | 47 | #### 前置条件 48 | - 目前只支持windows 49 | - 下载phpMyAdmin解压到 phpMyAdmin目录 https://www.phpmyadmin.net/downloads/ 50 | 51 | #### 安装依赖 52 | ```bash 53 | # 克隆仓库 54 | git clone https://github.com/maskerprc/pmatron.git 55 | 56 | # 进入项目目录 57 | cd pmatron 58 | 59 | # 安装依赖 60 | npm install 61 | 62 | # 启动应用程序 63 | npm start 64 | ``` 65 | 66 | ## 🗺️ 开发里程碑 67 | 68 | 以下是 PMAtron 的开发里程碑: 69 | 70 | - [x] **通过 UI 选择当前登录的 MySQL 数据库账号密码** 71 | 允许用户通过图形界面方便地选择和管理 MySQL 数据库的登录凭证。 72 | 73 | - [ ] **支持使用 HTTP 和 SOCKS5 代理访问 MySQL** 74 | 实现通过 HTTP 和 SOCKS5 代理服务器连接到 MySQL 数据库,增强网络连接的灵活性和安全性。 75 | 76 | - [ ] **优化 UI 交互体验,通过 Hack CSS 的方式** 77 | 通过自定义 CSS 样式提升用户界面的美观性和交互的流畅性,提供更好的用户体验。 78 | 79 | - [ ] **支持 PostgreSQL 和 SQLite 数据库** 80 | 扩展数据库支持范围,增加对 PostgreSQL 和 SQLite 的兼容,满足更多用户的需求。 81 | 82 | - [ ] **支持自动更新客户端** 83 | 实现客户端的自动更新功能,确保用户始终使用最新版本,享受最新的功能和安全补丁。 84 | 85 | - [ ] **支持连接导入导出** 86 | 提供连接配置的导入和导出功能,方便用户在不同设备或环境中快速配置数据库连接。 87 | 88 | - [ ] **支持多用户** 89 | 增加多用户支持,允许多个用户在同一客户端中管理不同的数据库连接和配置。 90 | 91 | - [ ] **多语言 i18n 支持** 92 | 实现国际化(i18n),支持多种语言界面,方便全球用户使用 PMAtron。 93 | 94 | 95 | ## 🤝 欢迎贡献 96 | 97 | 我们非常欢迎社区贡献!你可以通过以下方式参与到 PMAtron 的建设中: 98 | 99 | ### 开发流程 100 | 1. Fork 本仓库 101 | 2. 创建分支以添加新特性(`git checkout -b feature/AmazingFeature`) 102 | 3. 提交你的改动(`git commit -m 'Add some AmazingFeature'`) 103 | 4. 推送到远程分支(`git push origin feature/AmazingFeature`) 104 | 5. 发起 Pull Request 105 | 106 | ### 开发环境搭建 107 | 108 | ```bash 109 | # 安装开发依赖 110 | npm install --dev 111 | 112 | # 运行测试 113 | npm test 114 | 115 | # 构建生产版本 116 | npm run build 117 | ``` 118 | 119 | ### 代码规范 120 | - 遵循现有的代码风格 121 | - 使用有意义的变量与函数命名 122 | - 对复杂逻辑添加必要的注释 123 | - 为新增特性编写测试 124 | 125 | ## 📈 项目成长轨迹 126 | 127 | [![Star History Chart](https://api.star-history.com/svg?repos=MaskerPRC/pmatron&type=Date)](https://star-history.com/#MaskerPRC/pmatron&Date) 128 | 129 | 自项目诞生以来,得益于出色的社区成员与用户,我们的星标与关注度持续增长。 130 | 131 | ## 🙏 致谢 132 | 133 | PMAtron 的成长离不开众多优秀项目与社区的贡献与支持: 134 | 135 | - 感谢 phpMyAdmin 团队提供出色的数据库管理工具 136 | - 感谢 Electron 团队,让跨平台桌面应用成为可能 137 | - 感谢 PHP-WASM 项目,为我们带来在浏览器环境中运行 PHP 的新思路 138 | - 感谢所有为 PMAtron 做出贡献的开发者与社区成员 139 | - 感谢每一位为本项目加星标、Fork 或反馈问题的用户 140 | 141 | ## 📄 授权许可 142 | 143 | PMAtron 在 MIT 许可下发布。详情请查看 [LICENSE](LICENSE) 文件。 144 | 145 | --- 146 | 147 |
148 | 以 ❤️ 倾注的 PMAtron 团队 149 |
150 | -------------------------------------------------------------------------------- /doc/img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaskerPRC/pmatron/138efdc59a10bfe805dfde49bd4061a36e0ecaed/doc/img.png -------------------------------------------------------------------------------- /doc/img_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaskerPRC/pmatron/138efdc59a10bfe805dfde49bd4061a36e0ecaed/doc/img_1.png -------------------------------------------------------------------------------- /doc/img_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaskerPRC/pmatron/138efdc59a10bfe805dfde49bd4061a36e0ecaed/doc/img_2.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pmatron", 3 | "version": "1.0.0", 4 | "main": "src/main.js", 5 | "type": "module", 6 | "scripts": { 7 | "start": "electron src/main.js" 8 | }, 9 | "private": true, 10 | "dependencies": { 11 | "@php-wasm/logger": "^1.0.15", 12 | "@php-wasm/node": "^1.0.14", 13 | "@php-wasm/scopes": "^1.0.14", 14 | "@php-wasm/universal": "^1.0.14", 15 | "@wp-playground/wordpress": "^1.0.14", 16 | "axios": "^1.7.9", 17 | "compressible": "^2.0.18", 18 | "compression": "^1.7.5", 19 | "electron": "^30.1.0", 20 | "express": "^4.19.2", 21 | "file-url": "^4.0.0", 22 | "http-proxy": "^1.18.1", 23 | "https-proxy-agent": "^7.0.6", 24 | "node-fetch": "^3.3.2", 25 | "ws": "^8.18.0" 26 | }, 27 | "keywords": [], 28 | "author": "", 29 | "license": "ISC", 30 | "description": "" 31 | } 32 | -------------------------------------------------------------------------------- /phpMyAdmin/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MaskerPRC/pmatron/138efdc59a10bfe805dfde49bd4061a36e0ecaed/phpMyAdmin/.gitkeep -------------------------------------------------------------------------------- /phpMyAdmin/config.inc.php: -------------------------------------------------------------------------------- 1 | . 8 | */ 9 | 10 | declare(strict_types=1); 11 | 12 | /** 13 | * This is needed for cookie based authentication to encrypt the cookie. 14 | * Needs to be a 32-bytes long string of random bytes. See FAQ 2.10. 15 | */ 16 | $cfg['blowfish_secret'] = 'jukt3J4qjoQvtans15ApZwz7mjVhBnEn'; /* YOU MUST FILL IN THIS FOR COOKIE AUTH! */ 17 | $cfg['CheckConfigurationPermissions'] = false; 18 | 19 | $i = 0; 20 | $i++; 21 | 22 | $cfg['Servers'][$i]['AllowNoPassword'] = true; 23 | $cfg['Servers'][$i]['host'] = '127.0.0.1'; 24 | /* Authentication type */ 25 | $cfg['Servers'][$i]['auth_type'] = 'config'; 26 | $cfg['Servers'][$i]['user'] = 'root'; 27 | $cfg['Servers'][$i]['password'] = 'root'; 28 | $cfg['Servers'][$i]['controluser'] = 'root'; 29 | $cfg['Servers'][$i]['controlpass'] = 'root'; 30 | 31 | 32 | 33 | $cfg['UploadDir'] = ''; 34 | $cfg['SaveDir'] = ''; 35 | 36 | -------------------------------------------------------------------------------- /pnpm-lock.yaml: -------------------------------------------------------------------------------- 1 | lockfileVersion: '9.0' 2 | 3 | settings: 4 | autoInstallPeers: true 5 | excludeLinksFromLockfile: false 6 | 7 | importers: 8 | 9 | .: 10 | dependencies: 11 | '@php-wasm/node': 12 | specifier: ^1.0.14 13 | version: 1.0.14 14 | '@php-wasm/scopes': 15 | specifier: ^1.0.14 16 | version: 1.0.14 17 | '@php-wasm/universal': 18 | specifier: ^1.0.14 19 | version: 1.0.14 20 | '@wp-playground/wordpress': 21 | specifier: ^1.0.14 22 | version: 1.0.14 23 | axios: 24 | specifier: ^1.7.9 25 | version: 1.7.9 26 | compressible: 27 | specifier: ^2.0.18 28 | version: 2.0.18 29 | compression: 30 | specifier: ^1.7.5 31 | version: 1.7.5 32 | electron: 33 | specifier: ^30.1.0 34 | version: 30.5.1 35 | express: 36 | specifier: ^4.19.2 37 | version: 4.21.2 38 | file-url: 39 | specifier: ^4.0.0 40 | version: 4.0.0 41 | node-fetch: 42 | specifier: ^3.3.2 43 | version: 3.3.2 44 | 45 | packages: 46 | 47 | '@electron/get@2.0.3': 48 | resolution: {integrity: sha512-Qkzpg2s9GnVV2I2BjRksUi43U5e6+zaQMcjoJy0C+C5oxaKl+fmckGDQFtRpZpZV0NQekuZZ+tGz7EA9TVnQtQ==} 49 | engines: {node: '>=12'} 50 | 51 | '@php-wasm/logger@1.0.14': 52 | resolution: {integrity: sha512-sDzjT1Aj9bEDw+rs03tvZoPAFB6Hb0nU5ZTBPa6J9Uqx2M6F1ir0tYZ8orgOsVpX1BaEO0JI7V6SZT99N5nsbw==} 53 | engines: {node: '>=18.18.0', npm: '>=8.11.0'} 54 | 55 | '@php-wasm/node-polyfills@1.0.14': 56 | resolution: {integrity: sha512-59R0dcMlQrrps/QmZjN3LC6x9Db70/8RIrEbqcgog6II0cZGjBWeSIftyccj6g1vnK9ZKLUfKHCI5SphJUlYBg==} 57 | 58 | '@php-wasm/node@1.0.14': 59 | resolution: {integrity: sha512-qmnC+QwMBfDiiv/J6yh6M5IoeH6hGc9KmP8dXiUT+2yVOBjriBaAhMZaXcndgB3sh2iRnMtA+MHUJJLOfoh24w==} 60 | engines: {node: '>=18.18.0', npm: '>=8.11.0'} 61 | 62 | '@php-wasm/progress@1.0.14': 63 | resolution: {integrity: sha512-hhlvTCicBvoBL8Bb73aRRiCbOvvPzmGvdo8nifpVw5ykCbv5suScbMiB3kcaBB/rRK4NhEdsNF5cba/BhF5n3Q==} 64 | engines: {node: '>=18.18.0', npm: '>=8.11.0'} 65 | 66 | '@php-wasm/scopes@1.0.14': 67 | resolution: {integrity: sha512-ceKv2Y0vx6bvtsLYeTBJeXaiSPS/Gw7NK+mhUjLDgRpuxVenl2Ek6/z6WQ8IU4gI+7Hg4mKIDF4N3Aj7gGjYxw==} 68 | engines: {node: '>=16.15.1', npm: '>=8.11.0'} 69 | 70 | '@php-wasm/stream-compression@1.0.14': 71 | resolution: {integrity: sha512-QZDrar+DpNaOYM1r1hIcCCfZ59IDKT57UeuuNYUuE+KBFafQapsPopPlUycafcvbhpbZTrQI+uMy5miaKRWf9A==} 72 | 73 | '@php-wasm/universal@1.0.14': 74 | resolution: {integrity: sha512-GuQ5E+oiXcaUsrbnQF+XI/UX0nV9DLh4LUEtJDUQAA9/KcEGkmMV+yRhFR2HfMW/qP4V1PlDlsG99DqruBjahw==} 75 | engines: {node: '>=18.18.0', npm: '>=8.11.0'} 76 | 77 | '@php-wasm/util@1.0.14': 78 | resolution: {integrity: sha512-PKUvOd0M+C3LXZ7SNuL4AZ4TUBDqcqEBemG4FRjT721deYMMiixaMo6sorQ4nf7ppWyhyYBCyqmb94sp1VW6xw==} 79 | engines: {node: '>=18.18.0', npm: '>=8.11.0'} 80 | 81 | '@sindresorhus/is@4.6.0': 82 | resolution: {integrity: sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==} 83 | engines: {node: '>=10'} 84 | 85 | '@szmarczak/http-timer@4.0.6': 86 | resolution: {integrity: sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==} 87 | engines: {node: '>=10'} 88 | 89 | '@types/cacheable-request@6.0.3': 90 | resolution: {integrity: sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==} 91 | 92 | '@types/http-cache-semantics@4.0.4': 93 | resolution: {integrity: sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==} 94 | 95 | '@types/keyv@3.1.4': 96 | resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==} 97 | 98 | '@types/node@20.17.9': 99 | resolution: {integrity: sha512-0JOXkRyLanfGPE2QRCwgxhzlBAvaRdCNMcvbd7jFfpmD4eEXll7LRwy5ymJmyeZqk7Nh7eD2LeUyQ68BbndmXw==} 100 | 101 | '@types/responselike@1.0.3': 102 | resolution: {integrity: sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==} 103 | 104 | '@types/yauzl@2.10.3': 105 | resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==} 106 | 107 | '@wp-playground/common@1.0.14': 108 | resolution: {integrity: sha512-5LH2Rf/sMM7wt/ZYJqwqaxA9s/esiNDtExOTzdTcA5hhQ1yCWLVpQRjV8w0QI0puz9DwmYF1QeK16a5YE0YUrw==} 109 | engines: {node: '>=18.18.0', npm: '>=8.11.0'} 110 | 111 | '@wp-playground/wordpress@1.0.14': 112 | resolution: {integrity: sha512-4KW/GYCm6Ot16xW3tr2/24fZMdogTRKi3NJB1DfQqOWKIggD6leVTUlvWCyx1uxJtTrEu+LCaO11NmMBvuTWMQ==} 113 | engines: {node: '>=18.18.0', npm: '>=8.11.0'} 114 | 115 | accepts@1.3.8: 116 | resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} 117 | engines: {node: '>= 0.6'} 118 | 119 | ansi-regex@5.0.1: 120 | resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} 121 | engines: {node: '>=8'} 122 | 123 | ansi-styles@4.3.0: 124 | resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} 125 | engines: {node: '>=8'} 126 | 127 | array-flatten@1.1.1: 128 | resolution: {integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==} 129 | 130 | asynckit@0.4.0: 131 | resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} 132 | 133 | axios@1.7.9: 134 | resolution: {integrity: sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==} 135 | 136 | body-parser@1.20.2: 137 | resolution: {integrity: sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==} 138 | engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} 139 | 140 | body-parser@1.20.3: 141 | resolution: {integrity: sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==} 142 | engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} 143 | 144 | boolean@3.2.0: 145 | resolution: {integrity: sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==} 146 | deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. 147 | 148 | buffer-crc32@0.2.13: 149 | resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==} 150 | 151 | bytes@3.1.2: 152 | resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} 153 | engines: {node: '>= 0.8'} 154 | 155 | cacheable-lookup@5.0.4: 156 | resolution: {integrity: sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==} 157 | engines: {node: '>=10.6.0'} 158 | 159 | cacheable-request@7.0.4: 160 | resolution: {integrity: sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==} 161 | engines: {node: '>=8'} 162 | 163 | call-bind-apply-helpers@1.0.0: 164 | resolution: {integrity: sha512-CCKAP2tkPau7D3GE8+V8R6sQubA9R5foIzGp+85EXCVSCivuxBNAWqcpn72PKYiIcqoViv/kcUDpaEIMBVi1lQ==} 165 | engines: {node: '>= 0.4'} 166 | 167 | call-bind@1.0.8: 168 | resolution: {integrity: sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==} 169 | engines: {node: '>= 0.4'} 170 | 171 | cliui@8.0.1: 172 | resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} 173 | engines: {node: '>=12'} 174 | 175 | clone-response@1.0.3: 176 | resolution: {integrity: sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==} 177 | 178 | color-convert@2.0.1: 179 | resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} 180 | engines: {node: '>=7.0.0'} 181 | 182 | color-name@1.1.4: 183 | resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} 184 | 185 | combined-stream@1.0.8: 186 | resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} 187 | engines: {node: '>= 0.8'} 188 | 189 | comlink@4.4.2: 190 | resolution: {integrity: sha512-OxGdvBmJuNKSCMO4NTl1L47VRp6xn2wG4F/2hYzB6tiCb709otOxtEYCSvK80PtjODfXXZu8ds+Nw5kVCjqd2g==} 191 | 192 | compressible@2.0.18: 193 | resolution: {integrity: sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==} 194 | engines: {node: '>= 0.6'} 195 | 196 | compression@1.7.5: 197 | resolution: {integrity: sha512-bQJ0YRck5ak3LgtnpKkiabX5pNF7tMUh1BSy2ZBOTh0Dim0BUu6aPPwByIns6/A5Prh8PufSPerMDUklpzes2Q==} 198 | engines: {node: '>= 0.8.0'} 199 | 200 | content-disposition@0.5.4: 201 | resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} 202 | engines: {node: '>= 0.6'} 203 | 204 | content-type@1.0.5: 205 | resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} 206 | engines: {node: '>= 0.6'} 207 | 208 | cookie-signature@1.0.6: 209 | resolution: {integrity: sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==} 210 | 211 | cookie@0.6.0: 212 | resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==} 213 | engines: {node: '>= 0.6'} 214 | 215 | cookie@0.7.1: 216 | resolution: {integrity: sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==} 217 | engines: {node: '>= 0.6'} 218 | 219 | data-uri-to-buffer@4.0.1: 220 | resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==} 221 | engines: {node: '>= 12'} 222 | 223 | debug@2.6.9: 224 | resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} 225 | peerDependencies: 226 | supports-color: '*' 227 | peerDependenciesMeta: 228 | supports-color: 229 | optional: true 230 | 231 | debug@4.4.0: 232 | resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==} 233 | engines: {node: '>=6.0'} 234 | peerDependencies: 235 | supports-color: '*' 236 | peerDependenciesMeta: 237 | supports-color: 238 | optional: true 239 | 240 | decompress-response@6.0.0: 241 | resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} 242 | engines: {node: '>=10'} 243 | 244 | defer-to-connect@2.0.1: 245 | resolution: {integrity: sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==} 246 | engines: {node: '>=10'} 247 | 248 | define-data-property@1.1.4: 249 | resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} 250 | engines: {node: '>= 0.4'} 251 | 252 | define-properties@1.2.1: 253 | resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} 254 | engines: {node: '>= 0.4'} 255 | 256 | delayed-stream@1.0.0: 257 | resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} 258 | engines: {node: '>=0.4.0'} 259 | 260 | depd@2.0.0: 261 | resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} 262 | engines: {node: '>= 0.8'} 263 | 264 | destroy@1.2.0: 265 | resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} 266 | engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} 267 | 268 | detect-node@2.1.0: 269 | resolution: {integrity: sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==} 270 | 271 | dunder-proto@1.0.0: 272 | resolution: {integrity: sha512-9+Sj30DIu+4KvHqMfLUGLFYL2PkURSYMVXJyXe92nFRvlYq5hBjLEhblKB+vkd/WVlUYMWigiY07T91Fkk0+4A==} 273 | engines: {node: '>= 0.4'} 274 | 275 | ee-first@1.1.1: 276 | resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} 277 | 278 | electron@30.5.1: 279 | resolution: {integrity: sha512-AhL7+mZ8Lg14iaNfoYTkXQ2qee8mmsQyllKdqxlpv/zrKgfxz6jNVtcRRbQtLxtF8yzcImWdfTQROpYiPumdbw==} 280 | engines: {node: '>= 12.20.55'} 281 | hasBin: true 282 | 283 | emoji-regex@8.0.0: 284 | resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} 285 | 286 | encodeurl@1.0.2: 287 | resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==} 288 | engines: {node: '>= 0.8'} 289 | 290 | encodeurl@2.0.0: 291 | resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} 292 | engines: {node: '>= 0.8'} 293 | 294 | end-of-stream@1.4.4: 295 | resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} 296 | 297 | env-paths@2.2.1: 298 | resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} 299 | engines: {node: '>=6'} 300 | 301 | es-define-property@1.0.1: 302 | resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} 303 | engines: {node: '>= 0.4'} 304 | 305 | es-errors@1.3.0: 306 | resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} 307 | engines: {node: '>= 0.4'} 308 | 309 | es6-error@4.1.1: 310 | resolution: {integrity: sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==} 311 | 312 | escalade@3.2.0: 313 | resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} 314 | engines: {node: '>=6'} 315 | 316 | escape-html@1.0.3: 317 | resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} 318 | 319 | escape-string-regexp@4.0.0: 320 | resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} 321 | engines: {node: '>=10'} 322 | 323 | etag@1.8.1: 324 | resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} 325 | engines: {node: '>= 0.6'} 326 | 327 | events@3.3.0: 328 | resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} 329 | engines: {node: '>=0.8.x'} 330 | 331 | express@4.19.2: 332 | resolution: {integrity: sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==} 333 | engines: {node: '>= 0.10.0'} 334 | 335 | express@4.21.2: 336 | resolution: {integrity: sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==} 337 | engines: {node: '>= 0.10.0'} 338 | 339 | extract-zip@2.0.1: 340 | resolution: {integrity: sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==} 341 | engines: {node: '>= 10.17.0'} 342 | hasBin: true 343 | 344 | fd-slicer@1.1.0: 345 | resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==} 346 | 347 | fetch-blob@3.2.0: 348 | resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==} 349 | engines: {node: ^12.20 || >= 14.13} 350 | 351 | file-url@4.0.0: 352 | resolution: {integrity: sha512-vRCdScQ6j3Ku6Kd7W1kZk9c++5SqD6Xz5Jotrjr/nkY714M14RFHy/AAVA2WQvpsqVAVgTbDrYyBpU205F0cLw==} 353 | engines: {node: '>=12'} 354 | 355 | finalhandler@1.2.0: 356 | resolution: {integrity: sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==} 357 | engines: {node: '>= 0.8'} 358 | 359 | finalhandler@1.3.1: 360 | resolution: {integrity: sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==} 361 | engines: {node: '>= 0.8'} 362 | 363 | follow-redirects@1.15.9: 364 | resolution: {integrity: sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==} 365 | engines: {node: '>=4.0'} 366 | peerDependencies: 367 | debug: '*' 368 | peerDependenciesMeta: 369 | debug: 370 | optional: true 371 | 372 | form-data@4.0.1: 373 | resolution: {integrity: sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==} 374 | engines: {node: '>= 6'} 375 | 376 | formdata-polyfill@4.0.10: 377 | resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==} 378 | engines: {node: '>=12.20.0'} 379 | 380 | forwarded@0.2.0: 381 | resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} 382 | engines: {node: '>= 0.6'} 383 | 384 | fresh@0.5.2: 385 | resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} 386 | engines: {node: '>= 0.6'} 387 | 388 | fs-extra@8.1.0: 389 | resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==} 390 | engines: {node: '>=6 <7 || >=8'} 391 | 392 | function-bind@1.1.2: 393 | resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} 394 | 395 | get-caller-file@2.0.5: 396 | resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} 397 | engines: {node: 6.* || 8.* || >= 10.*} 398 | 399 | get-intrinsic@1.2.5: 400 | resolution: {integrity: sha512-Y4+pKa7XeRUPWFNvOOYHkRYrfzW07oraURSvjDmRVOJ748OrVmeXtpE4+GCEHncjCjkTxPNRt8kEbxDhsn6VTg==} 401 | engines: {node: '>= 0.4'} 402 | 403 | get-stream@5.2.0: 404 | resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==} 405 | engines: {node: '>=8'} 406 | 407 | global-agent@3.0.0: 408 | resolution: {integrity: sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==} 409 | engines: {node: '>=10.0'} 410 | 411 | globalthis@1.0.4: 412 | resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} 413 | engines: {node: '>= 0.4'} 414 | 415 | gopd@1.2.0: 416 | resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} 417 | engines: {node: '>= 0.4'} 418 | 419 | got@11.8.6: 420 | resolution: {integrity: sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==} 421 | engines: {node: '>=10.19.0'} 422 | 423 | graceful-fs@4.2.11: 424 | resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} 425 | 426 | has-property-descriptors@1.0.2: 427 | resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} 428 | 429 | has-symbols@1.1.0: 430 | resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} 431 | engines: {node: '>= 0.4'} 432 | 433 | hasown@2.0.2: 434 | resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} 435 | engines: {node: '>= 0.4'} 436 | 437 | http-cache-semantics@4.1.1: 438 | resolution: {integrity: sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==} 439 | 440 | http-errors@2.0.0: 441 | resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} 442 | engines: {node: '>= 0.8'} 443 | 444 | http2-wrapper@1.0.3: 445 | resolution: {integrity: sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==} 446 | engines: {node: '>=10.19.0'} 447 | 448 | iconv-lite@0.4.24: 449 | resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} 450 | engines: {node: '>=0.10.0'} 451 | 452 | inherits@2.0.4: 453 | resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} 454 | 455 | ini@4.1.2: 456 | resolution: {integrity: sha512-AMB1mvwR1pyBFY/nSevUX6y8nJWS63/SzUKD3JyQn97s4xgIdgQPT75IRouIiBAN4yLQBUShNYVW0+UG25daCw==} 457 | engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} 458 | 459 | ipaddr.js@1.9.1: 460 | resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} 461 | engines: {node: '>= 0.10'} 462 | 463 | is-fullwidth-code-point@3.0.0: 464 | resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} 465 | engines: {node: '>=8'} 466 | 467 | json-buffer@3.0.1: 468 | resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} 469 | 470 | json-stringify-safe@5.0.1: 471 | resolution: {integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==} 472 | 473 | jsonfile@4.0.0: 474 | resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} 475 | 476 | keyv@4.5.4: 477 | resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} 478 | 479 | lowercase-keys@2.0.0: 480 | resolution: {integrity: sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==} 481 | engines: {node: '>=8'} 482 | 483 | matcher@3.0.0: 484 | resolution: {integrity: sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==} 485 | engines: {node: '>=10'} 486 | 487 | media-typer@0.3.0: 488 | resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} 489 | engines: {node: '>= 0.6'} 490 | 491 | merge-descriptors@1.0.1: 492 | resolution: {integrity: sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==} 493 | 494 | merge-descriptors@1.0.3: 495 | resolution: {integrity: sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==} 496 | 497 | methods@1.1.2: 498 | resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==} 499 | engines: {node: '>= 0.6'} 500 | 501 | mime-db@1.52.0: 502 | resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} 503 | engines: {node: '>= 0.6'} 504 | 505 | mime-db@1.53.0: 506 | resolution: {integrity: sha512-oHlN/w+3MQ3rba9rqFr6V/ypF10LSkdwUysQL7GkXoTgIWeV+tcXGA852TBxH+gsh8UWoyhR1hKcoMJTuWflpg==} 507 | engines: {node: '>= 0.6'} 508 | 509 | mime-types@2.1.35: 510 | resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} 511 | engines: {node: '>= 0.6'} 512 | 513 | mime@1.6.0: 514 | resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} 515 | engines: {node: '>=4'} 516 | hasBin: true 517 | 518 | mimic-response@1.0.1: 519 | resolution: {integrity: sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==} 520 | engines: {node: '>=4'} 521 | 522 | mimic-response@3.1.0: 523 | resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} 524 | engines: {node: '>=10'} 525 | 526 | ms@2.0.0: 527 | resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} 528 | 529 | ms@2.1.3: 530 | resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} 531 | 532 | negotiator@0.6.3: 533 | resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} 534 | engines: {node: '>= 0.6'} 535 | 536 | negotiator@0.6.4: 537 | resolution: {integrity: sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==} 538 | engines: {node: '>= 0.6'} 539 | 540 | node-domexception@1.0.0: 541 | resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} 542 | engines: {node: '>=10.5.0'} 543 | 544 | node-fetch@3.3.2: 545 | resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==} 546 | engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} 547 | 548 | normalize-url@6.1.0: 549 | resolution: {integrity: sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==} 550 | engines: {node: '>=10'} 551 | 552 | object-inspect@1.13.3: 553 | resolution: {integrity: sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==} 554 | engines: {node: '>= 0.4'} 555 | 556 | object-keys@1.1.1: 557 | resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} 558 | engines: {node: '>= 0.4'} 559 | 560 | on-finished@2.4.1: 561 | resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} 562 | engines: {node: '>= 0.8'} 563 | 564 | on-headers@1.0.2: 565 | resolution: {integrity: sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==} 566 | engines: {node: '>= 0.8'} 567 | 568 | once@1.4.0: 569 | resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} 570 | 571 | p-cancelable@2.1.1: 572 | resolution: {integrity: sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==} 573 | engines: {node: '>=8'} 574 | 575 | parseurl@1.3.3: 576 | resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} 577 | engines: {node: '>= 0.8'} 578 | 579 | path-to-regexp@0.1.12: 580 | resolution: {integrity: sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==} 581 | 582 | path-to-regexp@0.1.7: 583 | resolution: {integrity: sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==} 584 | 585 | pend@1.2.0: 586 | resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==} 587 | 588 | progress@2.0.3: 589 | resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} 590 | engines: {node: '>=0.4.0'} 591 | 592 | proxy-addr@2.0.7: 593 | resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} 594 | engines: {node: '>= 0.10'} 595 | 596 | proxy-from-env@1.1.0: 597 | resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} 598 | 599 | pump@3.0.2: 600 | resolution: {integrity: sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==} 601 | 602 | qs@6.11.0: 603 | resolution: {integrity: sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==} 604 | engines: {node: '>=0.6'} 605 | 606 | qs@6.13.0: 607 | resolution: {integrity: sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==} 608 | engines: {node: '>=0.6'} 609 | 610 | quick-lru@5.1.1: 611 | resolution: {integrity: sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==} 612 | engines: {node: '>=10'} 613 | 614 | range-parser@1.2.1: 615 | resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} 616 | engines: {node: '>= 0.6'} 617 | 618 | raw-body@2.5.2: 619 | resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==} 620 | engines: {node: '>= 0.8'} 621 | 622 | require-directory@2.1.1: 623 | resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} 624 | engines: {node: '>=0.10.0'} 625 | 626 | resolve-alpn@1.2.1: 627 | resolution: {integrity: sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==} 628 | 629 | responselike@2.0.1: 630 | resolution: {integrity: sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==} 631 | 632 | roarr@2.15.4: 633 | resolution: {integrity: sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==} 634 | engines: {node: '>=8.0'} 635 | 636 | safe-buffer@5.2.1: 637 | resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} 638 | 639 | safer-buffer@2.1.2: 640 | resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} 641 | 642 | semver-compare@1.0.0: 643 | resolution: {integrity: sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==} 644 | 645 | semver@6.3.1: 646 | resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} 647 | hasBin: true 648 | 649 | semver@7.6.3: 650 | resolution: {integrity: sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==} 651 | engines: {node: '>=10'} 652 | hasBin: true 653 | 654 | send@0.18.0: 655 | resolution: {integrity: sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==} 656 | engines: {node: '>= 0.8.0'} 657 | 658 | send@0.19.0: 659 | resolution: {integrity: sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==} 660 | engines: {node: '>= 0.8.0'} 661 | 662 | serialize-error@7.0.1: 663 | resolution: {integrity: sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==} 664 | engines: {node: '>=10'} 665 | 666 | serve-static@1.15.0: 667 | resolution: {integrity: sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==} 668 | engines: {node: '>= 0.8.0'} 669 | 670 | serve-static@1.16.2: 671 | resolution: {integrity: sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==} 672 | engines: {node: '>= 0.8.0'} 673 | 674 | set-function-length@1.2.2: 675 | resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} 676 | engines: {node: '>= 0.4'} 677 | 678 | setprototypeof@1.2.0: 679 | resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} 680 | 681 | side-channel@1.0.6: 682 | resolution: {integrity: sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==} 683 | engines: {node: '>= 0.4'} 684 | 685 | sprintf-js@1.1.3: 686 | resolution: {integrity: sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==} 687 | 688 | statuses@2.0.1: 689 | resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} 690 | engines: {node: '>= 0.8'} 691 | 692 | string-width@4.2.3: 693 | resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} 694 | engines: {node: '>=8'} 695 | 696 | strip-ansi@6.0.1: 697 | resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} 698 | engines: {node: '>=8'} 699 | 700 | sumchecker@3.0.1: 701 | resolution: {integrity: sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg==} 702 | engines: {node: '>= 8.0'} 703 | 704 | toidentifier@1.0.1: 705 | resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} 706 | engines: {node: '>=0.6'} 707 | 708 | type-fest@0.13.1: 709 | resolution: {integrity: sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==} 710 | engines: {node: '>=10'} 711 | 712 | type-is@1.6.18: 713 | resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} 714 | engines: {node: '>= 0.6'} 715 | 716 | undici-types@6.19.8: 717 | resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} 718 | 719 | universalify@0.1.2: 720 | resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} 721 | engines: {node: '>= 4.0.0'} 722 | 723 | unpipe@1.0.0: 724 | resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} 725 | engines: {node: '>= 0.8'} 726 | 727 | utils-merge@1.0.1: 728 | resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} 729 | engines: {node: '>= 0.4.0'} 730 | 731 | vary@1.1.2: 732 | resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} 733 | engines: {node: '>= 0.8'} 734 | 735 | wasm-feature-detect@1.8.0: 736 | resolution: {integrity: sha512-zksaLKM2fVlnB5jQQDqKXXwYHLQUVH9es+5TOOHwGOVJOCeRBCiPjwSg+3tN2AdTCzjgli4jijCH290kXb/zWQ==} 737 | 738 | web-streams-polyfill@3.3.3: 739 | resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==} 740 | engines: {node: '>= 8'} 741 | 742 | wrap-ansi@7.0.0: 743 | resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} 744 | engines: {node: '>=10'} 745 | 746 | wrappy@1.0.2: 747 | resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} 748 | 749 | ws@8.18.0: 750 | resolution: {integrity: sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==} 751 | engines: {node: '>=10.0.0'} 752 | peerDependencies: 753 | bufferutil: ^4.0.1 754 | utf-8-validate: '>=5.0.2' 755 | peerDependenciesMeta: 756 | bufferutil: 757 | optional: true 758 | utf-8-validate: 759 | optional: true 760 | 761 | y18n@5.0.8: 762 | resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} 763 | engines: {node: '>=10'} 764 | 765 | yargs-parser@21.1.1: 766 | resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} 767 | engines: {node: '>=12'} 768 | 769 | yargs@17.7.2: 770 | resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} 771 | engines: {node: '>=12'} 772 | 773 | yauzl@2.10.0: 774 | resolution: {integrity: sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==} 775 | 776 | snapshots: 777 | 778 | '@electron/get@2.0.3': 779 | dependencies: 780 | debug: 4.4.0 781 | env-paths: 2.2.1 782 | fs-extra: 8.1.0 783 | got: 11.8.6 784 | progress: 2.0.3 785 | semver: 6.3.1 786 | sumchecker: 3.0.1 787 | optionalDependencies: 788 | global-agent: 3.0.0 789 | transitivePeerDependencies: 790 | - supports-color 791 | 792 | '@php-wasm/logger@1.0.14': 793 | dependencies: 794 | '@php-wasm/node-polyfills': 1.0.14 795 | 796 | '@php-wasm/node-polyfills@1.0.14': {} 797 | 798 | '@php-wasm/node@1.0.14': 799 | dependencies: 800 | '@php-wasm/logger': 1.0.14 801 | '@php-wasm/node-polyfills': 1.0.14 802 | '@php-wasm/universal': 1.0.14 803 | '@php-wasm/util': 1.0.14 804 | '@wp-playground/common': 1.0.14 805 | '@wp-playground/wordpress': 1.0.14 806 | comlink: 4.4.2 807 | events: 3.3.0 808 | express: 4.19.2 809 | ini: 4.1.2 810 | wasm-feature-detect: 1.8.0 811 | ws: 8.18.0 812 | yargs: 17.7.2 813 | transitivePeerDependencies: 814 | - bufferutil 815 | - supports-color 816 | - utf-8-validate 817 | 818 | '@php-wasm/progress@1.0.14': 819 | dependencies: 820 | '@php-wasm/logger': 1.0.14 821 | '@php-wasm/node-polyfills': 1.0.14 822 | 823 | '@php-wasm/scopes@1.0.14': {} 824 | 825 | '@php-wasm/stream-compression@1.0.14': 826 | dependencies: 827 | '@php-wasm/node-polyfills': 1.0.14 828 | '@php-wasm/util': 1.0.14 829 | 830 | '@php-wasm/universal@1.0.14': 831 | dependencies: 832 | '@php-wasm/logger': 1.0.14 833 | '@php-wasm/node-polyfills': 1.0.14 834 | '@php-wasm/progress': 1.0.14 835 | '@php-wasm/stream-compression': 1.0.14 836 | '@php-wasm/util': 1.0.14 837 | comlink: 4.4.2 838 | ini: 4.1.2 839 | 840 | '@php-wasm/util@1.0.14': {} 841 | 842 | '@sindresorhus/is@4.6.0': {} 843 | 844 | '@szmarczak/http-timer@4.0.6': 845 | dependencies: 846 | defer-to-connect: 2.0.1 847 | 848 | '@types/cacheable-request@6.0.3': 849 | dependencies: 850 | '@types/http-cache-semantics': 4.0.4 851 | '@types/keyv': 3.1.4 852 | '@types/node': 20.17.9 853 | '@types/responselike': 1.0.3 854 | 855 | '@types/http-cache-semantics@4.0.4': {} 856 | 857 | '@types/keyv@3.1.4': 858 | dependencies: 859 | '@types/node': 20.17.9 860 | 861 | '@types/node@20.17.9': 862 | dependencies: 863 | undici-types: 6.19.8 864 | 865 | '@types/responselike@1.0.3': 866 | dependencies: 867 | '@types/node': 20.17.9 868 | 869 | '@types/yauzl@2.10.3': 870 | dependencies: 871 | '@types/node': 20.17.9 872 | optional: true 873 | 874 | '@wp-playground/common@1.0.14': 875 | dependencies: 876 | '@php-wasm/universal': 1.0.14 877 | '@php-wasm/util': 1.0.14 878 | comlink: 4.4.2 879 | ini: 4.1.2 880 | 881 | '@wp-playground/wordpress@1.0.14': 882 | dependencies: 883 | '@php-wasm/logger': 1.0.14 884 | '@php-wasm/node': 1.0.14 885 | '@php-wasm/universal': 1.0.14 886 | '@php-wasm/util': 1.0.14 887 | '@wp-playground/common': 1.0.14 888 | comlink: 4.4.2 889 | events: 3.3.0 890 | express: 4.19.2 891 | ini: 4.1.2 892 | wasm-feature-detect: 1.8.0 893 | ws: 8.18.0 894 | yargs: 17.7.2 895 | transitivePeerDependencies: 896 | - bufferutil 897 | - supports-color 898 | - utf-8-validate 899 | 900 | accepts@1.3.8: 901 | dependencies: 902 | mime-types: 2.1.35 903 | negotiator: 0.6.3 904 | 905 | ansi-regex@5.0.1: {} 906 | 907 | ansi-styles@4.3.0: 908 | dependencies: 909 | color-convert: 2.0.1 910 | 911 | array-flatten@1.1.1: {} 912 | 913 | asynckit@0.4.0: {} 914 | 915 | axios@1.7.9: 916 | dependencies: 917 | follow-redirects: 1.15.9 918 | form-data: 4.0.1 919 | proxy-from-env: 1.1.0 920 | transitivePeerDependencies: 921 | - debug 922 | 923 | body-parser@1.20.2: 924 | dependencies: 925 | bytes: 3.1.2 926 | content-type: 1.0.5 927 | debug: 2.6.9 928 | depd: 2.0.0 929 | destroy: 1.2.0 930 | http-errors: 2.0.0 931 | iconv-lite: 0.4.24 932 | on-finished: 2.4.1 933 | qs: 6.11.0 934 | raw-body: 2.5.2 935 | type-is: 1.6.18 936 | unpipe: 1.0.0 937 | transitivePeerDependencies: 938 | - supports-color 939 | 940 | body-parser@1.20.3: 941 | dependencies: 942 | bytes: 3.1.2 943 | content-type: 1.0.5 944 | debug: 2.6.9 945 | depd: 2.0.0 946 | destroy: 1.2.0 947 | http-errors: 2.0.0 948 | iconv-lite: 0.4.24 949 | on-finished: 2.4.1 950 | qs: 6.13.0 951 | raw-body: 2.5.2 952 | type-is: 1.6.18 953 | unpipe: 1.0.0 954 | transitivePeerDependencies: 955 | - supports-color 956 | 957 | boolean@3.2.0: 958 | optional: true 959 | 960 | buffer-crc32@0.2.13: {} 961 | 962 | bytes@3.1.2: {} 963 | 964 | cacheable-lookup@5.0.4: {} 965 | 966 | cacheable-request@7.0.4: 967 | dependencies: 968 | clone-response: 1.0.3 969 | get-stream: 5.2.0 970 | http-cache-semantics: 4.1.1 971 | keyv: 4.5.4 972 | lowercase-keys: 2.0.0 973 | normalize-url: 6.1.0 974 | responselike: 2.0.1 975 | 976 | call-bind-apply-helpers@1.0.0: 977 | dependencies: 978 | es-errors: 1.3.0 979 | function-bind: 1.1.2 980 | 981 | call-bind@1.0.8: 982 | dependencies: 983 | call-bind-apply-helpers: 1.0.0 984 | es-define-property: 1.0.1 985 | get-intrinsic: 1.2.5 986 | set-function-length: 1.2.2 987 | 988 | cliui@8.0.1: 989 | dependencies: 990 | string-width: 4.2.3 991 | strip-ansi: 6.0.1 992 | wrap-ansi: 7.0.0 993 | 994 | clone-response@1.0.3: 995 | dependencies: 996 | mimic-response: 1.0.1 997 | 998 | color-convert@2.0.1: 999 | dependencies: 1000 | color-name: 1.1.4 1001 | 1002 | color-name@1.1.4: {} 1003 | 1004 | combined-stream@1.0.8: 1005 | dependencies: 1006 | delayed-stream: 1.0.0 1007 | 1008 | comlink@4.4.2: {} 1009 | 1010 | compressible@2.0.18: 1011 | dependencies: 1012 | mime-db: 1.53.0 1013 | 1014 | compression@1.7.5: 1015 | dependencies: 1016 | bytes: 3.1.2 1017 | compressible: 2.0.18 1018 | debug: 2.6.9 1019 | negotiator: 0.6.4 1020 | on-headers: 1.0.2 1021 | safe-buffer: 5.2.1 1022 | vary: 1.1.2 1023 | transitivePeerDependencies: 1024 | - supports-color 1025 | 1026 | content-disposition@0.5.4: 1027 | dependencies: 1028 | safe-buffer: 5.2.1 1029 | 1030 | content-type@1.0.5: {} 1031 | 1032 | cookie-signature@1.0.6: {} 1033 | 1034 | cookie@0.6.0: {} 1035 | 1036 | cookie@0.7.1: {} 1037 | 1038 | data-uri-to-buffer@4.0.1: {} 1039 | 1040 | debug@2.6.9: 1041 | dependencies: 1042 | ms: 2.0.0 1043 | 1044 | debug@4.4.0: 1045 | dependencies: 1046 | ms: 2.1.3 1047 | 1048 | decompress-response@6.0.0: 1049 | dependencies: 1050 | mimic-response: 3.1.0 1051 | 1052 | defer-to-connect@2.0.1: {} 1053 | 1054 | define-data-property@1.1.4: 1055 | dependencies: 1056 | es-define-property: 1.0.1 1057 | es-errors: 1.3.0 1058 | gopd: 1.2.0 1059 | 1060 | define-properties@1.2.1: 1061 | dependencies: 1062 | define-data-property: 1.1.4 1063 | has-property-descriptors: 1.0.2 1064 | object-keys: 1.1.1 1065 | optional: true 1066 | 1067 | delayed-stream@1.0.0: {} 1068 | 1069 | depd@2.0.0: {} 1070 | 1071 | destroy@1.2.0: {} 1072 | 1073 | detect-node@2.1.0: 1074 | optional: true 1075 | 1076 | dunder-proto@1.0.0: 1077 | dependencies: 1078 | call-bind-apply-helpers: 1.0.0 1079 | es-errors: 1.3.0 1080 | gopd: 1.2.0 1081 | 1082 | ee-first@1.1.1: {} 1083 | 1084 | electron@30.5.1: 1085 | dependencies: 1086 | '@electron/get': 2.0.3 1087 | '@types/node': 20.17.9 1088 | extract-zip: 2.0.1 1089 | transitivePeerDependencies: 1090 | - supports-color 1091 | 1092 | emoji-regex@8.0.0: {} 1093 | 1094 | encodeurl@1.0.2: {} 1095 | 1096 | encodeurl@2.0.0: {} 1097 | 1098 | end-of-stream@1.4.4: 1099 | dependencies: 1100 | once: 1.4.0 1101 | 1102 | env-paths@2.2.1: {} 1103 | 1104 | es-define-property@1.0.1: {} 1105 | 1106 | es-errors@1.3.0: {} 1107 | 1108 | es6-error@4.1.1: 1109 | optional: true 1110 | 1111 | escalade@3.2.0: {} 1112 | 1113 | escape-html@1.0.3: {} 1114 | 1115 | escape-string-regexp@4.0.0: 1116 | optional: true 1117 | 1118 | etag@1.8.1: {} 1119 | 1120 | events@3.3.0: {} 1121 | 1122 | express@4.19.2: 1123 | dependencies: 1124 | accepts: 1.3.8 1125 | array-flatten: 1.1.1 1126 | body-parser: 1.20.2 1127 | content-disposition: 0.5.4 1128 | content-type: 1.0.5 1129 | cookie: 0.6.0 1130 | cookie-signature: 1.0.6 1131 | debug: 2.6.9 1132 | depd: 2.0.0 1133 | encodeurl: 1.0.2 1134 | escape-html: 1.0.3 1135 | etag: 1.8.1 1136 | finalhandler: 1.2.0 1137 | fresh: 0.5.2 1138 | http-errors: 2.0.0 1139 | merge-descriptors: 1.0.1 1140 | methods: 1.1.2 1141 | on-finished: 2.4.1 1142 | parseurl: 1.3.3 1143 | path-to-regexp: 0.1.7 1144 | proxy-addr: 2.0.7 1145 | qs: 6.11.0 1146 | range-parser: 1.2.1 1147 | safe-buffer: 5.2.1 1148 | send: 0.18.0 1149 | serve-static: 1.15.0 1150 | setprototypeof: 1.2.0 1151 | statuses: 2.0.1 1152 | type-is: 1.6.18 1153 | utils-merge: 1.0.1 1154 | vary: 1.1.2 1155 | transitivePeerDependencies: 1156 | - supports-color 1157 | 1158 | express@4.21.2: 1159 | dependencies: 1160 | accepts: 1.3.8 1161 | array-flatten: 1.1.1 1162 | body-parser: 1.20.3 1163 | content-disposition: 0.5.4 1164 | content-type: 1.0.5 1165 | cookie: 0.7.1 1166 | cookie-signature: 1.0.6 1167 | debug: 2.6.9 1168 | depd: 2.0.0 1169 | encodeurl: 2.0.0 1170 | escape-html: 1.0.3 1171 | etag: 1.8.1 1172 | finalhandler: 1.3.1 1173 | fresh: 0.5.2 1174 | http-errors: 2.0.0 1175 | merge-descriptors: 1.0.3 1176 | methods: 1.1.2 1177 | on-finished: 2.4.1 1178 | parseurl: 1.3.3 1179 | path-to-regexp: 0.1.12 1180 | proxy-addr: 2.0.7 1181 | qs: 6.13.0 1182 | range-parser: 1.2.1 1183 | safe-buffer: 5.2.1 1184 | send: 0.19.0 1185 | serve-static: 1.16.2 1186 | setprototypeof: 1.2.0 1187 | statuses: 2.0.1 1188 | type-is: 1.6.18 1189 | utils-merge: 1.0.1 1190 | vary: 1.1.2 1191 | transitivePeerDependencies: 1192 | - supports-color 1193 | 1194 | extract-zip@2.0.1: 1195 | dependencies: 1196 | debug: 4.4.0 1197 | get-stream: 5.2.0 1198 | yauzl: 2.10.0 1199 | optionalDependencies: 1200 | '@types/yauzl': 2.10.3 1201 | transitivePeerDependencies: 1202 | - supports-color 1203 | 1204 | fd-slicer@1.1.0: 1205 | dependencies: 1206 | pend: 1.2.0 1207 | 1208 | fetch-blob@3.2.0: 1209 | dependencies: 1210 | node-domexception: 1.0.0 1211 | web-streams-polyfill: 3.3.3 1212 | 1213 | file-url@4.0.0: {} 1214 | 1215 | finalhandler@1.2.0: 1216 | dependencies: 1217 | debug: 2.6.9 1218 | encodeurl: 1.0.2 1219 | escape-html: 1.0.3 1220 | on-finished: 2.4.1 1221 | parseurl: 1.3.3 1222 | statuses: 2.0.1 1223 | unpipe: 1.0.0 1224 | transitivePeerDependencies: 1225 | - supports-color 1226 | 1227 | finalhandler@1.3.1: 1228 | dependencies: 1229 | debug: 2.6.9 1230 | encodeurl: 2.0.0 1231 | escape-html: 1.0.3 1232 | on-finished: 2.4.1 1233 | parseurl: 1.3.3 1234 | statuses: 2.0.1 1235 | unpipe: 1.0.0 1236 | transitivePeerDependencies: 1237 | - supports-color 1238 | 1239 | follow-redirects@1.15.9: {} 1240 | 1241 | form-data@4.0.1: 1242 | dependencies: 1243 | asynckit: 0.4.0 1244 | combined-stream: 1.0.8 1245 | mime-types: 2.1.35 1246 | 1247 | formdata-polyfill@4.0.10: 1248 | dependencies: 1249 | fetch-blob: 3.2.0 1250 | 1251 | forwarded@0.2.0: {} 1252 | 1253 | fresh@0.5.2: {} 1254 | 1255 | fs-extra@8.1.0: 1256 | dependencies: 1257 | graceful-fs: 4.2.11 1258 | jsonfile: 4.0.0 1259 | universalify: 0.1.2 1260 | 1261 | function-bind@1.1.2: {} 1262 | 1263 | get-caller-file@2.0.5: {} 1264 | 1265 | get-intrinsic@1.2.5: 1266 | dependencies: 1267 | call-bind-apply-helpers: 1.0.0 1268 | dunder-proto: 1.0.0 1269 | es-define-property: 1.0.1 1270 | es-errors: 1.3.0 1271 | function-bind: 1.1.2 1272 | gopd: 1.2.0 1273 | has-symbols: 1.1.0 1274 | hasown: 2.0.2 1275 | 1276 | get-stream@5.2.0: 1277 | dependencies: 1278 | pump: 3.0.2 1279 | 1280 | global-agent@3.0.0: 1281 | dependencies: 1282 | boolean: 3.2.0 1283 | es6-error: 4.1.1 1284 | matcher: 3.0.0 1285 | roarr: 2.15.4 1286 | semver: 7.6.3 1287 | serialize-error: 7.0.1 1288 | optional: true 1289 | 1290 | globalthis@1.0.4: 1291 | dependencies: 1292 | define-properties: 1.2.1 1293 | gopd: 1.2.0 1294 | optional: true 1295 | 1296 | gopd@1.2.0: {} 1297 | 1298 | got@11.8.6: 1299 | dependencies: 1300 | '@sindresorhus/is': 4.6.0 1301 | '@szmarczak/http-timer': 4.0.6 1302 | '@types/cacheable-request': 6.0.3 1303 | '@types/responselike': 1.0.3 1304 | cacheable-lookup: 5.0.4 1305 | cacheable-request: 7.0.4 1306 | decompress-response: 6.0.0 1307 | http2-wrapper: 1.0.3 1308 | lowercase-keys: 2.0.0 1309 | p-cancelable: 2.1.1 1310 | responselike: 2.0.1 1311 | 1312 | graceful-fs@4.2.11: {} 1313 | 1314 | has-property-descriptors@1.0.2: 1315 | dependencies: 1316 | es-define-property: 1.0.1 1317 | 1318 | has-symbols@1.1.0: {} 1319 | 1320 | hasown@2.0.2: 1321 | dependencies: 1322 | function-bind: 1.1.2 1323 | 1324 | http-cache-semantics@4.1.1: {} 1325 | 1326 | http-errors@2.0.0: 1327 | dependencies: 1328 | depd: 2.0.0 1329 | inherits: 2.0.4 1330 | setprototypeof: 1.2.0 1331 | statuses: 2.0.1 1332 | toidentifier: 1.0.1 1333 | 1334 | http2-wrapper@1.0.3: 1335 | dependencies: 1336 | quick-lru: 5.1.1 1337 | resolve-alpn: 1.2.1 1338 | 1339 | iconv-lite@0.4.24: 1340 | dependencies: 1341 | safer-buffer: 2.1.2 1342 | 1343 | inherits@2.0.4: {} 1344 | 1345 | ini@4.1.2: {} 1346 | 1347 | ipaddr.js@1.9.1: {} 1348 | 1349 | is-fullwidth-code-point@3.0.0: {} 1350 | 1351 | json-buffer@3.0.1: {} 1352 | 1353 | json-stringify-safe@5.0.1: 1354 | optional: true 1355 | 1356 | jsonfile@4.0.0: 1357 | optionalDependencies: 1358 | graceful-fs: 4.2.11 1359 | 1360 | keyv@4.5.4: 1361 | dependencies: 1362 | json-buffer: 3.0.1 1363 | 1364 | lowercase-keys@2.0.0: {} 1365 | 1366 | matcher@3.0.0: 1367 | dependencies: 1368 | escape-string-regexp: 4.0.0 1369 | optional: true 1370 | 1371 | media-typer@0.3.0: {} 1372 | 1373 | merge-descriptors@1.0.1: {} 1374 | 1375 | merge-descriptors@1.0.3: {} 1376 | 1377 | methods@1.1.2: {} 1378 | 1379 | mime-db@1.52.0: {} 1380 | 1381 | mime-db@1.53.0: {} 1382 | 1383 | mime-types@2.1.35: 1384 | dependencies: 1385 | mime-db: 1.52.0 1386 | 1387 | mime@1.6.0: {} 1388 | 1389 | mimic-response@1.0.1: {} 1390 | 1391 | mimic-response@3.1.0: {} 1392 | 1393 | ms@2.0.0: {} 1394 | 1395 | ms@2.1.3: {} 1396 | 1397 | negotiator@0.6.3: {} 1398 | 1399 | negotiator@0.6.4: {} 1400 | 1401 | node-domexception@1.0.0: {} 1402 | 1403 | node-fetch@3.3.2: 1404 | dependencies: 1405 | data-uri-to-buffer: 4.0.1 1406 | fetch-blob: 3.2.0 1407 | formdata-polyfill: 4.0.10 1408 | 1409 | normalize-url@6.1.0: {} 1410 | 1411 | object-inspect@1.13.3: {} 1412 | 1413 | object-keys@1.1.1: 1414 | optional: true 1415 | 1416 | on-finished@2.4.1: 1417 | dependencies: 1418 | ee-first: 1.1.1 1419 | 1420 | on-headers@1.0.2: {} 1421 | 1422 | once@1.4.0: 1423 | dependencies: 1424 | wrappy: 1.0.2 1425 | 1426 | p-cancelable@2.1.1: {} 1427 | 1428 | parseurl@1.3.3: {} 1429 | 1430 | path-to-regexp@0.1.12: {} 1431 | 1432 | path-to-regexp@0.1.7: {} 1433 | 1434 | pend@1.2.0: {} 1435 | 1436 | progress@2.0.3: {} 1437 | 1438 | proxy-addr@2.0.7: 1439 | dependencies: 1440 | forwarded: 0.2.0 1441 | ipaddr.js: 1.9.1 1442 | 1443 | proxy-from-env@1.1.0: {} 1444 | 1445 | pump@3.0.2: 1446 | dependencies: 1447 | end-of-stream: 1.4.4 1448 | once: 1.4.0 1449 | 1450 | qs@6.11.0: 1451 | dependencies: 1452 | side-channel: 1.0.6 1453 | 1454 | qs@6.13.0: 1455 | dependencies: 1456 | side-channel: 1.0.6 1457 | 1458 | quick-lru@5.1.1: {} 1459 | 1460 | range-parser@1.2.1: {} 1461 | 1462 | raw-body@2.5.2: 1463 | dependencies: 1464 | bytes: 3.1.2 1465 | http-errors: 2.0.0 1466 | iconv-lite: 0.4.24 1467 | unpipe: 1.0.0 1468 | 1469 | require-directory@2.1.1: {} 1470 | 1471 | resolve-alpn@1.2.1: {} 1472 | 1473 | responselike@2.0.1: 1474 | dependencies: 1475 | lowercase-keys: 2.0.0 1476 | 1477 | roarr@2.15.4: 1478 | dependencies: 1479 | boolean: 3.2.0 1480 | detect-node: 2.1.0 1481 | globalthis: 1.0.4 1482 | json-stringify-safe: 5.0.1 1483 | semver-compare: 1.0.0 1484 | sprintf-js: 1.1.3 1485 | optional: true 1486 | 1487 | safe-buffer@5.2.1: {} 1488 | 1489 | safer-buffer@2.1.2: {} 1490 | 1491 | semver-compare@1.0.0: 1492 | optional: true 1493 | 1494 | semver@6.3.1: {} 1495 | 1496 | semver@7.6.3: 1497 | optional: true 1498 | 1499 | send@0.18.0: 1500 | dependencies: 1501 | debug: 2.6.9 1502 | depd: 2.0.0 1503 | destroy: 1.2.0 1504 | encodeurl: 1.0.2 1505 | escape-html: 1.0.3 1506 | etag: 1.8.1 1507 | fresh: 0.5.2 1508 | http-errors: 2.0.0 1509 | mime: 1.6.0 1510 | ms: 2.1.3 1511 | on-finished: 2.4.1 1512 | range-parser: 1.2.1 1513 | statuses: 2.0.1 1514 | transitivePeerDependencies: 1515 | - supports-color 1516 | 1517 | send@0.19.0: 1518 | dependencies: 1519 | debug: 2.6.9 1520 | depd: 2.0.0 1521 | destroy: 1.2.0 1522 | encodeurl: 1.0.2 1523 | escape-html: 1.0.3 1524 | etag: 1.8.1 1525 | fresh: 0.5.2 1526 | http-errors: 2.0.0 1527 | mime: 1.6.0 1528 | ms: 2.1.3 1529 | on-finished: 2.4.1 1530 | range-parser: 1.2.1 1531 | statuses: 2.0.1 1532 | transitivePeerDependencies: 1533 | - supports-color 1534 | 1535 | serialize-error@7.0.1: 1536 | dependencies: 1537 | type-fest: 0.13.1 1538 | optional: true 1539 | 1540 | serve-static@1.15.0: 1541 | dependencies: 1542 | encodeurl: 1.0.2 1543 | escape-html: 1.0.3 1544 | parseurl: 1.3.3 1545 | send: 0.18.0 1546 | transitivePeerDependencies: 1547 | - supports-color 1548 | 1549 | serve-static@1.16.2: 1550 | dependencies: 1551 | encodeurl: 2.0.0 1552 | escape-html: 1.0.3 1553 | parseurl: 1.3.3 1554 | send: 0.19.0 1555 | transitivePeerDependencies: 1556 | - supports-color 1557 | 1558 | set-function-length@1.2.2: 1559 | dependencies: 1560 | define-data-property: 1.1.4 1561 | es-errors: 1.3.0 1562 | function-bind: 1.1.2 1563 | get-intrinsic: 1.2.5 1564 | gopd: 1.2.0 1565 | has-property-descriptors: 1.0.2 1566 | 1567 | setprototypeof@1.2.0: {} 1568 | 1569 | side-channel@1.0.6: 1570 | dependencies: 1571 | call-bind: 1.0.8 1572 | es-errors: 1.3.0 1573 | get-intrinsic: 1.2.5 1574 | object-inspect: 1.13.3 1575 | 1576 | sprintf-js@1.1.3: 1577 | optional: true 1578 | 1579 | statuses@2.0.1: {} 1580 | 1581 | string-width@4.2.3: 1582 | dependencies: 1583 | emoji-regex: 8.0.0 1584 | is-fullwidth-code-point: 3.0.0 1585 | strip-ansi: 6.0.1 1586 | 1587 | strip-ansi@6.0.1: 1588 | dependencies: 1589 | ansi-regex: 5.0.1 1590 | 1591 | sumchecker@3.0.1: 1592 | dependencies: 1593 | debug: 4.4.0 1594 | transitivePeerDependencies: 1595 | - supports-color 1596 | 1597 | toidentifier@1.0.1: {} 1598 | 1599 | type-fest@0.13.1: 1600 | optional: true 1601 | 1602 | type-is@1.6.18: 1603 | dependencies: 1604 | media-typer: 0.3.0 1605 | mime-types: 2.1.35 1606 | 1607 | undici-types@6.19.8: {} 1608 | 1609 | universalify@0.1.2: {} 1610 | 1611 | unpipe@1.0.0: {} 1612 | 1613 | utils-merge@1.0.1: {} 1614 | 1615 | vary@1.1.2: {} 1616 | 1617 | wasm-feature-detect@1.8.0: {} 1618 | 1619 | web-streams-polyfill@3.3.3: {} 1620 | 1621 | wrap-ansi@7.0.0: 1622 | dependencies: 1623 | ansi-styles: 4.3.0 1624 | string-width: 4.2.3 1625 | strip-ansi: 6.0.1 1626 | 1627 | wrappy@1.0.2: {} 1628 | 1629 | ws@8.18.0: {} 1630 | 1631 | y18n@5.0.8: {} 1632 | 1633 | yargs-parser@21.1.1: {} 1634 | 1635 | yargs@17.7.2: 1636 | dependencies: 1637 | cliui: 8.0.1 1638 | escalade: 3.2.0 1639 | get-caller-file: 2.0.5 1640 | require-directory: 2.1.1 1641 | string-width: 4.2.3 1642 | y18n: 5.0.8 1643 | yargs-parser: 21.1.1 1644 | 1645 | yauzl@2.10.0: 1646 | dependencies: 1647 | buffer-crc32: 0.2.13 1648 | fd-slicer: 1.1.0 1649 | -------------------------------------------------------------------------------- /src/login.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PMAtron 登录 6 | 249 | 250 | 251 | 252 | 253 |
254 |

用PMAtron 登录你的Mysql

255 |
256 | 257 | 258 |
259 |
260 | 请选择主机名 261 | 262 |
263 |
264 | 265 |
266 |
267 | 268 | 269 | 270 |
271 |
272 | 请选择用户名 273 | 274 |
275 |
276 | 277 |
278 |
279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 |
287 |
288 | 289 | 290 | 300 | 301 | 554 | 555 | 556 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import {app, BrowserWindow, protocol, ipcMain} from 'electron' 2 | import path from 'path' 3 | import fs from 'fs' 4 | import { 5 | createNodeFsMountHandler, 6 | getPHPLoaderModule, 7 | } from '@php-wasm/node'; 8 | 9 | import { 10 | withNetworking 11 | } from './networking/with-networking.js' 12 | import { 13 | PHP, 14 | PHPRequestHandler, 15 | setPhpIniEntries, 16 | proxyFileSystem, 17 | loadPHPRuntime, 18 | } from '@php-wasm/universal'; 19 | import {wordPressRewriteRules, getFileNotFoundActionForWordPress} from '@wp-playground/wordpress'; 20 | import {rootCertificates} from 'tls'; 21 | import compressible from 'compressible'; 22 | 23 | 24 | function shouldCompress( _, res ) { 25 | const types = res.getHeader( 'content-type' ); 26 | const type = Array.isArray( types ) ? types[ 0 ] : types; 27 | return type && compressible( type ); 28 | } 29 | 30 | // Configuration for the PHP environment 31 | const environment = { 32 | php: { 33 | version: '8.3', 34 | }, 35 | server: { 36 | scheme: 'phpapp', 37 | host: 'localhost', 38 | path: path.resolve('./phpMyAdmin'), 39 | mount: '/phpMyAdmin', 40 | debug: false, 41 | } 42 | }; 43 | 44 | // Create preload script content 45 | const preloadScript = ` 46 | const { contextBridge, ipcRenderer } = require('electron'); 47 | 48 | contextBridge.exposeInMainWorld('api', { 49 | updateConfig: (credentials) => ipcRenderer.send('update-config', credentials), 50 | onUpdateSuccess: (callback) => ipcRenderer.on('update-config-success', callback), 51 | onUpdateFailure: (callback) => ipcRenderer.on('update-config-failure', callback) 52 | }); 53 | `; 54 | 55 | export async function loadNodeRuntime( 56 | phpVersion, 57 | options = {} 58 | ) { 59 | const emscriptenOptions = { 60 | quit: function (code, error) { 61 | throw error; 62 | }, 63 | ...(options.emscriptenOptions || {}), 64 | }; 65 | return await loadPHPRuntime( 66 | await getPHPLoaderModule(phpVersion), 67 | await withNetworking(emscriptenOptions) 68 | ); 69 | } 70 | 71 | // Initialize PHP instance with necessary configurations 72 | async function getPHPInstance(isPrimary, requestHandler) { 73 | const id = await loadNodeRuntime(environment.php.version); 74 | const php = new PHP(id); 75 | php.requestHandler = requestHandler; 76 | 77 | await setPhpIniEntries(php, { 78 | memory_limit: '2048M', 79 | disable_functions: 'openssl_random_pseudo_bytes', 80 | allow_url_fopen: '1', 81 | 'openssl.cafile': '/internal/shared/ca-bundle.crt', 82 | }); 83 | 84 | return {php, runtimeId: id}; 85 | } 86 | 87 | // Convert request body to bytes 88 | const requestBodyToBytes = async (request) => { 89 | return Buffer.from(await request.arrayBuffer()); 90 | } 91 | 92 | // Initialize PHP handler 93 | async function initializePHPHandler() { 94 | const requestHandler = new PHPRequestHandler({ 95 | phpFactory: async ({isPrimary, requestHandler: reqHandler}) => { 96 | const {php} = await getPHPInstance(isPrimary, reqHandler); 97 | if (!isPrimary) { 98 | proxyFileSystem(await requestHandler.getPrimaryPhp(), php, [ 99 | '/tmp', 100 | requestHandler.documentRoot, 101 | ]); 102 | } 103 | if (reqHandler) { 104 | php.requestHandler = reqHandler; 105 | } 106 | return php; 107 | }, 108 | documentRoot: environment.server.mount, 109 | absoluteUrl: `${environment.server.scheme}://${environment.server.host}`, 110 | rewriteRules: wordPressRewriteRules, 111 | getFileNotFoundAction: getFileNotFoundActionForWordPress, 112 | }); 113 | 114 | const php = await requestHandler.getPrimaryPhp(); 115 | php.mkdir(environment.server.mount); 116 | php.mount(environment.server.mount, createNodeFsMountHandler(environment.server.path)); 117 | php.chdir(environment.server.mount); 118 | php.writeFile('/internal/shared/ca-bundle.crt', rootCertificates.join('\n')); 119 | 120 | 121 | return php; 122 | } 123 | 124 | // Create temporary preload script file 125 | const preloadPath = path.resolve("./src/preload.js"); 126 | fs.writeFileSync(preloadPath, preloadScript); 127 | 128 | // Register custom protocol 129 | protocol.registerSchemesAsPrivileged([ 130 | { 131 | scheme: environment.server.scheme, 132 | privileges: { 133 | standard: true, 134 | secure: true, 135 | supportFetchAPI: true, 136 | corsEnabled: true, 137 | stream: true 138 | } 139 | } 140 | ]); 141 | async function createWindow() { 142 | 143 | const win = new BrowserWindow({ 144 | width: 1024, 145 | height: 768, 146 | webPreferences: { 147 | nodeIntegration: false, 148 | contextIsolation: true, 149 | preload: preloadPath, 150 | webSecurity: false, // Note: In production, consider security implications 151 | allowRunningInsecureContent: true 152 | } 153 | }); 154 | 155 | // Initialize PHP handler 156 | const php = await initializePHPHandler(); 157 | 158 | // Content Security Policy middleware 159 | const cspMiddleware = (headers) => { 160 | const csp = [ 161 | "default-src 'self' 'unsafe-inline' 'unsafe-eval'", 162 | `script-src 'self' 'unsafe-inline' 'unsafe-eval' ${environment.server.scheme}://${environment.server.host}`, 163 | "style-src 'self' 'unsafe-inline'", 164 | "img-src 'self' data: blob:", 165 | `connect-src 'self' ${environment.server.scheme}://${environment.server.host}`, 166 | ].join('; '); 167 | 168 | headers['Content-Security-Policy'] = csp; 169 | return headers; 170 | }; 171 | 172 | // Register protocol handler 173 | protocol.handle(environment.server.scheme, async (request) => { 174 | try { 175 | const url = new URL(request.url); 176 | const headers = {}; 177 | request.headers.forEach((value, key) => { 178 | headers[key.toLowerCase()] = value; 179 | }); 180 | 181 | // Handle static files (including JavaScript files) 182 | if (url.pathname.endsWith('.js') || url.pathname.endsWith('.css') || url.pathname.endsWith('.html') || url.pathname.endsWith('.png') || url.pathname.endsWith('.jpg')) { 183 | const filePath = path.join(environment.server.path, url.pathname); 184 | try { 185 | let contentType = 'text/plain'; 186 | if (url.pathname.endsWith('.js')) { 187 | contentType = 'application/javascript'; 188 | } else if (url.pathname.endsWith('.css')) { 189 | contentType = 'text/css'; 190 | } else if (url.pathname.endsWith('.html')) { 191 | contentType = 'text/html'; 192 | if (url.pathname.endsWith('login.html')) { 193 | const content = fs.readFileSync(path.resolve("./src/login.html")); 194 | return new Response(content, { 195 | headers: { 196 | 'Content-Type': contentType, 197 | 'Access-Control-Allow-Origin': '*' 198 | } 199 | }); 200 | } 201 | } else if (url.pathname.endsWith('.png')) { 202 | contentType = 'image/png'; 203 | } else if (url.pathname.endsWith('.jpg')) { 204 | contentType = 'image/jpeg'; 205 | } 206 | const content = fs.readFileSync(filePath); 207 | return new Response(content, { 208 | headers: { 209 | 'Content-Type': contentType, 210 | 'Access-Control-Allow-Origin': '*' 211 | } 212 | }); 213 | } catch (err) { 214 | console.error('Static file error:', err); 215 | } 216 | } 217 | 218 | // Prepare request data for PHP 219 | const requestData = { 220 | url: url.pathname + url.search, 221 | headers: headers, 222 | method: request.method, 223 | body: await requestBodyToBytes(request), 224 | }; 225 | 226 | // Process request through PHP handler 227 | const response = await php.requestHandler.request(requestData); 228 | 229 | if (environment.server.debug) { 230 | console.log('Request:', { 231 | url: url.toString(), 232 | method: request.method, 233 | headers: headers 234 | }); 235 | console.log('Response:', { 236 | status: response.httpStatusCode, 237 | headers: response.headers 238 | }); 239 | } 240 | 241 | // Apply security headers and return response 242 | const finalHeaders = cspMiddleware({...response.headers}); 243 | return new Response(response.bytes, { 244 | status: response.httpStatusCode, 245 | headers: finalHeaders 246 | }); 247 | } catch (error) { 248 | console.error('Error handling request:', error); 249 | return new Response('Internal Server Error', { 250 | status: 500, 251 | headers: {'Content-Type': 'text/plain'} 252 | }); 253 | } 254 | }); 255 | 256 | // Load initial page 257 | await win.loadURL(`${environment.server.scheme}://${environment.server.host}/login.html`); 258 | 259 | if (environment.server.debug) { 260 | } 261 | win.webContents.openDevTools(); 262 | 263 | // Cleanup preload script on window close 264 | win.on('closed', () => { 265 | try { 266 | fs.unlinkSync(preloadPath); 267 | } catch (err) { 268 | console.error('Error cleaning up preload script:', err); 269 | } 270 | }); 271 | 272 | // 监听来自渲染进程的更新配置请求 273 | ipcMain.on('update-config', (event, { hostname, username, password }) => { 274 | try { 275 | const configFilePath = path.join(environment.server.path, 'config.inc.php'); 276 | let configContent = fs.readFileSync(configFilePath, 'utf-8'); 277 | 278 | configContent = configContent.replace(/\$cfg\['Servers'\]\[\$i\]\['host'\] = '.*?';/, `$cfg['Servers'][$i]['host'] = '${hostname}';`); 279 | configContent = configContent.replace(/\$cfg\['Servers'\]\[\$i\]\['user'\] = '.*?';/, `$cfg['Servers'][$i]['user'] = '${username}';`); 280 | configContent = configContent.replace(/\$cfg\['Servers'\]\[\$i\]\['password'\] = '.*?';/, `$cfg['Servers'][$i]['password'] = '${password}';`); 281 | 282 | fs.writeFileSync(configFilePath, configContent, 'utf-8'); 283 | 284 | // 通知渲染进程更新成功 285 | event.sender.send('update-config-success'); 286 | 287 | // 更新完成后跳转至根页面 288 | win.loadURL(`${environment.server.scheme}://${environment.server.host}/`); 289 | } catch (err) { 290 | console.error('Error updating config:', err); 291 | // 通知渲染进程更新失败 292 | event.sender.send('update-config-failure', err.message); 293 | } 294 | }); 295 | } 296 | 297 | app.whenReady().then(async () => { 298 | await createWindow(); 299 | 300 | app.on('activate', async () => { 301 | if (BrowserWindow.getAllWindows().length === 0) { 302 | await createWindow(); 303 | } 304 | }); 305 | }); 306 | 307 | app.on('window-all-closed', () => { 308 | if (process.platform !== 'darwin') { 309 | app.quit(); 310 | } 311 | }); 312 | 313 | -------------------------------------------------------------------------------- /src/networking/inbound-tcp-to-ws-proxy.js: -------------------------------------------------------------------------------- 1 | import { createServer } from 'net'; 2 | import { WebSocketServer, WebSocket } from 'ws'; 3 | import { debugLog } from './utils.js'; 4 | function log(...args) { 5 | debugLog('[TCP Server]', ...args); 6 | } 7 | 8 | export function addTCPServerToWebSocketServerClass( 9 | wsListenPort, 10 | WSServer 11 | ) { 12 | return class PHPWasmWebSocketServer extends WSServer { 13 | constructor(options, callback) { 14 | const requestedPort = options.port; 15 | options.port = wsListenPort; 16 | listenTCPToWSProxy({ 17 | tcpListenPort: requestedPort, 18 | wsConnectPort: wsListenPort, 19 | }); 20 | super(options, callback); 21 | } 22 | }; 23 | } 24 | 25 | export function listenTCPToWSProxy(options) { 26 | options = { 27 | wsConnectHost: '127.0.0.1', 28 | ...options, 29 | }; 30 | const { tcpListenPort, wsConnectHost, wsConnectPort } = options; 31 | const server = createServer(); 32 | server.on('connection', function handleConnection(tcpSource) { 33 | const inBuffer = []; 34 | 35 | const wsTarget = new WebSocket( 36 | `ws://${wsConnectHost}:${wsConnectPort}/` 37 | ); 38 | wsTarget.binaryType = 'arraybuffer'; 39 | function wsSend(data) { 40 | wsTarget.send(new Uint8Array(data)); 41 | } 42 | 43 | wsTarget.addEventListener('open', function () { 44 | log('Outbound WebSocket connection established'); 45 | while (inBuffer.length > 0) { 46 | wsSend(inBuffer.shift()); 47 | } 48 | }); 49 | wsTarget.addEventListener('message', (e) => { 50 | log( 51 | 'WS->TCP message:', 52 | new TextDecoder().decode(e.data) 53 | ); 54 | // @ts-ignore-next-line 55 | tcpSource.write(Buffer.from(e.data)); 56 | }); 57 | wsTarget.addEventListener('close', () => { 58 | log('WebSocket connection closed'); 59 | tcpSource.end(); 60 | }); 61 | 62 | tcpSource.on('data', function (data) { 63 | log('TCP->WS message:', data); 64 | if (wsTarget.readyState === WebSocket.OPEN) { 65 | while (inBuffer.length > 0) { 66 | wsSend(inBuffer.shift()); 67 | } 68 | wsSend(data); 69 | } else { 70 | inBuffer.push(data); 71 | } 72 | }); 73 | tcpSource.once('close', function () { 74 | log('TCP connection closed'); 75 | wsTarget.close(); 76 | }); 77 | tcpSource.on('error', function () { 78 | log('TCP connection error'); 79 | wsTarget.close(); 80 | }); 81 | }); 82 | server.listen(tcpListenPort, function () { 83 | log('TCP server listening'); 84 | }); 85 | } 86 | -------------------------------------------------------------------------------- /src/networking/outbound-ws-to-tcp-proxy.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | import * as dns from 'dns'; 4 | import * as util from 'node:util'; 5 | import * as net from 'net'; 6 | import * as http from 'http'; 7 | import { WebSocketServer } from 'ws'; 8 | import { debugLog } from './utils.js'; 9 | 10 | function log(...args) { 11 | debugLog('[WS Server]', ...args); 12 | } 13 | 14 | const lookup = util.promisify(dns.lookup); 15 | 16 | function prependByte( 17 | chunk, 18 | byte 19 | ) { 20 | if (typeof chunk === 'string') { 21 | chunk = String.fromCharCode(byte) + chunk; 22 | } else if ( 23 | chunk instanceof ArrayBuffer || 24 | 'byteLength' in chunk /* for Node.js */ 25 | ) { 26 | const buffer = new Uint8Array((chunk).byteLength + 1); 27 | buffer[0] = byte; 28 | buffer.set(new Uint8Array(chunk), 1); 29 | chunk = buffer.buffer; 30 | } else { 31 | log({ chunk }); 32 | throw new Error('Unsupported chunk type: ' + typeof chunk); 33 | } 34 | return chunk; 35 | } 36 | 37 | /** 38 | * Send a chunk of data to the remote server. 39 | */ 40 | export const COMMAND_CHUNK = 0x01; 41 | /** 42 | * Set a TCP socket option. 43 | */ 44 | export const COMMAND_SET_SOCKETOPT = 0x02; 45 | 46 | /** 47 | * Adds support for TCP socket options to WebSocket class. 48 | * 49 | * Socket options are implemented by adopting a specific data transmission 50 | * protocol between WS client and WS server The first byte 51 | * of every message is a command type, and the remaining bytes 52 | * are the actual data. 53 | * 54 | * @param WebSocketConstructor 55 | * @returns Decorated constructor 56 | */ 57 | export function addSocketOptionsSupportToWebSocketClass( 58 | WebSocketConstructor 59 | ) { 60 | return class PHPWasmWebSocketConstructor extends WebSocketConstructor { 61 | // @ts-ignore 62 | send(chunk, callback) { 63 | return this.sendCommand(COMMAND_CHUNK, chunk, callback); 64 | } 65 | 66 | setSocketOpt( 67 | optionClass, 68 | optionName, 69 | optionValue 70 | ) { 71 | return this.sendCommand( 72 | COMMAND_SET_SOCKETOPT, 73 | new Uint8Array([optionClass, optionName, optionValue]).buffer, 74 | () => undefined 75 | ); 76 | } 77 | sendCommand( 78 | commandType, 79 | chunk, 80 | callback 81 | ) { 82 | return (WebSocketConstructor.prototype.send).call( 83 | this, 84 | prependByte(chunk, commandType), 85 | callback 86 | ); 87 | } 88 | }; 89 | } 90 | 91 | export function initOutboundWebsocketProxyServer( 92 | listenPort, 93 | listenHost = '127.0.0.1' 94 | ) { 95 | log(`Binding the WebSockets server to ${listenHost}:${listenPort}...`); 96 | const webServer = http.createServer((request, response) => { 97 | response.writeHead(403, { 'Content-Type': 'text/plain' }); 98 | response.write( 99 | '403 Permission Denied\nOnly websockets are allowed here.\n' 100 | ); 101 | response.end(); 102 | }); 103 | return new Promise((resolve) => { 104 | webServer.listen(listenPort, listenHost, function () { 105 | const wsServer = new WebSocketServer({ server: webServer }); 106 | wsServer.on('connection', onWsConnect); 107 | resolve(webServer); 108 | }); 109 | }); 110 | } 111 | 112 | // Handle new WebSocket client 113 | async function onWsConnect(client, request) { 114 | const clientAddr = client?._socket?.remoteAddress || client.url; 115 | const clientLog = function (...args) { 116 | log(' ' + clientAddr + ': ', ...args); 117 | }; 118 | 119 | clientLog( 120 | 'WebSocket connection from : ' + 121 | clientAddr + 122 | ' at URL ' + 123 | (request ? request.url : client.upgradeReq.url) 124 | ); 125 | clientLog( 126 | 'Version ' + 127 | client.protocolVersion + 128 | ', subprotocol: ' + 129 | client.protocol 130 | ); 131 | 132 | // Parse the search params (the host doesn't matter): 133 | const reqUrl = new URL(`ws://0.0.0.0` + request.url); 134 | const reqTargetPort = Number(reqUrl.searchParams.get('port')); 135 | const reqTargetHost = reqUrl.searchParams.get('host'); 136 | if (!reqTargetPort || !reqTargetHost) { 137 | clientLog('Missing host or port information'); 138 | client.close(3000); 139 | return; 140 | } 141 | 142 | // eslint-disable-next-line prefer-const 143 | let target; 144 | const recvQueue = []; 145 | function flushMessagesQueue() { 146 | while (recvQueue.length > 0) { 147 | const msg = recvQueue.pop(); 148 | const commandType = msg[0]; 149 | clientLog('flushing', { commandType }, msg); 150 | if (commandType === COMMAND_CHUNK) { 151 | target.write(msg.slice(1)); 152 | } else if (commandType === COMMAND_SET_SOCKETOPT) { 153 | const SOL_SOCKET = 1; 154 | const SO_KEEPALIVE = 9; 155 | 156 | const IPPROTO_TCP = 6; 157 | const TCP_NODELAY = 1; 158 | if (msg[1] === SOL_SOCKET && msg[2] === SO_KEEPALIVE) { 159 | target.setKeepAlive(msg[3]); 160 | } else if (msg[1] === IPPROTO_TCP && msg[2] === TCP_NODELAY) { 161 | target.setNoDelay(msg[3]); 162 | } 163 | } else { 164 | clientLog('Unknown command type: ' + commandType); 165 | process.exit(); 166 | } 167 | } 168 | } 169 | 170 | client.on('message', function (msg) { 171 | // clientLog('PHP -> network buffer:', msg); 172 | recvQueue.unshift(msg); 173 | if (target) { 174 | flushMessagesQueue(); 175 | } 176 | }); 177 | client.on('close', function (code, reason) { 178 | clientLog( 179 | 'WebSocket client disconnected: ' + code + ' [' + reason + ']' 180 | ); 181 | if (target) { 182 | target.end(); 183 | } 184 | }); 185 | client.on('error', function (a) { 186 | clientLog('WebSocket client error: ' + a); 187 | target.end(); 188 | }); 189 | 190 | // Resolve the target host to an IP address if it isn't one already 191 | let reqTargetIp; 192 | if (net.isIP(reqTargetHost) === 0) { 193 | clientLog('resolving ' + reqTargetHost + '... '); 194 | try { 195 | const resolution = await lookup(reqTargetHost); 196 | reqTargetIp = resolution.address; 197 | clientLog('resolved ' + reqTargetHost + ' -> ' + reqTargetIp); 198 | } catch (e) { 199 | clientLog("can't resolve " + reqTargetHost + ' due to:', e); 200 | // Send empty binary data to notify requester that connection was 201 | // initiated 202 | client.send([]); 203 | client.close(3000); 204 | return; 205 | } 206 | } else { 207 | reqTargetIp = reqTargetHost; 208 | } 209 | clientLog( 210 | 'Opening a socket connection to ' + reqTargetIp + ':' + reqTargetPort 211 | ); 212 | target = net.createConnection(reqTargetPort, reqTargetIp, function () { 213 | clientLog('Connected to target'); 214 | flushMessagesQueue(); 215 | }); 216 | target.on('data', function (data) { 217 | // clientLog( 218 | // 'network -> PHP buffer:', 219 | // [...data.slice(0, 100)].join(', ') + '...' 220 | // ); 221 | try { 222 | client.send(data); 223 | } catch (e) { 224 | clientLog('Client closed, cleaning up target'); 225 | target.end(); 226 | } 227 | }); 228 | target.on('end', function () { 229 | clientLog('target disconnected'); 230 | client.close(); 231 | }); 232 | target.on('error', function (e) { 233 | clientLog('target connection error', e); 234 | target.end(); 235 | client.close(3000); 236 | }); 237 | } 238 | -------------------------------------------------------------------------------- /src/networking/php.ini: -------------------------------------------------------------------------------- 1 | error_reporting=E_ALL 2 | display_errors=1 3 | html_errors=1 4 | display_startup_errors=On 5 | log_errors=1 6 | always_populate_raw_post_data=-1 7 | upload_max_filesize=2000M 8 | post_max_size=2000M 9 | disable_functions=proc_open,popen,curl_exec,curl_multi_exec 10 | allow_url_fopen=On 11 | session.save_path=/home/web_user 12 | implicit_flush=1 13 | output_buffering=4096 14 | -------------------------------------------------------------------------------- /src/networking/utils.js: -------------------------------------------------------------------------------- 1 | import * as net from 'net'; 2 | import { logger } from '@php-wasm/logger'; 3 | 4 | export function debugLog(message, ...args) { 5 | if (process.env['DEV'] && !process.env['TEST']) { 6 | logger.log(message, ...args); 7 | } 8 | } 9 | 10 | export async function findFreePorts(n) { 11 | const serversPromises = []; 12 | for (let i = 0; i < n; i++) { 13 | serversPromises.push(listenOnRandomPort()); 14 | } 15 | 16 | const servers = await Promise.all(serversPromises); 17 | const ports = []; 18 | for (const server of servers) { 19 | const address = server.address(); 20 | ports.push(address.port); 21 | server.close(); 22 | } 23 | 24 | return ports; 25 | } 26 | 27 | function listenOnRandomPort() { 28 | return new Promise((resolve) => { 29 | const server = net.createServer(); 30 | server.listen(0, () => { 31 | resolve(server); 32 | }); 33 | }); 34 | } 35 | -------------------------------------------------------------------------------- /src/networking/with-networking.js: -------------------------------------------------------------------------------- 1 | import { 2 | initOutboundWebsocketProxyServer, 3 | addSocketOptionsSupportToWebSocketClass, 4 | } from './outbound-ws-to-tcp-proxy.js'; 5 | import { addTCPServerToWebSocketServerClass } from './inbound-tcp-to-ws-proxy.js'; 6 | import { findFreePorts } from './utils.js'; 7 | 8 | export async function withNetworking( 9 | phpModuleArgs = {} 10 | ) { 11 | const [inboundProxyWsServerPort, outboundProxyWsServerPort] = 12 | await findFreePorts(2); 13 | 14 | const outboundNetworkProxyServer = await initOutboundWebsocketProxyServer( 15 | outboundProxyWsServerPort 16 | ); 17 | 18 | return { 19 | ...phpModuleArgs, 20 | outboundNetworkProxyServer, 21 | websocket: { 22 | ...(phpModuleArgs['websocket'] || {}), 23 | url: (_, host, port) => { 24 | const query = new URLSearchParams({ 25 | host, 26 | port, 27 | }).toString(); 28 | return `ws://127.0.0.1:${outboundProxyWsServerPort}/?${query}`; 29 | }, 30 | subprotocol: 'binary', 31 | decorator: addSocketOptionsSupportToWebSocketClass, 32 | serverDecorator: addTCPServerToWebSocketServerClass.bind( 33 | null, 34 | inboundProxyWsServerPort 35 | ), 36 | }, 37 | }; 38 | } 39 | -------------------------------------------------------------------------------- /tests/fetch-hook.js: -------------------------------------------------------------------------------- 1 | const originalFetch = global.fetch; 2 | 3 | global.fetch = async function(...args) { 4 | console.log('Fetch Request:', args); 5 | return originalFetch.apply(this, args); 6 | }; 7 | -------------------------------------------------------------------------------- /tests/http-hook.js: -------------------------------------------------------------------------------- 1 | 2 | import http from 'http' 3 | import https from 'https' 4 | 5 | // 保存原始的 http.request 和 https.request 方法 6 | const originalHttpRequest = http.request; 7 | const originalHttpsRequest = https.request; 8 | 9 | // 重写 http.request 和 https.request 方法 10 | http.request = function(options, callback) { 11 | console.log(`HTTP Request:${options.host} ${options.port} ${options.path}`, ); 12 | return originalHttpRequest.call(http, options, callback); 13 | }; 14 | 15 | https.request = function(options, callback) { 16 | console.log('HTTPS Request:', options); 17 | return originalHttpsRequest.call(https, options, callback); 18 | }; 19 | -------------------------------------------------------------------------------- /tests/ip-hook.js: -------------------------------------------------------------------------------- 1 | 2 | import net from 'net' 3 | import dgram from 'dgram' 4 | 5 | // 保存原始方法 6 | const originalCreateConnection = net.createConnection; 7 | const originalConnect = net.connect; 8 | const originalCreateServer = net.createServer; 9 | const originalCreateSocket = dgram.createSocket; 10 | 11 | // 重写 net.createConnection 12 | net.createConnection = function(...args) { 13 | console.log('net.createConnection called with:', args); 14 | return originalCreateConnection.apply(net, args); 15 | }; 16 | 17 | // 重写 net.connect 18 | net.connect = function(...args) { 19 | console.log('net.connect called with:', args); 20 | return originalConnect.apply(net, args); 21 | }; 22 | 23 | // 重写 net.createServer 24 | net.createServer = function(...args) { 25 | console.log('net.createServer called with:', args); 26 | return originalCreateServer.apply(net, args); 27 | }; 28 | 29 | // 重写 dgram.createSocket 30 | dgram.createSocket = function(...args) { 31 | console.log('dgram.createSocket called with:', args); 32 | const socket = originalCreateSocket.apply(dgram, args); 33 | 34 | // 拦截 socket 的 send 方法 35 | const originalSend = socket.send; 36 | socket.send = function(...sendArgs) { 37 | console.log('dgram.send called with:', sendArgs); 38 | return originalSend.apply(socket, sendArgs); 39 | }; 40 | 41 | // 拦截 socket 的 bind 方法 42 | const originalBind = socket.bind; 43 | socket.bind = function(...bindArgs) { 44 | console.log('dgram.bind called with:', bindArgs); 45 | return originalBind.apply(socket, bindArgs); 46 | }; 47 | 48 | return socket; 49 | }; 50 | -------------------------------------------------------------------------------- /tests/ws-hook.js: -------------------------------------------------------------------------------- 1 | // wsInterceptor.mjs 2 | import WebSocket from 'ws'; 3 | 4 | // 保存原始的 WebSocket 构造函数 5 | const OriginalWebSocket = WebSocket; 6 | 7 | // 创建一个自定义的 WebSocket 构造函数 8 | class InterceptedWebSocket extends OriginalWebSocket { 9 | constructor(address, protocols, options) { 10 | super(address, protocols, options); 11 | 12 | // 监听连接打开事件 13 | this.on('open', () => { 14 | console.log(`WebSocket连接已打开: ${address}`); 15 | // 您可以在这里添加自定义逻辑,如记录连接信息 16 | }); 17 | 18 | // 监听消息接收事件 19 | this.on('message', (data) => { 20 | console.log(`WebSocket接收到消息: ${data}`); 21 | // 您可以在这里添加自定义逻辑,如处理或记录消息 22 | }); 23 | 24 | // 监听连接关闭事件 25 | this.on('close', (code, reason) => { 26 | console.log(`WebSocket连接已关闭: ${address}, 代码: ${code}, 原因: ${reason}`); 27 | // 您可以在这里添加自定义逻辑,如清理资源 28 | }); 29 | 30 | // 监听错误事件 31 | this.on('error', (error) => { 32 | console.error(`WebSocket错误: ${error}`); 33 | // 您可以在这里添加自定义逻辑,如错误处理 34 | }); 35 | } 36 | 37 | // 重写 send 方法,以便在发送消息时执行自定义逻辑 38 | send(data, options, callback) { 39 | console.log(`WebSocket发送消息: ${data}`); 40 | // 您可以在这里修改数据或执行其他操作 41 | 42 | // 调用原始的 send 方法 43 | super.send(data, options, callback); 44 | } 45 | } 46 | 47 | // 定义一个函数来替换全局的 WebSocket 48 | export async function hookWebSocket() { 49 | globalThis.WebSocket = InterceptedWebSocket; 50 | 51 | // 如果 ws 模块已经被导入过,则需要替换其默认导出 52 | try { 53 | const wsModule = await import('ws'); 54 | if (wsModule.default) { 55 | wsModule.default = InterceptedWebSocket; 56 | } 57 | } catch (err) { 58 | console.warn('ws 模块尚未被导入,稍后可能需要重新拦截。'); 59 | } 60 | } 61 | --------------------------------------------------------------------------------