.github/000077500000000000000000000000001517715050700124265ustar00rootroot00000000000000.github/workflows/000077500000000000000000000000001517715050700144635ustar00rootroot00000000000000.github/workflows/build.yml000066400000000000000000000016761517715050700163170ustar00rootroot00000000000000# This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node # For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions name: build on: schedule: - cron: '0 0 1 * *' push: branches: [ master ] pull_request: branches: [ master ] jobs: build: runs-on: ubuntu-latest strategy: matrix: node-version: ['lts/*'] # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ steps: - uses: actions/checkout@v2 - name: Use Node.js ${{ matrix.node-version }} uses: actions/setup-node@v2 with: node-version: ${{ matrix.node-version }} - run: npm i -g grunt-cli - run: npm install - run: grunt .gitignore000066400000000000000000000003731517715050700130610ustar00rootroot00000000000000/build /node_modules /*.log /*.iws .idea/workspace.xml .idea/tasks.xml .idea/profiles_settings.xml .idea/inspectionProfiles/Project_Default.xml .idea/inspectionProfiles/profiles_settings.xml node_modules/.yarn-integrity /p3xrs.json .DS_Store /dist.npmignore000066400000000000000000000002441517715050700130650ustar00rootroot00000000000000/.idea /build /test /node_modules /*.iml /*.ipr /*.iws /.travis.yml /.scrutinizer.yml /Gruntfile.js /*.lock *.log /corifeus-boot.json /secure /.github /.vscode /srcGruntfile.cjs000066400000000000000000000004631517715050700135310ustar00rootroot00000000000000const utils = require('corifeus-utils'); module.exports = (grunt) => { const _ = require('lodash'); const builder = require(`corifeus-builder`); const loader = new builder.loader(grunt); loader.js({ }); grunt.registerTask('default', ['cory-npm', 'clean', 'cory-replace']); } LICENSE000066400000000000000000000036511517715050700121000ustar00rootroot00000000000000P3X Redis UI License Copyright (c) Patrik Laszlo / PatrikX3 / Corifeus FREE TIER - MIT LICENSE Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. PRO AND ENTERPRISE TIERS - COMMERCIAL LICENSE A valid purchased license key is required to use Pro or Enterprise features. These features include but are not limited to: Pro: SSH tunneling, Readonly connection mode, Edit JSON, Upload binary, Download binary. Enterprise: All Pro features plus Cluster and Sentinel connections, --readonly-connections/-r flag. Terms: 1. A license key is required for each installation (per seat). 2. License keys are non-transferable. 3. All payments are non-refundable. 4. License validation requires internet access. 5. Unauthorized use of Pro or Enterprise features without a valid license is prohibited. For pricing, trial information, and purchasing, please refer to the README at https://github.com/patrikx3/redis-ui#tier-feature-policy or contact support@corifeus.com. README.md000066400000000000000000000160151517715050700123500ustar00rootroot00000000000000# This is a development package For the full-blown package, please follow: https://github.com/patrikx3/redis-ui https://www.npmjs.com/package/p3x-redis-ui https://corifeus.com/redis-ui [//]: #@corifeus-header [![NPM](https://img.shields.io/npm/v/p3x-redis-ui-server.svg)](https://www.npmjs.com/package/p3x-redis-ui-server) [![Donate for PatrikX3 / P3X](https://img.shields.io/badge/Donate-PatrikX3-003087.svg)](https://paypal.me/patrikx3) [![Contact Corifeus / P3X](https://img.shields.io/badge/Contact-P3X-ff9900.svg)](https://www.patrikx3.com/en/front/contact) [![Corifeus @ Facebook](https://img.shields.io/badge/Facebook-Corifeus-3b5998.svg)](https://www.facebook.com/corifeus.software) [![Uptime ratio (90 days)](https://network.corifeus.com/public/api/uptime-shield/31ad7a5c194347c33e5445dbaf8.svg)](https://network.corifeus.com/status/31ad7a5c194347c33e5445dbaf8) --- # 🏍️ The p3x-redis-ui-server package motor that is connected to the p3x-redis-ui-material web user interface v2026.4.312 🌌 **Bugs are evidentβ„’ - MATRIX️** 🚧 **This project is under active development!** πŸ“’ **We welcome your feedback and contributions.** ### NodeJS LTS is supported ### πŸ› οΈ Built on NodeJs version ```txt v24.14.1 ``` # πŸ“ Description [//]: #@corifeus-header:end This version require minimum NodeJs v22. This is part of a composable `p3x-redis-ui` package. This is the server based on Socket.IO (no rest at all). The server will be using the `p3x-redis-ui-material` web client package based on built with Webpack, Socket.IO and AngularJs Material. This package is named as `p3x-redis-ui-server`. ## Configuration For now, there are 2 configuration files: ```bash p3xrs --config ./p3xrs.json ``` The 2nd configuration is the list of the connections if found in `p3xrs.json` it either in the config: ```text p3xrs.json/p3xrs.connections['home-dir'] = undefined|home|absolute|relative ``` The best is to keep it undefined and it will be in your home dir, but you can choose any place as well. You may also set connections file name which overrides default .p3xrs-conns.json ```text p3xrs --connections-file-name .p3xrs-conns.json ``` ## Optional HTTP Basic Authentication The server can protect both HTTP routes and Socket.IO with HTTP Basic authentication. Config (`p3xrs.json`): ```json { "p3xrs": { "httpAuth": { "enabled": true, "username": "admin", "passwordHash": "$2b$10$..." } } } ``` Generate BCrypt password hash: ```bash node ./bin/bcrypt-password.js -p myplainpass ``` Environment variables: - `HTTP_USER` - `HTTP_PASSWORD` - `HTTP_PASSWORD_HASH` - `HTTP_PASSWORD_HASH_FILE` - `HTTP_AUTH_ENABLED` (`true|false`) CLI options: - `--http-auth-enable` - `--http-auth-disable` - `--http-auth-username` - `--http-auth-password` - `--http-auth-password-hash` - `--http-auth-password-hash-file` Notes: - `passwordHash` is preferred over plain `password`. - Use HTTPS/reverse proxy TLS when HTTP auth is enabled. ### Verbose CLI help ```text patrikx3@workstation:~/Projects/patrikx3/redis-ui-workspace/redis-ui-server$ p3xrs.js --help Usage: p3xrs [options] Options: -V, --version output the version number -c, --config [config] Set the p3xr.json p3x-redis-ui-server configuration, see more help in p3x-redis-ui-server -r, --readonly-connections Set the connections to be readonly, no adding, saving or delete a connection -n, --connections-file-name [filename] Set the connections file name, overrides default .p3xrs-conns.json --http-auth-enable Enable HTTP Basic auth --http-auth-disable Disable HTTP Basic auth --http-auth-username [username] HTTP Basic auth username --http-auth-password [password] HTTP Basic auth plain password --http-auth-password-hash [hash] HTTP Basic auth bcrypt password hash --http-auth-password-hash-file [file] Read HTTP Basic auth bcrypt password hash from file -h, --help output usage information ``` # For development standalone For file names do not use camelCase, but use kebab-case. Folder should be named as kebab-case as well. As you can see, all code filenames are using it like that, please do not change that. Please apply the `.editorconfig` settings in your IDE. Copy from `./artifacts/boot/p3xrs.json` to the root folder (`./p3xrs.json`). ```bash npm install npm run dev ``` It uses `nodemon` and when any file is changed, it will re-load it. The server app is available @ http://localhost:7843 [//]: #@corifeus-footer --- ## πŸš€ Quick and Affordable Web Development Services If you want to quickly and affordably develop your next digital project, visit [corifeus.eu](https://corifeus.eu) for expert solutions tailored to your needs. --- ## 🌐 Powerful Online Networking Tool Discover the powerful and free online networking tool at [network.corifeus.com](https://network.corifeus.com). **πŸ†“ Free** Designed for professionals and enthusiasts, this tool provides essential features for network analysis, troubleshooting, and management. Additionally, it offers tools for: - πŸ“‘ Monitoring TCP, HTTP, and Ping to ensure optimal network performance and reliability. - πŸ“Š Status page management to track uptime, performance, and incidents in real time with customizable dashboards. All these features are completely free to use. --- ## ❀️ Support Our Open-Source Project If you appreciate our work, consider ⭐ starring this repository or πŸ’° making a donation to support server maintenance and ongoing development. Your support means the world to usβ€”thank you! --- ### 🌍 About My Domains All my domains, including [patrikx3.com](https://patrikx3.com), [corifeus.eu](https://corifeus.eu), and [corifeus.com](https://corifeus.com), are developed in my spare time. While you may encounter minor errors, the sites are generally stable and fully functional. --- ### πŸ“ˆ Versioning Policy **Version Structure:** We follow a **Major.Minor.Patch** versioning scheme: - **Major:** πŸ“… Corresponds to the current year. - **Minor:** πŸŒ“ Set as 4 for releases from January to June, and 10 for July to December. - **Patch:** πŸ”§ Incremental, updated with each build. **🚨 Important Changes:** Any breaking changes are prominently noted in the readme to keep you informed. --- [**P3X-REDIS-UI-SERVER**](https://corifeus.com/redis-ui-server) Build v2026.4.312 [![NPM](https://img.shields.io/npm/v/p3x-redis-ui-server.svg)](https://www.npmjs.com/package/p3x-redis-ui-server) [![Donate for PatrikX3 / P3X](https://img.shields.io/badge/Donate-PatrikX3-003087.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=QZVM4V6HVZJW6) [![Contact Corifeus / P3X](https://img.shields.io/badge/Contact-P3X-ff9900.svg)](https://www.patrikx3.com/en/front/contact) [![Like Corifeus @ Facebook](https://img.shields.io/badge/LIKE-Corifeus-3b5998.svg)](https://www.facebook.com/corifeus.software) [//]: #@corifeus-footer:end artifacts/000077500000000000000000000000001517715050700130465ustar00rootroot00000000000000artifacts/boot/000077500000000000000000000000001517715050700140115ustar00rootroot00000000000000artifacts/boot/p3xrs.json000066400000000000000000000024651517715050700157720ustar00rootroot00000000000000{ "p3xrs": { "http": { "port-info": "this is ommitted, it will be default 7843", "port": 7843, "bind-info": "the interface with listen to, could be 127.0.0.1 or 0.0.0.0 or specific interface", "bind": "0.0.0.0" }, "connections": { "home-dir-info": "if the dir config is empty or home, the connections are saved in the home folder, otherwise it will resolve the directory set as it is, either relative ./ or absolute starting with /. NodeJs will resolve this directory in p3xrs.connections.dir", "home-dir": "home" }, "static-info": "This is the best configuration, if it starts with ~, then it is in resolve the path in the node_modules, otherwise it resolves to the current process current working directory.", "static-disabled": "~p3x-redis-ui-material/dist", "httpAuth-info": "Optional HTTP Basic auth for HTTP + Socket.IO. Prefer passwordHash (bcrypt).", "httpAuth": { "enabled": false, "username": "admin", "password": "", "passwordHash": "" }, "licenseEditable": true, "licenseKey": "", "treeDividers": [ ":", "/", "|", "-", "@" ] } } artifacts/cluster.md000066400000000000000000000054611517715050700150570ustar00rootroot00000000000000[//]: #@corifeus-header # 🏍️ The p3x-redis-ui-server package motor that is connected to the p3x-redis-ui-material web user interface [//]: #@corifeus-header:end # Run a Docker Cluster ```bash docker run -e "IP=0.0.0.0" -p 7000:7000 -p 7001:7001 -p 7002:7002 -p 7003:7003 -p 7004:7004 -p 7005:7005 grokzen/redis-cluster:latest ``` [//]: #@corifeus-footer --- ## πŸš€ Quick and Affordable Web Development Services If you want to quickly and affordably develop your next digital project, visit [corifeus.eu](https://corifeus.eu) for expert solutions tailored to your needs. --- ## 🌐 Powerful Online Networking Tool Discover the powerful and free online networking tool at [network.corifeus.com](https://network.corifeus.com). **πŸ†“ Free** Designed for professionals and enthusiasts, this tool provides essential features for network analysis, troubleshooting, and management. Additionally, it offers tools for: - πŸ“‘ Monitoring TCP, HTTP, and Ping to ensure optimal network performance and reliability. - πŸ“Š Status page management to track uptime, performance, and incidents in real time with customizable dashboards. All these features are completely free to use. --- ## ❀️ Support Our Open-Source Project If you appreciate our work, consider ⭐ starring this repository or πŸ’° making a donation to support server maintenance and ongoing development. Your support means the world to usβ€”thank you! --- ### 🌍 About My Domains All my domains, including [patrikx3.com](https://patrikx3.com), [corifeus.eu](https://corifeus.eu), and [corifeus.com](https://corifeus.com), are developed in my spare time. While you may encounter minor errors, the sites are generally stable and fully functional. --- ### πŸ“ˆ Versioning Policy **Version Structure:** We follow a **Major.Minor.Patch** versioning scheme: - **Major:** πŸ“… Corresponds to the current year. - **Minor:** πŸŒ“ Set as 4 for releases from January to June, and 10 for July to December. - **Patch:** πŸ”§ Incremental, updated with each build. **🚨 Important Changes:** Any breaking changes are prominently noted in the readme to keep you informed. --- [**P3X-REDIS-UI-SERVER**](https://corifeus.com/redis-ui-server) Build v2026.4.312 [![NPM](https://img.shields.io/npm/v/p3x-redis-ui-server.svg)](https://www.npmjs.com/package/p3x-redis-ui-server) [![Donate for PatrikX3 / P3X](https://img.shields.io/badge/Donate-PatrikX3-003087.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=QZVM4V6HVZJW6) [![Contact Corifeus / P3X](https://img.shields.io/badge/Contact-P3X-ff9900.svg)](https://www.patrikx3.com/en/front/contact) [![Like Corifeus @ Facebook](https://img.shields.io/badge/LIKE-Corifeus-3b5998.svg)](https://www.facebook.com/corifeus.software) [//]: #@corifeus-footer:end bin/000077500000000000000000000000001517715050700116365ustar00rootroot00000000000000bin/bcrypt-password.mjs000066400000000000000000000010701517715050700155120ustar00rootroot00000000000000#!/usr/bin/env node import { program } from 'commander' import bcrypt from 'bcryptjs' program .requiredOption('-p, --password ', 'Password to hash') .option('-r, --rounds ', 'BCrypt rounds', '10') .parse(process.argv) const options = program.opts() const rounds = Number.parseInt(options.rounds, 10) if (!Number.isInteger(rounds) || rounds < 4 || rounds > 31) { console.error('Invalid rounds value. Use an integer between 4 and 31.') process.exit(1) } const hash = bcrypt.hashSync(options.password, rounds) console.log(hash) bin/p3xrs.mjs000066400000000000000000000011551517715050700134320ustar00rootroot00000000000000#!/usr/bin/env node import fs from 'fs' import path from 'path' import { fileURLToPath } from 'url' const __dirname = path.dirname(fileURLToPath(import.meta.url)) const isDevelopment = process.env.NODE_ENV === 'development' const srcBootModule = '../src/lib/boot.mjs' const distBootModule = '../dist/lib/boot.mjs' const distBootFilename = path.resolve(__dirname, '../dist/lib/boot.mjs') const bootModule = isDevelopment || !fs.existsSync(distBootFilename) ? srcBootModule : distBootModule const bootPath = path.resolve(__dirname, bootModule) const mod = await import(bootPath) const boot = mod.default boot() package.json000066400000000000000000000044061517715050700133600ustar00rootroot00000000000000{ "name": "p3x-redis-ui-server", "version": "2026.4.312", "description": "🏍️ The p3x-redis-ui-server package motor that is connected to the p3x-redis-ui-material web user interface", "corifeus": { "icon": "fas fa-flag-checkered", "code": "Reverse", "opencollective": false, "build": true, "nodejs": "v24.14.1", "reponame": "redis-ui-server", "publish": true, "prefix": "p3x-", "type": "p3x" }, "type": "module", "main": "dist/index.mjs", "bin": { "p3xrs": "./bin/p3xrs.mjs" }, "scripts": { "test": "grunt --gruntfile Gruntfile.cjs", "build:compressed": "node ./scripts/build-compressed.mjs", "start": "node ./bin/p3xrs.mjs --config ./p3xrs.json", "dev": "NODE_ENV=development nodemon --watch src --watch package.json --watch bin ./bin/p3xrs.mjs --config ../p3xrs.json", "dev-readonly-connections": "NODE_ENV=development nodemon --watch src --watch package.json --watch bin ./bin/p3xrs.mjs --config ../p3xrs.json --readonly-connections" }, "watch": { "run": "src/**/*.mjs" }, "repository": { "type": "git", "url": "https://github.com/patrikx3/redis-ui-server.git" }, "keywords": [ "redis", "ui", "gui", "web", "electron", "desktop", "server", "angularjs", "javascript", "material", "dark", "light" ], "author": "Patrik Laszlo ", "license": "SEE LICENSE IN LICENSE", "devDependencies": { "corifeus-builder": "^2025.4.135", "nodemon": "^3.1.14", "terser": "^5.46.1" }, "dependencies": { "@msgpack/msgpack": "^3.1.3", "bcryptjs": "^3.0.3", "chalk": "^5.6.2", "commander": "^14.0.3", "corifeus-utils": "^2025.4.123", "express": "^5.2.1", "fzstd": "^0.1.1", "groq-sdk": "^1.1.2", "ioredis": "^5.10.1", "lodash-es": "^4.18.1", "lz4js": "^0.2.0", "snappyjs": "^0.7.0", "socket.io": "^4.8.3", "tunnel-ssh": "^5.2.0" }, "engines": { "node": ">=12.13.0" }, "homepage": "https://corifeus.com/redis-ui-server" }scripts/000077500000000000000000000000001517715050700125555ustar00rootroot00000000000000scripts/build-compressed.mjs000066400000000000000000000045271517715050700165410ustar00rootroot00000000000000#!/usr/bin/env node import fs from 'fs/promises' import path from 'path' import { fileURLToPath } from 'url' import { minify } from 'terser' const __dirname = path.dirname(fileURLToPath(import.meta.url)) const rootDir = path.resolve(__dirname, '..') const srcDir = path.join(rootDir, 'src') const distDir = path.join(rootDir, 'dist') const walkFiles = async (dir) => { const entries = await fs.readdir(dir, { withFileTypes: true }) const files = [] for (const entry of entries) { const fullPath = path.join(dir, entry.name) if (entry.isDirectory()) { files.push(...await walkFiles(fullPath)) } else if (entry.isFile()) { files.push(fullPath) } } return files } const minifyJavaScript = async (sourceCode, relativePath) => { const result = await minify(sourceCode, { module: true, compress: { passes: 2, keep_infinity: true, unsafe_arrows: true, }, mangle: true, format: { ascii_only: true, comments: false, }, }) if (!result || typeof result.code !== 'string' || result.code.length === 0) { throw new Error(`minify-empty-output: ${relativePath}`) } return `${result.code}\n` } const buildCompressed = async () => { console.log('build-compressed: started') await fs.rm(distDir, { recursive: true, force: true }) const sourceFiles = await walkFiles(srcDir) let minifiedCount = 0 let copiedCount = 0 for (const sourceFile of sourceFiles) { const relativePath = path.relative(srcDir, sourceFile) const outputFile = path.join(distDir, relativePath) await fs.mkdir(path.dirname(outputFile), { recursive: true }) if (path.extname(sourceFile) === '.mjs') { const sourceCode = await fs.readFile(sourceFile, 'utf8') const minifiedCode = await minifyJavaScript(sourceCode, relativePath) await fs.writeFile(outputFile, minifiedCode, 'utf8') minifiedCount++ continue } await fs.copyFile(sourceFile, outputFile) copiedCount++ } console.log(`build-compressed: done (minified=${minifiedCount}, copied=${copiedCount})`) } buildCompressed().catch((error) => { console.error('build-compressed: failed', error) process.exit(1) }) scripts/redis-bulk.lua000066400000000000000000000003541517715050700153230ustar00rootroot00000000000000-- -- Created by IntelliJ IDEA. -- User: patrikx3 -- Date: 7/9/20 -- Time: 4:59 PM -- To change this template use File | Settings | File Templates. -- for i = 1, 100000, 1 do redis.call("SET", "bulk-key-"..i, i) end return "Ok!" scripts/redis-cluster.sh000077500000000000000000000002311517715050700156750ustar00rootroot00000000000000#!/usr/bin/env bash docker run -e "IP=0.0.0.0" -p 7000:7000 -p 7001:7001 -p 7002:7002 -p 7003:7003 -p 7004:7004 -p 7005:7005 grokzen/redis-cluster:latestsrc/000077500000000000000000000000001517715050700116555ustar00rootroot00000000000000src/index.mjs000066400000000000000000000001701517715050700134750ustar00rootroot00000000000000import lib from './lib/index.mjs' import services from './service/index.mjs' export default { lib, services, } src/lib/000077500000000000000000000000001517715050700124235ustar00rootroot00000000000000src/lib/boot.mjs000066400000000000000000000034311517715050700141020ustar00rootroot00000000000000import 'corifeus-utils' import cli from './cli.mjs' import consoleStamp from './console-stamp.mjs' import httpService from '../service/http/index.mjs' import socketIoService from '../service/socket.io/index.mjs' const boot = async () => { global.p3xrs = {} p3xrs.cfg = undefined if (!(await cli())) { return; } consoleStamp() p3xrs.http = new httpService() await p3xrs.http.boot() p3xrs.socketIo = new socketIoService(); await p3xrs.socketIo.boot({ httpService: p3xrs.http }) p3xrs.redisConnections = {} p3xrs.redisConnectionsSubscriber = {} process.on('uncaughtException', (error) => { console.error('Uncaught Exception:', error); }); const gracefulShutdown = async (signal) => { console.info(`Received ${signal}, shutting down gracefully...`) try { // Close Socket.IO connections if (p3xrs.socketIo?.socketio) { p3xrs.socketIo.socketio.close() } // Close all Redis connections if (p3xrs.redisConnections) { for (const key of Object.keys(p3xrs.redisConnections)) { try { if (p3xrs.redisConnections[key]?.ioredis) { p3xrs.redisConnections[key].ioredis.disconnect() } } catch {} } } // Close HTTP server if (p3xrs.http?.server) { p3xrs.http.server.close() } } catch (e) { console.error('Error during shutdown:', e) } process.exit(0) } process.on('SIGTERM', () => gracefulShutdown('SIGTERM')) process.on('SIGINT', () => gracefulShutdown('SIGINT')) } export default boot src/lib/cli.mjs000066400000000000000000000316351517715050700137150ustar00rootroot00000000000000import path from 'path' import fs from 'fs' import os from 'os' import { program } from 'commander' import { parseBoolean, readPasswordHashFromFile } from './http-auth.mjs' const isPlainObject = (value) => { return Boolean(value) && typeof value === 'object' && !Array.isArray(value) } const mergeDeep = (target, source) => { const output = isPlainObject(target) ? { ...target } : {} if (!isPlainObject(source)) { return output } for (const [key, value] of Object.entries(source)) { if (Array.isArray(value)) { output[key] = value.slice() continue } if (isPlainObject(value)) { output[key] = mergeDeep(isPlainObject(output[key]) ? output[key] : {}, value) continue } output[key] = value } return output } const loadJsonFile = (filePath) => { if (!filePath || !fs.existsSync(filePath)) { return undefined } try { const content = fs.readFileSync(filePath, 'utf8') return JSON.parse(content) } catch (e) { console.warn(`Could not read config ${filePath}:`, e.message) return undefined } } const cli = async () => { const pkg = JSON.parse(fs.readFileSync(new URL('../../package.json', import.meta.url), 'utf8')) program .version(pkg.version) .option('-c, --config [config]', 'Set the p3xr.json p3x-redis-ui-server configuration, see more help in p3x-redis-ui-server') .option('-r, --readonly-connections', 'Set the connections to be readonly, no adding, saving or delete a connection') .option('-n, --connections-file-name [filename]', 'Set the connections file name, overrides default .p3xrs-conns.json') .option('--http-auth-enable', 'Enable HTTP Basic auth') .option('--http-auth-disable', 'Disable HTTP Basic auth') .option('--http-auth-username [username]', 'HTTP Basic auth username') .option('--http-auth-password [password]', 'HTTP Basic auth plain password') .option('--http-auth-password-hash [hash]', 'HTTP Basic auth bcrypt password hash') .option('--http-auth-password-hash-file [file]', 'Read HTTP Basic auth bcrypt password hash from file') .option('--groq-api-key [key]', 'Groq API key for AI-powered Redis query translation (get a free key at console.groq.com)') .option('--groq-api-key-readonly', 'Prevent users from changing the Groq API key via the UI') .parse(process.argv); const programOptions = program.opts(); if (!process.versions.hasOwnProperty('electron') && !process.env.hasOwnProperty('P3XRS_DOCKER_HOME')) { if (!programOptions.config) { const findConfigFile = (startPath, filename) => { let currentPath = startPath; while (currentPath !== path.resolve(currentPath, '..')) { // Check until we reach the root directory const filePath = path.join(currentPath, filename); if (fs.existsSync(filePath)) { return filePath; } currentPath = path.resolve(currentPath, '..'); // Move up one directory level } throw new Error('The specified configuration file could not be found.'); } const resolveConfigPath = () => { // Attempt to find the config file starting from the directory of the main script or current directory const startPath = process.cwd(); return findConfigFile(startPath, 'p3xrs.json'); } programOptions.config = resolveConfigPath() // program.outputHelp() // return false } const configPath = path.resolve(process.cwd(), programOptions.config) //console.log(configPath) p3xrs.configPath = configPath p3xrs.cfg = JSON.parse(fs.readFileSync(configPath, 'utf8')).p3xrs if (programOptions.readonlyConnections) { // console.warn(programOptions.readonlyConnections) p3xrs.cfg.readonlyConnections = true //console.warn(p3xrs.cfg.readonlyConnections === true) } if (typeof programOptions.groqApiKey === 'string' && programOptions.groqApiKey.trim()) { p3xrs.cfg.groqApiKey = programOptions.groqApiKey.trim() } if (programOptions.groqApiKeyReadonly) { p3xrs.cfg.groqApiKeyReadonly = true } if (typeof programOptions.connectionsFileName !== 'undefined' && programOptions.connectionsFileName) { // console.warn(programOptions.connectionsFileName) p3xrs.cfg.connectionsFileName = programOptions.connectionsFileName //console.warn(p3xrs.cfg.readonlyConnections === true) } } else { const defaultElectronConfig = { "http": { "port-info": "this is ommitted, it will be default 7843", "port": process.env.hasOwnProperty('P3XRS_DOCKER_HOME') ? 7843 : global.p3xrsElectronPort, "bind-info": "the interface with listen to, could be 127.0.0.1 or 0.0.0.0 or specific interface", "bind": "0.0.0.0", }, "connections": { "home-dir-info": "if the dir config is empty or home, the connections are saved in the home folder, otherwise it will resolve the directory set as it is, either relative ./ or absolute starting with /. NodeJs will resolve this directory in p3xrs.connections.dir", "home-dir": "home" }, "static-info": "This is the best configuration, if it starts with ~, then it is in resolve the path in the node_modules, otherwise it resolves to the current process current working directory.", "static": "~p3x-redis-ui-material/dist", "httpAuth": { "enabled": false, "username": "admin", "password": "", "passwordHash": "", }, "treeDividers": [ ":", "/", "|", "-", "@" ] } let electronUserDataDir = '' try { const electron = await import('electron') const electronApp = electron.default?.app || electron.app if (electronApp && typeof electronApp.getPath === 'function') { electronUserDataDir = electronApp.getPath('userData') } } catch (e) { electronUserDataDir = '' } const configuredDir = typeof process.env.P3XRS_ELECTRON_CONFIG_DIR === 'string' ? process.env.P3XRS_ELECTRON_CONFIG_DIR.trim() : '' const electronConfigDir = configuredDir || electronUserDataDir || os.homedir() p3xrs.configPath = path.resolve(electronConfigDir, 'p3xrs.json') let persistedRoot = loadJsonFile(p3xrs.configPath) if ((!persistedRoot || !isPlainObject(persistedRoot.p3xrs))) { const legacyConfigPath = path.resolve(process.cwd(), 'p3xrs.json') const legacyRoot = loadJsonFile(legacyConfigPath) if (legacyRoot && isPlainObject(legacyRoot.p3xrs)) { persistedRoot = legacyRoot } } const persistedConfig = persistedRoot && isPlainObject(persistedRoot.p3xrs) ? persistedRoot.p3xrs : {} p3xrs.cfg = mergeDeep(defaultElectronConfig, persistedConfig) if (programOptions.readonlyConnections) { p3xrs.cfg.readonlyConnections = true } else { p3xrs.cfg.readonlyConnections = false } if (typeof programOptions.groqApiKey === 'string' && programOptions.groqApiKey.trim()) { p3xrs.cfg.groqApiKey = programOptions.groqApiKey.trim() } if (programOptions.groqApiKeyReadonly) { p3xrs.cfg.groqApiKeyReadonly = true } } const applyHttpAuthConfig = () => { if (!p3xrs.cfg.httpAuth || typeof p3xrs.cfg.httpAuth !== 'object') { if (p3xrs.cfg.server && typeof p3xrs.cfg.server.httpAuth === 'object') { p3xrs.cfg.httpAuth = Object.assign({}, p3xrs.cfg.server.httpAuth) } else { p3xrs.cfg.httpAuth = {} } } const httpAuth = p3xrs.cfg.httpAuth if (typeof p3xrs.cfg.httpUser === 'string' && !httpAuth.username) { httpAuth.username = p3xrs.cfg.httpUser } if (typeof p3xrs.cfg.httpPassword === 'string' && !httpAuth.password) { httpAuth.password = p3xrs.cfg.httpPassword } if (typeof process.env.HTTP_USER === 'string' && process.env.HTTP_USER.trim() !== '') { httpAuth.username = process.env.HTTP_USER.trim() } if (typeof process.env.HTTP_PASSWORD === 'string') { httpAuth.password = process.env.HTTP_PASSWORD } if (typeof process.env.HTTP_PASSWORD_HASH === 'string' && process.env.HTTP_PASSWORD_HASH.trim() !== '') { httpAuth.passwordHash = process.env.HTTP_PASSWORD_HASH.trim() } if (typeof process.env.HTTP_PASSWORD_HASH_FILE === 'string' && process.env.HTTP_PASSWORD_HASH_FILE.trim() !== '') { const hashFromFile = readPasswordHashFromFile(process.env.HTTP_PASSWORD_HASH_FILE) if (hashFromFile) { httpAuth.passwordHash = hashFromFile } } const envEnabled = parseBoolean(process.env.HTTP_AUTH_ENABLED) if (envEnabled !== undefined) { httpAuth.enabled = envEnabled } if (typeof programOptions.httpAuthUsername === 'string' && programOptions.httpAuthUsername.trim() !== '') { httpAuth.username = programOptions.httpAuthUsername.trim() } if (typeof programOptions.httpAuthPassword === 'string') { httpAuth.password = programOptions.httpAuthPassword } if (typeof programOptions.httpAuthPasswordHash === 'string' && programOptions.httpAuthPasswordHash.trim() !== '') { httpAuth.passwordHash = programOptions.httpAuthPasswordHash.trim() } if (typeof programOptions.httpAuthPasswordHashFile === 'string' && programOptions.httpAuthPasswordHashFile.trim() !== '') { const hashFromCliFile = readPasswordHashFromFile(programOptions.httpAuthPasswordHashFile) if (hashFromCliFile) { httpAuth.passwordHash = hashFromCliFile } } if (programOptions.httpAuthEnable === true) { httpAuth.enabled = true } if (programOptions.httpAuthDisable === true) { httpAuth.enabled = false } } applyHttpAuthConfig() const authLog = p3xrs.cfg && p3xrs.cfg.httpAuth && typeof p3xrs.cfg.httpAuth === 'object' ? p3xrs.cfg.httpAuth : {} const authEnabled = parseBoolean(authLog.enabled) === true const authHasHash = typeof authLog.passwordHash === 'string' && authLog.passwordHash.trim().length > 0 const authHasPlain = typeof authLog.password === 'string' && authLog.password.length > 0 console.info(`http auth: ${authEnabled ? 'enabled' : 'disabled'} (user=${authLog.username || 'admin'}, hash=${authHasHash ? 'set' : 'empty'}, plain=${authHasPlain ? 'set' : 'empty'})`) if (p3xrs.cfg.connectionsFileName === undefined) { p3xrs.cfg.connectionsFileName = '.p3xrs-conns.json' } if (!p3xrs.cfg.hasOwnProperty('static')) { } if (!p3xrs.cfg.hasOwnProperty('connections')) { p3xrs.cfg.connections = {} } if (!p3xrs.cfg.connections.hasOwnProperty('home-dir')) { p3xrs.cfg.connections = 'home' } if (p3xrs.cfg.connections['home-dir'] === 'home') { p3xrs.cfg.connections['home-dir'] = os.homedir(); } if (process.env.hasOwnProperty('P3XRS_DOCKER_HOME')) { p3xrs.cfg.connections['home-dir'] = process.env.P3XRS_DOCKER_HOME } if (process.env.FLATPAK_ID) { // process.env.XDG_DATA_HOME p3xrs.cfg.connections['home-dir'] = '/var/data/' } if (process.env.hasOwnProperty('P3XRS_PORT')) { p3xrs.cfg.http.port = process.env.P3XRS_PORT } p3xrs.cfg.connections['home'] = path.resolve(p3xrs.cfg.connections['home-dir'], p3xrs.cfg.connectionsFileName) console.info('using home config is', p3xrs.cfg.connections['home']) if (!fs.existsSync(p3xrs.cfg.connections.home)) { fs.writeFileSync(p3xrs.cfg.connections.home, JSON.stringify({ update: new Date(), list: [], }, null, 4)) } p3xrs.connections = JSON.parse(fs.readFileSync(p3xrs.cfg.connections.home, 'utf8')) //console.log(p3xrs.cfg.connections.home, p3xrs.connections) //console.log(p3xrs.connections) /* p3xrs.redis = {} let keyStreamPaging = 10000 Object.defineProperty(p3xrs.redis, 'key-stream-paging', { get: () => { return keyStreamPaging }, set: (value) => { keyStreamPaging = value } }) */ return true; } export default cli; src/lib/console-stamp.mjs000066400000000000000000000026371517715050700157320ustar00rootroot00000000000000import chalk from 'chalk' const consoleStamp = () => { // overriding the console should be after this!!! console.warn = console.log const methods = ['log', 'info', 'warn', 'error', 'debug'] const originalMethods = {} for(let method of methods) { originalMethods[method] = console[method] console[method] = function() { if (arguments[0]) { let label switch(method) { case 'error': label = chalk.bold.red(method.toUpperCase()); break; case 'warn': label = chalk.bold.blue(method.toUpperCase()); break; default: label = chalk.green(method.toUpperCase()); } let data = '' //chalk`${moment().format(`YYYY/MM/DD HH:mm:ss.SSS`)} ` data += chalk.gray('[P3XRS]') + ` [PID: ${(String(process.pid).padStart(6, 0))}] [${label.padStart(5, ' ')}]: ` //arguments[0] = data + arguments[0] const mainArguments = Array.prototype.slice.call(arguments); mainArguments.unshift(data); originalMethods[method].apply(null, mainArguments) } else { originalMethods[method].apply(null, arguments) } } } } export default consoleStamp src/lib/http-auth.mjs000066400000000000000000000071161517715050700150610ustar00rootroot00000000000000import fs from 'fs' import crypto from 'crypto' import bcrypt from 'bcryptjs' const parseBoolean = (value) => { if (typeof value === 'boolean') { return value } if (typeof value !== 'string') { return undefined } const normalized = value.trim().toLowerCase() if (['1', 'true', 'yes', 'on'].includes(normalized)) { return true } if (['0', 'false', 'no', 'off'].includes(normalized)) { return false } return undefined } const resolveConfiguredHttpAuth = () => { const cfg = p3xrs && p3xrs.cfg && typeof p3xrs.cfg === 'object' ? p3xrs.cfg : {} const fromServer = cfg.server && typeof cfg.server.httpAuth === 'object' ? cfg.server.httpAuth : {} const fromRoot = cfg.httpAuth && typeof cfg.httpAuth === 'object' ? cfg.httpAuth : {} const merged = Object.assign({}, fromServer, fromRoot) const username = typeof merged.username === 'string' && merged.username.length > 0 ? merged.username : 'admin' const password = typeof merged.password === 'string' ? merged.password : '' const passwordHash = typeof merged.passwordHash === 'string' ? merged.passwordHash.trim() : '' const enabledRaw = parseBoolean(merged.enabled) const hasSecret = password.length > 0 || passwordHash.length > 0 const enabled = enabledRaw === undefined ? hasSecret : enabledRaw return { enabled, username, password, passwordHash, } } const safeCompare = (a, b) => { const aBuffer = Buffer.from(typeof a === 'string' ? a : '', 'utf8') const bBuffer = Buffer.from(typeof b === 'string' ? b : '', 'utf8') if (aBuffer.length !== bBuffer.length) { return false } return crypto.timingSafeEqual(aBuffer, bBuffer) } const parseBasicAuthorizationHeader = (headerValue) => { if (typeof headerValue !== 'string' || headerValue.length === 0) { return null } const parts = headerValue.split(' ') if (parts.length !== 2 || parts[0].toLowerCase() !== 'basic') { return null } const decoded = Buffer.from(parts[1], 'base64').toString('utf8') const colonIndex = decoded.indexOf(':') if (colonIndex === -1) { return null } return { username: decoded.slice(0, colonIndex), password: decoded.slice(colonIndex + 1), } } const verifyCredentials = ({ username, password }) => { const settings = resolveConfiguredHttpAuth() if (!settings.enabled) { return true } if (!safeCompare(username, settings.username)) { return false } if (settings.passwordHash.length > 0) { try { return bcrypt.compareSync(password, settings.passwordHash) } catch (e) { return false } } if (settings.password.length > 0) { return safeCompare(password, settings.password) } return false } const verifyAuthorizationHeader = (headerValue) => { const settings = resolveConfiguredHttpAuth() if (!settings.enabled) { return true } const parsed = parseBasicAuthorizationHeader(headerValue) if (!parsed) { return false } return verifyCredentials(parsed) } const readPasswordHashFromFile = (filename) => { if (typeof filename !== 'string' || filename.trim() === '') { return '' } const resolved = filename.trim() if (!fs.existsSync(resolved)) { return '' } try { return fs.readFileSync(resolved, 'utf8').trim() } catch (e) { return '' } } export { parseBoolean, resolveConfiguredHttpAuth, verifyAuthorizationHeader, readPasswordHashFromFile, } src/lib/index.mjs000066400000000000000000000002421517715050700142430ustar00rootroot00000000000000import boot from './boot.mjs' import cli from './cli.mjs' import consoleStamp from './console-stamp.mjs' export default { boot, cli, consoleStamp, } src/lib/ioredis-cluster/000077500000000000000000000000001517715050700155405ustar00rootroot00000000000000src/lib/ioredis-cluster/cluster.mjs000066400000000000000000000125231517715050700177370ustar00rootroot00000000000000import Redis from 'ioredis' import { EventEmitter } from 'events' import redisInfo from './redis-info.mjs' import setDefaultOptionsFromServer from './set-default-options-from-server.mjs' export default class Cluster extends Redis.Cluster { constructor(server, options = {}) { server = Array.isArray(server) ? server : [server] options = setDefaultOptionsFromServer(options, server) options.clusterRetryStrategy = null super(server, options) } /* async infoObject(...args) { const [key] = args if (key === 'keyspace') { return { databases: [await this._getClusterInfoKeyspace()] } } const info = await this.info(...args) const infoObject = redisInfo.parse(info) if (!key) { infoObject.databases[0] = await this._getClusterInfoKeyspace() } return infoObject } */ async _getClusterInfoKeyspace(info) { const keyspaceList = await Promise.all(this.nodes('master').map(node => { return node.info('keyspace') })) let keys = 0 let expires = 0 let avg_ttl = 0 for (const nodeKeyspace of keyspaceList) { if (!nodeKeyspace) { continue } const parsed = redisInfo.parse(nodeKeyspace) const db0 = parsed.databases[0] if (!db0) { continue } const { keys: nodeKeys = 0, expires: nodeExpires = 0, avg_ttl: nodeAvgTtl = 0, } = db0 keys += nodeKeys expires += nodeExpires avg_ttl += nodeAvgTtl } avg_ttl = avg_ttl ? Math.round(avg_ttl / expires) : 0 const clusterKeyspace = { keys, expires, avg_ttl, } // console.log({clusterKeyspace}) return clusterKeyspace } async originalDbsize(...args) { return super.dbsize(...args) } async dbsize() { const nodeCounts = await Promise.all(this.nodes('master').map(node => { return node.dbsize() })) const count = nodeCounts.reduce((tt, c) => tt + c, 0) return count } originalRename(...args) { return super.rename(...args) } async rename(key, keyNew, callback) { let res = null let err = null try { let [value, ttl] = await Promise.all([ this.dumpBuffer(key), this.ttl(key), ]) ttl = parseInt(ttl) if (ttl < 0) { ttl = 0 } await this.del(keyNew) await this.restore(keyNew, ttl, value) await this.del(key) res = 'OK' } catch (e) { err = e } if (typeof callback === 'function') { callback(err, res) } else if (err) { throw err } return res } originalPipeline(...args) { return super.pipeline(...args) } pipeline(...pipelineArgs) { const calls = [] async function exec(calls) { const results = [] for (let c of calls) { const result = await c() results.push(result) } // console.log({results}) return results } const proxy = new Proxy(calls, { get: (calls, method) => { return (...callArgs) => { switch (method) { case 'exec': return exec(calls) break } const callback = async () => { let err = null let result = null try { result = await this[method](...callArgs) } catch (e) { err = e } return [err, result] } calls.push(callback) return proxy } } }) return proxy } scanStream(...args) { const stream = new EventEmitter() this._streamNodes({ stream, method: 'scanStream', params: args, }) return stream } async _streamNodes(options = {}) { let { nodes = this.nodes('master'), stream = new EventEmitter(), method, params = [], } = options try { const promises = [] for (let node of nodes) { promises.push( new Promise((resolve, reject) => { const nodeStream = node[method](...params) nodeStream.on('data', (resultKeys) => { // console.log({resultKeys}) stream.emit('data', resultKeys) }) nodeStream.on('end', () => { resolve() }) }) ) } await Promise.all(promises) } finally { stream.emit('end') } } } src/lib/ioredis-cluster/create-with-cluster-auto-detect.mjs000066400000000000000000000012061517715050700243610ustar00rootroot00000000000000import Redis from 'ioredis' import isClusterEnabled from './is-cluster-enabled.mjs' import Cluster from './cluster.mjs' import setDefaultOptionsFromServer from './set-default-options-from-server.mjs' export default async function createWithClusterAutoDetect(server, options = {}) { let isCluster if (Array.isArray(server)) { isCluster = true } else { isCluster = await isClusterEnabled(server, true) } if (!isCluster) { return new Redis(server) } // server = await getClusterNodes(server) options = setDefaultOptionsFromServer(options, server) return new Cluster(server, options) } src/lib/ioredis-cluster/get-cluster-nodes.mjs000066400000000000000000000036341517715050700216250ustar00rootroot00000000000000import Redis from 'ioredis' export default async function getClusterNodes(servers, options = {}) { if (!Array.isArray(servers)) { servers = [servers] } const errors = [] let nodes for (const server of servers) { let redis try { redis = new Redis({...server, retryStrategy: () => false}) const rawNodes = await new Promise((resolve, reject) => { redis.sendCommand( new Redis.Command( 'CLUSTER', ['NODES'], 'utf-8', function (err, value) { if (err) reject(err) else resolve(value.toString()) } ) ) }) const lines = rawNodes.trim().split("\n") nodes = lines.reduce((arr, line) => { if (!line) { return arr } const row = line.split(' ') const [ node_id, server, flags, ] = row const [target, slots] = server.split('@') const [host, port] = target.split(':') const node = { host, port, } arr.push(node) return arr }, []) return nodes } catch (error) { console.error(error) errors.push(error) } finally { redis.disconnect() } if (nodes) { break } } if (nodes) { return nodes } const errorsMsg = errors.map(e => e.toString()).join(', ') throw new Error('getClusterNodes errors: ' + errorsMsg) } src/lib/ioredis-cluster/get-info.mjs000066400000000000000000000004441517715050700177650ustar00rootroot00000000000000import Redis from 'ioredis' import redisInfo from './redis-info.mjs' export default async function getInfo(server, options = {}) { const redis = new Redis(server) const rawInfo = await redis.info() redis.disconnect() const info = redisInfo.parse(rawInfo) return info } src/lib/ioredis-cluster/index.mjs000066400000000000000000000000651517715050700173630ustar00rootroot00000000000000import Redis from './redis.mjs' export default Redis src/lib/ioredis-cluster/is-cluster-enabled.mjs000066400000000000000000000003331517715050700217340ustar00rootroot00000000000000import getInfo from './get-info.mjs' export default async function isClusterEnabled(server, cache = false) { const {cluster_enabled} = await getInfo(server, {cache}) return Boolean(parseInt(cluster_enabled)) } src/lib/ioredis-cluster/redis-info.mjs000066400000000000000000000055761517715050700203270ustar00rootroot00000000000000/* from npm's redis-info, adding missing avg_ttl */ import { fromPairs, find, has } from 'lodash-es' export default { parse: function (info) { return parseFields(splitStr(info)) } } function startWith(pattern) { return function (value) { return value.indexOf(pattern) === 0 } } function split(s) { return function (v) { return v.split(s) } } function orEmptyStr(v) { return v || '' } function takeN(func, n) { return function (v) { return func(v[n]) } } function takeFirst(func) { return takeN(func, 0) } /** * Split the info string by \n and : * @param {String} str the returned redis info * @return {Array} Array of [key, value] */ function splitStr(str) { return str.split('\n') .filter(function (line) { return line.length > 0 && line.indexOf('#') !== 0 }) .map(function (line) { return line.trim().split(':') }) } function parseDatabases(info) { return info .filter(takeFirst(startWith('db'))) .map(function _parseDatabaseInfo(args) { var dbName = args[0] var value = args[1] var values = orEmptyStr(value).split(',') // console.log({values}) function extract(param) { return parseInt(orEmptyStr(find(values, startWith(param))).split('=')[1] || 0, 10) } return { index: parseInt(dbName.substr(2), 10), keys: extract('keys'), expires: extract('expires'), avg_ttl: extract('avg_ttl'), } }) .reduce(function (m, v) { m[v.index] = { keys: v.keys, expires: v.expires, avg_ttl: v.avg_ttl, } return m }, {}) } function parseCommands(info) { return fromPairs(info.filter(function (a) { return orEmptyStr(a[0]).indexOf('cmdstat_') === 0 }) .map(function _parseCommands(args) { var v = args[0] var a = args[1] var val = fromPairs(orEmptyStr(a).split(',').map(split('='))) if (has(val, 'calls')) { val.calls = parseInt(val.calls, 10) } if (has(val, 'usec')) { val.usec = parseInt(val.usec, 10) } if (has(val, 'usec_per_call')) { val.usec_per_call = parseFloat(val.usec_per_call, 10) } return [orEmptyStr(v).split('_')[1], val] })) } function parseFields(info) { var fields = info.reduce(function (m, v) { if (!v[0].trim() || v[0].indexOf('db') === 0 || v[0].indexOf('cmdstat_') === 0) { return m } m[v[0]] = v[1] return m }, { databases: parseDatabases(info), commands: parseCommands(info) }) return fields } src/lib/ioredis-cluster/redis.mjs000066400000000000000000000020311517715050700173550ustar00rootroot00000000000000import IORedis from 'ioredis' import redisInfo from './redis-info.mjs' import Cluster from './cluster.mjs' import createWithClusterAutoDetect from './create-with-cluster-auto-detect.mjs' import getInfo from './get-info.mjs' import getClusterNodes from './get-cluster-nodes.mjs' import isClusterEnabled from './is-cluster-enabled.mjs' class Redis extends IORedis { constructor(server, {autoDetectCluster, ...options} = {}) { if (autoDetectCluster && !Array.isArray(server)) { return createWithClusterAutoDetect(server, options) } if (Array.isArray(server)) { return new Cluster(server, options) } super(server) } /* async infoObject(...args) { const info = await this.info(...args) return redisInfo.parse(info) } */ } Object.defineProperty(Redis, 'Cluster', { value: Cluster, writable: true, configurable: true }) Redis.isClusterEnabled = isClusterEnabled Redis.getClusterNodes = getClusterNodes Redis.getInfo = getInfo export default Redis src/lib/ioredis-cluster/set-default-options-from-server.mjs000066400000000000000000000025561517715050700244360ustar00rootroot00000000000000function getDefaultOptionsFromServer(server) { const server1 = Array.isArray(server) ? server[0] : server if (typeof server1 === 'object' && server1 !== null) { return server1 } } export default function(options, server) { const serverOptions = getDefaultOptionsFromServer(server) //console.log('serverOptions', serverOptions) let redisOptions = options.redisOptions if (redisOptions === undefined) { redisOptions = {} options.redisOptions = redisOptions } if (redisOptions.password === undefined) { redisOptions.password = serverOptions.password } if (serverOptions.tlsWithoutCert) { redisOptions.tls = { servername: serverOptions.host } } else if (typeof serverOptions.tlsCa === 'string' && serverOptions.tlsCa.trim() !== '') { redisOptions.tls = { cert: serverOptions.tlsCrt, key: serverOptions.tlsKey, ca: serverOptions.tlsCa, servername: serverOptions.host } } if (redisOptions.hasOwnProperty('tls')) { redisOptions.tls.rejectUnauthorized = redisOptions.tlsRejectUnauthorized === undefined ? false : redisOptions.tlsRejectUnauthorized if (!redisOptions.tls.hasOwnProperty('servername')) { redisOptions.tls.servername = serverOptions.host } } return options } src/lib/redis-command-hints.mjs000066400000000000000000000323371517715050700170130ustar00rootroot00000000000000/** * Static argument syntax hints for common Redis commands. * Format: command name (uppercase) β†’ syntax string */ export default { // Strings SET: 'key value [EX seconds | PX ms | EXAT unix-sec | PXAT unix-ms | KEEPTTL] [NX | XX] [GET]', GET: 'key', MSET: 'key value [key value ...]', MGET: 'key [key ...]', SETNX: 'key value', SETEX: 'key seconds value', PSETEX: 'key milliseconds value', GETSET: 'key value', GETDEL: 'key', GETEX: 'key [EX seconds | PX ms | EXAT unix-sec | PXAT unix-ms | PERSIST]', GETRANGE: 'key start end', SETRANGE: 'key offset value', APPEND: 'key value', STRLEN: 'key', INCR: 'key', INCRBY: 'key increment', INCRBYFLOAT: 'key increment', DECR: 'key', DECRBY: 'key decrement', MSETNX: 'key value [key value ...]', LCS: 'key1 key2 [LEN] [IDX] [MINMATCHLEN min] [WITHMATCHLEN]', SUBSTR: 'key start end', // Hash HSET: 'key field value [field value ...]', HGET: 'key field', HMSET: 'key field value [field value ...]', HMGET: 'key field [field ...]', HGETALL: 'key', HDEL: 'key field [field ...]', HEXISTS: 'key field', HINCRBY: 'key field increment', HINCRBYFLOAT: 'key field increment', HKEYS: 'key', HVALS: 'key', HLEN: 'key', HSETNX: 'key field value', HRANDFIELD: 'key [count [WITHVALUES]]', HSCAN: 'key cursor [MATCH pattern] [COUNT count]', // List LPUSH: 'key element [element ...]', RPUSH: 'key element [element ...]', LPOP: 'key [count]', RPOP: 'key [count]', LLEN: 'key', LRANGE: 'key start stop', LINDEX: 'key index', LSET: 'key index element', LINSERT: 'key BEFORE|AFTER pivot element', LREM: 'key count element', LTRIM: 'key start stop', LPOS: 'key element [RANK rank] [COUNT count] [MAXLEN len]', LMOVE: 'source destination LEFT|RIGHT LEFT|RIGHT', BLPOP: 'key [key ...] timeout', BRPOP: 'key [key ...] timeout', BLMOVE: 'source destination LEFT|RIGHT LEFT|RIGHT timeout', RPOPLPUSH: 'source destination', BRPOPLPUSH: 'source destination timeout', LPUSHX: 'key element [element ...]', RPUSHX: 'key element [element ...]', LMPOP: 'numkeys key [key ...] LEFT|RIGHT [COUNT count]', BLMPOP: 'timeout numkeys key [key ...] LEFT|RIGHT [COUNT count]', // Set SADD: 'key member [member ...]', SREM: 'key member [member ...]', SMEMBERS: 'key', SISMEMBER: 'key member', SMISMEMBER: 'key member [member ...]', SCARD: 'key', SPOP: 'key [count]', SRANDMEMBER: 'key [count]', SINTER: 'key [key ...]', SUNION: 'key [key ...]', SDIFF: 'key [key ...]', SINTERSTORE: 'destination key [key ...]', SUNIONSTORE: 'destination key [key ...]', SDIFFSTORE: 'destination key [key ...]', SMOVE: 'source destination member', SINTERCARD: 'numkeys key [key ...] [LIMIT limit]', SSCAN: 'key cursor [MATCH pattern] [COUNT count]', // Sorted Set ZADD: 'key [NX|XX] [GT|LT] [CH] [INCR] score member [score member ...]', ZREM: 'key member [member ...]', ZSCORE: 'key member', ZMSCORE: 'key member [member ...]', ZRANK: 'key member [WITHSCORE]', ZREVRANK: 'key member [WITHSCORE]', ZRANGE: 'key min max [BYSCORE|BYLEX] [REV] [LIMIT offset count] [WITHSCORES]', ZRANGEBYSCORE: 'key min max [WITHSCORES] [LIMIT offset count]', ZREVRANGEBYSCORE: 'key max min [WITHSCORES] [LIMIT offset count]', ZRANGEBYLEX: 'key min max [LIMIT offset count]', ZREVRANGEBYLEX: 'key max min [LIMIT offset count]', ZRANGESTORE: 'dst src min max [BYSCORE|BYLEX] [REV] [LIMIT offset count]', ZCARD: 'key', ZCOUNT: 'key min max', ZLEXCOUNT: 'key min max', ZINCRBY: 'key increment member', ZINTERSTORE: 'destination numkeys key [key ...] [WEIGHTS weight ...] [AGGREGATE SUM|MIN|MAX]', ZUNIONSTORE: 'destination numkeys key [key ...] [WEIGHTS weight ...] [AGGREGATE SUM|MIN|MAX]', ZDIFFSTORE: 'destination numkeys key [key ...]', ZINTER: 'numkeys key [key ...] [WEIGHTS weight ...] [AGGREGATE SUM|MIN|MAX] [WITHSCORES]', ZUNION: 'numkeys key [key ...] [WEIGHTS weight ...] [AGGREGATE SUM|MIN|MAX] [WITHSCORES]', ZDIFF: 'numkeys key [key ...] [WITHSCORES]', ZRANDMEMBER: 'key [count [WITHSCORES]]', ZPOPMIN: 'key [count]', ZPOPMAX: 'key [count]', BZPOPMIN: 'key [key ...] timeout', BZPOPMAX: 'key [key ...] timeout', ZMPOP: 'numkeys key [key ...] MIN|MAX [COUNT count]', BZMPOP: 'timeout numkeys key [key ...] MIN|MAX [COUNT count]', ZSCAN: 'key cursor [MATCH pattern] [COUNT count]', // Key DEL: 'key [key ...]', UNLINK: 'key [key ...]', EXISTS: 'key [key ...]', EXPIRE: 'key seconds [NX|XX|GT|LT]', PEXPIRE: 'key milliseconds [NX|XX|GT|LT]', EXPIREAT: 'key unix-time-seconds [NX|XX|GT|LT]', PEXPIREAT: 'key unix-time-milliseconds [NX|XX|GT|LT]', PERSIST: 'key', TTL: 'key', PTTL: 'key', EXPIRETIME: 'key', PEXPIRETIME: 'key', TYPE: 'key', RENAME: 'key newkey', RENAMENX: 'key newkey', KEYS: 'pattern', SCAN: 'cursor [MATCH pattern] [COUNT count] [TYPE type]', RANDOMKEY: '', DUMP: 'key', RESTORE: 'key ttl serialized-value [REPLACE] [ABSTTL] [IDLETIME seconds] [FREQ frequency]', COPY: 'source destination [DB destination-db] [REPLACE]', MOVE: 'key db', OBJECT: 'ENCODING|FREQ|HELP|IDLETIME|REFCOUNT key', SORT: 'key [BY pattern] [LIMIT offset count] [GET pattern ...] [ASC|DESC] [ALPHA] [STORE destination]', SORT_RO: 'key [BY pattern] [LIMIT offset count] [GET pattern ...] [ASC|DESC] [ALPHA]', TOUCH: 'key [key ...]', WAIT: 'numreplicas timeout', WAITAOF: 'numlocal numreplicas timeout', // Stream XADD: 'key [NOMKSTREAM] [MAXLEN|MINID [=|~] threshold [LIMIT count]] *|id field value [field value ...]', XREAD: '[COUNT count] [BLOCK ms] STREAMS key [key ...] id [id ...]', XREADGROUP: 'GROUP group consumer [COUNT count] [BLOCK ms] [NOACK] STREAMS key [key ...] id [id ...]', XRANGE: 'key start end [COUNT count]', XREVRANGE: 'key end start [COUNT count]', XLEN: 'key', XINFO: 'CONSUMERS|GROUPS|STREAM key [FULL [COUNT count]]', XACK: 'key group id [id ...]', XCLAIM: 'key group consumer min-idle-time id [id ...] [IDLE ms] [TIME ms] [RETRYCOUNT count] [FORCE] [JUSTID] [LASTID id]', XAUTOCLAIM: 'key group consumer min-idle-time start [COUNT count] [JUSTID]', XDEL: 'key id [id ...]', XTRIM: 'key MAXLEN|MINID [=|~] threshold [LIMIT count]', XGROUP: 'CREATE|CREATECONSUMER|DELCONSUMER|DESTROY|SETID key group [id|$] [MKSTREAM] [ENTRIESREAD entries-read]', XPENDING: 'key group [[IDLE min-idle-time] start end count [consumer]]', XSETID: 'key last-id [ENTRIESREAD entries-read]', // Server INFO: '[section ...]', DBSIZE: '', FLUSHDB: '[ASYNC|SYNC]', FLUSHALL: '[ASYNC|SYNC]', SELECT: 'index', PING: '[message]', ECHO: 'message', QUIT: '', CONFIG: 'GET|SET|RESETSTAT|REWRITE parameter [value]', SLOWLOG: 'GET|LEN|RESET [count]', CLIENT: 'GETNAME|ID|INFO|KILL|LIST|NO-EVICT|PAUSE|REPLY|SETNAME|UNPAUSE|NO-TOUCH ...', MEMORY: 'DOCTOR|HELP|MALLOC-STATS|PURGE|STATS|USAGE key [SAMPLES count]', TIME: '', LASTSAVE: '', BGSAVE: '[SCHEDULE]', BGREWRITEAOF: '', SAVE: '', SHUTDOWN: '[NOSAVE|SAVE] [NOW] [FORCE]', ACL: 'CAT|DELUSER|GENPASS|GETUSER|LIST|LOAD|LOG|SAVE|SETUSER|WHOAMI ...', MODULE: 'LIST|LOAD|LOADEX|UNLOAD ...', COMMAND: '[COUNT|DOCS|GETKEYS|INFO|LIST ...]', DEBUG: 'subcommand [arg ...]', LATENCY: 'HISTORY|LATEST|RESET event', SWAPDB: 'index1 index2', REPLICAOF: 'host port', SLAVEOF: 'host port', FAILOVER: '[TO host port [FORCE]] [ABORT] [TIMEOUT ms]', CLUSTER: 'INFO|NODES|SLOTS|MYID|RESET|KEYSLOT|...', // Pub/Sub PUBLISH: 'channel message', SUBSCRIBE: 'channel [channel ...]', UNSUBSCRIBE: '[channel ...]', PSUBSCRIBE: 'pattern [pattern ...]', PUNSUBSCRIBE: '[pattern ...]', PUBSUB: 'CHANNELS|NUMSUB|NUMPAT|SHARDCHANNELS|SHARDNUMSUB [argument ...]', // Scripting EVAL: '"script" numkeys [key ...] [arg ...]', EVALSHA: 'sha1 numkeys [key ...] [arg ...]', EVALRO: '"script" numkeys [key ...] [arg ...]', EVALSHA_RO: 'sha1 numkeys [key ...] [arg ...]', SCRIPT: 'DEBUG|EXISTS|FLUSH|LOAD ...', FCALL: 'function numkeys [key ...] [arg ...]', FCALL_RO: 'function numkeys [key ...] [arg ...]', FUNCTION: 'CREATE|DELETE|DUMP|FLUSH|LIST|LOAD|RESTORE|STATS ...', // Geo GEOADD: 'key [NX|XX] [CH] longitude latitude member [longitude latitude member ...]', GEOPOS: 'key member [member ...]', GEODIST: 'key member1 member2 [M|KM|FT|MI]', GEOSEARCH: 'key FROMMEMBER member|FROMLONLAT lng lat BYRADIUS radius M|KM|FT|MI|BYBOX width height M|KM|FT|MI [ASC|DESC] [COUNT count [ANY]] [WITHCOORD] [WITHDIST] [WITHHASH]', GEOSEARCHSTORE: 'destination source FROMMEMBER member|FROMLONLAT lng lat BYRADIUS radius unit|BYBOX width height unit [ASC|DESC] [COUNT count [ANY]] [STOREDIST]', GEORADIUS: 'key longitude latitude radius M|KM|FT|MI [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] [ASC|DESC] [STORE key] [STOREDIST key]', GEORADIUSBYMEMBER: 'key member radius M|KM|FT|MI [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] [ASC|DESC] [STORE key] [STOREDIST key]', GEOHASH: 'key member [member ...]', // HyperLogLog PFADD: 'key element [element ...]', PFCOUNT: 'key [key ...]', PFMERGE: 'destkey sourcekey [sourcekey ...]', // Bitmap SETBIT: 'key offset value', GETBIT: 'key offset', BITCOUNT: 'key [start end [BYTE|BIT]]', BITPOS: 'key bit [start [end [BYTE|BIT]]]', BITOP: 'AND|OR|XOR|NOT destkey key [key ...]', BITFIELD: 'key [GET encoding offset | SET encoding offset value | INCRBY encoding offset increment | OVERFLOW WRAP|SAT|FAIL] ...', BITFIELD_RO: 'key GET encoding offset [GET encoding offset ...]', // JSON (ReJSON) 'JSON.SET': 'key path value [NX|XX]', 'JSON.GET': 'key [path ...]', 'JSON.DEL': 'key [path]', 'JSON.MGET': 'key [key ...] path', 'JSON.TYPE': 'key [path]', 'JSON.NUMINCRBY': 'key path value', 'JSON.NUMMULTBY': 'key path value', 'JSON.STRAPPEND': 'key [path] value', 'JSON.STRLEN': 'key [path]', 'JSON.ARRAPPEND': 'key path value [value ...]', 'JSON.ARRINDEX': 'key path value [start [stop]]', 'JSON.ARRINSERT': 'key path index value [value ...]', 'JSON.ARRLEN': 'key [path]', 'JSON.ARRPOP': 'key [path [index]]', 'JSON.ARRTRIM': 'key path start stop', 'JSON.OBJKEYS': 'key [path]', 'JSON.OBJLEN': 'key [path]', 'JSON.RESP': 'key [path]', // RediSearch 'FT.CREATE': 'index [ON HASH|JSON] [PREFIX count prefix ...] SCHEMA field TYPE [SORTABLE] ...', 'FT.SEARCH': 'index query [NOCONTENT] [VERBATIM] [NOSTOPWORDS] [WITHSCORES] [WITHPAYLOADS] [WITHSORTKEYS] [FILTER field min max ...] [GEOFILTER field lon lat radius unit] [INKEYS count key ...] [INFIELDS count field ...] [RETURN count field ...] [SUMMARIZE ...] [HIGHLIGHT ...] [SLOP slop] [TIMEOUT timeout] [INORDER] [LANGUAGE lang] [EXPANDER exp] [SCORER scorer] [SORTBY field [ASC|DESC]] [LIMIT offset num]', 'FT.AGGREGATE': 'index query [LOAD count field ...] [GROUPBY count field ... REDUCE func nargs arg ... [AS name]] [SORTBY count field [ASC|DESC] ...] [APPLY expr AS name] [LIMIT offset num] [FILTER expr]', 'FT.INFO': 'index', 'FT.DROPINDEX': 'index [DD]', 'FT._LIST': '', 'FT.ALTER': 'index SCHEMA ADD field TYPE ...', 'FT.ALIASADD': 'alias index', 'FT.ALIASDEL': 'alias', 'FT.ALIASUPDATE': 'alias index', 'FT.TAGVALS': 'index field', 'FT.EXPLAIN': 'index query', // TimeSeries 'TS.CREATE': 'key [RETENTION retentionPeriod] [ENCODING UNCOMPRESSED|COMPRESSED] [CHUNK_SIZE size] [DUPLICATE_POLICY policy] [LABELS label value ...]', 'TS.ADD': 'key timestamp value [RETENTION retentionPeriod] [ENCODING UNCOMPRESSED|COMPRESSED] [CHUNK_SIZE size] [ON_DUPLICATE policy] [LABELS label value ...]', 'TS.MADD': 'key timestamp value [key timestamp value ...]', 'TS.INCRBY': 'key value [TIMESTAMP timestamp] [RETENTION retentionPeriod] [LABELS label value ...]', 'TS.DECRBY': 'key value [TIMESTAMP timestamp] [RETENTION retentionPeriod] [LABELS label value ...]', 'TS.DEL': 'key fromTimestamp toTimestamp', 'TS.RANGE': 'key fromTimestamp toTimestamp [LATEST] [FILTER_BY_TS ts ...] [FILTER_BY_VALUE min max] [COUNT count] [AGGREGATION aggregator bucketDuration]', 'TS.REVRANGE': 'key fromTimestamp toTimestamp [LATEST] [FILTER_BY_TS ts ...] [FILTER_BY_VALUE min max] [COUNT count] [AGGREGATION aggregator bucketDuration]', 'TS.MRANGE': 'fromTimestamp toTimestamp [LATEST] [FILTER_BY_TS ts ...] [FILTER_BY_VALUE min max] [WITHLABELS] [COUNT count] [AGGREGATION aggregator bucketDuration] FILTER filter ...', 'TS.MREVRANGE': 'fromTimestamp toTimestamp [LATEST] [FILTER_BY_TS ts ...] [FILTER_BY_VALUE min max] [WITHLABELS] [COUNT count] [AGGREGATION aggregator bucketDuration] FILTER filter ...', 'TS.GET': 'key [LATEST]', 'TS.MGET': '[LATEST] [WITHLABELS] FILTER filter ...', 'TS.INFO': 'key [DEBUG]', 'TS.ALTER': 'key [RETENTION retentionPeriod] [CHUNK_SIZE size] [DUPLICATE_POLICY policy] [LABELS label value ...]', 'TS.QUERYINDEX': 'filter ...', 'TS.CREATERULE': 'sourceKey destKey AGGREGATION aggregator bucketDuration [alignTimestamp]', 'TS.DELETERULE': 'sourceKey destKey', } src/lib/redis-command-meta.mjs000066400000000000000000000047471517715050700166200ustar00rootroot00000000000000import commandHints from './redis-command-hints.mjs' /** * Category display names and order for autocomplete grouping. */ const categoryMap = { '@string': 'String', '@hash': 'Hash', '@list': 'List', '@set': 'Set', '@sortedset': 'Sorted Set', '@stream': 'Stream', '@geo': 'Geo', '@hyperloglog': 'HyperLogLog', '@bitmap': 'Bitmap', '@keyspace': 'Key', '@connection': 'Connection', '@server': 'Server', '@generic': 'Generic', '@pubsub': 'Pub/Sub', '@scripting': 'Scripting', '@transactions': 'Transactions', '@cluster': 'Cluster', '@slow': null, // skip β€” too generic '@fast': null, '@read': null, '@write': null, '@dangerous': null, '@admin': null, } /** * Process raw redis.command() output into metadata for autocomplete. * Returns: { [commandName]: { syntax, group } } */ export function buildCommandMeta(rawCommands) { const meta = {} if (Array.isArray(rawCommands)) { // Static commands format: array of arrays for (const entry of rawCommands) { processEntry(entry, meta) } } else if (rawCommands && typeof rawCommands === 'object') { // Live redis.command() format: object keyed by command name for (const key of Object.keys(rawCommands)) { processEntry(rawCommands[key], meta) } } return meta } function processEntry(entry, meta) { if (!Array.isArray(entry) || entry.length < 7) return const name = entry[0]?.toUpperCase() if (!name) return const categories = entry[6] || [] // Find the best display category let group = 'Other' for (const cat of categories) { if (categoryMap.hasOwnProperty(cat) && categoryMap[cat] !== null) { group = categoryMap[cat] break } } // Static syntax hint or empty const syntax = commandHints[name] ?? '' meta[name] = { syntax, group } // Process subcommands (index 9) const subcommands = entry[9] if (Array.isArray(subcommands)) { for (const sub of subcommands) { if (!Array.isArray(sub) || sub.length < 7) continue const subName = sub[0]?.toUpperCase() if (!subName) continue // Subcommand names come as "config|get" β€” convert to "CONFIG GET" const displayName = subName.replace('|', ' ') const subSyntax = commandHints[displayName] ?? '' meta[displayName] = { syntax: subSyntax, group } } } } src/lib/redis-static-commands.mjs000066400000000000000000002053261517715050700173400ustar00rootroot00000000000000export default [["zrandmember",-2,["readonly"],1,1,1,["@read","@sortedset","@slow"],["nondeterministic_output"],[["flags",["RO","access"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["geosearch",-7,["readonly"],1,1,1,["@read","@geo","@slow"],[],[["flags",["RO","access"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["auth",-2,["noscript","loading","stale","fast","no_auth","allow_busy"],0,0,0,["@fast","@connection"],[],[],[]],["touch",-2,["readonly","fast"],1,-1,1,["@keyspace","@read","@fast"],["request_policy:multi_shard","response_policy:agg_sum"],[["flags",["RO"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",-1,"keystep",1,"limit",0]]]],[]],["dump",2,["readonly"],1,1,1,["@keyspace","@read","@slow"],["nondeterministic_output"],[["flags",["RO","access"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["config",-2,[],0,0,0,["@slow"],[],[],[["config|help",2,["loading","stale"],0,0,0,["@slow"],[],[],[]],["config|rewrite",2,["admin","noscript","loading","stale"],0,0,0,["@admin","@slow","@dangerous"],[],[],[]],["config|get",-3,["admin","noscript","loading","stale"],0,0,0,["@admin","@slow","@dangerous"],[],[],[]],["config|resetstat",2,["admin","noscript","loading","stale"],0,0,0,["@admin","@slow","@dangerous"],[],[],[]],["config|set",-4,["admin","noscript","loading","stale"],0,0,0,["@admin","@slow","@dangerous"],["request_policy:all_nodes","response_policy:all_succeeded"],[],[]]]],["pttl",2,["readonly","fast"],1,1,1,["@keyspace","@read","@fast"],["nondeterministic_output"],[["flags",["RO","access"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["set",-3,["write","denyoom"],1,1,1,["@write","@string","@slow"],[],[["notes","RW and ACCESS due to the optional `GET` argument","flags",["RW","access","update","variable_flags"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["smove",4,["write","fast"],1,2,1,["@write","@set","@fast"],[],[["flags",["RW","access","delete"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]],["flags",["RW","insert"],"begin_search",["type","index","spec",["index",2]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["randomkey",1,["readonly"],0,0,0,["@keyspace","@read","@slow"],["request_policy:all_shards","nondeterministic_output"],[],[]],["getdel",2,["write","fast"],1,1,1,["@write","@string","@fast"],[],[["flags",["RW","access","delete"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["xinfo",-2,[],0,0,0,["@slow"],[],[],[["xinfo|help",2,["loading","stale"],0,0,0,["@stream","@slow"],[],[],[]],["xinfo|stream",-3,["readonly"],2,2,1,["@read","@stream","@slow"],[],[["flags",["RO","access"],"begin_search",["type","index","spec",["index",2]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["xinfo|consumers",4,["readonly"],2,2,1,["@read","@stream","@slow"],["nondeterministic_output"],[["flags",["RO","access"],"begin_search",["type","index","spec",["index",2]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["xinfo|groups",3,["readonly"],2,2,1,["@read","@stream","@slow"],[],[["flags",["RO","access"],"begin_search",["type","index","spec",["index",2]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]]]],["replicaof",3,["admin","noscript","stale","no_async_loading"],0,0,0,["@admin","@slow","@dangerous"],[],[],[]],["wait",3,["noscript"],0,0,0,["@slow","@connection"],["request_policy:all_shards","response_policy:agg_min"],[],[]],["move",3,["write","fast"],1,1,1,["@keyspace","@write","@fast"],[],[["flags",["RW","access","update"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["bzmpop",-5,["write","blocking","movablekeys"],0,0,0,["@write","@sortedset","@slow","@blocking"],[],[["flags",["RW","access","delete"],"begin_search",["type","index","spec",["index",2]],"find_keys",["type","keynum","spec",["keynumidx",0,"firstkey",1,"keystep",1]]]],[]],["lpos",-3,["readonly"],1,1,1,["@read","@list","@slow"],[],[["flags",["RO","access"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["module",-2,[],0,0,0,["@slow"],[],[],[["module|loadex",-3,["admin","noscript","no_async_loading"],0,0,0,["@admin","@slow","@dangerous"],[],[],[]],["module|help",2,["loading","stale"],0,0,0,["@slow"],[],[],[]],["module|load",-3,["admin","noscript","no_async_loading"],0,0,0,["@admin","@slow","@dangerous"],[],[],[]],["module|list",2,["admin","noscript"],0,0,0,["@admin","@slow","@dangerous"],["nondeterministic_output_order"],[],[]],["module|unload",3,["admin","noscript","no_async_loading"],0,0,0,["@admin","@slow","@dangerous"],[],[],[]]]],["zrevrangebyscore",-4,["readonly"],1,1,1,["@read","@sortedset","@slow"],[],[["flags",["RO","access"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["zremrangebyscore",4,["write"],1,1,1,["@write","@sortedset","@slow"],[],[["flags",["RW","delete"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["hrandfield",-2,["readonly"],1,1,1,["@read","@hash","@slow"],["nondeterministic_output"],[["flags",["RO","access"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["pfdebug",3,["write","denyoom","admin"],2,2,1,["@write","@hyperloglog","@admin","@slow","@dangerous"],[],[["flags",["RW","access"],"begin_search",["type","index","spec",["index",2]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["migrate",-6,["write","movablekeys"],3,3,1,["@keyspace","@write","@slow","@dangerous"],["nondeterministic_output"],[["flags",["RW","access","delete"],"begin_search",["type","index","spec",["index",3]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]],["flags",["RW","access","delete","incomplete"],"begin_search",["type","keyword","spec",["keyword","KEYS","startfrom",-2]],"find_keys",["type","range","spec",["lastkey",-1,"keystep",1,"limit",0]]]],[]],["xack",-4,["write","fast"],1,1,1,["@write","@stream","@fast"],[],[["flags",["RW","update"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["flushall",-1,["write"],0,0,0,["@keyspace","@write","@slow","@dangerous"],["request_policy:all_shards","response_policy:all_succeeded"],[],[]],["multi",1,["noscript","loading","stale","fast","allow_busy"],0,0,0,["@fast","@transaction"],[],[],[]],["renamenx",3,["write","fast"],1,2,1,["@keyspace","@write","@fast"],[],[["flags",["RW","access","delete"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]],["flags",["OW","insert"],"begin_search",["type","index","spec",["index",2]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["pexpiretime",2,["readonly","fast"],1,1,1,["@keyspace","@read","@fast"],[],[["flags",["RO","access"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["sync",1,["admin","noscript","no_async_loading","no_multi"],0,0,0,["@admin","@slow","@dangerous"],[],[],[]],["lpush",-3,["write","denyoom","fast"],1,1,1,["@write","@list","@fast"],[],[["flags",["RW","insert"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["strlen",2,["readonly","fast"],1,1,1,["@read","@string","@fast"],[],[["flags",["RO"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["zinter",-3,["readonly","movablekeys"],0,0,0,["@read","@sortedset","@slow"],[],[["flags",["RO","access"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","keynum","spec",["keynumidx",0,"firstkey",1,"keystep",1]]]],[]],["geoadd",-5,["write","denyoom"],1,1,1,["@write","@geo","@slow"],[],[["flags",["RW","update"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["xadd",-5,["write","denyoom","fast"],1,1,1,["@write","@stream","@fast"],["nondeterministic_output"],[["notes","UPDATE instead of INSERT because of the optional trimming feature","flags",["RW","update"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["lolwut",-1,["readonly","fast"],0,0,0,["@read","@fast"],[],[],[]],["xlen",2,["readonly","fast"],1,1,1,["@read","@stream","@fast"],[],[["flags",["RO"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["zinterstore",-4,["write","denyoom","movablekeys"],1,1,1,["@write","@sortedset","@slow"],[],[["flags",["OW","update"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]],["flags",["RO","access"],"begin_search",["type","index","spec",["index",2]],"find_keys",["type","keynum","spec",["keynumidx",0,"firstkey",1,"keystep",1]]]],[]],["unwatch",1,["noscript","loading","stale","fast","allow_busy"],0,0,0,["@fast","@transaction"],[],[],[]],["xtrim",-4,["write"],1,1,1,["@write","@stream","@slow"],["nondeterministic_output"],[["flags",["RW","delete"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["hdel",-3,["write","fast"],1,1,1,["@write","@hash","@fast"],[],[["flags",["RW","delete"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["xsetid",-3,["write","denyoom","fast"],1,1,1,["@write","@stream","@fast"],[],[["flags",["RW","update"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["reset",1,["noscript","loading","stale","fast","no_auth","allow_busy"],0,0,0,["@fast","@connection"],[],[],[]],["scan",-2,["readonly"],0,0,0,["@keyspace","@read","@slow"],["nondeterministic_output","request_policy:special"],[],[]],["del",-2,["write"],1,-1,1,["@keyspace","@write","@slow"],["request_policy:multi_shard","response_policy:agg_sum"],[["flags",["RM","delete"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",-1,"keystep",1,"limit",0]]]],[]],["zlexcount",4,["readonly","fast"],1,1,1,["@read","@sortedset","@fast"],[],[["flags",["RO","access"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["zdiff",-3,["readonly","movablekeys"],0,0,0,["@read","@sortedset","@slow"],[],[["flags",["RO","access"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","keynum","spec",["keynumidx",0,"firstkey",1,"keystep",1]]]],[]],["sinterstore",-3,["write","denyoom"],1,-1,1,["@write","@set","@slow"],[],[["flags",["RW","update"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]],["flags",["RO","access"],"begin_search",["type","index","spec",["index",2]],"find_keys",["type","range","spec",["lastkey",-1,"keystep",1,"limit",0]]]],[]],["zincrby",4,["write","denyoom","fast"],1,1,1,["@write","@sortedset","@fast"],[],[["flags",["RW","access","update"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["georadiusbymember",-5,["write","denyoom","movablekeys"],1,1,1,["@write","@geo","@slow"],[],[["flags",["RO","access"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]],["flags",["OW","update"],"begin_search",["type","keyword","spec",["keyword","STORE","startfrom",5]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]],["flags",["OW","update"],"begin_search",["type","keyword","spec",["keyword","STOREDIST","startfrom",5]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["rpushx",-3,["write","denyoom","fast"],1,1,1,["@write","@list","@fast"],[],[["flags",["RW","insert"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["swapdb",3,["write","fast"],0,0,0,["@keyspace","@write","@fast","@dangerous"],[],[],[]],["command",-1,["loading","stale"],0,0,0,["@slow","@connection"],["nondeterministic_output_order"],[],[["command|getkeys",-4,["loading","stale"],0,0,0,["@slow","@connection"],[],[],[]],["command|docs",-2,["loading","stale"],0,0,0,["@slow","@connection"],["nondeterministic_output_order"],[],[]],["command|info",-2,["loading","stale"],0,0,0,["@slow","@connection"],["nondeterministic_output_order"],[],[]],["command|help",2,["loading","stale"],0,0,0,["@slow","@connection"],[],[],[]],["command|count",2,["loading","stale"],0,0,0,["@slow","@connection"],[],[],[]],["command|list",-2,["loading","stale"],0,0,0,["@slow","@connection"],["nondeterministic_output_order"],[],[]],["command|getkeysandflags",-4,["loading","stale"],0,0,0,["@slow","@connection"],[],[],[]]]],["function",-2,[],0,0,0,["@slow"],[],[],[["function|delete",3,["write","noscript"],0,0,0,["@write","@slow","@scripting"],["request_policy:all_shards","response_policy:all_succeeded"],[],[]],["function|help",2,["loading","stale"],0,0,0,["@slow","@scripting"],[],[],[]],["function|load",-3,["write","denyoom","noscript"],0,0,0,["@write","@slow","@scripting"],["request_policy:all_shards","response_policy:all_succeeded"],[],[]],["function|dump",2,["noscript"],0,0,0,["@slow","@scripting"],[],[],[]],["function|flush",-2,["write","noscript"],0,0,0,["@write","@slow","@scripting"],["request_policy:all_shards","response_policy:all_succeeded"],[],[]],["function|list",-2,["noscript"],0,0,0,["@slow","@scripting"],["nondeterministic_output_order"],[],[]],["function|kill",2,["noscript","allow_busy"],0,0,0,["@slow","@scripting"],["request_policy:all_shards","response_policy:one_succeeded"],[],[]],["function|restore",-3,["write","denyoom","noscript"],0,0,0,["@write","@slow","@scripting"],["request_policy:all_shards","response_policy:all_succeeded"],[],[]],["function|stats",2,["noscript","allow_busy"],0,0,0,["@slow","@scripting"],["nondeterministic_output","request_policy:all_shards","response_policy:special"],[],[]]]],["script",-2,[],0,0,0,["@slow"],[],[],[["script|exists",-3,["noscript"],0,0,0,["@slow","@scripting"],["request_policy:all_shards","response_policy:agg_logical_and"],[],[]],["script|flush",-2,["noscript"],0,0,0,["@slow","@scripting"],["request_policy:all_nodes","response_policy:all_succeeded"],[],[]],["script|debug",3,["noscript"],0,0,0,["@slow","@scripting"],[],[],[]],["script|help",2,["loading","stale"],0,0,0,["@slow","@scripting"],[],[],[]],["script|load",3,["noscript","stale"],0,0,0,["@slow","@scripting"],["request_policy:all_nodes","response_policy:all_succeeded"],[],[]],["script|kill",2,["noscript","allow_busy"],0,0,0,["@slow","@scripting"],["request_policy:all_shards","response_policy:one_succeeded"],[],[]]]],["sadd",-3,["write","denyoom","fast"],1,1,1,["@write","@set","@fast"],[],[["flags",["RW","insert"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["subscribe",-2,["pubsub","noscript","loading","stale"],0,0,0,["@pubsub","@slow"],[],[],[]],["srem",-3,["write","fast"],1,1,1,["@write","@set","@fast"],[],[["flags",["RW","delete"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["hlen",2,["readonly","fast"],1,1,1,["@read","@hash","@fast"],[],[["flags",["RO"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["hstrlen",3,["readonly","fast"],1,1,1,["@read","@hash","@fast"],[],[["flags",["RO"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["exists",-2,["readonly","fast"],1,-1,1,["@keyspace","@read","@fast"],["request_policy:multi_shard","response_policy:agg_sum"],[["flags",["RO"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",-1,"keystep",1,"limit",0]]]],[]],["zadd",-4,["write","denyoom","fast"],1,1,1,["@write","@sortedset","@fast"],[],[["flags",["RW","update"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["substr",4,["readonly"],1,1,1,["@read","@string","@slow"],[],[["flags",["RO","access"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["restore",-4,["write","denyoom"],1,1,1,["@keyspace","@write","@slow","@dangerous"],[],[["flags",["OW","update"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["fcall",-3,["noscript","stale","skip_monitor","no_mandatory_keys","movablekeys"],0,0,0,["@slow","@scripting"],[],[["notes","We cannot tell how the keys will be used so we assume the worst, RW and UPDATE","flags",["RW","access","update"],"begin_search",["type","index","spec",["index",2]],"find_keys",["type","keynum","spec",["keynumidx",0,"firstkey",1,"keystep",1]]]],[]],["lcs",-3,["readonly"],1,2,1,["@read","@string","@slow"],[],[["flags",["RO","access"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",1,"keystep",1,"limit",0]]]],[]],["bgrewriteaof",1,["admin","noscript","no_async_loading"],0,0,0,["@admin","@slow","@dangerous"],[],[],[]],["hgetall",2,["readonly"],1,1,1,["@read","@hash","@slow"],["nondeterministic_output_order"],[["flags",["RO","access"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["debug",-2,["admin","noscript","loading","stale"],0,0,0,["@admin","@slow","@dangerous"],[],[],[]],["spublish",3,["pubsub","loading","stale","fast"],1,1,1,["@pubsub","@fast"],[],[["flags",["not_key"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["setex",4,["write","denyoom"],1,1,1,["@write","@string","@slow"],[],[["flags",["OW","update"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["expireat",-3,["write","fast"],1,1,1,["@keyspace","@write","@fast"],[],[["flags",["RW","update"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["incr",2,["write","denyoom","fast"],1,1,1,["@write","@string","@fast"],[],[["flags",["RW","access","update"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["rpush",-3,["write","denyoom","fast"],1,1,1,["@write","@list","@fast"],[],[["flags",["RW","insert"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["get",2,["readonly","fast"],1,1,1,["@read","@string","@fast"],[],[["flags",["RO","access"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["zrange",-4,["readonly"],1,1,1,["@read","@sortedset","@slow"],[],[["flags",["RO","access"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["sunion",-2,["readonly"],1,-1,1,["@read","@set","@slow"],["nondeterministic_output_order"],[["flags",["RO","access"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",-1,"keystep",1,"limit",0]]]],[]],["geosearchstore",-8,["write","denyoom"],1,2,1,["@write","@geo","@slow"],[],[["flags",["OW","update"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]],["flags",["RO","access"],"begin_search",["type","index","spec",["index",2]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["sismember",3,["readonly","fast"],1,1,1,["@read","@set","@fast"],[],[["flags",["RO"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["pexpire",-3,["write","fast"],1,1,1,["@keyspace","@write","@fast"],[],[["flags",["RW","update"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["linsert",5,["write","denyoom"],1,1,1,["@write","@list","@slow"],[],[["flags",["RW","insert"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["geopos",-2,["readonly"],1,1,1,["@read","@geo","@slow"],[],[["flags",["RO","access"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["quit",-1,["noscript","loading","stale","fast","no_auth","allow_busy"],0,0,0,["@fast","@connection"],[],[],[]],["pfselftest",1,["admin"],0,0,0,["@hyperloglog","@admin","@slow","@dangerous"],[],[],[]],["mset",-3,["write","denyoom"],1,-1,2,["@write","@string","@slow"],["request_policy:multi_shard","response_policy:all_succeeded"],[["flags",["OW","update"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",-1,"keystep",2,"limit",0]]]],[]],["readonly",1,["loading","stale","fast"],0,0,0,["@fast","@connection"],[],[],[]],["zcount",4,["readonly","fast"],1,1,1,["@read","@sortedset","@fast"],[],[["flags",["RO","access"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["evalsha_ro",-3,["readonly","noscript","stale","skip_monitor","no_mandatory_keys","movablekeys"],0,0,0,["@slow","@scripting"],[],[["flags",["RO","access"],"begin_search",["type","index","spec",["index",2]],"find_keys",["type","keynum","spec",["keynumidx",0,"firstkey",1,"keystep",1]]]],[]],["zscore",3,["readonly","fast"],1,1,1,["@read","@sortedset","@fast"],[],[["flags",["RO","access"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["lrem",4,["write"],1,1,1,["@write","@list","@slow"],[],[["flags",["RW","delete"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["lmove",5,["write","denyoom"],1,2,1,["@write","@list","@slow"],[],[["flags",["RW","access","delete"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]],["flags",["RW","insert"],"begin_search",["type","index","spec",["index",2]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["restore-asking",-4,["write","denyoom","asking"],1,1,1,["@keyspace","@write","@slow","@dangerous"],[],[["flags",["OW","update"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["client",-2,[],0,0,0,["@slow"],[],[],[["client|unpause",2,["admin","noscript","loading","stale"],0,0,0,["@admin","@slow","@dangerous","@connection"],[],[],[]],["client|setname",3,["noscript","loading","stale"],0,0,0,["@slow","@connection"],[],[],[]],["client|caching",3,["noscript","loading","stale"],0,0,0,["@slow","@connection"],[],[],[]],["client|info",2,["noscript","loading","stale"],0,0,0,["@slow","@connection"],["nondeterministic_output"],[],[]],["client|getname",2,["noscript","loading","stale"],0,0,0,["@slow","@connection"],[],[],[]],["client|tracking",-3,["noscript","loading","stale"],0,0,0,["@slow","@connection"],[],[],[]],["client|id",2,["noscript","loading","stale"],0,0,0,["@slow","@connection"],[],[],[]],["client|reply",3,["noscript","loading","stale"],0,0,0,["@slow","@connection"],[],[],[]],["client|help",2,["loading","stale"],0,0,0,["@slow","@connection"],[],[],[]],["client|pause",-3,["admin","noscript","loading","stale"],0,0,0,["@admin","@slow","@dangerous","@connection"],[],[],[]],["client|no-evict",3,["admin","noscript","loading","stale"],0,0,0,["@admin","@slow","@dangerous","@connection"],[],[],[]],["client|unblock",-3,["admin","noscript","loading","stale"],0,0,0,["@admin","@slow","@dangerous","@connection"],[],[],[]],["client|trackinginfo",2,["noscript","loading","stale"],0,0,0,["@slow","@connection"],[],[],[]],["client|list",-2,["admin","noscript","loading","stale"],0,0,0,["@admin","@slow","@dangerous","@connection"],["nondeterministic_output"],[],[]],["client|kill",-3,["admin","noscript","loading","stale"],0,0,0,["@admin","@slow","@dangerous","@connection"],[],[],[]],["client|getredir",2,["noscript","loading","stale"],0,0,0,["@slow","@connection"],[],[],[]]]],["bitfield",-2,["write","denyoom"],1,1,1,["@write","@bitmap","@slow"],[],[["notes","This command allows both access and modification of the key","flags",["RW","access","update","variable_flags"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["dbsize",1,["readonly","fast"],0,0,0,["@keyspace","@read","@fast"],["request_policy:all_shards","response_policy:agg_sum"],[],[]],["hincrby",4,["write","denyoom","fast"],1,1,1,["@write","@hash","@fast"],[],[["flags",["RW","access","update"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["lpushx",-3,["write","denyoom","fast"],1,1,1,["@write","@list","@fast"],[],[["flags",["RW","insert"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["decrby",3,["write","denyoom","fast"],1,1,1,["@write","@string","@fast"],[],[["flags",["RW","access","update"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["zscan",-3,["readonly"],1,1,1,["@read","@sortedset","@slow"],["nondeterministic_output"],[["flags",["RO","access"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["zpopmin",-2,["write","fast"],1,1,1,["@write","@sortedset","@fast"],[],[["flags",["RW","access","delete"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["lindex",3,["readonly"],1,1,1,["@read","@list","@slow"],[],[["flags",["RO","access"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["zremrangebylex",4,["write"],1,1,1,["@write","@sortedset","@slow"],[],[["flags",["RW","delete"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["object",-2,[],0,0,0,["@slow"],[],[],[["object|help",2,["loading","stale"],0,0,0,["@keyspace","@slow"],[],[],[]],["object|encoding",3,["readonly"],2,2,1,["@keyspace","@read","@slow"],["nondeterministic_output"],[["flags",["RO"],"begin_search",["type","index","spec",["index",2]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["object|idletime",3,["readonly"],2,2,1,["@keyspace","@read","@slow"],["nondeterministic_output"],[["flags",["RO"],"begin_search",["type","index","spec",["index",2]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["object|refcount",3,["readonly"],2,2,1,["@keyspace","@read","@slow"],["nondeterministic_output"],[["flags",["RO"],"begin_search",["type","index","spec",["index",2]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["object|freq",3,["readonly"],2,2,1,["@keyspace","@read","@slow"],["nondeterministic_output"],[["flags",["RO"],"begin_search",["type","index","spec",["index",2]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]]]],["sdiff",-2,["readonly"],1,-1,1,["@read","@set","@slow"],["nondeterministic_output_order"],[["flags",["RO","access"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",-1,"keystep",1,"limit",0]]]],[]],["lmpop",-4,["write","movablekeys"],0,0,0,["@write","@list","@slow"],[],[["flags",["RW","access","delete"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","keynum","spec",["keynumidx",0,"firstkey",1,"keystep",1]]]],[]],["zremrangebyrank",4,["write"],1,1,1,["@write","@sortedset","@slow"],[],[["flags",["RW","delete"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["asking",1,["fast"],0,0,0,["@fast","@connection"],[],[],[]],["blpop",-3,["write","noscript","blocking"],1,-2,1,["@write","@list","@slow","@blocking"],[],[["flags",["RW","access","delete"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",-2,"keystep",1,"limit",0]]]],[]],["sort",-2,["write","denyoom","movablekeys"],1,1,1,["@write","@set","@sortedset","@list","@slow","@dangerous"],[],[["flags",["RO","access"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]],["notes","For the optional BY/GET keyword. It is marked 'unknown' because the key names derive from the content of the key we sort","flags",["RO","access"],"begin_search",["type","unknown","spec",[]],"find_keys",["type","unknown","spec",[]]],["notes","For the optional STORE keyword. It is marked 'unknown' because the keyword can appear anywhere in the argument array","flags",["OW","update"],"begin_search",["type","unknown","spec",[]],"find_keys",["type","unknown","spec",[]]]],[]],["ping",-1,["fast"],0,0,0,["@fast","@connection"],["request_policy:all_shards","response_policy:all_succeeded"],[],[]],["publish",3,["pubsub","loading","stale","fast"],0,0,0,["@pubsub","@fast"],[],[],[]],["geodist",-4,["readonly"],1,1,1,["@read","@geo","@slow"],[],[["flags",["RO","access"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["zrem",-3,["write","fast"],1,1,1,["@write","@sortedset","@fast"],[],[["flags",["RW","delete"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["georadius_ro",-6,["readonly"],1,1,1,["@read","@geo","@slow"],[],[["flags",["RO","access"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["ttl",2,["readonly","fast"],1,1,1,["@keyspace","@read","@fast"],["nondeterministic_output"],[["flags",["RO","access"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["setrange",4,["write","denyoom"],1,1,1,["@write","@string","@slow"],[],[["flags",["RW","update"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["bitpos",-3,["readonly"],1,1,1,["@read","@bitmap","@slow"],[],[["flags",["RO","access"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["zcard",2,["readonly","fast"],1,1,1,["@read","@sortedset","@fast"],[],[["flags",["RO"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["sunsubscribe",-1,["pubsub","noscript","loading","stale"],1,-1,1,["@pubsub","@slow"],[],[["flags",["not_key"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",-1,"keystep",1,"limit",0]]]],[]],["zpopmax",-2,["write","fast"],1,1,1,["@write","@sortedset","@fast"],[],[["flags",["RW","access","delete"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["llen",2,["readonly","fast"],1,1,1,["@read","@list","@fast"],[],[["flags",["RO"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["brpop",-3,["write","noscript","blocking"],1,-2,1,["@write","@list","@slow","@blocking"],[],[["flags",["RW","access","delete"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",-2,"keystep",1,"limit",0]]]],[]],["lrange",4,["readonly"],1,1,1,["@read","@list","@slow"],[],[["flags",["RO","access"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["expire",-3,["write","fast"],1,1,1,["@keyspace","@write","@fast"],[],[["flags",["RW","update"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["unsubscribe",-1,["pubsub","noscript","loading","stale"],0,0,0,["@pubsub","@slow"],[],[],[]],["zrevrangebylex",-4,["readonly"],1,1,1,["@read","@sortedset","@slow"],[],[["flags",["RO","access"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["spop",-2,["write","fast"],1,1,1,["@write","@set","@fast"],["nondeterministic_output"],[["flags",["RW","access","delete"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["zunionstore",-4,["write","denyoom","movablekeys"],1,1,1,["@write","@sortedset","@slow"],[],[["flags",["OW","update"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]],["flags",["RO","access"],"begin_search",["type","index","spec",["index",2]],"find_keys",["type","keynum","spec",["keynumidx",0,"firstkey",1,"keystep",1]]]],[]],["incrby",3,["write","denyoom","fast"],1,1,1,["@write","@string","@fast"],[],[["flags",["RW","access","update"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["sintercard",-3,["readonly","movablekeys"],0,0,0,["@read","@set","@slow"],[],[["flags",["RO","access"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","keynum","spec",["keynumidx",0,"firstkey",1,"keystep",1]]]],[]],["xclaim",-6,["write","fast"],1,1,1,["@write","@stream","@fast"],["nondeterministic_output"],[["flags",["RW","update"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["getrange",4,["readonly"],1,1,1,["@read","@string","@slow"],[],[["flags",["RO","access"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["sinter",-2,["readonly"],1,-1,1,["@read","@set","@slow"],["nondeterministic_output_order"],[["flags",["RO","access"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",-1,"keystep",1,"limit",0]]]],[]],["psync",-3,["admin","noscript","no_async_loading","no_multi"],0,0,0,["@admin","@slow","@dangerous"],[],[],[]],["failover",-1,["admin","noscript","stale"],0,0,0,["@admin","@slow","@dangerous"],[],[],[]],["pfcount",-2,["readonly"],1,-1,1,["@read","@hyperloglog","@slow"],[],[["notes","RW because it may change the internal representation of the key, and propagate to replicas","flags",["RW","access"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",-1,"keystep",1,"limit",0]]]],[]],["hscan",-3,["readonly"],1,1,1,["@read","@hash","@slow"],["nondeterministic_output"],[["flags",["RO","access"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["zrangebylex",-4,["readonly"],1,1,1,["@read","@sortedset","@slow"],[],[["flags",["RO","access"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["replconf",-1,["admin","noscript","loading","stale","allow_busy"],0,0,0,["@admin","@slow","@dangerous"],[],[],[]],["acl",-2,[],0,0,0,["@slow"],[],[],[["acl|cat",-2,["noscript","loading","stale"],0,0,0,["@slow"],[],[],[]],["acl|save",2,["admin","noscript","loading","stale"],0,0,0,["@admin","@slow","@dangerous"],[],[],[]],["acl|users",2,["admin","noscript","loading","stale"],0,0,0,["@admin","@slow","@dangerous"],[],[],[]],["acl|getuser",3,["admin","noscript","loading","stale"],0,0,0,["@admin","@slow","@dangerous"],[],[],[]],["acl|dryrun",-4,["admin","noscript","loading","stale"],0,0,0,["@admin","@slow","@dangerous"],[],[],[]],["acl|help",2,["loading","stale"],0,0,0,["@slow"],[],[],[]],["acl|load",2,["admin","noscript","loading","stale"],0,0,0,["@admin","@slow","@dangerous"],[],[],[]],["acl|list",2,["admin","noscript","loading","stale"],0,0,0,["@admin","@slow","@dangerous"],[],[],[]],["acl|whoami",2,["noscript","loading","stale"],0,0,0,["@slow"],[],[],[]],["acl|genpass",-2,["noscript","loading","stale"],0,0,0,["@slow"],[],[],[]],["acl|deluser",-3,["admin","noscript","loading","stale"],0,0,0,["@admin","@slow","@dangerous"],[],[],[]],["acl|log",-2,["admin","noscript","loading","stale"],0,0,0,["@admin","@slow","@dangerous"],[],[],[]],["acl|setuser",-3,["admin","noscript","loading","stale"],0,0,0,["@admin","@slow","@dangerous"],[],[],[]]]],["eval_ro",-3,["readonly","noscript","stale","skip_monitor","no_mandatory_keys","movablekeys"],0,0,0,["@slow","@scripting"],[],[["notes","We cannot tell how the keys will be used so we assume the worst, RO and ACCESS","flags",["RO","access"],"begin_search",["type","index","spec",["index",2]],"find_keys",["type","keynum","spec",["keynumidx",0,"firstkey",1,"keystep",1]]]],[]],["xgroup",-2,[],0,0,0,["@slow"],[],[],[["xgroup|create",-5,["write","denyoom"],2,2,1,["@write","@stream","@slow"],[],[["flags",["RW","insert"],"begin_search",["type","index","spec",["index",2]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["xgroup|delconsumer",5,["write"],2,2,1,["@write","@stream","@slow"],[],[["flags",["RW","delete"],"begin_search",["type","index","spec",["index",2]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["xgroup|createconsumer",5,["write","denyoom"],2,2,1,["@write","@stream","@slow"],[],[["flags",["RW","insert"],"begin_search",["type","index","spec",["index",2]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["xgroup|help",2,["loading","stale"],0,0,0,["@stream","@slow"],[],[],[]],["xgroup|setid",-5,["write"],2,2,1,["@write","@stream","@slow"],[],[["flags",["RW","update"],"begin_search",["type","index","spec",["index",2]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["xgroup|destroy",4,["write"],2,2,1,["@write","@stream","@slow"],[],[["flags",["RW","delete"],"begin_search",["type","index","spec",["index",2]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]]]],["ltrim",4,["write"],1,1,1,["@write","@list","@slow"],[],[["flags",["RW","delete"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["psubscribe",-2,["pubsub","noscript","loading","stale"],0,0,0,["@pubsub","@slow"],[],[],[]],["hmget",-3,["readonly","fast"],1,1,1,["@read","@hash","@fast"],[],[["flags",["RO","access"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["info",-1,["loading","stale"],0,0,0,["@slow","@dangerous"],["nondeterministic_output","request_policy:all_shards","response_policy:special"],[],[]],["watch",-2,["noscript","loading","stale","fast","allow_busy"],1,-1,1,["@fast","@transaction"],[],[["flags",["RO"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",-1,"keystep",1,"limit",0]]]],[]],["slowlog",-2,[],0,0,0,["@slow"],[],[],[["slowlog|help",2,["loading","stale"],0,0,0,["@slow"],[],[],[]],["slowlog|get",-2,["admin","loading","stale"],0,0,0,["@admin","@slow","@dangerous"],["request_policy:all_nodes","nondeterministic_output"],[],[]],["slowlog|reset",2,["admin","loading","stale"],0,0,0,["@admin","@slow","@dangerous"],["request_policy:all_nodes","response_policy:all_succeeded"],[],[]],["slowlog|len",2,["admin","loading","stale"],0,0,0,["@admin","@slow","@dangerous"],["request_policy:all_nodes","response_policy:agg_sum","nondeterministic_output"],[],[]]]],["xautoclaim",-6,["write","fast"],1,1,1,["@write","@stream","@fast"],["nondeterministic_output"],[["flags",["RW","delete"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["bitop",-4,["write","denyoom"],2,-1,1,["@write","@bitmap","@slow"],[],[["flags",["OW","update"],"begin_search",["type","index","spec",["index",2]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]],["flags",["RO","access"],"begin_search",["type","index","spec",["index",3]],"find_keys",["type","range","spec",["lastkey",-1,"keystep",1,"limit",0]]]],[]],["hvals",2,["readonly"],1,1,1,["@read","@hash","@slow"],["nondeterministic_output_order"],[["flags",["RO","access"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["latency",-2,[],0,0,0,["@slow"],[],[],[["latency|help",2,["loading","stale"],0,0,0,["@slow"],[],[],[]],["latency|histogram",-2,["admin","noscript","loading","stale"],0,0,0,["@admin","@slow","@dangerous"],["nondeterministic_output","request_policy:all_nodes","response_policy:special"],[],[]],["latency|history",3,["admin","noscript","loading","stale"],0,0,0,["@admin","@slow","@dangerous"],["nondeterministic_output","request_policy:all_nodes","response_policy:special"],[],[]],["latency|graph",3,["admin","noscript","loading","stale"],0,0,0,["@admin","@slow","@dangerous"],["nondeterministic_output","request_policy:all_nodes","response_policy:special"],[],[]],["latency|reset",-2,["admin","noscript","loading","stale"],0,0,0,["@admin","@slow","@dangerous"],["request_policy:all_nodes","response_policy:all_succeeded"],[],[]],["latency|doctor",2,["admin","noscript","loading","stale"],0,0,0,["@admin","@slow","@dangerous"],["nondeterministic_output","request_policy:all_nodes","response_policy:special"],[],[]],["latency|latest",2,["admin","noscript","loading","stale"],0,0,0,["@admin","@slow","@dangerous"],["nondeterministic_output","request_policy:all_nodes","response_policy:special"],[],[]]]],["time",1,["loading","stale","fast"],0,0,0,["@fast"],["nondeterministic_output"],[],[]],["xrange",-4,["readonly"],1,1,1,["@read","@stream","@slow"],[],[["flags",["RO","access"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["hkeys",2,["readonly"],1,1,1,["@read","@hash","@slow"],["nondeterministic_output_order"],[["flags",["RO","access"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["copy",-3,["write","denyoom"],1,2,1,["@keyspace","@write","@slow"],[],[["flags",["RO","access"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]],["flags",["OW","update"],"begin_search",["type","index","spec",["index",2]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["punsubscribe",-1,["pubsub","noscript","loading","stale"],0,0,0,["@pubsub","@slow"],[],[],[]],["hello",-1,["noscript","loading","stale","fast","no_auth","allow_busy"],0,0,0,["@fast","@connection"],[],[],[]],["xdel",-3,["write","fast"],1,1,1,["@write","@stream","@fast"],[],[["flags",["RW","delete"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["blmove",6,["write","denyoom","noscript","blocking"],1,2,1,["@write","@list","@slow","@blocking"],[],[["flags",["RW","access","delete"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]],["flags",["RW","insert"],"begin_search",["type","index","spec",["index",2]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["bzpopmin",-3,["write","noscript","blocking","fast"],1,-2,1,["@write","@sortedset","@fast","@blocking"],[],[["flags",["RW","access","delete"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",-2,"keystep",1,"limit",0]]]],[]],["flushdb",-1,["write"],0,0,0,["@keyspace","@write","@slow","@dangerous"],["request_policy:all_shards","response_policy:all_succeeded"],[],[]],["hmset",-4,["write","denyoom","fast"],1,1,1,["@write","@hash","@fast"],[],[["flags",["RW","update"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["sdiffstore",-3,["write","denyoom"],1,-1,1,["@write","@set","@slow"],[],[["flags",["OW","update"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]],["flags",["RO","access"],"begin_search",["type","index","spec",["index",2]],"find_keys",["type","range","spec",["lastkey",-1,"keystep",1,"limit",0]]]],[]],["smismember",-3,["readonly","fast"],1,1,1,["@read","@set","@fast"],[],[["flags",["RO","access"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["getex",-2,["write","fast"],1,1,1,["@write","@string","@fast"],[],[["notes","RW and UPDATE because it changes the TTL","flags",["RW","access","update"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["bitcount",-2,["readonly"],1,1,1,["@read","@bitmap","@slow"],[],[["flags",["RO","access"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["discard",1,["noscript","loading","stale","fast","allow_busy"],0,0,0,["@fast","@transaction"],[],[],[]],["pfadd",-2,["write","denyoom","fast"],1,1,1,["@write","@hyperloglog","@fast"],[],[["flags",["RW","insert"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["msetnx",-3,["write","denyoom"],1,-1,2,["@write","@string","@slow"],["request_policy:multi_shard","response_policy:agg_min"],[["flags",["OW","insert"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",-1,"keystep",2,"limit",0]]]],[]],["unlink",-2,["write","fast"],1,-1,1,["@keyspace","@write","@fast"],["request_policy:multi_shard","response_policy:agg_sum"],[["flags",["RM","delete"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",-1,"keystep",1,"limit",0]]]],[]],["bgsave",-1,["admin","noscript","no_async_loading"],0,0,0,["@admin","@slow","@dangerous"],[],[],[]],["zmscore",-3,["readonly","fast"],1,1,1,["@read","@sortedset","@fast"],[],[["flags",["RO","access"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["smembers",2,["readonly"],1,1,1,["@read","@set","@slow"],["nondeterministic_output_order"],[["flags",["RO","access"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["echo",2,["loading","stale","fast"],0,0,0,["@fast","@connection"],[],[],[]],["decr",2,["write","denyoom","fast"],1,1,1,["@write","@string","@fast"],[],[["flags",["RW","access","update"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["shutdown",-1,["admin","noscript","loading","stale","no_multi","allow_busy"],0,0,0,["@admin","@slow","@dangerous"],[],[],[]],["rename",3,["write"],1,2,1,["@keyspace","@write","@slow"],[],[["flags",["RW","access","delete"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]],["flags",["OW","update"],"begin_search",["type","index","spec",["index",2]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["lpop",-2,["write","fast"],1,1,1,["@write","@list","@fast"],[],[["flags",["RW","access","delete"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["zrevrange",-4,["readonly"],1,1,1,["@read","@sortedset","@slow"],[],[["flags",["RO","access"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["hget",3,["readonly","fast"],1,1,1,["@read","@hash","@fast"],[],[["flags",["RO","access"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["brpoplpush",4,["write","denyoom","noscript","blocking"],1,2,1,["@write","@list","@slow","@blocking"],[],[["flags",["RW","access","delete"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]],["flags",["RW","insert"],"begin_search",["type","index","spec",["index",2]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["xrevrange",-4,["readonly"],1,1,1,["@read","@stream","@slow"],[],[["flags",["RO","access"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["persist",2,["write","fast"],1,1,1,["@keyspace","@write","@fast"],[],[["flags",["RW","update"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["fcall_ro",-3,["readonly","noscript","stale","skip_monitor","no_mandatory_keys","movablekeys"],0,0,0,["@slow","@scripting"],[],[["notes","We cannot tell how the keys will be used so we assume the worst, RO and ACCESS","flags",["RO","access"],"begin_search",["type","index","spec",["index",2]],"find_keys",["type","keynum","spec",["keynumidx",0,"firstkey",1,"keystep",1]]]],[]],["setbit",4,["write","denyoom"],1,1,1,["@write","@bitmap","@slow"],[],[["flags",["RW","access","update"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["srandmember",-2,["readonly"],1,1,1,["@read","@set","@slow"],["nondeterministic_output"],[["flags",["RO","access"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["geohash",-2,["readonly"],1,1,1,["@read","@geo","@slow"],[],[["flags",["RO","access"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["incrbyfloat",3,["write","denyoom","fast"],1,1,1,["@write","@string","@fast"],[],[["flags",["RW","access","update"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["rpoplpush",3,["write","denyoom"],1,2,1,["@write","@list","@slow"],[],[["flags",["RW","access","delete"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]],["flags",["RW","insert"],"begin_search",["type","index","spec",["index",2]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["exec",1,["noscript","loading","stale","skip_slowlog"],0,0,0,["@slow","@transaction"],[],[],[]],["xread",-4,["readonly","blocking","movablekeys"],0,0,0,["@read","@stream","@slow","@blocking"],[],[["flags",["RO","access"],"begin_search",["type","keyword","spec",["keyword","STREAMS","startfrom",1]],"find_keys",["type","range","spec",["lastkey",-1,"keystep",1,"limit",2]]]],[]],["memory",-2,[],0,0,0,["@slow"],[],[],[["memory|purge",2,[],0,0,0,["@slow"],["request_policy:all_shards","response_policy:all_succeeded"],[],[]],["memory|malloc-stats",2,[],0,0,0,["@slow"],["nondeterministic_output","request_policy:all_shards","response_policy:special"],[],[]],["memory|doctor",2,[],0,0,0,["@slow"],["nondeterministic_output","request_policy:all_shards","response_policy:special"],[],[]],["memory|help",2,["loading","stale"],0,0,0,["@slow"],[],[],[]],["memory|stats",2,[],0,0,0,["@slow"],["nondeterministic_output","request_policy:all_shards","response_policy:special"],[],[]],["memory|usage",-3,["readonly"],2,2,1,["@read","@slow"],[],[["flags",["RO"],"begin_search",["type","index","spec",["index",2]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]]]],["georadius",-6,["write","denyoom","movablekeys"],1,1,1,["@write","@geo","@slow"],[],[["flags",["RO","access"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]],["flags",["OW","update"],"begin_search",["type","keyword","spec",["keyword","STORE","startfrom",6]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]],["flags",["OW","update"],"begin_search",["type","keyword","spec",["keyword","STOREDIST","startfrom",6]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["save",1,["admin","noscript","no_async_loading","no_multi"],0,0,0,["@admin","@slow","@dangerous"],[],[],[]],["hsetnx",4,["write","denyoom","fast"],1,1,1,["@write","@hash","@fast"],[],[["flags",["RW","insert"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["lastsave",1,["loading","stale","fast"],0,0,0,["@admin","@fast","@dangerous"],["nondeterministic_output"],[],[]],["zrangebyscore",-4,["readonly"],1,1,1,["@read","@sortedset","@slow"],[],[["flags",["RO","access"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["eval",-3,["noscript","stale","skip_monitor","no_mandatory_keys","movablekeys"],0,0,0,["@slow","@scripting"],[],[["notes","We cannot tell how the keys will be used so we assume the worst, RW and UPDATE","flags",["RW","access","update"],"begin_search",["type","index","spec",["index",2]],"find_keys",["type","keynum","spec",["keynumidx",0,"firstkey",1,"keystep",1]]]],[]],["sunionstore",-3,["write","denyoom"],1,-1,1,["@write","@set","@slow"],[],[["flags",["OW","update"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]],["flags",["RO","access"],"begin_search",["type","index","spec",["index",2]],"find_keys",["type","range","spec",["lastkey",-1,"keystep",1,"limit",0]]]],[]],["hexists",3,["readonly","fast"],1,1,1,["@read","@hash","@fast"],[],[["flags",["RO"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["zmpop",-4,["write","movablekeys"],0,0,0,["@write","@sortedset","@slow"],[],[["flags",["RW","access","delete"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","keynum","spec",["keynumidx",0,"firstkey",1,"keystep",1]]]],[]],["cluster",-2,[],0,0,0,["@slow"],[],[],[["cluster|keyslot",3,["stale"],0,0,0,["@slow"],[],[],[]],["cluster|setslot",-4,["admin","stale","no_async_loading"],0,0,0,["@admin","@slow","@dangerous"],[],[],[]],["cluster|links",2,["stale"],0,0,0,["@slow"],["nondeterministic_output"],[],[]],["cluster|saveconfig",2,["admin","stale","no_async_loading"],0,0,0,["@admin","@slow","@dangerous"],[],[],[]],["cluster|reset",-2,["admin","noscript","stale"],0,0,0,["@admin","@slow","@dangerous"],[],[],[]],["cluster|set-config-epoch",3,["admin","stale","no_async_loading"],0,0,0,["@admin","@slow","@dangerous"],[],[],[]],["cluster|slaves",3,["admin","stale"],0,0,0,["@admin","@slow","@dangerous"],["nondeterministic_output"],[],[]],["cluster|shards",2,["stale"],0,0,0,["@slow"],["nondeterministic_output"],[],[]],["cluster|delslotsrange",-4,["admin","stale","no_async_loading"],0,0,0,["@admin","@slow","@dangerous"],[],[],[]],["cluster|bumpepoch",2,["admin","stale","no_async_loading"],0,0,0,["@admin","@slow","@dangerous"],["nondeterministic_output"],[],[]],["cluster|replicate",3,["admin","stale","no_async_loading"],0,0,0,["@admin","@slow","@dangerous"],[],[],[]],["cluster|info",2,["stale"],0,0,0,["@slow"],["nondeterministic_output"],[],[]],["cluster|getkeysinslot",4,["stale"],0,0,0,["@slow"],["nondeterministic_output"],[],[]],["cluster|replicas",3,["admin","stale"],0,0,0,["@admin","@slow","@dangerous"],["nondeterministic_output"],[],[]],["cluster|flushslots",2,["admin","stale","no_async_loading"],0,0,0,["@admin","@slow","@dangerous"],[],[],[]],["cluster|addslotsrange",-4,["admin","stale","no_async_loading"],0,0,0,["@admin","@slow","@dangerous"],[],[],[]],["cluster|meet",-4,["admin","stale","no_async_loading"],0,0,0,["@admin","@slow","@dangerous"],[],[],[]],["cluster|delslots",-3,["admin","stale","no_async_loading"],0,0,0,["@admin","@slow","@dangerous"],[],[],[]],["cluster|help",2,["loading","stale"],0,0,0,["@slow"],[],[],[]],["cluster|addslots",-3,["admin","stale","no_async_loading"],0,0,0,["@admin","@slow","@dangerous"],[],[],[]],["cluster|slots",2,["stale"],0,0,0,["@slow"],["nondeterministic_output"],[],[]],["cluster|nodes",2,["stale"],0,0,0,["@slow"],["nondeterministic_output"],[],[]],["cluster|countkeysinslot",3,["stale"],0,0,0,["@slow"],[],[],[]],["cluster|count-failure-reports",3,["admin","stale"],0,0,0,["@admin","@slow","@dangerous"],["nondeterministic_output"],[],[]],["cluster|forget",3,["admin","stale","no_async_loading"],0,0,0,["@admin","@slow","@dangerous"],[],[],[]],["cluster|failover",-2,["admin","stale","no_async_loading"],0,0,0,["@admin","@slow","@dangerous"],[],[],[]],["cluster|myid",2,["stale"],0,0,0,["@slow"],[],[],[]]]],["scard",2,["readonly","fast"],1,1,1,["@read","@set","@fast"],[],[["flags",["RO"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["slaveof",3,["admin","noscript","stale","no_async_loading"],0,0,0,["@admin","@slow","@dangerous"],[],[],[]],["rpop",-2,["write","fast"],1,1,1,["@write","@list","@fast"],[],[["flags",["RW","access","delete"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["zrangestore",-5,["write","denyoom"],1,2,1,["@write","@sortedset","@slow"],[],[["flags",["OW","update"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]],["flags",["RO","access"],"begin_search",["type","index","spec",["index",2]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["select",2,["loading","stale","fast"],0,0,0,["@fast","@connection"],[],[],[]],["zrevrank",3,["readonly","fast"],1,1,1,["@read","@sortedset","@fast"],[],[["flags",["RO","access"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["hset",-4,["write","denyoom","fast"],1,1,1,["@write","@hash","@fast"],[],[["flags",["RW","update"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["pubsub",-2,[],0,0,0,["@slow"],[],[],[["pubsub|channels",-2,["pubsub","loading","stale"],0,0,0,["@pubsub","@slow"],[],[],[]],["pubsub|help",2,["loading","stale"],0,0,0,["@slow"],[],[],[]],["pubsub|numsub",-2,["pubsub","loading","stale"],0,0,0,["@pubsub","@slow"],[],[],[]],["pubsub|shardchannels",-2,["pubsub","loading","stale"],0,0,0,["@pubsub","@slow"],[],[],[]],["pubsub|numpat",2,["pubsub","loading","stale"],0,0,0,["@pubsub","@slow"],[],[],[]],["pubsub|shardnumsub",-2,["pubsub","loading","stale"],0,0,0,["@pubsub","@slow"],[],[],[]]]],["zrank",3,["readonly","fast"],1,1,1,["@read","@sortedset","@fast"],[],[["flags",["RO","access"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["role",1,["noscript","loading","stale","fast"],0,0,0,["@admin","@fast","@dangerous"],[],[],[]],["zunion",-3,["readonly","movablekeys"],0,0,0,["@read","@sortedset","@slow"],[],[["flags",["RO","access"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","keynum","spec",["keynumidx",0,"firstkey",1,"keystep",1]]]],[]],["getbit",3,["readonly","fast"],1,1,1,["@read","@bitmap","@fast"],[],[["flags",["RO","access"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["pexpireat",-3,["write","fast"],1,1,1,["@keyspace","@write","@fast"],[],[["flags",["RW","update"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["setnx",3,["write","denyoom","fast"],1,1,1,["@write","@string","@fast"],[],[["flags",["OW","insert"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["blmpop",-5,["write","blocking","movablekeys"],0,0,0,["@write","@list","@slow","@blocking"],[],[["flags",["RW","access","delete"],"begin_search",["type","index","spec",["index",2]],"find_keys",["type","keynum","spec",["keynumidx",0,"firstkey",1,"keystep",1]]]],[]],["xreadgroup",-7,["write","blocking","movablekeys"],0,0,0,["@write","@stream","@slow","@blocking"],[],[["flags",["RO","access"],"begin_search",["type","keyword","spec",["keyword","STREAMS","startfrom",4]],"find_keys",["type","range","spec",["lastkey",-1,"keystep",1,"limit",2]]]],[]],["bitfield_ro",-2,["readonly","fast"],1,1,1,["@read","@bitmap","@fast"],[],[["flags",["RO","access"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["lset",4,["write","denyoom"],1,1,1,["@write","@list","@slow"],[],[["flags",["RW","update"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["append",3,["write","denyoom","fast"],1,1,1,["@write","@string","@fast"],[],[["flags",["RW","insert"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["keys",2,["readonly"],0,0,0,["@keyspace","@read","@slow","@dangerous"],["request_policy:all_shards","nondeterministic_output_order"],[],[]],["xpending",-3,["readonly"],1,1,1,["@read","@stream","@slow"],["nondeterministic_output"],[["flags",["RO","access"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["hincrbyfloat",4,["write","denyoom","fast"],1,1,1,["@write","@hash","@fast"],[],[["flags",["RW","access","update"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["mget",-2,["readonly","fast"],1,-1,1,["@read","@string","@fast"],["request_policy:multi_shard"],[["flags",["RO","access"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",-1,"keystep",1,"limit",0]]]],[]],["evalsha",-3,["noscript","stale","skip_monitor","no_mandatory_keys","movablekeys"],0,0,0,["@slow","@scripting"],[],[["flags",["RW","access","update"],"begin_search",["type","index","spec",["index",2]],"find_keys",["type","keynum","spec",["keynumidx",0,"firstkey",1,"keystep",1]]]],[]],["psetex",4,["write","denyoom"],1,1,1,["@write","@string","@slow"],[],[["flags",["OW","update"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["zintercard",-3,["readonly","movablekeys"],0,0,0,["@read","@sortedset","@slow"],[],[["flags",["RO","access"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","keynum","spec",["keynumidx",0,"firstkey",1,"keystep",1]]]],[]],["readwrite",1,["loading","stale","fast"],0,0,0,["@fast","@connection"],[],[],[]],["monitor",1,["admin","noscript","loading","stale"],0,0,0,["@admin","@slow","@dangerous"],[],[],[]],["bzpopmax",-3,["write","noscript","blocking","fast"],1,-2,1,["@write","@sortedset","@fast","@blocking"],[],[["flags",["RW","access","delete"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",-2,"keystep",1,"limit",0]]]],[]],["sort_ro",-2,["readonly","movablekeys"],1,1,1,["@read","@set","@sortedset","@list","@slow","@dangerous"],[],[["flags",["RO","access"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]],["notes","For the optional BY/GET keyword. It is marked 'unknown' because the key names derive from the content of the key we sort","flags",["RO","access"],"begin_search",["type","unknown","spec",[]],"find_keys",["type","unknown","spec",[]]]],[]],["zdiffstore",-4,["write","denyoom","movablekeys"],1,1,1,["@write","@sortedset","@slow"],[],[["flags",["OW","update"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]],["flags",["RO","access"],"begin_search",["type","index","spec",["index",2]],"find_keys",["type","keynum","spec",["keynumidx",0,"firstkey",1,"keystep",1]]]],[]],["getset",3,["write","denyoom","fast"],1,1,1,["@write","@string","@fast"],[],[["flags",["RW","access","update"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["pfmerge",-2,["write","denyoom"],1,-1,1,["@write","@hyperloglog","@slow"],[],[["flags",["RW","access","insert"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]],["flags",["RO","access"],"begin_search",["type","index","spec",["index",2]],"find_keys",["type","range","spec",["lastkey",-1,"keystep",1,"limit",0]]]],[]],["georadiusbymember_ro",-5,["readonly"],1,1,1,["@read","@geo","@slow"],[],[["flags",["RO","access"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["expiretime",2,["readonly","fast"],1,1,1,["@keyspace","@read","@fast"],[],[["flags",["RO","access"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["type",2,["readonly","fast"],1,1,1,["@keyspace","@read","@fast"],[],[["flags",["RO"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["sscan",-3,["readonly"],1,1,1,["@read","@set","@slow"],["nondeterministic_output"],[["flags",["RO","access"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",0,"keystep",1,"limit",0]]]],[]],["ssubscribe",-2,["pubsub","noscript","loading","stale"],1,-1,1,["@pubsub","@slow"],[],[["flags",["not_key"],"begin_search",["type","index","spec",["index",1]],"find_keys",["type","range","spec",["lastkey",-1,"keystep",1,"limit",0]]]],[]]]src/service/000077500000000000000000000000001517715050700133155ustar00rootroot00000000000000src/service/decompress.mjs000066400000000000000000000101261517715050700161740ustar00rootroot00000000000000import zlib from 'node:zlib' import snappyjs from 'snappyjs' import lz4 from 'lz4js' import { decompress as fzstdDecompress } from 'fzstd' /** * Detect compression format by magic bytes. * Returns { algorithm, decompressed } or null if not compressed. */ export async function tryDecompress(buffer) { if (!Buffer.isBuffer(buffer) || buffer.length < 2) return null const b0 = buffer[0] const b1 = buffer[1] // GZIP: 1F 8B if (b0 === 0x1f && b1 === 0x8b) { try { const result = zlib.gunzipSync(buffer) if (isLikelyUtf8(result)) return { algorithm: 'gzip', decompressed: result } } catch {} return null } // ZIP (PKZip): 50 4B 03 04 if (buffer.length >= 4 && b0 === 0x50 && b1 === 0x4b && buffer[2] === 0x03 && buffer[3] === 0x04) { try { const result = await decompressZip(buffer) if (result && isLikelyUtf8(result)) return { algorithm: 'zip', decompressed: result } } catch {} return null } // zlib/deflate: first byte 0x78, CMF/FLG checksum: (0x78 * 256 + b1) % 31 === 0 if (b0 === 0x78 && (0x78 * 256 + b1) % 31 === 0) { try { const result = zlib.inflateSync(buffer) if (isLikelyUtf8(result)) return { algorithm: 'zlib', decompressed: result } } catch {} return null } // Zstandard: 28 B5 2F FD if (buffer.length >= 4 && b0 === 0x28 && b1 === 0xb5 && buffer[2] === 0x2f && buffer[3] === 0xfd) { try { const result = Buffer.from(fzstdDecompress(buffer)) if (isLikelyUtf8(result)) return { algorithm: 'zstd', decompressed: result } } catch {} return null } // LZ4 frame: 04 22 4D 18 if (buffer.length >= 4 && b0 === 0x04 && b1 === 0x22 && buffer[2] === 0x4d && buffer[3] === 0x18) { try { const result = Buffer.from(lz4.decompress(buffer)) if (isLikelyUtf8(result)) return { algorithm: 'lz4', decompressed: result } } catch {} return null } // Snappy + Brotli: no reliable magic bytes β€” try on binary data only if (!isLikelyUtf8(buffer)) { try { const result = Buffer.from(snappyjs.uncompress(buffer)) if (result.length > 0 && isLikelyUtf8(result)) { return { algorithm: 'snappy', decompressed: result } } } catch { /* not snappy */ } try { const result = zlib.brotliDecompressSync(buffer) if (result.length > 0 && isLikelyUtf8(result)) { return { algorithm: 'brotli', decompressed: result } } } catch { /* not brotli */ } } return null } /** * Extract the first file from a ZIP archive using raw inflate (no external dependency). * ZIP local file header: signature 50 4B 03 04, then metadata, then compressed data. */ async function decompressZip(buffer) { // Local file header offsets const compressionMethod = buffer.readUInt16LE(8) const compressedSize = buffer.readUInt32LE(18) const filenameLen = buffer.readUInt16LE(26) const extraLen = buffer.readUInt16LE(28) const dataOffset = 30 + filenameLen + extraLen if (dataOffset + compressedSize > buffer.length) return null const compressedData = buffer.subarray(dataOffset, dataOffset + compressedSize) if (compressionMethod === 0) { // Stored (no compression) return Buffer.from(compressedData) } if (compressionMethod === 8) { // Deflated β€” use raw inflate (no zlib header) return zlib.inflateRawSync(compressedData) } return null } /** * Quick check if buffer looks like valid UTF-8 text. * Checks first 64 bytes for control characters (except common ones). */ function isLikelyUtf8(buffer) { const checkLen = Math.min(buffer.length, 64) for (let i = 0; i < checkLen; i++) { const b = buffer[i] // Allow: tab (9), newline (10), carriage return (13), printable ASCII (32-126), and UTF-8 continuation bytes (128+) if (b < 9 || (b > 13 && b < 32) || b === 127) { return false } } return true } src/service/http/000077500000000000000000000000001517715050700142745ustar00rootroot00000000000000src/service/http/index.mjs000066400000000000000000000067541517715050700161320ustar00rootroot00000000000000import express from 'express' import fs from 'fs' import path from 'path' import http from 'http' import { fileURLToPath } from 'url' import { resolveConfiguredHttpAuth, verifyAuthorizationHeader } from '../../lib/http-auth.mjs' const __dirname = path.dirname(fileURLToPath(import.meta.url)) const httpService = function () { const self = this self.boot = async () => { const app = express() this.app = app app.disable('x-powered-by') // Health endpoint β€” before auth, always accessible app.get('/health', (req, res) => { res.json({ status: 'ok', version: p3xrs.cfg?.version || 'unknown', uptime: process.uptime(), }) }) app.use((req, res, next) => { const httpAuth = resolveConfiguredHttpAuth() if (!httpAuth.enabled) { next() return } const authHeader = req.get('authorization') if (verifyAuthorizationHeader(authHeader)) { next() return } res.set('WWW-Authenticate', 'Basic realm="P3X Redis UI"') res.status(401).json({ error: 'http_auth_required', }) }) const findModulePath = (startPath, targetPath) => { let currentPath = startPath while (currentPath !== path.resolve(currentPath, '..')) { const nodeModulesPath = path.join(currentPath, targetPath) if (fs.existsSync(nodeModulesPath)) { return nodeModulesPath } currentPath = path.resolve(currentPath, '..') } throw new Error('The specified module could not be found in any node_modules directory') } const resolvePath = (inputPath) => { if (inputPath.startsWith('~')) { const inputPathFromNodeModules = inputPath.substring(1) const startPath = __dirname return findModulePath(startPath, inputPathFromNodeModules) } return path.resolve(process.cwd(), inputPath) } let hasStatic = false let staticPath if (typeof p3xrs.cfg.static === 'string') { hasStatic = true staticPath = resolvePath(p3xrs.cfg.static) app.use(express.static(staticPath)) } if (hasStatic) { app.use((req, res, next) => { if (req.path.startsWith('/socket.io')) { next() return } res.sendFile(path.resolve(staticPath, 'index.html'), (error) => { if (error) { next(error) } }) }) } else { app.use((req, res) => { res.json({ status: 'operational' }) }) } app.use((error, req, res, next) => { console.error('express server error', error) if (res.headersSent) { next(error) return } res.status(500).json({ error: 'internal_server_error', }) }) const server = http.createServer(app) this.server = server server.listen(p3xrs.cfg.http.port || 7843, p3xrs.cfg.http.bind ? p3xrs.cfg.http.bind : '0.0.0.0') } } export default httpService src/service/index.mjs000066400000000000000000000001021517715050700151300ustar00rootroot00000000000000import http from './http/index.mjs' export default { http, } src/service/socket.io/000077500000000000000000000000001517715050700152135ustar00rootroot00000000000000src/service/socket.io/index.mjs000066400000000000000000000021761517715050700170430ustar00rootroot00000000000000import { Server } from 'socket.io' import { resolveConfiguredHttpAuth, verifyAuthorizationHeader } from '../../lib/http-auth.mjs' import socketHandler from './socket.mjs' const socketIoService = function () { const self = this; self.boot = async (options) => { const httpService = options.httpService const socketio = new Server(httpService.server, { secure: true, path: '/socket.io', maxHttpBufferSize: 256 * 1024 * 1024, // 256 MB }); socketio.use((socket, next) => { const httpAuth = resolveConfiguredHttpAuth() if (!httpAuth.enabled) { next() return } const authHeader = socket.handshake && socket.handshake.headers ? socket.handshake.headers.authorization : undefined if (verifyAuthorizationHeader(authHeader)) { next() return } const error = new Error('http_auth_required') next(error) }) socketHandler(socketio); this.socketio = socketio } } export default socketIoService src/service/socket.io/request/000077500000000000000000000000001517715050700167035ustar00rootroot00000000000000src/service/socket.io/request/ai-redis-query.mjs000066400000000000000000000247731517715050700222730ustar00rootroot00000000000000import Groq from 'groq-sdk' const AI_NETWORK_URL_PROD = 'https://network.corifeus.com' const AI_NETWORK_URL_DEV = 'http://localhost:8003' const SYSTEM_PROMPT = `You are an expert Redis command generator embedded in a Redis GUI console. Users type natural language in any human language (English, Hungarian, Chinese, etc.) and you translate it into valid Redis CLI commands. # Output Format One or more Redis commands (one per line), then a separator, then an explanation: \`\`\` COMMAND1 COMMAND2 --- Brief explanation in the user's language \`\`\` - For simple requests: output a single command line - For complex requests needing multiple steps: output multiple command lines (one per line) - For bulk operations: prefer a single EVAL script, but use multiple commands if clearer - The --- separator is REQUIRED between commands and explanation - The explanation should be in the SAME LANGUAGE as the user's input # Core Principles 1. Generate ONLY real, valid Redis commands that a Redis server will accept 2. Never invent key names, index names, or field names β€” use only what is provided in context or use wildcard patterns 3. The user's Redis GUI will execute your command directly β€” it must be syntactically correct 4. Support all human languages as input β€” always output a Redis command regardless of input language # Command Selection Guide ## Key Discovery & Listing - "show all keys" / "list keys" β†’ KEYS * - "find keys matching user" β†’ KEYS user:* - "keys starting with session" β†’ KEYS session:* - "how many keys" β†’ DBSIZE ## Key Type Filtering When user asks for keys of a specific data type, use SCAN with TYPE filter: - "show all hash keys" β†’ SCAN 0 MATCH * TYPE hash COUNT 10000 - "show all json keys" / "rejson keys" β†’ SCAN 0 MATCH * TYPE ReJSON-RL COUNT 10000 - "show all set keys" β†’ SCAN 0 MATCH * TYPE set COUNT 10000 - "show all list keys" β†’ SCAN 0 MATCH * TYPE list COUNT 10000 - "show all string keys" β†’ SCAN 0 MATCH * TYPE string COUNT 10000 - "show all stream keys" β†’ SCAN 0 MATCH * TYPE stream COUNT 10000 - "show all sorted set keys" β†’ SCAN 0 MATCH * TYPE zset COUNT 10000 - For checking a single key's type β†’ TYPE keyname Note: SCAN returns [cursor, [keys...]]. cursor=0 means scan complete. ## Reading Values - String: GET key - Hash: HGETALL key | HGET key field - List: LRANGE key 0 -1 - Set: SMEMBERS key - Sorted Set: ZRANGE key 0 -1 WITHSCORES - Stream: XRANGE key - + - JSON/ReJSON: JSON.GET key $ | JSON.GET key $.fieldname - Multiple strings: MGET key1 key2 - Multiple JSON: JSON.MGET key1 key2 $ ## Writing Values - String: SET key value [EX seconds] - Hash: HSET key field value [field value ...] - List: LPUSH/RPUSH key value [value ...] - Set: SADD key member [member ...] - Sorted Set: ZADD key score member [score member ...] - Stream: XADD key * field value [field value ...] - JSON: JSON.SET key $ 'jsonvalue' ## Key Management - Delete: DEL key [key ...] - Rename: RENAME key newkey - TTL check: TTL key | PTTL key - Set expiry: EXPIRE key seconds | PEXPIRE key ms - Persist (remove TTL): PERSIST key - Check existence: EXISTS key [key ...] ## Server & Info - Server info: INFO [section] (sections: server, clients, memory, stats, replication, cpu, modules, keyspace, all) - Memory usage: MEMORY USAGE key | INFO memory - Connected clients: CLIENT LIST - Config: CONFIG GET parameter - Slow log: SLOWLOG GET [count] - Database size: DBSIZE - Flush database: FLUSHDB - Flush all: FLUSHALL - Last save: LASTSAVE - Server time: TIME ## RediSearch (only when explicitly requested) - Search: FT.SEARCH indexname query - List indexes: FT._LIST - Index info: FT.INFO indexname - Aggregate: FT.AGGREGATE indexname query - Create index: FT.CREATE indexname ON HASH PREFIX 1 prefix: SCHEMA field TYPE ... - Drop index: FT.DROPINDEX indexname ## Pub/Sub - Publish: PUBLISH channel message - Subscribe: SUBSCRIBE channel ## Cluster - Cluster info: CLUSTER INFO - Cluster nodes: CLUSTER NODES ## Multi-step operations β€” PREFER multiple commands over EVAL When the user needs multiple Redis operations, output them as separate commands (one per line): - SET test:str hello - HSET test:hash f1 v1 f2 v2 - RPUSH test:list a b c This is ALWAYS preferred over EVAL unless a loop is needed. ## Scripting (EVAL) β€” ONLY for loops or atomic operations Use EVAL ONLY when a loop or atomicity is required (e.g. "generate 100 random keys"): - EVAL "lua_script" numkeys [key ...] [arg ...] - Write Lua code with REAL line breaks inside the quotes β€” the console supports multi-line input - NEVER use literal \\n escape sequences β€” they cause Redis script compilation errors - CORRECT example: EVAL " for i=1,3 do redis.call('SET','k'..i,i) end return 'done' " 0 - WRONG: EVAL "for i=1,3 do\\nredis.call('SET','k'..i,i)\\nend" 0 # Redis Type Names (for TYPE command responses) - string, list, set, zset, hash, stream, ReJSON-RL # Critical Rules - NEVER use FT.SEARCH or FT.AGGREGATE unless the user explicitly mentions "search index", "full-text search", "FT.", or "RediSearch" - NEVER fabricate key names β€” if unsure, use patterns like KEYS * or KEYS prefix:* - NEVER fabricate index names β€” if indexes are provided in context, use those exact names - When the user mentions "rejson", "json keys", or "JSON type", they mean keys stored with the RedisJSON module - Prefer simple commands β€” KEYS over SCAN for readability in a GUI console - If the user asks something that needs multiple steps, output multiple commands (one per line)` function buildSystemPrompt(context) { let prompt = SYSTEM_PROMPT if (context) { const parts = [] if (context.redisVersion) parts.push(`Redis version: ${context.redisVersion}`) if (context.redisMode) parts.push(`Mode: ${context.redisMode}`) if (context.usedMemory) parts.push(`Memory: ${context.usedMemory}`) if (context.connectedClients) parts.push(`Clients: ${context.connectedClients}`) if (context.os) parts.push(`OS: ${context.os}`) if (context.modules) parts.push(`Loaded modules: ${JSON.stringify(context.modules)}`) if (context.databases && context.databases.length > 0) parts.push(`Databases: ${context.databases.join(', ')}`) if (parts.length > 0) { prompt += `\n\n# Connected Redis Server\n${parts.join('\n')}` } if (context.indexes && context.indexes.length > 0) { prompt += `\n\nAvailable RediSearch indexes: ${context.indexes.join(', ')}` } if (context.schema) { prompt += `\n\nSchema information: ${JSON.stringify(context.schema)}` } if (context.uiLanguage) { prompt += `\n\n# Response Language\nYou MUST write the explanation (line 2) in the SAME language as the user's prompt. If they write in Hungarian, respond in Hungarian. If in English, respond in English. Always match the user's language.` } } return prompt } function getNetworkUrl() { if (typeof p3xrs.cfg.aiNetworkUrl === 'string' && p3xrs.cfg.aiNetworkUrl.length > 0) { return p3xrs.cfg.aiNetworkUrl } const isDev = process.env.NODE_ENV === 'development' return isDev ? AI_NETWORK_URL_DEV : AI_NETWORK_URL_PROD } function parseAiResponse(responseText) { const separatorIndex = responseText.indexOf('\n---') if (separatorIndex !== -1) { const command = responseText.substring(0, separatorIndex).trim() const explanation = responseText.substring(separatorIndex).replace(/^[\n\r]*---[\n\r]*/, '').trim() return { command, explanation } } // Fallback: first line is command, rest is explanation const lines = responseText.split('\n').filter(line => line.trim().length > 0) return { command: lines[0] || '', explanation: lines.slice(1).join(' ') || '', } } async function callGroqDirect(prompt, context, apiKey) { const client = new Groq({ apiKey }) const systemPrompt = buildSystemPrompt(context) const chatCompletion = await client.chat.completions.create({ messages: [ { role: 'system', content: systemPrompt }, { role: 'user', content: prompt }, ], model: 'openai/gpt-oss-120b', max_tokens: 4096, temperature: 0.1, }) const responseText = chatCompletion.choices?.[0]?.message?.content?.trim() || '' return parseAiResponse(responseText) } async function callNetworkProxy(prompt, context, apiKey) { const networkUrl = getNetworkUrl() let response try { response = await fetch(`${networkUrl}/public/ai/redis-query`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ prompt, context: context || {}, apiKey: apiKey || undefined, }), }) } catch (fetchError) { throw new Error('AI service is not reachable') } const contentType = response.headers.get('content-type') || '' if (!contentType.includes('application/json')) { throw new Error(`AI service returned invalid response (${response.status})`) } const data = await response.json() if (data.status !== 'ok') { throw new Error(data.message || 'AI query failed') } return { command: data.data.command, explanation: data.data.explanation, } } export default async (options) => { const { socket, payload } = options try { const { prompt, context } = payload if (!prompt || typeof prompt !== 'string' || prompt.trim().length === 0) { throw new Error('AI_PROMPT_REQUIRED') } if (p3xrs.cfg.aiEnabled === false) { throw new Error('AI_DISABLED') } const apiKey = p3xrs.cfg.groqApiKey || '' const useOwnKey = p3xrs.cfg.aiUseOwnKey === true let result if (useOwnKey && apiKey) { // Direct Groq call - no network dependency console.info('ai-redis-query: using direct Groq API (own key)') result = await callGroqDirect(prompt.trim(), context, apiKey) } else { // Proxy through network.corifeus.com (default) console.info('ai-redis-query: using network proxy') result = await callNetworkProxy(prompt.trim(), context, apiKey || undefined) } socket.emit(options.responseEvent, { status: 'ok', command: result.command, explanation: result.explanation, }) } catch (e) { console.error('ai-redis-query error', e) socket.emit(options.responseEvent, { status: 'error', error: e.message, }) } } src/service/socket.io/request/client-kill.mjs000066400000000000000000000010331517715050700216220ustar00rootroot00000000000000import * as sharedIoRedis from '../shared.mjs' export default async (options) => { const {socket, payload} = options try { sharedIoRedis.ensureReadonlyConnection({ socket }) const redis = socket.p3xrs.ioredis await redis.client('KILL', 'ID', payload.id) socket.emit(options.responseEvent, { status: 'ok', }) } catch (e) { console.error(e) socket.emit(options.responseEvent, { status: 'error', error: e.message, }) } } src/service/socket.io/request/client-list.mjs000066400000000000000000000031631517715050700216500ustar00rootroot00000000000000export default async (options) => { const {socket} = options try { const redis = socket.p3xrs.ioredis const raw = await redis.client('LIST') // Parse CLIENT LIST output (each line is a client, fields separated by spaces, key=value) const clients = [] for (const line of raw.split('\n')) { const trimmed = line.trim() if (!trimmed) continue const client = {} for (const pair of trimmed.split(' ')) { const eqIdx = pair.indexOf('=') if (eqIdx > 0) { client[pair.slice(0, eqIdx)] = pair.slice(eqIdx + 1) } } if (client.id) { clients.push({ id: client.id, addr: client.addr || '', name: client.name || '', age: parseInt(client.age) || 0, idle: parseInt(client.idle) || 0, db: parseInt(client.db) || 0, cmd: client.cmd || '', flags: client.flags || '', sub: parseInt(client.sub) || 0, psub: parseInt(client.psub) || 0, multi: parseInt(client.multi) || -1, omem: parseInt(client.omem) || 0, }) } } socket.emit(options.responseEvent, { status: 'ok', data: clients, }) } catch (e) { console.error(e) socket.emit(options.responseEvent, { status: 'error', error: e.message, }) } } src/service/socket.io/request/connection-connect.mjs000066400000000000000000000345321517715050700232130ustar00rootroot00000000000000import Redis from '../../../lib/ioredis-cluster/index.mjs' import * as sharedIoRedis from '../shared.mjs' import staticCommands from '../../../lib/redis-static-commands.mjs' import { buildCommandMeta } from '../../../lib/redis-command-meta.mjs' const consolePrefix = 'socket.io connection-connect'; const generateConnectInfo = async (options) => { const {socket, redis, payload} = options const { db} = payload // console.warn('generateConnectInfo', options.payload) let databases //let results let commands = staticCommands const probeDatabaseCount = async() => { let tryUntilSelectDatabaseIsNotOk = true let currentDb = 0 let totalDb = 0 let maxDb = 512 await new Promise(resolve => setTimeout(resolve, 1000)); while(tryUntilSelectDatabaseIsNotOk) { try { await redis.call('select', currentDb) //console.info('found correct database index', currentDb) if (currentDb > maxDb) { console.warn(`limiting to max ${maxDb} database index, as it could crash with a big db index number`) tryUntilSelectDatabaseIsNotOk = false } currentDb++ } catch(e) { console.error(e); console.warn('found wrong current db index', currentDb) tryUntilSelectDatabaseIsNotOk = false } } totalDb = currentDb if (db <= totalDb) { try { await redis.call('select', db) } catch(e) { console.error(e) } } console.log('calculated max databases index', totalDb) return totalDb } if (options.payload.connection.cluster === true) { databases = 1 //commands = await redis.command() } else { try { databases = (await redis.config('get', 'databases'))[1] console.info(options.payload.connection.name, 'instance successfully works the database listing') } catch(e) { console.warn(options.payload.connection.name, 'instance get databases listing is disabled', e) databases = await probeDatabaseCount() } } console.info(options.payload.connection.name, 'databases got', databases) try { //commands = await redis.call('command2') commands = await redis.command() console.info(options.payload.connection.name, 'instance command listing is available') // , JSON.stringify(commands)) } catch(e) { console.warn(options.payload.connection.name, 'instance command listing is not available, not all redis instances are not available command listing', e) } // Detect loaded Redis modules (e.g. ReJSON, RediSearch, RedisTimeSeries) const modules = await sharedIoRedis.detectModules(redis) if (modules.length > 0) { console.info(options.payload.connection.name, 'modules detected:', modules.map(m => m.name).join(', ')) } await sharedIoRedis.getFullInfoAndSendSocket({ setDb: true, redis: redis, responseEvent: options.responseEvent, socket: socket, extend: { databases: databases, commands: commands, commandsMeta: buildCommandMeta(commands), modules: modules, }, payload: payload, }) } export default async (options) => { const {socket, payload} = options; const {connection, db} = payload try { if (socket.p3xrs.connectionId !== connection.id) { sharedIoRedis.disconnectRedis({ socket: socket, }) } if (!p3xrs.redisConnections.hasOwnProperty(connection.id)) { p3xrs.redisConnections[connection.id] = { connection: connection, clients: [] } } if (!p3xrs.redisConnections[connection.id].clients.includes(socket.id)) { console.info(consolePrefix, 'added new socket.id', socket.id, 'to', connection.id, 'name with', connection.name) p3xrs.redisConnections[connection.id].clients.push(socket.id) } if (socket.p3xrs.ioredis !== undefined) { console.info(consolePrefix, 'redis was already connected') socket.p3xrs.connectionId = connection.id await generateConnectInfo({ redis: socket.p3xrs.ioredis, socket: socket, responseEvent: options.responseEvent, payload: payload }) sharedIoRedis.sendStatus({ socket: socket, }) } else { const actualConnection = p3xrs.connections.list.find(con => options.payload.connection.id === con.id) if (actualConnection === undefined) { throw new Error('auto-connection-failed') } if (connection.askAuth) { actualConnection.username = undefined actualConnection.password = undefined if (connection.username) { actualConnection.username = connection.username } if (connection.password) { actualConnection.password = connection.password } } let redisConfig = Object.assign({}, actualConnection); const sentinelName = redisConfig.sentinelName delete redisConfig.name delete redisConfig.id redisConfig.retryStrategy = null // module.exports = class Cluster extends Redis.Cluster <- right as it says redisConfig.clusterRetryStrategy = null /* redisConfig.showFriendlyErrorStack = true if (db !== undefined) { redisConfig.db = db } */ if (redisConfig.tlsWithoutCert) { redisConfig.tls = { servername: redisConfig.host } } else if (typeof redisConfig.tlsCa === 'string' && redisConfig.tlsCa.trim() !== '') { redisConfig.tls = { //rejectUnauthorized: false, cert: redisConfig.tlsCrt, key: redisConfig.tlsKey, ca: redisConfig.tlsCa, servername: redisConfig.host } } if (redisConfig.hasOwnProperty('tls')) { redisConfig.tls.rejectUnauthorized = redisConfig.tlsRejectUnauthorized === undefined ? false : redisConfig.tlsRejectUnauthorized // Ensure SNI is always set to the host if (!redisConfig.tls.hasOwnProperty('servername')) { redisConfig.tls.servername = redisConfig.host } } const closeRedis = () => { sharedIoRedis.disconnectRedis({ socket: socket, }) socket.p3xrs.connectionId = undefined socket.p3xrs.ioredis = undefined socket.p3xrs.ioredisSubscriber = undefined } // SSH tunnel creation - single SSH connection, multiple port forwards let connectionNodes = actualConnection.nodes || [] if (redisConfig.ssh === true) { const { createTunnel } = await import('tunnel-ssh') const net = await import('net') const sshOptions = { host: redisConfig.sshHost, port: redisConfig.sshPort, username: redisConfig.sshUsername, }; if (redisConfig.sshPrivateKey) { sshOptions.privateKey = redisConfig.sshPrivateKey } else { sshOptions.password = redisConfig.sshPassword } const tunnelServers = [] // Create primary tunnel (establishes the single SSH connection) let [primaryServer, sshClient] = await createTunnel({ autoClose: true }, null, sshOptions, { dstAddr: redisConfig.host, dstPort: redisConfig.port, }); tunnelServers.push(primaryServer) redisConfig.port = primaryServer.address().port // Create port forwards for additional nodes through the same SSH connection if (connectionNodes.length > 0) { connectionNodes = connectionNodes.map(node => Object.assign({}, node)) for (const node of connectionNodes) { const nodeServer = await new Promise((resolve, reject) => { const server = net.createServer((sock) => { sshClient.forwardOut('127.0.0.1', 0, node.host || 'localhost', node.port, (err, channel) => { if (err) { sock.end() return } sock.pipe(channel).pipe(sock) }) }) server.listen(0, '127.0.0.1', () => resolve(server)) server.on('error', reject) }) tunnelServers.push(nodeServer) node.port = nodeServer.address().port } } socket.p3xrs.tunnels = tunnelServers socket.p3xrs.sshClient = sshClient // Error handlers sshClient.on('error', async(e)=>{ console.error('ssh client error', e); closeRedis() socket.emit(options.responseEvent, { status: 'error', error: e.message }) }); for (const server of tunnelServers) { server.on('error', async(e)=>{ console.error('ssh tunnel server error', e); closeRedis() socket.emit(options.responseEvent, { status: 'error', error: e.message }) }); } } if (redisConfig.hasOwnProperty('sentinel') && redisConfig.sentinel === true) { redisConfig = [redisConfig].concat(connectionNodes) } else if (redisConfig.cluster === true) { redisConfig = [redisConfig].concat(connectionNodes) } if (Array.isArray(redisConfig) && redisConfig[0].hasOwnProperty('sentinel') && redisConfig[0].sentinel === true) { redisConfig = { sentinels: redisConfig, name: sentinelName, sentinelPassword: redisConfig[0].password, sentinelRetryStrategy: () => { return false } } } let redis = new Redis(redisConfig) //console.warn('redis connection', redisConfig) let redisSubscriber = new Redis(redisConfig) // let redis = await new Redis(redisConfig, {autoDetectCluster: true}) // let redisSubscriber = await new Redis(redisConfig, {autoDetectCluster: true}) socket.p3xrs.connectionId = connection.id socket.p3xrs.readonly = actualConnection.readonly === true && isProOrEnterpriseTier() socket.p3xrs.ioredis = redis socket.p3xrs.ioredisSubscriber = redisSubscriber let didConnected = false const redisErrorFun = async function (error) { if (!error) { error = new Error('Connection is closed.') error.p3xCode = 'disconnect' } const consolePrefix = 'socket.io connection-connect redis error fun' console.warn(consolePrefix, connection.id, connection.name, 'error') console.error(error) console.warn(consolePrefix, 'didConnected', didConnected) if (!didConnected) { socket.emit(options.responseEvent, { status: 'error', error: error.message }) } const disconnectedData = { connectionId: socket.p3xrs.connectionId, error: error.message, status: 'error', } console.warn(consolePrefix, 'disconnectedData', disconnectedData) socket.p3xrs.io.emit('redis-disconnected', disconnectedData) try { await sharedIoRedis.disconnectRedis({ socket: socket, }) } catch (e) { console.warn(consolePrefix, 'disconnectRedis') console.error(e) } closeRedis() sharedIoRedis.sendStatus({ socket: socket, }) } redis.on('error', redisErrorFun) redis.on('disconnect', redisErrorFun) redisSubscriber.on('error', redisErrorFun) redis.on('connect', async function () { try { console.info(consolePrefix, options.payload.connection.id, options.payload.connection.name, 'connected') didConnected = true await generateConnectInfo({ redis: redis, socket: socket, responseEvent: options.responseEvent, payload: options.payload, }) } catch (e) { console.error(e) socket.emit(options.responseEvent, { status: 'error', error: e.message, }) } finally { sharedIoRedis.sendStatus({ socket: socket, }) } }) } } catch (e) { console.error(e) socket.emit(options.responseEvent, { status: 'error', error: e.message }) } } src/service/socket.io/request/connection-delete.mjs000066400000000000000000000027771517715050700230320ustar00rootroot00000000000000import fs from 'fs' import * as sharedIoRedis from '../shared.mjs' export default async (options) => { const {socket} = options; const connectionSaveId = options.payload.id; let connectionIndexExisting; let disableReadonlyConnections = true try { sharedIoRedis.ensureReadonlyConnections() disableReadonlyConnections = false for (let connectionIndex in p3xrs.connections.list) { const connection = p3xrs.connections.list[connectionIndex] if (connection.id === connectionSaveId) { connectionIndexExisting = connectionIndex break; } } if (connectionIndexExisting !== undefined) { p3xrs.connections.list.splice(connectionIndexExisting, 1) p3xrs.connections.update = new Date() fs.writeFileSync(p3xrs.cfg.connections.home, JSON.stringify(p3xrs.connections, null, 4)) } socket.emit(options.responseEvent, { status: 'ok', }) } catch (e) { console.error(e) socket.emit(options.responseEvent, { status: 'error', error: e.message }) } finally { if (!disableReadonlyConnections) { sharedIoRedis.sendConnections({ socket: socket, }) sharedIoRedis.triggerDisconnect({ connectionId: connectionSaveId, code: 'delete-connection', socket: socket, }) } } } src/service/socket.io/request/connection-disconnect.mjs000066400000000000000000000016221517715050700237050ustar00rootroot00000000000000import * as sharedIoRedis from '../shared.mjs' const consolePrefix = 'socket.io connection disconnect' export default async (options) => { const {socket, payload} = options; const {connectionId} = payload; console.warn(consolePrefix, 'connectionId', connectionId, 'socket.p3xrs.connectionId', socket.p3xrs.connectionId) try { if (socket.p3xrs.connectionId === connectionId) { console.warn(consolePrefix, 'will disconnect from redis') sharedIoRedis.disconnectRedis({ socket: socket, }) } socket.emit(options.responseEvent, { status: 'ok', }) } catch (e) { console.error(e) socket.emit(options.responseEvent, { status: 'error', error: e.message }) } finally { sharedIoRedis.sendStatus({ socket: socket, }) } } src/service/socket.io/request/connection-save.mjs000066400000000000000000000070701517715050700225150ustar00rootroot00000000000000import fs from 'fs' import * as sharedIoRedis from '../shared.mjs' export default async (options) => { const {socket} = options; const connectionSave = options.payload.model; let disableReadonlyConnections = true try { sharedIoRedis.ensureReadonlyConnections() disableReadonlyConnections = false let connectionIndexExisting; for (let connectionIndex in p3xrs.connections.list) { const connection = p3xrs.connections.list[connectionIndex] if (connection.id === connectionSave.id) { connectionIndexExisting = connectionIndex break; } } p3xrs.connections.update = new Date() if (connectionIndexExisting !== undefined) { if (p3xrs.connections.list[connectionIndexExisting].id === connectionSave.password) { connectionSave.password = p3xrs.connections.list[connectionIndexExisting].password; } if (p3xrs.connections.list[connectionIndexExisting].id === connectionSave.tlsCrt) { connectionSave.tlsCrt = p3xrs.connections.list[connectionIndexExisting].tlsCrt; } if (p3xrs.connections.list[connectionIndexExisting].id === connectionSave.tlsKey) { connectionSave.tlsKey = p3xrs.connections.list[connectionIndexExisting].tlsKey; } if (p3xrs.connections.list[connectionIndexExisting].id === connectionSave.tlsCa) { connectionSave.tlsCa = p3xrs.connections.list[connectionIndexExisting].tlsCa; } if (p3xrs.connections.list[connectionIndexExisting].id === connectionSave.tlsCa) { connectionSave.tlsCa = p3xrs.connections.list[connectionIndexExisting].tlsCa; } if (p3xrs.connections.list[connectionIndexExisting].id === connectionSave.sshPassword) { connectionSave.sshPassword = p3xrs.connections.list[connectionIndexExisting].sshPassword } if (p3xrs.connections.list[connectionIndexExisting].id === connectionSave.sshPrivateKey) { connectionSave.sshPrivateKey = p3xrs.connections.list[connectionIndexExisting].sshPrivateKey } //TODO fix secured nodes password if (Array.isArray(connectionSave.nodes)) { for (let node of connectionSave.nodes) { const findNode = p3xrs.connections.list[connectionIndexExisting].nodes.find((findNode) => { return findNode.id === node.id && node.password === findNode.id }) if (findNode !== undefined) { node.password = findNode.password } } } p3xrs.connections.list[connectionIndexExisting] = connectionSave } else { p3xrs.connections.list.push(connectionSave) } fs.writeFileSync(p3xrs.cfg.connections.home, JSON.stringify(p3xrs.connections, null, 4)) socket.emit(options.responseEvent, { status: 'ok', }) } catch (e) { console.error(e) socket.emit(options.responseEvent, { status: 'error', error: e.message }) } finally { if (!disableReadonlyConnections) { sharedIoRedis.sendConnections({ socket: socket, }) sharedIoRedis.triggerDisconnect({ connectionId: connectionSave.id, code: 'save-connection', socket: socket, }) } } } src/service/socket.io/request/connections-reorder.mjs000066400000000000000000000052071517715050700234040ustar00rootroot00000000000000import fs from 'fs' import * as sharedIoRedis from '../shared.mjs' export default async (options) => { const {socket} = options; try { sharedIoRedis.ensureReadonlyConnections() const { group, ids } = options.payload; if (!Array.isArray(ids) || ids.length === 0) { socket.emit(options.responseEvent, { status: 'ok' }) return } // Build a lookup of current connections by id const byId = new Map() for (const conn of p3xrs.connections.list) { byId.set(conn.id, conn) } if (group !== undefined) { // Reorder within a specific group const reordered = [] for (const id of ids) { const conn = byId.get(id) if (conn) { reordered.push(conn) } } // Rebuild the full list preserving the relative position of groups const newList = [] let groupInserted = false const targetGroup = (group || '').trim() for (const conn of p3xrs.connections.list) { const connGroup = (conn.group || '').trim() if (connGroup === targetGroup) { if (!groupInserted) { newList.push(...reordered) groupInserted = true } } else { newList.push(conn) } } if (!groupInserted) { newList.push(...reordered) } p3xrs.connections.list = newList } else { // Full reorder (e.g. dragging groups) β€” ids contains all connection ids in new order const newList = [] for (const id of ids) { const conn = byId.get(id) if (conn) { newList.push(conn) } } // Append any connections not in the ids list (safety) for (const conn of p3xrs.connections.list) { if (!ids.includes(conn.id)) { newList.push(conn) } } p3xrs.connections.list = newList } p3xrs.connections.update = new Date() fs.writeFileSync(p3xrs.cfg.connections.home, JSON.stringify(p3xrs.connections, null, 4)) socket.emit(options.responseEvent, { status: 'ok' }) } catch (e) { console.error(e) socket.emit(options.responseEvent, { status: 'error', error: e.message }) } finally { sharedIoRedis.sendConnections({ socket }) } } src/service/socket.io/request/console.mjs000066400000000000000000000041561517715050700210660ustar00rootroot00000000000000import * as sharedIoRedis from '../shared.mjs' const parser = sharedIoRedis.argumentParser const disabledCommands = ['subscribe', 'monitor', 'quit', 'psubscribe'] const consolePrefix = 'socket.io console call' export default async (options) => { const {socket, payload} = options; const {command} = payload try { let redis = socket.p3xrs.ioredis const commands = parser( command); let mainCommand = commands.shift() mainCommand = mainCommand.toLowerCase(); if (disabledCommands.includes(mainCommand)) { throw new Error('invalid_console_command') } if (mainCommand !== 'select') { sharedIoRedis.ensureReadonlyConnection({ socket }) } console.info(consolePrefix, mainCommand, commands) /* if (!socket.p3xrs.commands.includes(mainCommand)) { throw new Error(`ERR Unknown command '${mainCommand}'.`) } */ let result = await redis.call(mainCommand, commands) const defaultEmit = {} let generatedCommand = mainCommand if (commands.length > 0) { generatedCommand += ' ' + commands.join(' ') } switch (mainCommand) { case 'select': defaultEmit.database = parseInt(commands[0]) break; } /* switch (generatedCommand) { case 'client list': //result = result.split(' ') break; } */ //console.warn(consolePrefix, typeof result, result) /* try { const clone = JSON.parse(JSON.stringify(result)) console.warn(consolePrefix, typeof clone, clone) } catch(e) { console.warn(e) } */ socket.emit(options.responseEvent, Object.assign(defaultEmit, { status: 'ok', result: result, generatedCommand: generatedCommand, })) } catch (e) { console.error(e) socket.emit(options.responseEvent, { status: 'error', error: e.message, }) } } src/service/socket.io/request/delete-search-keys.mjs000066400000000000000000000032521517715050700230760ustar00rootroot00000000000000import * as sharedIoRedis from '../shared.mjs' const consolePrefix = 'socket.io delete search keys' export default async (options) => { const {socket, payload} = options; try { sharedIoRedis.ensureReadonlyConnection({ socket }) let redis = socket.p3xrs.ioredis console.info(consolePrefix, payload.match) if (!payload.match || payload.match === '*') { // No search filter: use flushdb for efficiency const dbsize = await redis.dbsize() console.info(consolePrefix, 'flushdb, dbsize was', dbsize) await redis.flushdb() socket.emit(options.responseEvent, { status: 'ok', deletedCount: dbsize, }) } else { const keys = await sharedIoRedis.getStreamKeys({ redis: redis, match: payload.match, maxKeys: payload.maxKeys, }) if (keys.length === 0) { socket.emit(options.responseEvent, { status: 'ok', deletedCount: 0, }) return } const pipeline = redis.pipeline() for (let key of keys) { console.info(consolePrefix, 'delete key', key) pipeline.del(key) } await pipeline.exec() socket.emit(options.responseEvent, { status: 'ok', deletedCount: keys.length, }) } } catch (e) { console.error(e) socket.emit(options.responseEvent, { status: 'error', error: e.message, }) } } src/service/socket.io/request/delete.mjs000066400000000000000000000015111517715050700206560ustar00rootroot00000000000000import * as sharedIoRedis from '../shared.mjs' const consolePrefix = 'socket.io del key' export default async (options) => { const {socket, payload} = options; try { sharedIoRedis.ensureReadonlyConnection({ socket }) let redis = socket.p3xrs.ioredis console.info(consolePrefix, payload.key) await redis.del(payload.key) socket.emit(options.responseEvent, { status: 'ok', }) /* await sharedIoRedis.getFullInfoAndSendSocket({ redis: redis, responseEvent: options.responseEvent, socket: socket, payload: payload, }) */ } catch (e) { console.error(e) socket.emit(options.responseEvent, { status: 'error', error: e.message, }) } } src/service/socket.io/request/expire.mjs000066400000000000000000000012211517715050700207060ustar00rootroot00000000000000import * as sharedIoRedis from '../shared.mjs' const consolePrefix = 'socket.io expire' export default async (options) => { const {socket, payload} = options; try { sharedIoRedis.ensureReadonlyConnection({ socket }) let redis = socket.p3xrs.ioredis console.info(consolePrefix, payload.key, payload.ttl) await redis.expire(payload.key, parseInt(payload.ttl)) socket.emit(options.responseEvent, { status: 'ok', }) } catch (e) { console.error(e) socket.emit(options.responseEvent, { status: 'error', error: e.message, }) } } src/service/socket.io/request/key-del-tree.mjs000066400000000000000000000024011517715050700217020ustar00rootroot00000000000000import * as sharedIoRedis from '../shared.mjs' const consolePrefix = 'socket.io key del tree' export default async (options) => { const {socket, payload} = options; try { sharedIoRedis.ensureReadonlyConnection({ socket }) let redis = socket.p3xrs.ioredis const deleteTree = `${payload.key}${payload.redisTreeDivider}*`; console.info(consolePrefix, deleteTree) const keys = await sharedIoRedis.getStreamKeys({ redis: redis, match: deleteTree, maxKeys: payload.maxKeys, }) const pipelineDeleteTree = redis.pipeline() for (let key of keys) { console.info(consolePrefix, 'delete key ', key) pipelineDeleteTree.del(key) } await pipelineDeleteTree.exec(); socket.emit(options.responseEvent, { status: 'ok', }) /* await sharedIoRedis.getFullInfoAndSendSocket({ redis: redis, responseEvent: options.responseEvent, socket: socket, payload: payload, }) */ } catch (e) { console.error(e) socket.emit(options.responseEvent, { status: 'error', error: e.message, }) } } src/service/socket.io/request/key-export.mjs000066400000000000000000000112441517715050700215270ustar00rootroot00000000000000import * as sharedIoRedis from '../shared.mjs' export default async (options) => { const {socket, payload} = options try { const redis = socket.p3xrs.ioredis const keys = payload.keys if (!Array.isArray(keys) || keys.length === 0) { socket.emit(options.responseEvent, { status: 'error', error: 'No keys specified for export', }) return } // Get types for all keys const typePipeline = redis.pipeline() for (const key of keys) { typePipeline.type(key) } const typeResults = await typePipeline.exec() // Get TTLs for all keys const ttlPipeline = redis.pipeline() for (const key of keys) { ttlPipeline.pttl(key) } const ttlResults = await ttlPipeline.exec() // Read values based on type const exportedKeys = [] for (let i = 0; i < keys.length; i++) { const key = keys[i] let type = typeResults[i][1] const pttl = ttlResults[i][1] // Normalize ReJSON-RL if (type === 'ReJSON-RL') { type = 'json' } if (type === 'none') { continue } let value try { switch (type) { case 'string': { const buf = await redis.getBuffer(key) value = buf ? buf.toString('base64') : null break } case 'list': { const items = await redis.lrangeBuffer(key, 0, -1) value = items.map(item => item.toString('base64')) break } case 'set': { const members = await redis.smembersBuffer(key) value = members.map(m => m.toString('base64')) break } case 'zset': { // Returns [member, score, member, score, ...] const raw = await redis.zrangebyscoreBuffer(key, '-inf', '+inf', 'WITHSCORES') const entries = [] for (let j = 0; j < raw.length; j += 2) { entries.push({ member: raw[j].toString('base64'), score: parseFloat(raw[j + 1].toString()), }) } value = entries break } case 'hash': { const raw = await redis.hgetallBuffer(key) const entries = {} if (raw) { for (const [field, val] of Object.entries(raw)) { entries[field] = val.toString('base64') } } value = entries break } case 'stream': { const entries = await redis.xrange(key, '-', '+') value = entries.map(([id, fields]) => { const obj = { id } for (let j = 0; j < fields.length; j += 2) { obj[fields[j]] = fields[j + 1] } return obj }) break } case 'json': { const jsonStr = await redis.call('JSON.GET', key) value = jsonStr break } default: continue } } catch (e) { console.error(`key-export: failed to read key "${key}" (type=${type}):`, e.message) continue } exportedKeys.push({ key, type, value, pttl: pttl > 0 ? pttl : -1, }) } socket.emit(options.responseEvent, { status: 'ok', data: { version: 1, exportedAt: new Date().toISOString(), database: socket.p3xrs.currentDatabase || 0, keys: exportedKeys, }, }) } catch (e) { console.error(e) socket.emit(options.responseEvent, { status: 'error', error: e.message, }) } } src/service/socket.io/request/key-get-string-buffer.mjs000066400000000000000000000012421517715050700235350ustar00rootroot00000000000000const consolePrefix = 'socket.io key get string buffer' export default async (options) => { const {socket, payload} = options; try { let redis = socket.p3xrs.ioredis const key = payload.key; const buffer = await redis.getBuffer(key) const socketResult = { key: key, status: 'ok', bufferValue: buffer, }; // console.warn('socketResult', socketResult) socket.emit(options.responseEvent, socketResult) } catch (e) { console.error(e) socket.emit(options.responseEvent, { status: 'error', error: e.message, }) } } src/service/socket.io/request/key-get.mjs000066400000000000000000000235131517715050700207670ustar00rootroot00000000000000import { tryDecompress } from '../../decompress.mjs' const consolePrefix = 'socket.io key get full' export default async (options) => { const {socket, payload} = options; try { let redis = socket.p3xrs.ioredis const key = payload.key; //const type = payload.type; let type = await redis.type(key) // Normalize ReJSON-RL to json for the client. if (type === 'ReJSON-RL') { type = 'json' } // Normalize TSDB-TYPE to timeseries if (type === 'TSDB-TYPE') { type = 'timeseries' } //console.info(consolePrefix, payload, type, key) const viewPipeline = redis.pipeline() switch (type) { case 'string': //viewPipeline.get(key) viewPipeline.getBuffer(key) break; case 'list': //viewPipeline.lrange(key, 0, -1) viewPipeline.lrangeBuffer(key, 0, -1) break; case 'hash': //viewPipeline.hgetall(key) viewPipeline.hgetallBuffer(key) break; case 'set': //viewPipeline.smembers(key) viewPipeline.smembersBuffer(key) break; case 'zset': //viewPipeline.zrange(key, 0, -1, 'WITHSCORES') viewPipeline.zrangeBuffer(key, 0, -1, 'WITHSCORES') break; case 'stream': //viewPipeline.xrange(key, '-', '+') viewPipeline.xrangeBuffer(key, '-', '+') break; case 'json': viewPipeline.call('JSON.GET', key, '$') break; case 'timeseries': // TS.INFO via pipeline call viewPipeline.call('TS.INFO', key) break; } viewPipeline.ttl(key) // JSON and timeseries keys don't support OBJECT ENCODING if (type !== 'json' && type !== 'timeseries') { viewPipeline.object('encoding', key) } switch (type) { case 'stream': viewPipeline.xlen(key) break; case 'hash': viewPipeline.hlen(key) break; case 'list': viewPipeline.llen(key) break; case 'set': viewPipeline.scard(key) break; case 'zset': viewPipeline.zcard(key) break; } const viewPipelineResult = await viewPipeline.exec() // console.log(viewPipelineResult) let valueBuffer = viewPipelineResult[0][1] const ttl = viewPipelineResult[1][1] let encoding let length let pipelineIndex = 2 if (type === 'timeseries') { encoding = 'timeseries' // TS.INFO returns flat array [field, value, ...]; parse to object const tsInfo = {} if (Array.isArray(valueBuffer)) { for (let i = 0; i < valueBuffer.length; i += 2) { const field = valueBuffer[i] let value = valueBuffer[i + 1] if (field === 'labels' && Array.isArray(value)) { const labels = {} for (const pair of value) { if (Array.isArray(pair) && pair.length === 2) { labels[pair[0]] = pair[1] } } value = labels } if (field === 'rules' && Array.isArray(value)) { value = value.map(rule => Array.isArray(rule) ? { destKey: rule[0], bucketDuration: rule[1], aggregationType: rule[2] } : rule) } tsInfo[field] = value } } valueBuffer = Buffer.from(JSON.stringify(tsInfo)) length = tsInfo.totalSamples || 0 } else if (type === 'json') { encoding = 'json' // JSON.GET returns a JSON string; convert to Buffer for consistency if (typeof valueBuffer === 'string') { valueBuffer = Buffer.from(valueBuffer) } } else { encoding = viewPipelineResult[pipelineIndex][1] pipelineIndex++ } if (type !== 'string' && type !== 'json' && type !== 'timeseries') { length = viewPipelineResult[pipelineIndex][1] } // Try to decompress the value (string type: single buffer, collections: each item) let compression = null if (type === 'string' && Buffer.isBuffer(valueBuffer)) { const result = await tryDecompress(valueBuffer) if (result) { compression = { algorithm: result.algorithm, originalSize: valueBuffer.length, decompressedSize: result.decompressed.length, ratio: +((1 - valueBuffer.length / result.decompressed.length) * 100).toFixed(1), } valueBuffer = result.decompressed } } else if (type === 'list' && Array.isArray(valueBuffer)) { for (let i = 0; i < valueBuffer.length; i++) { if (Buffer.isBuffer(valueBuffer[i])) { const result = await tryDecompress(valueBuffer[i]) if (result) { if (!compression) { compression = { algorithm: result.algorithm, items: 0, originalSize: 0, decompressedSize: 0 } } compression.items++ compression.originalSize += valueBuffer[i].length compression.decompressedSize += result.decompressed.length valueBuffer[i] = result.decompressed } } } if (compression) { compression.ratio = +((1 - compression.originalSize / compression.decompressedSize) * 100).toFixed(1) } } else if (type === 'hash' && valueBuffer && typeof valueBuffer === 'object' && !Array.isArray(valueBuffer)) { for (const field of Object.keys(valueBuffer)) { if (Buffer.isBuffer(valueBuffer[field])) { const result = await tryDecompress(valueBuffer[field]) if (result) { if (!compression) { compression = { algorithm: result.algorithm, items: 0, originalSize: 0, decompressedSize: 0 } } compression.items++ compression.originalSize += valueBuffer[field].length compression.decompressedSize += result.decompressed.length valueBuffer[field] = result.decompressed } } } if (compression) { compression.ratio = +((1 - compression.originalSize / compression.decompressedSize) * 100).toFixed(1) } } else if ((type === 'set' || type === 'zset') && Array.isArray(valueBuffer)) { for (let i = 0; i < valueBuffer.length; i++) { if (Buffer.isBuffer(valueBuffer[i])) { const result = await tryDecompress(valueBuffer[i]) if (result) { if (!compression) { compression = { algorithm: result.algorithm, items: 0, originalSize: 0, decompressedSize: 0 } } compression.items++ compression.originalSize += valueBuffer[i].length compression.decompressedSize += result.decompressed.length valueBuffer[i] = result.decompressed } } } if (compression) { compression.ratio = +((1 - compression.originalSize / compression.decompressedSize) * 100).toFixed(1) } } else if (type === 'stream' && Array.isArray(valueBuffer)) { // Stream entries: [[id, [field, value, field, value]], ...] for (const entry of valueBuffer) { if (!Array.isArray(entry) || !Array.isArray(entry[1])) continue const fields = entry[1] for (let i = 0; i < fields.length; i++) { if (Buffer.isBuffer(fields[i])) { const result = await tryDecompress(fields[i]) if (result) { if (!compression) { compression = { algorithm: result.algorithm, items: 0, originalSize: 0, decompressedSize: 0 } } compression.items++ compression.originalSize += fields[i].length compression.decompressedSize += result.decompressed.length fields[i] = result.decompressed } } } } if (compression) { compression.ratio = +((1 - compression.originalSize / compression.decompressedSize) * 100).toFixed(1) } } const socketResult = { length: length, key: key, status: 'ok', type: type, valueBuffer: valueBuffer, ttl: ttl, encoding: encoding, compression: compression, }; // console.warn('socketResult', socketResult) socket.emit(options.responseEvent, socketResult) } catch (e) { console.error(e) socket.emit(options.responseEvent, { status: 'error', error: e.message, }) } } src/service/socket.io/request/key-hash-delete-field.mjs000066400000000000000000000011541517715050700234510ustar00rootroot00000000000000import * as sharedIoRedis from '../shared.mjs' const consolePrefix = 'socket.io key hash delete key' export default async (options) => { const {socket, payload} = options; const redis = socket.p3xrs.ioredis try { sharedIoRedis.ensureReadonlyConnection({ socket }) const {hashKey, key} = payload; await redis.hdel(key, hashKey) socket.emit(options.responseEvent, { status: 'ok', }) } catch (e) { console.error(e) socket.emit(options.responseEvent, { status: 'error', error: e.message }) } } src/service/socket.io/request/key-import.mjs000066400000000000000000000227561517715050700215320ustar00rootroot00000000000000import * as sharedIoRedis from '../shared.mjs' const BATCH_SIZE = 500 export default async (options) => { const {socket, payload} = options try { sharedIoRedis.ensureReadonlyConnection({ socket }) const redis = socket.p3xrs.ioredis const { keys, conflictMode } = payload // conflictMode: 'overwrite' | 'skip' if (!Array.isArray(keys) || keys.length === 0) { socket.emit(options.responseEvent, { status: 'error', error: 'No keys to import', }) return } // Check existing keys if skip mode (pipelined in batches) let existingKeys = new Set() if (conflictMode === 'skip') { for (let i = 0; i < keys.length; i += BATCH_SIZE) { const batch = keys.slice(i, i + BATCH_SIZE) const pipeline = redis.pipeline() for (const entry of batch) { pipeline.exists(entry.key) } const results = await pipeline.exec() for (let j = 0; j < batch.length; j++) { if (results[j][0]) { // exists check itself failed β€” treat as not existing continue } if (results[j][1] === 1) { existingKeys.add(batch[j].key) } } } } let created = 0 let skipped = 0 let errors = 0 // Process keys in batches for (let i = 0; i < keys.length; i += BATCH_SIZE) { const batch = keys.slice(i, i + BATCH_SIZE) // Filter out skipped keys const toImport = [] for (const entry of batch) { if (conflictMode === 'skip' && existingKeys.has(entry.key)) { skipped++ } else { toImport.push(entry) } } if (toImport.length === 0) continue // Separate pipelineable types from sequential (stream, json) const pipelineable = [] const sequential = [] for (const entry of toImport) { if (entry.type === 'stream' || entry.type === 'json') { sequential.push(entry) } else { pipelineable.push(entry) } } // --- Pipelineable keys (string, list, set, zset, hash) --- if (pipelineable.length > 0) { // Delete existing keys if overwriting if (conflictMode === 'overwrite') { const delPipeline = redis.pipeline() for (const entry of pipelineable) { delPipeline.del(entry.key) } await delPipeline.exec() } // Build write pipeline β€” track which entries map to which pipeline slot const writePipeline = redis.pipeline() const pipelineEntries = [] // entries that actually got a command for (const entry of pipelineable) { try { switch (entry.type) { case 'string': writePipeline.set(entry.key, Buffer.from(entry.value, 'base64')) pipelineEntries.push(entry) break case 'list': if (Array.isArray(entry.value) && entry.value.length > 0) { writePipeline.rpush(entry.key, ...entry.value.map(v => Buffer.from(v, 'base64'))) pipelineEntries.push(entry) } else { created++ // empty list, nothing to write } break case 'set': if (Array.isArray(entry.value) && entry.value.length > 0) { writePipeline.sadd(entry.key, ...entry.value.map(v => Buffer.from(v, 'base64'))) pipelineEntries.push(entry) } else { created++ } break case 'zset': if (Array.isArray(entry.value) && entry.value.length > 0) { const args = [] for (const e of entry.value) { args.push(e.score, Buffer.from(e.member, 'base64')) } writePipeline.zadd(entry.key, ...args) pipelineEntries.push(entry) } else { created++ } break case 'hash': if (entry.value && typeof entry.value === 'object') { const args = [] for (const [field, val] of Object.entries(entry.value)) { args.push(field, Buffer.from(val, 'base64')) } if (args.length > 0) { writePipeline.hset(entry.key, ...args) pipelineEntries.push(entry) } else { created++ } } break default: errors++ } } catch (e) { console.error(`key-import: failed to prepare key "${entry.key}":`, e.message) errors++ } } if (pipelineEntries.length > 0) { const writeResults = await writePipeline.exec() // Collect successful entries for TTL restore const successEntries = [] for (let j = 0; j < writeResults.length; j++) { if (writeResults[j][0]) { console.error(`key-import: write failed for key "${pipelineEntries[j].key}":`, writeResults[j][0].message) errors++ } else { created++ if (pipelineEntries[j].pttl && pipelineEntries[j].pttl > 0) { successEntries.push(pipelineEntries[j]) } } } // Pipeline TTL restore only for successfully written keys if (successEntries.length > 0) { const ttlPipeline = redis.pipeline() for (const entry of successEntries) { ttlPipeline.pexpire(entry.key, entry.pttl) } await ttlPipeline.exec() } } } // --- Sequential keys (stream, json) --- for (const entry of sequential) { try { if (conflictMode === 'overwrite') { await redis.del(entry.key) } if (entry.type === 'stream') { if (Array.isArray(entry.value) && entry.value.length > 0) { const streamPipeline = redis.pipeline() for (const streamEntry of entry.value) { const { id, ...fields } = streamEntry const args = [] for (const [k, v] of Object.entries(fields)) { args.push(k, v) } if (args.length > 0) { streamPipeline.xadd(entry.key, '*', ...args) } } await streamPipeline.exec() } } else if (entry.type === 'json') { if (entry.value !== null && entry.value !== undefined) { await redis.call('JSON.SET', entry.key, '$', typeof entry.value === 'string' ? entry.value : JSON.stringify(entry.value)) } } if (entry.pttl && entry.pttl > 0) { await redis.pexpire(entry.key, entry.pttl) } created++ } catch (e) { console.error(`key-import: failed to import key "${entry.key}" (type=${entry.type}):`, e.message) errors++ } } } socket.emit(options.responseEvent, { status: 'ok', data: { created, skipped, errors }, }) } catch (e) { console.error(e) socket.emit(options.responseEvent, { status: 'error', error: e.message, }) } } src/service/socket.io/request/key-json-set.mjs000066400000000000000000000013251517715050700217470ustar00rootroot00000000000000import * as sharedIoRedis from '../shared.mjs' export default async (options) => { const {socket, payload} = options; const redis = socket.p3xrs.ioredis try { sharedIoRedis.ensureReadonlyConnection({ socket }) const { key, path, value } = payload if (!key) { throw new Error('key is required') } const jsonPath = path || '$' await redis.call('JSON.SET', key, jsonPath, value) socket.emit(options.responseEvent, { status: 'ok', key: key, }) } catch (e) { console.error(e) socket.emit(options.responseEvent, { status: 'error', error: e.message }) } } src/service/socket.io/request/key-list-delete-index.mjs000066400000000000000000000014741517715050700235320ustar00rootroot00000000000000import * as sharedIoRedis from '../shared.mjs' import utils from 'corifeus-utils' const consolePrefix = 'socket.io key list delete index' export default async (options) => { const {socket, payload} = options; const redis = socket.p3xrs.ioredis try { sharedIoRedis.ensureReadonlyConnection({ socket }) const {index, key} = payload; const uniqueValue = utils.random.complexUuid() console.log(consolePrefix, key, index, uniqueValue) await redis.lset(key, index, uniqueValue) await redis.lrem(key, 1, uniqueValue) socket.emit(options.responseEvent, { status: 'ok', }) } catch (e) { console.error(e) socket.emit(options.responseEvent, { status: 'error', error: e.message }) } } src/service/socket.io/request/key-new-or-set.mjs000066400000000000000000000215431517715050700222110ustar00rootroot00000000000000import * as sharedIoRedis from '../shared.mjs' const isBinaryLike = (value) => { if (value === undefined || value === null) { return false } if (Buffer.isBuffer(value)) { return true } if (typeof ArrayBuffer !== 'undefined' && value instanceof ArrayBuffer) { return true } if (typeof ArrayBuffer !== 'undefined' && ArrayBuffer.isView && ArrayBuffer.isView(value)) { return true } return false } const consolePrefix = 'socket.io key new' export default async (options) => { const {socket, payload} = options; const redis = socket.p3xrs.ioredis try { sharedIoRedis.ensureReadonlyConnection({ socket }) const {model} = payload; model.score = model.score === null ? undefined : model.score model.index = model.index === null ? undefined : model.index model.hashKey = model.hashKey === null ? undefined : model.hashKey //console.warn(consolePrefix, payload) switch (model.type) { case 'stream': const xaddArgs = [ model.key, model.streamTimestamp, ].concat(sharedIoRedis.argumentParser(model.value)) await redis.xadd(...xaddArgs) break; case 'string': await redis.set(model.key, model.value) break; case 'list': if (model.index === undefined) { await redis.rpush(model.key, model.value) } else { if (model.index === -1) { await redis.lpush(model.key, model.value) } else { const size = await redis.llen(model.key); if (model.index > -1 && model.index < size) { await redis.lset(model.key, model.index, model.value) } else { const listOutOBoundsError = new Error('list-out-of-bounds') listOutOBoundsError.code = 'list-out-of-bounds' throw listOutOBoundsError } } } break; case 'hash': if (payload.hasOwnProperty('originalHashKey')) { await redis.hdel(model.key, payload.originalHashKey) } await redis.hset(model.key, model.hashKey, model.value) break; case 'set': if (payload.hasOwnProperty('originalValue')) { await redis.srem(model.key, payload.originalValue) } await redis.sadd(model.key, model.value) break; case 'zset': if (payload.hasOwnProperty('originalValue')) { await redis.zrem(model.key, payload.originalValue) } await redis.zadd(model.key, model.score, model.value) break; case 'timeseries': if (payload.type === 'add' || payload.type === 'append') { // For new keys, create first with options if (payload.type === 'add') { try { const createArgs = [model.key, 'DUPLICATE_POLICY', model.tsDuplicatePolicy || 'LAST'] const retention = parseInt(model.tsRetention) if (!isNaN(retention) && retention > 0) { createArgs.push('RETENTION', retention) } await redis.call('TS.CREATE', ...createArgs) } catch (e) { if (!e.message.includes('already exists')) { throw e } } // Always set labels via TS.ALTER (works for both new and existing keys) const labelStr = model.tsLabels && model.tsLabels.trim().length > 0 ? model.tsLabels.trim() : `key ${model.key}` console.info('timeseries set labels:', model.key, labelStr) await redis.call('TS.ALTER', model.key, 'LABELS', ...labelStr.split(/\s+/)) } if (model.tsBulkMode) { // Bulk mode: value is multiline "timestamp value\n..." const spread = parseInt(model.tsSpread) || 60000 const lines = model.value.split('\n').map(l => l.trim()).filter(l => l.length > 0) let autoTs = Date.now() for (const line of lines) { const parts = line.split(/\s+/) if (parts.length >= 2) { let ts = parts[0] if (ts === '*') { ts = autoTs autoTs += spread } await redis.call('TS.ADD', model.key, ts, parseFloat(parts[1]), 'ON_DUPLICATE', 'LAST') } } } else { await redis.call('TS.ADD', model.key, model.tsTimestamp || '*', parseFloat(model.value), 'ON_DUPLICATE', 'LAST') } } else if (payload.type === 'edit') { if (model.tsEditAll) { // Global edit: value is multiline "timestamp value\n..." format // Delete all existing points first, then re-add from the edited text const tsInfo = await redis.call('TS.INFO', model.key) let firstTs = 0, lastTs = 0 for (let i = 0; i < tsInfo.length; i += 2) { if (tsInfo[i] === 'firstTimestamp') firstTs = tsInfo[i + 1] if (tsInfo[i] === 'lastTimestamp') lastTs = tsInfo[i + 1] } if (firstTs !== 0 || lastTs !== 0) { await redis.call('TS.DEL', model.key, firstTs, lastTs) } // Parse and re-add each line // For * timestamps, space them by the selected spread interval const spread = parseInt(model.tsSpread) || 60000 const lines = model.value.split('\n').map(l => l.trim()).filter(l => l.length > 0) let autoTs = Date.now() for (const line of lines) { const parts = line.split(/\s+/) if (parts.length >= 2) { let ts = parts[0] if (ts === '*') { ts = autoTs autoTs += spread } await redis.call('TS.ADD', model.key, ts, parseFloat(parts[1]), 'ON_DUPLICATE', 'LAST') } } } else { // Single point edit: delete original, add new if (model.originalTimestamp !== undefined) { await redis.call('TS.DEL', model.key, model.originalTimestamp, model.originalTimestamp) } await redis.call('TS.ADD', model.key, model.tsTimestamp || '*', parseFloat(model.value), 'ON_DUPLICATE', 'LAST') } // Update labels if provided if (model.tsLabels && model.tsLabels.trim().length > 0) { await redis.call('TS.ALTER', model.key, 'LABELS', ...model.tsLabels.trim().split(/\s+/)) } } break; case 'json': // Validate JSON before sending to Redis try { JSON.parse(model.value) } catch (e) { throw new Error('invalid-json-value') } await redis.call('JSON.SET', model.key, '$', model.value) break; } socket.emit(options.responseEvent, { status: 'ok', key: model.key, }) /* await sharedIoRedis.getFullInfoAndSendSocket({ redis: redis, responseEvent: options.responseEvent, socket: socket, extend: { key: model.key }, payload: payload, }) */ } catch (e) { console.error(e) socket.emit(options.responseEvent, { status: 'error', error: e.message }) } } src/service/socket.io/request/key-set-delete-member.mjs000066400000000000000000000011601517715050700235020ustar00rootroot00000000000000import * as sharedIoRedis from '../shared.mjs' const consolePrefix = 'socket.io key list delete index' export default async (options) => { const {socket, payload} = options; const redis = socket.p3xrs.ioredis try { sharedIoRedis.ensureReadonlyConnection({ socket }) const {key, value} = payload; await redis.sremBuffer(key, value) socket.emit(options.responseEvent, { status: 'ok', }) } catch (e) { console.error(e) socket.emit(options.responseEvent, { status: 'error', error: e.message }) } } src/service/socket.io/request/key-set.mjs000066400000000000000000000021001517715050700207700ustar00rootroot00000000000000import * as sharedIoRedis from '../shared.mjs' const isBinaryLike = (value) => { if (value === undefined || value === null) { return false } if (Buffer.isBuffer(value)) { return true } if (typeof ArrayBuffer !== 'undefined' && value instanceof ArrayBuffer) { return true } if (typeof ArrayBuffer !== 'undefined' && ArrayBuffer.isView && ArrayBuffer.isView(value)) { return true } return false } export default async (options) => { const {socket, payload} = options; const redis = socket.p3xrs.ioredis try { sharedIoRedis.ensureReadonlyConnection({ socket }) const ttl = await redis.ttl(payload.key) await redis.set(payload.key, payload.value) if (ttl !== -1) { await redis.expire(payload.key, ttl) } socket.emit(options.responseEvent, { status: 'ok', }) } catch (e) { console.error(e) socket.emit(options.responseEvent, { status: 'error', error: e.message }) } } src/service/socket.io/request/key-stream-delete-timestamp.mjs000066400000000000000000000012031517715050700247340ustar00rootroot00000000000000import * as sharedIoRedis from '../shared.mjs' const consolePrefix = 'socket.io stream delete timestamp id' export default async (options) => { const {socket, payload} = options; const redis = socket.p3xrs.ioredis try { sharedIoRedis.ensureReadonlyConnection({ socket }) const {key, streamTimestamp} = payload; await redis.xdel(key, streamTimestamp) socket.emit(options.responseEvent, { status: 'ok', }) } catch (e) { console.error(e) socket.emit(options.responseEvent, { status: 'error', error: e.message }) } } src/service/socket.io/request/key-zset-delete-member.mjs000066400000000000000000000011611517715050700236750ustar00rootroot00000000000000import * as sharedIoRedis from '../shared.mjs' const consolePrefix = 'socket.io key zsit delete member' export default async (options) => { const {socket, payload} = options; const redis = socket.p3xrs.ioredis try { sharedIoRedis.ensureReadonlyConnection({ socket }) const {key, value} = payload; await redis.zremBuffer(key, value) socket.emit(options.responseEvent, { status: 'ok', }) } catch (e) { console.error(e) socket.emit(options.responseEvent, { status: 'error', error: e.message }) } } src/service/socket.io/request/memory-analysis.mjs000066400000000000000000000127731517715050700225610ustar00rootroot00000000000000export default async (options) => { const {socket, payload} = options try { const redis = socket.p3xrs.ioredis if (!redis) { socket.emit(options.responseEvent, { status: 'error', error: 'Not connected to Redis' }) return } const maxScanKeys = payload.maxScanKeys || 5000 const topN = payload.topN || 20 const BATCH = 500 // Get total key count const dbSize = await redis.dbsize() // Scan keys let cursor = '0' const keys = [] do { const [nextCursor, batch] = await redis.scan(cursor, 'COUNT', 500) cursor = nextCursor keys.push(...batch) if (keys.length >= maxScanKeys) break } while (cursor !== '0') // Pipeline: TYPE + MEMORY USAGE + TTL for each key const typeDistribution = {} const typeMemory = {} const prefixBuckets = {} const allKeys = [] let withTTL = 0 let persistent = 0 let ttlSum = 0 for (let i = 0; i < keys.length; i += BATCH) { const batch = keys.slice(i, i + BATCH) const pipeline = redis.pipeline() for (const key of batch) { pipeline.type(key) pipeline.call('MEMORY', 'USAGE', key) pipeline.ttl(key) } const results = await pipeline.exec() for (let j = 0; j < batch.length; j++) { const key = batch[j] const typeErr = results[j * 3][0] const type = results[j * 3][1] || 'unknown' const memErr = results[j * 3 + 1][0] const bytes = results[j * 3 + 1][1] const ttlErr = results[j * 3 + 2][0] const ttl = results[j * 3 + 2][1] const keyType = !typeErr ? type : 'unknown' const keyBytes = (!memErr && typeof bytes === 'number') ? bytes : 0 // Type distribution typeDistribution[keyType] = (typeDistribution[keyType] || 0) + 1 typeMemory[keyType] = (typeMemory[keyType] || 0) + keyBytes // Prefix buckets (split by first : delimiter) const colonIdx = key.indexOf(':') const prefix = colonIdx > 0 ? key.substring(0, colonIdx + 1) : '(no prefix)' if (!prefixBuckets[prefix]) { prefixBuckets[prefix] = { keyCount: 0, totalBytes: 0 } } prefixBuckets[prefix].keyCount++ prefixBuckets[prefix].totalBytes += keyBytes // TTL if (!ttlErr && typeof ttl === 'number') { if (ttl >= 0) { withTTL++ ttlSum += ttl } else { persistent++ } } else { persistent++ } // Top keys if (keyBytes > 0) { allKeys.push({ key, bytes: keyBytes, type: keyType }) } } } // Sort top keys allKeys.sort((a, b) => b.bytes - a.bytes) const topKeys = allKeys.slice(0, topN) // Sort prefix buckets by memory const prefixMemory = Object.entries(prefixBuckets) .map(([prefix, data]) => ({ prefix, ...data })) .sort((a, b) => b.totalBytes - a.totalBytes) .slice(0, 50) // INFO server const serverInfoRaw = await redis.info('server') const serverInfo = {} const serverFields = { redis_version: 'version', redis_mode: 'mode', uptime_in_seconds: 'uptime', } for (const line of serverInfoRaw.split('\r\n')) { const [k, v] = line.split(':') if (k && serverFields[k]) { serverInfo[serverFields[k]] = k === 'uptime_in_seconds' ? (parseInt(v) || 0) : (v || 'unknown') } } if (!serverInfo.mode) serverInfo.mode = 'standalone' // INFO memory const memoryInfoRaw = await redis.info('memory') const memoryInfo = {} const fields = { used_memory: 'used', used_memory_human: 'usedHuman', used_memory_rss: 'rss', used_memory_rss_human: 'rssHuman', used_memory_peak: 'peak', used_memory_peak_human: 'peakHuman', used_memory_lua: 'lua', used_memory_overhead: 'overhead', used_memory_dataset: 'dataset', mem_fragmentation_ratio: 'fragRatio', mem_allocator: 'allocator', } for (const line of memoryInfoRaw.split('\r\n')) { const [k, v] = line.split(':') if (k && fields[k]) { memoryInfo[fields[k]] = isNaN(v) ? v : Number(v) } } socket.emit(options.responseEvent, { status: 'ok', data: { totalScanned: keys.length, dbSize, typeDistribution, typeMemory, prefixMemory, topKeys, expirationOverview: { withTTL, persistent, avgTTL: withTTL > 0 ? Math.round(ttlSum / withTTL) : 0, }, memoryInfo, serverInfo, }, }) } catch (e) { console.error(e) socket.emit(options.responseEvent, { status: 'error', error: e.message, }) } } src/service/socket.io/request/memory-top-keys.mjs000066400000000000000000000032171517715050700225020ustar00rootroot00000000000000export default async (options) => { const {socket, payload} = options try { const redis = socket.p3xrs.ioredis const maxKeys = payload.maxKeys || 100 const topN = payload.topN || 20 // Scan keys let cursor = '0' const keys = [] do { const [nextCursor, batch] = await redis.scan(cursor, 'COUNT', 200) cursor = nextCursor keys.push(...batch) if (keys.length >= maxKeys) break } while (cursor !== '0') // Get memory usage for each key in pipeline batches const BATCH = 500 const results = [] for (let i = 0; i < keys.length; i += BATCH) { const batch = keys.slice(i, i + BATCH) const pipeline = redis.pipeline() for (const key of batch) { pipeline.call('MEMORY', 'USAGE', key) } const pipeResults = await pipeline.exec() for (let j = 0; j < batch.length; j++) { const err = pipeResults[j][0] const bytes = pipeResults[j][1] if (!err && typeof bytes === 'number') { results.push({ key: batch[j], bytes }) } } } // Sort by size descending and take top N results.sort((a, b) => b.bytes - a.bytes) const topKeys = results.slice(0, topN) socket.emit(options.responseEvent, { status: 'ok', data: topKeys, }) } catch (e) { console.error(e) socket.emit(options.responseEvent, { status: 'error', error: e.message, }) } } src/service/socket.io/request/monitor-info.mjs000066400000000000000000000074471517715050700220520ustar00rootroot00000000000000export default async (options) => { const {socket} = options try { const redis = socket.p3xrs.ioredis const [infoRaw, slowlog] = await Promise.all([ redis.info(), redis.slowlog('GET', 10), ]) // Parse INFO into sections const info = {} let currentSection = '' for (const line of infoRaw.split('\n')) { const trimmed = line.trim() if (!trimmed || trimmed.startsWith('#')) { if (trimmed.startsWith('# ')) { currentSection = trimmed.slice(2).toLowerCase() info[currentSection] = {} } continue } const colonIdx = trimmed.indexOf(':') if (colonIdx > 0) { const key = trimmed.slice(0, colonIdx) const value = trimmed.slice(colonIdx + 1) if (currentSection) { info[currentSection][key] = value } } } // Extract key metrics const memory = info.memory || {} const stats = info.stats || {} const clients = info.clients || {} const server = info.server || {} const keyspace = info.keyspace || {} // Parse keyspace hit/miss const hits = parseInt(stats.keyspace_hits) || 0 const misses = parseInt(stats.keyspace_misses) || 0 const hitRate = hits + misses > 0 ? ((hits / (hits + misses)) * 100).toFixed(1) : '0.0' const data = { timestamp: Date.now(), server: { version: server.redis_version, uptime: parseInt(server.uptime_in_seconds) || 0, mode: server.redis_mode || 'standalone', }, memory: { used: parseInt(memory.used_memory) || 0, usedHuman: memory.used_memory_human || '0B', rss: parseInt(memory.used_memory_rss) || 0, rssHuman: memory.used_memory_rss_human || '0B', peak: parseInt(memory.used_memory_peak) || 0, peakHuman: memory.used_memory_peak_human || '0B', fragRatio: parseFloat(memory.mem_fragmentation_ratio) || 0, }, stats: { opsPerSec: parseInt(stats.instantaneous_ops_per_sec) || 0, totalCommands: parseInt(stats.total_commands_processed) || 0, hits, misses, hitRate: parseFloat(hitRate), inputKbps: parseFloat(stats.instantaneous_input_kbps) || 0, outputKbps: parseFloat(stats.instantaneous_output_kbps) || 0, totalNetInput: parseInt(stats.total_net_input_bytes) || 0, totalNetOutput: parseInt(stats.total_net_output_bytes) || 0, expiredKeys: parseInt(stats.expired_keys) || 0, evictedKeys: parseInt(stats.evicted_keys) || 0, }, clients: { connected: parseInt(clients.connected_clients) || 0, blocked: parseInt(clients.blocked_clients) || 0, maxInput: parseInt(clients.client_recent_max_input_buffer) || 0, maxOutput: parseInt(clients.client_recent_max_output_buffer) || 0, }, keyspace, slowlog: slowlog.map(entry => ({ id: entry[0], timestamp: entry[1], duration: entry[2], command: Array.isArray(entry[3]) ? entry[3].join(' ') : String(entry[3]), })), } socket.emit(options.responseEvent, { status: 'ok', data, }) } catch (e) { console.error(e) socket.emit(options.responseEvent, { status: 'error', error: e.message, }) } } src/service/socket.io/request/persist.mjs000066400000000000000000000010721517715050700211070ustar00rootroot00000000000000import * as sharedIoRedis from '../shared.mjs' const consolePrefix = 'socket.io persists' export default async (options) => { const {socket, payload} = options; try { sharedIoRedis.ensureReadonlyConnection({ socket }) let redis = socket.p3xrs.ioredis await redis.persist(payload.key) socket.emit(options.responseEvent, { status: 'ok', }) } catch (e) { console.error(e) socket.emit(options.responseEvent, { status: 'error', error: e.message, }) } } src/service/socket.io/request/redis-test-connection.mjs000066400000000000000000000203621517715050700236410ustar00rootroot00000000000000import Redis from '../../../lib/ioredis-cluster/index.mjs' export default async (options) => { const {socket} = options; try { let redisConfig = options.payload.model; const actualConnection = p3xrs.connections.list.find(con => redisConfig.id === con.id) if (actualConnection !== undefined) { if (redisConfig.password === actualConnection.id) { redisConfig.password = actualConnection.password; } if (redisConfig.tlsCrt === actualConnection.id) { redisConfig.tlsCrt = actualConnection.tlsCrt; } if (redisConfig.tlsKey === actualConnection.id) { redisConfig.tlsKey = actualConnection.tlsKey; } if (redisConfig.tlsCa === actualConnection.id) { redisConfig.tlsCa = actualConnection.tlsCa; } if (redisConfig.sshPassword === actualConnection.id) { redisConfig.sshPassword = actualConnection.sshPassword; } if (redisConfig.sshPrivateKey === actualConnection.id) { redisConfig.sshPrivateKey = actualConnection.sshPrivateKey; } } const sentinelName = redisConfig.sentinelName //TODO fix secured nodes password delete redisConfig.name delete redisConfig.id if (redisConfig.tlsWithoutCert) { redisConfig.tls = { servername: redisConfig.host } } else if (typeof redisConfig.tlsCa === 'string' && redisConfig.tlsCa.trim() !== '') { redisConfig.tls = { //rejectUnauthorized: false, cert: redisConfig.tlsCrt, key: redisConfig.tlsKey, ca: redisConfig.tlsCa, servername: redisConfig.host } } if (redisConfig.hasOwnProperty('tls')) { redisConfig.tls.rejectUnauthorized = redisConfig.tlsRejectUnauthorized === undefined ? false : redisConfig.tlsRejectUnauthorized // Ensure SNI is always set to the host if (!redisConfig.tls.hasOwnProperty('servername')) { redisConfig.tls.servername = redisConfig.host } } // Fix node passwords if (Array.isArray(redisConfig.nodes)) { redisConfig.nodes = redisConfig.nodes.map((node) => { if (actualConnection !== undefined && node.password === node.id) { const foundNode = actualConnection.nodes.find((findNode) => findNode.id === node.password) if (foundNode) { node.password = foundNode.password } } return node }) } // SSH tunnel creation - single SSH connection, multiple port forwards let sshTunnelServers = [] let sshClient = undefined let redis = undefined let settled = false let didReady = false let lastRedisError = undefined let timeout = undefined const closeSshTunnels = () => { for (const server of sshTunnelServers) { server.close() } sshTunnelServers = [] if (sshClient) { sshClient.end() sshClient = undefined } } const settle = (payload) => { if (settled) { return } settled = true if (timeout) { clearTimeout(timeout) timeout = undefined } socket.emit(options.responseEvent, payload) if (redis) { redis.disconnect() redis = undefined } closeSshTunnels() } if (redisConfig.ssh === true) { const { createTunnel } = await import('tunnel-ssh') const net = await import('net') const sshOptions = { host: redisConfig.sshHost, port: redisConfig.sshPort, username: redisConfig.sshUsername, }; if (redisConfig.sshPrivateKey) { sshOptions.privateKey = redisConfig.sshPrivateKey } else { sshOptions.password = redisConfig.sshPassword } // Create primary tunnel (establishes the single SSH connection) let [primaryServer, sshConn] = await createTunnel({ autoClose: false }, null, sshOptions, { dstAddr: redisConfig.host, dstPort: redisConfig.port, }); sshTunnelServers.push(primaryServer) sshClient = sshConn redisConfig.port = primaryServer.address().port // Create port forwards for additional nodes through the same SSH connection if (Array.isArray(redisConfig.nodes)) { for (const node of redisConfig.nodes) { const nodeServer = await new Promise((resolve, reject) => { const server = net.createServer((sock) => { sshClient.forwardOut('127.0.0.1', 0, node.host || 'localhost', node.port, (err, channel) => { if (err) { sock.end() return } sock.pipe(channel).pipe(sock) }) }) server.listen(0, '127.0.0.1', () => resolve(server)) server.on('error', reject) }) sshTunnelServers.push(nodeServer) node.port = nodeServer.address().port } } // Error handlers sshClient.on('error', (e)=>{ console.error('ssh client error', e); settle({ status: 'error', error: e.message }) }); for (const server of sshTunnelServers) { server.on('error', (e)=>{ console.error('ssh tunnel server error', e); settle({ status: 'error', error: e.message }) }); } } // Cluster/sentinel conversion if (redisConfig.hasOwnProperty('sentinel') && redisConfig.sentinel === true) { redisConfig = [redisConfig].concat(redisConfig.nodes || []) } else if (redisConfig.cluster === true) { redisConfig = [redisConfig].concat(redisConfig.nodes || []) } if (Array.isArray(redisConfig) && redisConfig[0].hasOwnProperty('sentinel') && redisConfig[0].sentinel === true) { redisConfig = { sentinels: redisConfig, name: sentinelName, sentinelPassword: redisConfig[0].password, sentinelRetryStrategy: () => { return false } } } redis = new Redis(redisConfig) //console.info('redis-test-connection', redisConfig) redis.on('error', function (error) { lastRedisError = error console.error(error) }) redis.on('ready', function () { didReady = true settle({ status: 'ok', }) }) redis.on('close', function () { if (!didReady) { settle({ status: 'error', error: lastRedisError?.message || 'Connection is closed.' }) } }) redis.on('end', function () { if (!didReady) { settle({ status: 'error', error: lastRedisError?.message || 'Connection is closed.' }) } }) timeout = setTimeout(() => { settle({ status: 'error', error: lastRedisError?.message || 'No response from server' }) }, 30000) } catch (e) { console.error(e) socket.emit(options.responseEvent, { status: 'error', error: e.message }) } } src/service/socket.io/request/refresh.mjs000066400000000000000000000011221517715050700210500ustar00rootroot00000000000000import * as sharedIoRedis from '../shared.mjs' //const consolePrefix = 'socket.io refresh redis' export default async (options) => { const {socket, payload} = options; const redis = socket.p3xrs.ioredis try { await sharedIoRedis.getFullInfoAndSendSocket({ redis: redis, responseEvent: options.responseEvent, socket: socket, payload: payload, }) } catch (e) { console.error(e) socket.emit(options.responseEvent, { status: 'error', error: e.message }) } } src/service/socket.io/request/rename.mjs000066400000000000000000000015411517715050700206660ustar00rootroot00000000000000import * as sharedIoRedis from '../shared.mjs' const consolePrefix = 'socket.io rename key' export default async (options) => { const {socket, payload} = options; try { sharedIoRedis.ensureReadonlyConnection({ socket }) let redis = socket.p3xrs.ioredis console.info(consolePrefix, payload.key) await redis.rename(payload.key, payload.keyNew) /* await sharedIoRedis.getFullInfoAndSendSocket({ redis: redis, responseEvent: options.responseEvent, socket: socket, payload: payload, }) */ socket.emit(options.responseEvent, { status: 'ok', }) } catch (e) { console.error(e) socket.emit(options.responseEvent, { status: 'error', error: e.message, }) } } src/service/socket.io/request/save.mjs000066400000000000000000000010341517715050700203520ustar00rootroot00000000000000import * as sharedIoRedis from '../shared.mjs' export default async (options) => { const {socket} = options; const redis = socket.p3xrs.ioredis try { sharedIoRedis.ensureReadonlyConnection({ socket }) await redis.save() socket.emit(options.responseEvent, { status: 'ok', info: await redis.info(), }) } catch (e) { console.error(e) socket.emit(options.responseEvent, { status: 'error', error: e.message }) } } src/service/socket.io/request/search-index-create.mjs000066400000000000000000000023221517715050700232300ustar00rootroot00000000000000import * as sharedIoRedis from '../shared.mjs' export default async (options) => { const {socket, payload} = options try { sharedIoRedis.ensureReadonlyConnection({ socket }) const redis = socket.p3xrs.ioredis const { name, prefix, schema } = payload if (!name || !schema || !Array.isArray(schema) || schema.length === 0) { socket.emit(options.responseEvent, { status: 'error', error: 'Index name and schema are required', }) return } // Build FT.CREATE command const args = [name, 'ON', 'HASH'] if (prefix) { args.push('PREFIX', '1', prefix) } args.push('SCHEMA') for (const field of schema) { args.push(field.name, field.type) if (field.sortable) args.push('SORTABLE') if (field.noindex) args.push('NOINDEX') } await redis.call('FT.CREATE', ...args) socket.emit(options.responseEvent, { status: 'ok', }) } catch (e) { console.error(e) socket.emit(options.responseEvent, { status: 'error', error: e.message, }) } } src/service/socket.io/request/search-index-drop.mjs000066400000000000000000000010351517715050700227310ustar00rootroot00000000000000import * as sharedIoRedis from '../shared.mjs' export default async (options) => { const {socket, payload} = options try { sharedIoRedis.ensureReadonlyConnection({ socket }) const redis = socket.p3xrs.ioredis await redis.call('FT.DROPINDEX', payload.index) socket.emit(options.responseEvent, { status: 'ok', }) } catch (e) { console.error(e) socket.emit(options.responseEvent, { status: 'error', error: e.message, }) } } src/service/socket.io/request/search-index-info.mjs000066400000000000000000000013141517715050700227200ustar00rootroot00000000000000export default async (options) => { const {socket, payload} = options try { const redis = socket.p3xrs.ioredis const result = await redis.call('FT.INFO', payload.index) // Parse alternating key-value array into object const info = {} for (let i = 0; i < result.length; i += 2) { const key = result[i] const value = result[i + 1] info[key] = value } socket.emit(options.responseEvent, { status: 'ok', data: info, }) } catch (e) { console.error(e) socket.emit(options.responseEvent, { status: 'error', error: e.message, }) } } src/service/socket.io/request/search-list.mjs000066400000000000000000000007361517715050700216420ustar00rootroot00000000000000export default async (options) => { const {socket} = options try { const redis = socket.p3xrs.ioredis const indexes = await redis.call('FT._LIST') socket.emit(options.responseEvent, { status: 'ok', data: Array.isArray(indexes) ? indexes : [], }) } catch (e) { console.error(e) socket.emit(options.responseEvent, { status: 'error', error: e.message, }) } } src/service/socket.io/request/search-query.mjs000066400000000000000000000025121517715050700220260ustar00rootroot00000000000000export default async (options) => { const {socket, payload} = options try { const redis = socket.p3xrs.ioredis const { index, query, offset, limit } = payload if (!index || !query) { socket.emit(options.responseEvent, { status: 'error', error: 'Index and query are required', }) return } const args = [index, query, 'LIMIT', offset || 0, limit || 20] const result = await redis.call('FT.SEARCH', ...args) // Parse FT.SEARCH result: [totalCount, key1, [field, value, ...], key2, [...], ...] const total = result[0] const docs = [] for (let i = 1; i < result.length; i += 2) { const key = result[i] const fields = result[i + 1] const doc = { _key: key } if (Array.isArray(fields)) { for (let j = 0; j < fields.length; j += 2) { doc[fields[j]] = fields[j + 1] } } docs.push(doc) } socket.emit(options.responseEvent, { status: 'ok', data: { total, docs }, }) } catch (e) { console.error(e) socket.emit(options.responseEvent, { status: 'error', error: e.message, }) } } src/service/socket.io/request/set-groq-api-key.mjs000066400000000000000000000031341517715050700225150ustar00rootroot00000000000000import fs from 'fs' export default async (options) => { const { socket, payload } = options try { if (p3xrs.cfg.groqApiKeyReadonly === true) { throw new Error('GROQ_API_KEY_READONLY') } const apiKey = (payload.apiKey || '').trim() const aiEnabled = payload.aiEnabled !== false const aiUseOwnKey = payload.aiUseOwnKey === true // Update runtime config p3xrs.cfg.groqApiKey = apiKey || undefined p3xrs.cfg.aiEnabled = aiEnabled p3xrs.cfg.aiUseOwnKey = aiUseOwnKey // Persist to p3xrs.json if (p3xrs.configPath) { try { const raw = fs.readFileSync(p3xrs.configPath, 'utf8') const config = JSON.parse(raw) if (!config.p3xrs || typeof config.p3xrs !== 'object') { config.p3xrs = {} } if (apiKey) { config.p3xrs.groqApiKey = apiKey } else { delete config.p3xrs.groqApiKey } config.p3xrs.aiEnabled = aiEnabled config.p3xrs.aiUseOwnKey = aiUseOwnKey fs.writeFileSync(p3xrs.configPath, JSON.stringify(config, null, 4)) } catch (e) { console.error('failed to persist AI settings', e.message) } } socket.emit(options.responseEvent, { status: 'ok', }) } catch (e) { console.error(e) socket.emit(options.responseEvent, { status: 'error', error: e.message, }) } } src/service/socket.io/request/set-language.mjs000066400000000000000000000007551517715050700220010ustar00rootroot00000000000000export default async (options) => { const { socket, payload } = options; try { if (global.p3xre) { global.p3xre.setLanguage({ key: payload.key }) } socket.emit(options.responseEvent, { status: 'ok', key: payload.key, }) } catch (e) { console.error(e) socket.emit(options.responseEvent, { status: 'error', error: e.message }) } } src/service/socket.io/request/set-monitor.mjs000066400000000000000000000037111517715050700217000ustar00rootroot00000000000000export default async (options) => { const { socket, payload } = options; try { if (!socket.p3xrs.ioredis) { socket.emit(options.responseEvent, { status: 'error', error: 'Not connected to Redis' }) return } if (payload.enabled) { // Start MONITOR - need a dedicated connection since MONITOR blocks if (!socket.p3xrs.ioredisMonitor) { const redis = socket.p3xrs.ioredis // retry up to 3 times - redis.monitor() can fail with // "Command queue state error" if other commands are in flight let monitor for (let attempt = 0; attempt < 3; attempt++) { try { monitor = await redis.monitor() break } catch (e) { if (attempt === 2) throw e await new Promise(r => setTimeout(r, 200)) } } socket.p3xrs.ioredisMonitor = monitor socket.p3xrs.ioredisMonitor.on('monitor', (time, args, source, database) => { socket.emit('monitor-data', { time: time, args: args, source: source, database: database, }) }) console.info('MONITOR started for', socket.id) } } else { // Stop MONITOR if (socket.p3xrs.ioredisMonitor) { socket.p3xrs.ioredisMonitor.disconnect() socket.p3xrs.ioredisMonitor = undefined console.info('MONITOR stopped for', socket.id) } } socket.emit(options.responseEvent, { status: 'ok' }) } catch (e) { console.error('Monitor error:', e) socket.emit(options.responseEvent, { status: 'error', error: e.message }) } } src/service/socket.io/request/set-subscription.mjs000066400000000000000000000041661517715050700227420ustar00rootroot00000000000000export default async (options) => { const { socket, payload } = options; try { if (!socket.p3xrs.ioredisSubscriber) { socket.emit(options.responseEvent, { status: 'error', error: 'Not connected to Redis' }) return } //sharedIoRedis.ensureReadonlyConnection({ socket }) //console.log('Unsubscribing from all patterns'); await socket.p3xrs.ioredisSubscriber.punsubscribe(); //console.log('All patterns unsubscribed'); socket.p3xrs.ioredisSubscriber.removeAllListeners('pmessage'); socket.p3xrs.ioredisSubscriber.removeAllListeners('pmessageBuffer'); //console.log('Removed all pmessage listeners'); // Updating subscription settings socket.p3xrs.subscription = payload.subscription; if (typeof payload.subscriberPattern !== 'string' || payload.subscriberPattern.trim().length === 0) { payload.subscriberPattern = '*'; // Default pattern } if (socket.p3xrs.subscription === true) { // Subscribe to the pattern //console.log('socket.p3xrs.ioredisSubscriber.psubscribe', payload.subscriberPattern) await socket.p3xrs.ioredisSubscriber.psubscribe(payload.subscriberPattern); // Use pmessageBuffer to preserve raw binary data (e.g. msgpack from socket.io-adapter) // Socket.IO will transmit the Buffer as binary, frontend handles decoding socket.p3xrs.ioredisSubscriber.on('pmessageBuffer', (pattern, channel, message) => { const channelStr = channel.toString('utf-8'); console.log('socket.p3xrs.ioredisSubscriber.on(pmessageBuffer)', pattern.toString('utf-8'), channelStr) socket.emit('pubsub-message', { channel: channelStr, message: message, }); }); } // Confirm successful setup socket.emit(options.responseEvent, { status: 'ok' }); } catch (e) { console.error('Subscription error:', e); socket.emit(options.responseEvent, { status: 'error', error: e.message }); } }; src/service/socket.io/request/timeseries-add.mjs000066400000000000000000000015141517715050700223160ustar00rootroot00000000000000import * as sharedIoRedis from '../shared.mjs' const consolePrefix = 'socket.io timeseries add' export default async (options) => { const {socket, payload} = options; try { sharedIoRedis.ensureReadonlyConnection({ socket }) const redis = socket.p3xrs.ioredis const key = payload.key const timestamp = payload.timestamp || '*' const value = payload.value console.info(consolePrefix, key, timestamp, value) const result = await redis.call('TS.ADD', key, timestamp, value, 'ON_DUPLICATE', 'LAST') socket.emit(options.responseEvent, { status: 'ok', timestamp: result, }) } catch (e) { console.error(e) socket.emit(options.responseEvent, { status: 'error', error: e.message, }) } } src/service/socket.io/request/timeseries-alter.mjs000066400000000000000000000022111517715050700226700ustar00rootroot00000000000000import * as sharedIoRedis from '../shared.mjs' const consolePrefix = 'socket.io timeseries alter' export default async (options) => { const {socket, payload} = options; try { sharedIoRedis.ensureReadonlyConnection({ socket }) const redis = socket.p3xrs.ioredis const key = payload.key const args = [key] if (payload.retention !== undefined) { args.push('RETENTION', parseInt(payload.retention)) } if (payload.duplicatePolicy) { args.push('DUPLICATE_POLICY', payload.duplicatePolicy) } if (payload.labels !== undefined) { args.push('LABELS') if (payload.labels && payload.labels.trim().length > 0) { args.push(...payload.labels.trim().split(/\s+/)) } } console.info(consolePrefix, args) await redis.call('TS.ALTER', ...args) socket.emit(options.responseEvent, { status: 'ok', }) } catch (e) { console.error(e) socket.emit(options.responseEvent, { status: 'error', error: e.message, }) } } src/service/socket.io/request/timeseries-del.mjs000066400000000000000000000014151517715050700223320ustar00rootroot00000000000000import * as sharedIoRedis from '../shared.mjs' const consolePrefix = 'socket.io timeseries del' export default async (options) => { const {socket, payload} = options; try { sharedIoRedis.ensureReadonlyConnection({ socket }) const redis = socket.p3xrs.ioredis const key = payload.key const from = payload.from const to = payload.to console.info(consolePrefix, key, from, to) const deleted = await redis.call('TS.DEL', key, from, to) socket.emit(options.responseEvent, { status: 'ok', deleted: deleted, }) } catch (e) { console.error(e) socket.emit(options.responseEvent, { status: 'error', error: e.message, }) } } src/service/socket.io/request/timeseries-info.mjs000066400000000000000000000033521517715050700225230ustar00rootroot00000000000000const consolePrefix = 'socket.io timeseries info' export default async (options) => { const {socket, payload} = options; try { const redis = socket.p3xrs.ioredis const key = payload.key console.info(consolePrefix, key) const raw = await redis.call('TS.INFO', key) // TS.INFO returns flat array: [field, value, field, value, ...] const info = {} for (let i = 0; i < raw.length; i += 2) { const field = raw[i] let value = raw[i + 1] // Parse nested label pairs [[key, value], ...] if (field === 'labels' && Array.isArray(value)) { const labels = {} for (const pair of value) { if (Array.isArray(pair) && pair.length === 2) { labels[pair[0]] = pair[1] } } value = labels } // Parse rules [[destKey, bucketDuration, aggregationType], ...] if (field === 'rules' && Array.isArray(value)) { value = value.map(rule => { if (Array.isArray(rule)) { return { destKey: rule[0], bucketDuration: rule[1], aggregationType: rule[2], } } return rule }) } info[field] = value } socket.emit(options.responseEvent, { status: 'ok', data: info, }) } catch (e) { console.error(e) socket.emit(options.responseEvent, { status: 'error', error: e.message, }) } } src/service/socket.io/request/timeseries-mrange.mjs000066400000000000000000000036021517715050700230370ustar00rootroot00000000000000const consolePrefix = 'socket.io timeseries mrange' export default async (options) => { const {socket, payload} = options; try { const redis = socket.p3xrs.ioredis const from = payload.from || '-' const to = payload.to || '+' const filter = payload.filter if (!filter) { socket.emit(options.responseEvent, { status: 'ok', data: [], }) return } const args = [from, to, 'FILTER', filter] if (payload.aggregation && payload.aggregation.type && payload.aggregation.timeBucket) { args.push('AGGREGATION', payload.aggregation.type, payload.aggregation.timeBucket) } if (payload.count) { args.push('COUNT', payload.count) } console.info(consolePrefix, args) const raw = await redis.call('TS.MRANGE', ...args) // raw is [[key, labels, [[ts, val], ...]], ...] const data = raw.map(entry => { const key = entry[0] const labels = {} if (Array.isArray(entry[1])) { for (const pair of entry[1]) { if (Array.isArray(pair) && pair.length === 2) { labels[pair[0]] = pair[1] } } } const points = (entry[2] || []).map(point => ({ timestamp: typeof point[0] === 'number' ? point[0] : parseInt(point[0]), value: typeof point[1] === 'number' ? point[1] : parseFloat(point[1]), })) return { key, labels, data: points } }) socket.emit(options.responseEvent, { status: 'ok', data: data, }) } catch (e) { console.error(e) socket.emit(options.responseEvent, { status: 'error', error: e.message, }) } } src/service/socket.io/request/timeseries-range.mjs000066400000000000000000000026001517715050700226570ustar00rootroot00000000000000const consolePrefix = 'socket.io timeseries range' export default async (options) => { const {socket, payload} = options; try { const redis = socket.p3xrs.ioredis const key = payload.key const from = payload.from || '-' const to = payload.to || '+' const args = [key, from, to] // Optional aggregation: { type: 'avg'|'min'|'max'|'sum'|'count'|..., timeBucket: 5000 } if (payload.aggregation && payload.aggregation.type && payload.aggregation.timeBucket) { args.push('AGGREGATION', payload.aggregation.type, payload.aggregation.timeBucket) } // Optional count limit if (payload.count) { args.push('COUNT', payload.count) } console.info(consolePrefix, args) const raw = await redis.call('TS.RANGE', ...args) // raw is [[timestamp, value], [timestamp, value], ...] const data = raw.map(entry => ({ timestamp: typeof entry[0] === 'number' ? entry[0] : parseInt(entry[0]), value: typeof entry[1] === 'number' ? entry[1] : parseFloat(entry[1]), })) socket.emit(options.responseEvent, { status: 'ok', data: data, }) } catch (e) { console.error(e) socket.emit(options.responseEvent, { status: 'error', error: e.message, }) } } src/service/socket.io/request/trigger-redis-disconnect.mjs000066400000000000000000000011371517715050700243160ustar00rootroot00000000000000import * as sharedIoRedis from '../shared.mjs' const consolePrefix = 'socket.io trigger redis disconnect' export default async (options) => { const {socket} = options; try { console.warn(consolePrefix, 'socket.p3xrs.connectionId', socket.p3xrs.connectionId) sharedIoRedis.disconnectRedis({ socket: socket, }) socket.emit(options.responseEvent, { status: 'ok', }) } catch (e) { console.error(e) socket.emit(options.responseEvent, { status: 'error', error: e.message }) } } src/service/socket.io/request/validate-groq-api-key.mjs000066400000000000000000000017431517715050700235170ustar00rootroot00000000000000import Groq from 'groq-sdk' export default async (options) => { const { socket, payload } = options try { const apiKey = (payload.apiKey || '').trim() if (!apiKey) { socket.emit(options.responseEvent, { status: 'ok', valid: true }) return } if (!apiKey.startsWith('gsk_') || apiKey.length < 20) { socket.emit(options.responseEvent, { status: 'ok', valid: false, message: 'Invalid key format' }) return } const client = new Groq({ apiKey }) await client.chat.completions.create({ messages: [{ role: 'user', content: 'test' }], model: 'openai/gpt-oss-120b', max_tokens: 1, }) socket.emit(options.responseEvent, { status: 'ok', valid: true }) } catch (e) { console.error('validate-groq-api-key error', e.message) socket.emit(options.responseEvent, { status: 'ok', valid: false, message: e.message }) } } src/service/socket.io/shared.mjs000066400000000000000000000347321517715050700172050ustar00rootroot00000000000000import { cloneDeep } from 'lodash-es' const triggerDisconnect = (options) => { const {connectionId, code, socket} = options if (p3xrs.redisConnections.hasOwnProperty(connectionId)) { delete p3xrs.redisConnections[connectionId] socket.p3xrs.io.emit('redis-disconnected', { connectionId: connectionId, status: 'code', code: code }) sendStatus({ socket: socket }) } } const sendStatus = (options) => { const {socket} = options const redisConnections = {} Object.keys(p3xrs.redisConnections).forEach((redisConnectionKey) => { redisConnections[redisConnectionKey] = {} Object.keys(p3xrs.redisConnections[redisConnectionKey]).forEach(redisConnectionKey2 => { redisConnections[redisConnectionKey][redisConnectionKey2] = p3xrs.redisConnections[redisConnectionKey][redisConnectionKey2] }) }) socket.p3xrs.io.emit('redis-status', { redisConnections: redisConnections, }) } const consolePrefixDisconnectRedis = 'socket.io shared disconnect redis' const disconnectRedis = (options) => { const {socket} = options //console.warn(consolePrefixDisconnectRedis, `${socket.p3xrs.connectionId} !== ${connection.id}`) if (p3xrs.redisConnections.hasOwnProperty(socket.p3xrs.connectionId)) { console.warn(consolePrefixDisconnectRedis, `includes ${p3xrs.redisConnections[socket.p3xrs.connectionId].clients.includes(socket.id)} length === 1 ${p3xrs.redisConnections[socket.p3xrs.connectionId].clients.length}`) if (p3xrs.redisConnections[socket.p3xrs.connectionId].clients.includes(socket.id) && p3xrs.redisConnections[socket.p3xrs.connectionId].clients.length === 1) { //console.warn(consolePrefixDisconnectRedis, p3xrs.redisConnections[socket.p3xrs.connectionId]) //p3xrs.redisConnections[socket.p3xrs.connectionId].ioredis.disconnect() delete p3xrs.redisConnections[socket.p3xrs.connectionId] } else { let connectionIndexExisting = p3xrs.redisConnections[socket.p3xrs.connectionId].clients.indexOf(socket.id); console.warn(consolePrefixDisconnectRedis, socket.p3xrs.connectionId, p3xrs.redisConnections[socket.p3xrs.connectionId].clients, socket.id, connectionIndexExisting) if (connectionIndexExisting > -1) { p3xrs.redisConnections[socket.p3xrs.connectionId].clients.splice(connectionIndexExisting, 1) } } } if (p3xrs.redisConnections.hasOwnProperty(socket.p3xrs.connectionId) && p3xrs.redisConnections[socket.p3xrs.connectionId].hasOwnProperty('clients') && p3xrs.redisConnections[socket.p3xrs.connectionId].clients.length === 0) { delete p3xrs.redisConnections[socket.p3xrs.connectionId] } disconnectRedisIo(options) socket.p3xrs.connectionId = undefined } const sendConnections = (options) => { const {socket} = options const connections = cloneDeep(p3xrs.connections); let connectionsList = connections.list.map(connection => { delete connection.password delete connection.tlsCrt delete connection.tlsKey delete connection.tlsCa delete connection.sshPassword delete connection.sshPrivateKey //TODO fix secured nodes password if (Array.isArray(connection.nodes)) { connection.nodes = connection.nodes.map(node => { delete node.password return node }) } return connection }) connections.list = connectionsList socket.p3xrs.io.emit('connections', { status: 'ok', connections: connections }) } const disconnectRedisIo = (options) => { const {socket} = options console.warn('shared disconnectRedisIo', 'try') if (socket.p3xrs.ioredis !== undefined) { console.warn('shared disconnectRedisIo', 'executed') socket.p3xrs.ioredis.disconnect() socket.p3xrs.ioredisSubscriber.disconnect() socket.p3xrs.ioredis = undefined socket.p3xrs.ioredisSubscriber = undefined } if (Array.isArray(socket.p3xrs.tunnels) && socket.p3xrs.tunnels.length > 0) { for (const server of socket.p3xrs.tunnels) { server.close() } socket.p3xrs.tunnels = [] } if (socket.p3xrs.sshClient) { socket.p3xrs.sshClient.end() socket.p3xrs.sshClient = undefined } } const getStreamKeys = (options) => { const {redis, } = options let {dbsize, maxKeys} = options return new Promise(async (resolve, reject) => { try { /* if (dbsize === undefined) { dbsize = await redis.dbsize() } */ if (isNaN(maxKeys) || maxKeys < 5 || maxKeys > 100000) { maxKeys = 10000 } //console.warn('check if received max keys', maxKeys, typeof maxKeys, !isNaN(maxKeys), maxKeys < 5, maxKeys > 100000) /* let count = 100 if (dbsize > 110000) { count = 10000 } else if (dbsize > 11000) { count = 1000 } */ let count = Math.round(maxKeys / 10) if (count < 5) { count = 5 } //console.warn('socket.io getStreamKeys dbsize', dbsize, 'count', count, 'maxKeys', maxKeys) const stream = redis.scanStream({ match: options.match, count: count }); let keys = []; let ended = false stream.on('data', (resultKeys) => { /* keys = keys.concat(resultKeys); if (maxKeys && keys.length >= maxKeys && !ended) { ended = true console.warn('reached max key count', maxKeys, 'found', keys.length, 'keys our of unknown total') //stream.pause() //stream.destroy() stream.emit('end') } */ if (maxKeys && keys.length < maxKeys) { keys = keys.concat(resultKeys); if (keys.length >= maxKeys) { ended = true resolve(keys) //stream.emit('end') } } else if (!ended) { ended = true resolve(keys) } }); stream.on('end', () => { if (ended) { return } resolve(keys); }); /* stream.on('error', (error) => { console.error('getStreamKeys stream', error) reject(error) }) */ } catch (e) { reject(e) } }) } /* const getStreamTypedKeys = (options) => { const { redis, key, match } = options let { scan } = options if (scan === undefined) { scan = 'scanStream' } return new Promise((resolve, reject) => { let stream; if (scan === 'scanStream') { stream = redis[scan]({ match: match }); } else { stream = redis[scan](key, { match: match }); } let keys = []; stream.on('data', (resultKeys) => { keys = keys.concat(resultKeys); }); stream.on('end', async () => { try { resolve(keys); } catch (e) { console.error(e); reject(e) } }); }) } */ const getKeysInfo = async (options) => { const {redis, keys} = options; const keyTypePipeline = redis.pipeline() for (let key of keys) { keyTypePipeline.type(key) keyTypePipeline.ttl(key) } const keyTypeAndTtlResults = await keyTypePipeline.exec(); const result = {} const complexLengthPipeline = redis.pipeline() for (let keysIndex in keys) { const pipelineIndex = keysIndex * 2 const keyType = keyTypeAndTtlResults[pipelineIndex] const keyTtl = keyTypeAndTtlResults[pipelineIndex + 1] const key = keys[keysIndex] const obj = { type: keyType[1], ttl: keyTtl[1], } // Normalize ReJSON-RL to json for the client if (obj.type === 'ReJSON-RL') { obj.type = 'json' } // Normalize TSDB-TYPE to timeseries for the client if (obj.type === 'TSDB-TYPE') { obj.type = 'timeseries' } switch (obj.type) { case 'stream': complexLengthPipeline.xlen(key) break; case 'hash': complexLengthPipeline.hlen(key) break; case 'list': complexLengthPipeline.llen(key) break; case 'set': complexLengthPipeline.scard(key) break; case 'zset': complexLengthPipeline.zcard(key) break; case 'timeseries': complexLengthPipeline.call('TS.INFO', key) break; } result[key] = obj } const lengthsPipeline = await complexLengthPipeline.exec() for (let keysIndex in keys) { const key = keys[keysIndex] const obj = result[key] if (obj.type === 'string' || obj.type === 'none' || obj.type === 'json') { continue } const lengthPipelineElement = lengthsPipeline.shift() if (lengthPipelineElement === undefined) { continue } if (obj.type === 'timeseries') { // TS.INFO returns flat array [field, value, ...]; extract totalSamples const tsInfo = lengthPipelineElement[1] if (Array.isArray(tsInfo)) { for (let i = 0; i < tsInfo.length; i += 2) { if (tsInfo[i] === 'totalSamples') { obj.length = tsInfo[i + 1] break } } } } else { obj.length = lengthPipelineElement[1] } } return result; } const ensureReadonlyConnections = () => { if (p3xrs.cfg.readonlyConnections === true) { const errorCode = new Error('readonly-connection-mode') throw errorCode; } } const ensureReadonlyConnection = ({ socket }) => { if (socket.p3xrs.readonly === true) { const errorCode = new Error('readonly-connection-mode') throw errorCode; } } const getFullInfo = async (options) => { const {redis} = options; let {payload} = options if (payload === undefined) { payload = {} } const dbsize = await redis.dbsize() const results = await Promise.all([ redis.info(), getStreamKeys({ dbsize: dbsize, redis: redis, match: payload.match, maxKeys: payload.maxKeys, }), redis.pubsub('channels', '*'), // redis.infoObject(), ]) const keys = results[1] let keysInfo = {} if (keys.length < 110000) { keysInfo = await getKeysInfo({ redis: redis, keys: keys, }) } // const keysInfo = [] const result = { info: results[0], // infoObject: results[3], keys: keys, keysInfo: keysInfo, dbsize: dbsize, channels: results[2] } //console.log('get full info', result) return result } const getFullInfoAndSendSocket = async (options) => { const {redis, socket, payload, setDb} = options if (setDb === true) { try { await redis.call('select', payload.db || 0) } catch(e) { console.warn(e) } } const result = await getFullInfo({ redis: redis, payload: payload, }) let {extend} = options if (extend === undefined) { extend = {} } socket.emit(options.responseEvent, Object.assign(extend, { status: 'ok', info: result.info, // infoObject: result.infoObject, keys: result.keys, keysInfo: result.keysInfo, keysInfoFetchedAt: Date.now(), dbsize: result.dbsize, })) } const argumentParser = (input, sep, keepQuotes) => { const separator = sep || /\s/g; let singleQuoteOpen = false; let doubleQuoteOpen = false; let tokenBuffer = []; const ret = []; console.log('argumentParser input', input) const arr = input.split(''); for (let i = 0; i < arr.length; ++i) { let element = arr[i]; let matches = element.match(separator); if (element === "'" && !doubleQuoteOpen) { if (keepQuotes === true) { tokenBuffer.push(element); } singleQuoteOpen = !singleQuoteOpen; continue; } else if (element === '"' && !singleQuoteOpen) { if (keepQuotes === true) { tokenBuffer.push(element); } doubleQuoteOpen = !doubleQuoteOpen; continue; } if (!singleQuoteOpen && !doubleQuoteOpen && matches) { if (tokenBuffer.length > 0) { ret.push(tokenBuffer.join('')); tokenBuffer = []; } else if (!!sep) { ret.push(element); } } else { tokenBuffer.push(element); } } if (tokenBuffer.length > 0) { ret.push(tokenBuffer.join('')); } else if (!!sep) { ret.push(''); } return ret; } const parseModuleList = (rawList) => { if (!Array.isArray(rawList)) return [] const modules = [] for (const entry of rawList) { if (!Array.isArray(entry)) continue const mod = {} for (let i = 0; i < entry.length; i += 2) { mod[entry[i]] = entry[i + 1] } if (mod.name) modules.push(mod) } return modules } const detectModules = async (redis) => { try { const rawList = await redis.call('MODULE', 'LIST') return parseModuleList(rawList) } catch (e) { // MODULE LIST not supported or disabled return [] } } export { argumentParser, ensureReadonlyConnections, triggerDisconnect, getStreamKeys, disconnectRedisIo, sendConnections, sendStatus, disconnectRedis, getKeysInfo, getFullInfo, getFullInfoAndSendSocket, ensureReadonlyConnection, parseModuleList, detectModules, } src/service/socket.io/socket.mjs000066400000000000000000000110521517715050700172150ustar00rootroot00000000000000import * as socketIoShared from './shared.mjs' import fs from 'fs' import { fileURLToPath } from 'url' import path from 'path' const __dirname = path.dirname(fileURLToPath(import.meta.url)) const originalPkg = JSON.parse(fs.readFileSync(new URL('../../../package.json', import.meta.url), 'utf8')) let pkg = originalPkg try { const parentPkgPath = path.resolve(__dirname, '../../../../../package.json') if (fs.existsSync(parentPkgPath)) { pkg = JSON.parse(fs.readFileSync(parentPkgPath, 'utf8')) if (pkg.name !== 'p3x-redis-ui') { console.warn('cannot find p3x-redis-ui version, but it is not required, found', pkg.name) pkg = originalPkg } } } catch(e) { console.warn('cannot find p3x-redis-ui version, but it is not required', e) } export default (io) => { io.on('connect', function (socket) { //const token = socket.handshake.query.token; socket.p3xrs = { address: socket.handshake.headers.origin, connectedAt: new Date(), connectionId: undefined, io: io, ioredis: undefined, ioredisSubscriber: undefined, tunnels: [], sshClient: undefined, readonly: undefined, // commands: undefined, subsciber: false, } console.info(`socket.io connected ${socket.id}`); socket.on('disconnect', function () { console.warn('socket.p3xrs.connectionId', socket.p3xrs.connectionId) if (socket.p3xrs.connectionId !== undefined) { const connectionId = socket.p3xrs.connectionId; if (p3xrs.redisConnections.hasOwnProperty(connectionId)) { const redisConnectionIndex = p3xrs.redisConnections[connectionId].clients.indexOf(socket.id); if (redisConnectionIndex !== -1) { p3xrs.redisConnections[connectionId].clients.splice(redisConnectionIndex, 1); } if (p3xrs.redisConnections[connectionId].clients.length === 0) { delete p3xrs.redisConnections[connectionId] } socketIoShared.disconnectRedisIo({ socket: socket, }) } } // Stop MONITOR if active if (socket.p3xrs.ioredisMonitor) { try { socket.p3xrs.ioredisMonitor.disconnect() } catch {} socket.p3xrs.ioredisMonitor = undefined } // Call on disconnect. console.info('socket.io disconnected %s', socket.id); socketIoShared.sendStatus({ socket: socket, }) socketIoShared.disconnectRedis({ socket: socket, }) }); socket.on('p3xr-request', (options) => { options.socket = socket; options.responseEvent = `p3xr-response-${options.requestId}` if (options && options.action && typeof options.action === 'string' && !options.action.includes('.') && !options.action.includes('\\') && !options.action.includes('/')) { import(`./request/${options.action}.mjs`).then(mod => mod.default(options)).catch(err => { console.error('failed to load request handler', options.action, err) socket.emit(options.responseEvent, { status: 'error', error: err.message, }) }) } else { console.warn('trying bad action socket.on p3xr-request with options', options) } }) let dividers = [ ":", "/", "|", "-", "@" ] if (p3xrs.cfg.hasOwnProperty('treeDividers') && Array.isArray(p3xrs.cfg.treeDividers)) { dividers = p3xrs.cfg.treeDividers } socket.emit('configuration', { readonlyConnections: p3xrs.cfg.readonlyConnections === true, snapshot: pkg.name !== 'p3x-redis-ui', treeDividers: dividers, version: pkg.version, groqApiKey: p3xrs.cfg.groqApiKey || '', groqApiKeyReadonly: p3xrs.cfg.groqApiKeyReadonly === true, aiEnabled: p3xrs.cfg.aiEnabled !== false, aiUseOwnKey: p3xrs.cfg.aiUseOwnKey === true, }) socketIoShared.sendStatus({ socket: socket, }) socketIoShared.sendConnections({ socket: socket, }) }); } yarn.lock000066400000000000000000005341041517715050700127200ustar00rootroot00000000000000# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. # yarn lockfile v1 "@ioredis/commands@1.5.1": version "1.5.1" resolved "https://registry.npmjs.org/@ioredis/commands/-/commands-1.5.1.tgz" integrity sha512-JH8ZL/ywcJyR9MmJ5BNqZllXNZQqQbnVZOqpPQqE1vHiFgAw4NHbvE0FOduNU8IX9babitBT46571OnPTT0Zcw== "@isaacs/cliui@^8.0.2": version "8.0.2" resolved "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz" integrity sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA== dependencies: string-width "^5.1.2" string-width-cjs "npm:string-width@^4.2.0" strip-ansi "^7.0.1" strip-ansi-cjs "npm:strip-ansi@^6.0.1" wrap-ansi "^8.1.0" wrap-ansi-cjs "npm:wrap-ansi@^7.0.0" "@isaacs/fs-minipass@^4.0.0": version "4.0.1" resolved "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz" integrity sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w== dependencies: minipass "^7.0.4" "@isaacs/string-locale-compare@^1.1.0": version "1.1.0" resolved "https://registry.npmjs.org/@isaacs/string-locale-compare/-/string-locale-compare-1.1.0.tgz" integrity sha512-SQ7Kzhh9+D+ZW9MA0zkYv3VXhIDNx+LzM6EJ+/65I3QY+enU6Itte7E5XX7EWrqLW2FN4n06GWzBnPoC3th2aQ== "@jridgewell/gen-mapping@^0.3.5": version "0.3.13" resolved "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz" integrity sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA== dependencies: "@jridgewell/sourcemap-codec" "^1.5.0" "@jridgewell/trace-mapping" "^0.3.24" "@jridgewell/resolve-uri@^3.1.0": version "3.1.2" resolved "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz" integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== "@jridgewell/source-map@^0.3.3": version "0.3.11" resolved "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz" integrity sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA== dependencies: "@jridgewell/gen-mapping" "^0.3.5" "@jridgewell/trace-mapping" "^0.3.25" "@jridgewell/sourcemap-codec@^1.4.14", "@jridgewell/sourcemap-codec@^1.5.0": version "1.5.5" resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz" integrity sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og== "@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25": version "0.3.31" resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz" integrity sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw== dependencies: "@jridgewell/resolve-uri" "^3.1.0" "@jridgewell/sourcemap-codec" "^1.4.14" "@msgpack/msgpack@^3.1.3": version "3.1.3" resolved "https://registry.npmjs.org/@msgpack/msgpack/-/msgpack-3.1.3.tgz" integrity sha512-47XIizs9XZXvuJgoaJUIE2lFoID8ugvc0jzSHP+Ptfk8nTbnR8g788wv48N03Kx0UkAv559HWRQ3yzOgzlRNUA== "@npmcli/agent@^3.0.0": version "3.0.0" resolved "https://registry.npmjs.org/@npmcli/agent/-/agent-3.0.0.tgz" integrity sha512-S79NdEgDQd/NGCay6TCoVzXSj74skRZIKJcpJjC5lOq34SZzyI6MqtiiWoiVWoVrTcGjNeC4ipbh1VIHlpfF5Q== dependencies: agent-base "^7.1.0" http-proxy-agent "^7.0.0" https-proxy-agent "^7.0.1" lru-cache "^10.0.1" socks-proxy-agent "^8.0.3" "@npmcli/arborist@^9.0.0": version "9.0.0" resolved "https://registry.npmjs.org/@npmcli/arborist/-/arborist-9.0.0.tgz" integrity sha512-ZFsI/VJ7wJ2rTksLNJ9xqr75Ste/wiKvW+7w12ZGbcT67xWii97yS+aDlh3edNhqlqoXvdzYG4hTNui81VxJCA== dependencies: "@isaacs/string-locale-compare" "^1.1.0" "@npmcli/fs" "^4.0.0" "@npmcli/installed-package-contents" "^3.0.0" "@npmcli/map-workspaces" "^4.0.1" "@npmcli/metavuln-calculator" "^9.0.0" "@npmcli/name-from-folder" "^3.0.0" "@npmcli/node-gyp" "^4.0.0" "@npmcli/package-json" "^6.0.1" "@npmcli/query" "^4.0.0" "@npmcli/redact" "^3.0.0" "@npmcli/run-script" "^9.0.1" bin-links "^5.0.0" cacache "^19.0.1" common-ancestor-path "^1.0.1" hosted-git-info "^8.0.0" json-stringify-nice "^1.1.4" lru-cache "^10.2.2" minimatch "^9.0.4" nopt "^8.0.0" npm-install-checks "^7.1.0" npm-package-arg "^12.0.0" npm-pick-manifest "^10.0.0" npm-registry-fetch "^18.0.1" pacote "^21.0.0" parse-conflict-json "^4.0.0" proc-log "^5.0.0" proggy "^3.0.0" promise-all-reject-late "^1.0.0" promise-call-limit "^3.0.1" read-package-json-fast "^4.0.0" semver "^7.3.7" ssri "^12.0.0" treeverse "^3.0.0" walk-up-path "^4.0.0" "@npmcli/config@^10.0.1": version "10.0.1" resolved "https://registry.npmjs.org/@npmcli/config/-/config-10.0.1.tgz" integrity sha512-772OMXb+ItCUAIfajIynTVDkNgPmrYY367NgCrcPHEHQljjZdbU2qpufk1GIxUeJnkutVFLfc5XyWyzdTO7buw== dependencies: "@npmcli/map-workspaces" "^4.0.1" "@npmcli/package-json" "^6.0.1" ci-info "^4.0.0" ini "^5.0.0" nopt "^8.0.0" proc-log "^5.0.0" semver "^7.3.5" walk-up-path "^4.0.0" "@npmcli/fs@^4.0.0": version "4.0.0" resolved "https://registry.npmjs.org/@npmcli/fs/-/fs-4.0.0.tgz" integrity sha512-/xGlezI6xfGO9NwuJlnwz/K14qD1kCSAGtacBHnGzeAIuJGazcp45KP5NuyARXoKb7cwulAGWVsbeSxdG/cb0Q== dependencies: semver "^7.3.5" "@npmcli/git@^6.0.0", "@npmcli/git@^6.0.1": version "6.0.1" resolved "https://registry.npmjs.org/@npmcli/git/-/git-6.0.1.tgz" integrity sha512-BBWMMxeQzalmKadyimwb2/VVQyJB01PH0HhVSNLHNBDZN/M/h/02P6f8fxedIiFhpMj11SO9Ep5tKTBE7zL2nw== dependencies: "@npmcli/promise-spawn" "^8.0.0" ini "^5.0.0" lru-cache "^10.0.1" npm-pick-manifest "^10.0.0" proc-log "^5.0.0" promise-inflight "^1.0.1" promise-retry "^2.0.1" semver "^7.3.5" which "^5.0.0" "@npmcli/installed-package-contents@^3.0.0": version "3.0.0" resolved "https://registry.npmjs.org/@npmcli/installed-package-contents/-/installed-package-contents-3.0.0.tgz" integrity sha512-fkxoPuFGvxyrH+OQzyTkX2LUEamrF4jZSmxjAtPPHHGO0dqsQ8tTKjnIS8SAnPHdk2I03BDtSMR5K/4loKg79Q== dependencies: npm-bundled "^4.0.0" npm-normalize-package-bin "^4.0.0" "@npmcli/map-workspaces@^4.0.1", "@npmcli/map-workspaces@^4.0.2": version "4.0.2" resolved "https://registry.npmjs.org/@npmcli/map-workspaces/-/map-workspaces-4.0.2.tgz" integrity sha512-mnuMuibEbkaBTYj9HQ3dMe6L0ylYW+s/gfz7tBDMFY/la0w9Kf44P9aLn4/+/t3aTR3YUHKoT6XQL9rlicIe3Q== dependencies: "@npmcli/name-from-folder" "^3.0.0" "@npmcli/package-json" "^6.0.0" glob "^10.2.2" minimatch "^9.0.0" "@npmcli/metavuln-calculator@^9.0.0": version "9.0.0" resolved "https://registry.npmjs.org/@npmcli/metavuln-calculator/-/metavuln-calculator-9.0.0.tgz" integrity sha512-znLKqdy1ZEGNK3VB9j/RzGyb/P0BJb3fGpvEbHIAyBAXsps2l1ce8SVHfsGAFLl9s8072PxafqTn7RC8wSnQPg== dependencies: cacache "^19.0.0" json-parse-even-better-errors "^4.0.0" pacote "^21.0.0" proc-log "^5.0.0" semver "^7.3.5" "@npmcli/name-from-folder@^3.0.0": version "3.0.0" resolved "https://registry.npmjs.org/@npmcli/name-from-folder/-/name-from-folder-3.0.0.tgz" integrity sha512-61cDL8LUc9y80fXn+lir+iVt8IS0xHqEKwPu/5jCjxQTVoSCmkXvw4vbMrzAMtmghz3/AkiBjhHkDKUH+kf7kA== "@npmcli/node-gyp@^4.0.0": version "4.0.0" resolved "https://registry.npmjs.org/@npmcli/node-gyp/-/node-gyp-4.0.0.tgz" integrity sha512-+t5DZ6mO/QFh78PByMq1fGSAub/agLJZDRfJRMeOSNCt8s9YVlTjmGpIPwPhvXTGUIJk+WszlT0rQa1W33yzNA== "@npmcli/package-json@^6.0.0", "@npmcli/package-json@^6.0.1", "@npmcli/package-json@^6.1.0", "@npmcli/package-json@^6.1.1": version "6.1.1" resolved "https://registry.npmjs.org/@npmcli/package-json/-/package-json-6.1.1.tgz" integrity sha512-d5qimadRAUCO4A/Txw71VM7UrRZzV+NPclxz/dc+M6B2oYwjWTjqh8HA/sGQgs9VZuJ6I/P7XIAlJvgrl27ZOw== dependencies: "@npmcli/git" "^6.0.0" glob "^10.2.2" hosted-git-info "^8.0.0" json-parse-even-better-errors "^4.0.0" proc-log "^5.0.0" semver "^7.5.3" validate-npm-package-license "^3.0.4" "@npmcli/promise-spawn@^8.0.0", "@npmcli/promise-spawn@^8.0.2": version "8.0.2" resolved "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-8.0.2.tgz" integrity sha512-/bNJhjc+o6qL+Dwz/bqfTQClkEO5nTQ1ZEcdCkAQjhkZMHIh22LPG7fNh1enJP1NKWDqYiiABnjFCY7E0zHYtQ== dependencies: which "^5.0.0" "@npmcli/query@^4.0.0": version "4.0.0" resolved "https://registry.npmjs.org/@npmcli/query/-/query-4.0.0.tgz" integrity sha512-3pPbese0fbCiFJ/7/X1GBgxAKYFE8sxBddA7GtuRmOgNseH4YbGsXJ807Ig3AEwNITjDUISHglvy89cyDJnAwA== dependencies: postcss-selector-parser "^6.1.2" "@npmcli/redact@^3.0.0": version "3.0.0" resolved "https://registry.npmjs.org/@npmcli/redact/-/redact-3.0.0.tgz" integrity sha512-/1uFzjVcfzqrgCeGW7+SZ4hv0qLWmKXVzFahZGJ6QuJBj6Myt9s17+JL86i76NV9YSnJRcGXJYQbAU0rn1YTCQ== "@npmcli/run-script@^9.0.0", "@npmcli/run-script@^9.0.1": version "9.0.2" resolved "https://registry.npmjs.org/@npmcli/run-script/-/run-script-9.0.2.tgz" integrity sha512-cJXiUlycdizQwvqE1iaAb4VRUM3RX09/8q46zjvy+ct9GhfZRWd7jXYVc1tn/CfRlGPVkX/u4sstRlepsm7hfw== dependencies: "@npmcli/node-gyp" "^4.0.0" "@npmcli/package-json" "^6.0.0" "@npmcli/promise-spawn" "^8.0.0" node-gyp "^11.0.0" proc-log "^5.0.0" which "^5.0.0" "@pkgjs/parseargs@^0.11.0": version "0.11.0" resolved "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz" integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== "@sigstore/bundle@^3.0.0": version "3.0.0" resolved "https://registry.npmjs.org/@sigstore/bundle/-/bundle-3.0.0.tgz" integrity sha512-XDUYX56iMPAn/cdgh/DTJxz5RWmqKV4pwvUAEKEWJl+HzKdCd/24wUa9JYNMlDSCb7SUHAdtksxYX779Nne/Zg== dependencies: "@sigstore/protobuf-specs" "^0.3.2" "@sigstore/core@^2.0.0": version "2.0.0" resolved "https://registry.npmjs.org/@sigstore/core/-/core-2.0.0.tgz" integrity sha512-nYxaSb/MtlSI+JWcwTHQxyNmWeWrUXJJ/G4liLrGG7+tS4vAz6LF3xRXqLH6wPIVUoZQel2Fs4ddLx4NCpiIYg== "@sigstore/protobuf-specs@^0.3.2": version "0.3.3" resolved "https://registry.yarnpkg.com/@sigstore/protobuf-specs/-/protobuf-specs-0.3.3.tgz#7dd46d68b76c322873a2ef7581ed955af6f4dcde" integrity sha512-RpacQhBlwpBWd7KEJsRKcBQalbV28fvkxwTOJIqhIuDysMMaJW47V4OqW30iJB9uRpqOSxxEAQFdr8tTattReQ== "@sigstore/sign@^3.0.0": version "3.0.0" resolved "https://registry.npmjs.org/@sigstore/sign/-/sign-3.0.0.tgz" integrity sha512-UjhDMQOkyDoktpXoc5YPJpJK6IooF2gayAr5LvXI4EL7O0vd58okgfRcxuaH+YTdhvb5aa1Q9f+WJ0c2sVuYIw== dependencies: "@sigstore/bundle" "^3.0.0" "@sigstore/core" "^2.0.0" "@sigstore/protobuf-specs" "^0.3.2" make-fetch-happen "^14.0.1" proc-log "^5.0.0" promise-retry "^2.0.1" "@sigstore/tuf@^3.0.0": version "3.0.0" resolved "https://registry.npmjs.org/@sigstore/tuf/-/tuf-3.0.0.tgz" integrity sha512-9Xxy/8U5OFJu7s+OsHzI96IX/OzjF/zj0BSSaWhgJgTqtlBhQIV2xdrQI5qxLD7+CWWDepadnXAxzaZ3u9cvRw== dependencies: "@sigstore/protobuf-specs" "^0.3.2" tuf-js "^3.0.1" "@sigstore/verify@^2.0.0": version "2.0.0" resolved "https://registry.npmjs.org/@sigstore/verify/-/verify-2.0.0.tgz" integrity sha512-Ggtq2GsJuxFNUvQzLoXqRwS4ceRfLAJnrIHUDrzAD0GgnOhwujJkKkxM/s5Bako07c3WtAs/sZo5PJq7VHjeDg== dependencies: "@sigstore/bundle" "^3.0.0" "@sigstore/core" "^2.0.0" "@sigstore/protobuf-specs" "^0.3.2" "@sindresorhus/is@^0.7.0": version "0.7.0" resolved "https://registry.npmjs.org/@sindresorhus/is/-/is-0.7.0.tgz" integrity sha512-ONhaKPIufzzrlNbqtWFFd+jlnemX6lJAgq9ZeiZtS7I1PIf/la7CW4m83rTXRnVnsMbW2k56pGYu7AUFJD9Pow== "@socket.io/component-emitter@~3.1.0": version "3.1.2" resolved "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz" integrity sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA== "@tufjs/canonical-json@2.0.0": version "2.0.0" resolved "https://registry.npmjs.org/@tufjs/canonical-json/-/canonical-json-2.0.0.tgz" integrity sha512-yVtV8zsdo8qFHe+/3kw81dSLyF7D576A5cCFCi4X7B39tWT7SekaEFUnvnWJHz+9qO7qJTah1JbrDjWKqFtdWA== "@tufjs/models@3.0.1": version "3.0.1" resolved "https://registry.npmjs.org/@tufjs/models/-/models-3.0.1.tgz" integrity sha512-UUYHISyhCU3ZgN8yaear3cGATHb3SMuKHsQ/nVbHXcmnBf+LzQ/cQfhNG+rfaSHgqGKNEm2cOCLVLELStUQ1JA== dependencies: "@tufjs/canonical-json" "2.0.0" minimatch "^9.0.5" "@types/cookie@^0.4.1": version "0.4.1" resolved "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz" integrity sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q== "@types/cors@^2.8.12": version "2.8.17" resolved "https://registry.npmjs.org/@types/cors/-/cors-2.8.17.tgz" integrity sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA== dependencies: "@types/node" "*" "@types/node@*", "@types/node@>=10.0.0": version "22.10.2" resolved "https://registry.npmjs.org/@types/node/-/node-22.10.2.tgz" integrity sha512-Xxr6BBRCAOQixvonOye19wnzyDiUtTeqldOOmj3CkeblonbccA12PFwlufvRdrpjXxqnmUaeiU5EOA+7s5diUQ== dependencies: undici-types "~6.20.0" "@types/yauzl@^2.9.1": version "2.10.3" resolved "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz" integrity sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q== dependencies: "@types/node" "*" abbrev@1: version "1.1.1" resolved "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz" integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== abbrev@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/abbrev/-/abbrev-2.0.0.tgz" integrity sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ== abbrev@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/abbrev/-/abbrev-3.0.0.tgz" integrity sha512-+/kfrslGQ7TNV2ecmQwMJj/B65g5KVq1/L3SGVZ3tCYGqlzFuFCGBZJtMP99wH3NpEUyAjn0zPdPUg0D+DwrOA== accepts@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz" integrity sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng== dependencies: mime-types "^3.0.0" negotiator "^1.0.0" accepts@~1.3.4: version "1.3.8" resolved "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz" integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw== dependencies: mime-types "~2.1.34" negotiator "0.6.3" acorn@^8.15.0: version "8.16.0" resolved "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz" integrity sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw== agent-base@^7.1.0, agent-base@^7.1.2: version "7.1.3" resolved "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz" integrity sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw== ansi-colors@^4.1.3: version "4.1.3" resolved "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz" integrity sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw== ansi-regex@^2.0.0: version "2.1.1" resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz" integrity sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA== ansi-regex@^5.0.1: version "5.0.1" resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz" integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== ansi-regex@^6.0.1: version "6.1.0" resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz" integrity sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA== ansi-styles@^2.2.1: version "2.2.1" resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz" integrity sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA== ansi-styles@^3.2.1: version "3.2.1" resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz" integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== dependencies: color-convert "^1.9.0" ansi-styles@^4.0.0, ansi-styles@^4.1.0: version "4.3.0" resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz" integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== dependencies: color-convert "^2.0.1" ansi-styles@^6.1.0: version "6.2.1" resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz" integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== any-promise@^1.0.0: version "1.3.0" resolved "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz" integrity sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A== anymatch@~3.1.2: version "3.1.3" resolved "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz" integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== dependencies: normalize-path "^3.0.0" picomatch "^2.0.4" aproba@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz" integrity sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ== archive-type@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/archive-type/-/archive-type-4.0.0.tgz" integrity sha512-zV4Ky0v1F8dBrdYElwTvQhweQ0P7Kwc1aluqJsYtOBP01jXcWCyW2IEfI1YiqsG+Iy7ZR+o5LF1N+PGECBxHWA== dependencies: file-type "^4.2.0" archy@~1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz" integrity sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw== argparse@^1.0.7: version "1.0.10" resolved "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz" integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== dependencies: sprintf-js "~1.0.2" argparse@^2.0.1: version "2.0.1" resolved "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz" integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== array-each@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/array-each/-/array-each-1.0.1.tgz" integrity sha512-zHjL5SZa68hkKHBFBK6DJCTtr9sfTCPCaph/L7tMSLcTFgy+zX7E+6q5UArbtOtMBCtxdICpfTCspRse+ywyXA== array-slice@^1.0.0: version "1.1.0" resolved "https://registry.npmjs.org/array-slice/-/array-slice-1.1.0.tgz" integrity sha512-B1qMD3RBP7O8o0H2KbrXDyB0IccejMF15+87Lvlor12ONPRHP6gTjXMNkt/d3ZuOGbAe66hFmaCfECI24Ufp6w== asn1@^0.2.6: version "0.2.6" resolved "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz" integrity sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ== dependencies: safer-buffer "~2.1.0" async@^2.6.0: version "2.6.4" resolved "https://registry.npmjs.org/async/-/async-2.6.4.tgz" integrity sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA== dependencies: lodash "^4.17.14" async@^3.2.3, async@~3.2.0: version "3.2.6" resolved "https://registry.npmjs.org/async/-/async-3.2.6.tgz" integrity sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA== axios@^0.21.1: version "0.21.4" resolved "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz" integrity sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg== dependencies: follow-redirects "^1.14.0" balanced-match@^1.0.0: version "1.0.2" resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== balanced-match@^4.0.2: version "4.0.4" resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz" integrity sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA== base64-js@^1.3.1: version "1.5.1" resolved "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== base64id@2.0.0, base64id@~2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz" integrity sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog== bcrypt-pbkdf@^1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz" integrity sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w== dependencies: tweetnacl "^0.14.3" bcryptjs@^3.0.3: version "3.0.3" resolved "https://registry.npmjs.org/bcryptjs/-/bcryptjs-3.0.3.tgz" integrity sha512-GlF5wPWnSa/X5LKM1o0wz0suXIINz1iHRLvTS+sLyi7XPbe5ycmYI3DlZqVGZZtDgl4DmasFg7gOB3JYbphV5g== bin-links@^5.0.0: version "5.0.0" resolved "https://registry.npmjs.org/bin-links/-/bin-links-5.0.0.tgz" integrity sha512-sdleLVfCjBtgO5cNjA2HVRvWBJAHs4zwenaCPMNJAJU0yNxpzj80IpjOIimkpkr+mhlA+how5poQtt53PygbHA== dependencies: cmd-shim "^7.0.0" npm-normalize-package-bin "^4.0.0" proc-log "^5.0.0" read-cmd-shim "^5.0.0" write-file-atomic "^6.0.0" binary-extensions@^2.0.0: version "2.3.0" resolved "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz" integrity sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw== binary-extensions@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/binary-extensions/-/binary-extensions-3.0.0.tgz" integrity sha512-X0RfwMgXPEesg6PCXzytQZt9Unh9gtc4SfeTNJvKifUL//Oegcc/Yf31z6hThNZ8dnD3Ir3wkHVN0eWrTvP5ww== bl@^1.0.0: version "1.2.3" resolved "https://registry.npmjs.org/bl/-/bl-1.2.3.tgz" integrity sha512-pvcNpa0UU69UT341rO6AYy4FVAIkUHuZXRIWbq+zHnsVcRzDDjIAhGuuYoi0d//cwIwtt4pkpKycWEfjdV+vww== dependencies: readable-stream "^2.3.5" safe-buffer "^5.1.1" body-parser@^2.2.1: version "2.2.2" resolved "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz" integrity sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA== dependencies: bytes "^3.1.2" content-type "^1.0.5" debug "^4.4.3" http-errors "^2.0.0" iconv-lite "^0.7.0" on-finished "^2.4.1" qs "^6.14.1" raw-body "^3.0.1" type-is "^2.0.1" body@^5.1.0: version "5.1.0" resolved "https://registry.npmjs.org/body/-/body-5.1.0.tgz" integrity sha512-chUsBxGRtuElD6fmw1gHLpvnKdVLK302peeFa9ZqAEk8TyzZ3fygLyUEDDPTJvL9+Bor0dIwn6ePOsRM2y0zQQ== dependencies: continuable-cache "^0.3.1" error "^7.0.0" raw-body "~1.1.0" safe-json-parse "~1.0.1" brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz" integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== dependencies: balanced-match "^1.0.0" concat-map "0.0.1" brace-expansion@^2.0.1: version "2.0.1" resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz" integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== dependencies: balanced-match "^1.0.0" brace-expansion@^5.0.2: version "5.0.4" resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz" integrity sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg== dependencies: balanced-match "^4.0.2" braces@^3.0.3, braces@~3.0.2: version "3.0.3" resolved "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz" integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== dependencies: fill-range "^7.1.1" browser-stdout@^1.3.1: version "1.3.1" resolved "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz" integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== buffer-alloc-unsafe@^1.1.0: version "1.1.0" resolved "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz" integrity sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg== buffer-alloc@^1.2.0: version "1.2.0" resolved "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz" integrity sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow== dependencies: buffer-alloc-unsafe "^1.1.0" buffer-fill "^1.0.0" buffer-crc32@~0.2.3: version "0.2.13" resolved "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz" integrity sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ== buffer-fill@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz" integrity sha512-T7zexNBwiiaCOGDg9xNX9PBmjrubblRkENuptryuI64URkXDFum9il/JGL8Lm8wYfAXpredVXXZz7eMHilimiQ== buffer-from@^1.0.0: version "1.1.2" resolved "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz" integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== buffer@^5.2.1: version "5.7.1" resolved "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz" integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== dependencies: base64-js "^1.3.1" ieee754 "^1.1.13" buildcheck@~0.0.6: version "0.0.7" resolved "https://registry.yarnpkg.com/buildcheck/-/buildcheck-0.0.7.tgz#07a5e76c10ead8fa67d9e4c587b68f49e8f29d61" integrity sha512-lHblz4ahamxpTmnsk+MNTRWsjYKv965MwOrSJyeD588rR3Jcu7swE+0wN5F+PbL5cjgu/9ObkhfzEPuofEMwLA== bytes@1: version "1.0.0" resolved "https://registry.npmjs.org/bytes/-/bytes-1.0.0.tgz" integrity sha512-/x68VkHLeTl3/Ll8IvxdwzhrT+IyKc52e/oyHhA2RwqPqswSnjVbSddfPRwAsJtbilMAPSRWwAlpxdYsSWOTKQ== bytes@^3.1.2, bytes@~3.1.2: version "3.1.2" resolved "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz" integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== cacache@^19.0.0, cacache@^19.0.1: version "19.0.1" resolved "https://registry.npmjs.org/cacache/-/cacache-19.0.1.tgz" integrity sha512-hdsUxulXCi5STId78vRVYEtDAjq99ICAUktLTeTYsLoTE6Z8dS0c8pWNCxwdrk9YfJeobDZc2Y186hD/5ZQgFQ== dependencies: "@npmcli/fs" "^4.0.0" fs-minipass "^3.0.0" glob "^10.2.2" lru-cache "^10.0.1" minipass "^7.0.3" minipass-collect "^2.0.1" minipass-flush "^1.0.5" minipass-pipeline "^1.2.4" p-map "^7.0.2" ssri "^12.0.0" tar "^7.4.3" unique-filename "^4.0.0" cacheable-request@^2.1.1: version "2.1.4" resolved "https://registry.npmjs.org/cacheable-request/-/cacheable-request-2.1.4.tgz" integrity sha512-vag0O2LKZ/najSoUwDbVlnlCFvhBE/7mGTY2B5FgCBDcRD+oVV1HYTOwM6JZfMg/hIcM6IwnTZ1uQQL5/X3xIQ== dependencies: clone-response "1.0.2" get-stream "3.0.0" http-cache-semantics "3.8.1" keyv "3.0.0" lowercase-keys "1.0.0" normalize-url "2.0.1" responselike "1.0.2" call-bind-apply-helpers@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.1.tgz" integrity sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g== dependencies: es-errors "^1.3.0" function-bind "^1.1.2" call-bound@^1.0.2: version "1.0.3" resolved "https://registry.npmjs.org/call-bound/-/call-bound-1.0.3.tgz" integrity sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA== dependencies: call-bind-apply-helpers "^1.0.1" get-intrinsic "^1.2.6" camel-case@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/camel-case/-/camel-case-3.0.0.tgz" integrity sha512-+MbKztAYHXPr1jNTSKQF52VpcFjwY5RkR7fxksV8Doo4KAYc5Fl4UJRgthBbTmEx8C54DqahhbLJkDwjI3PI/w== dependencies: no-case "^2.2.0" upper-case "^1.1.1" camelcase@^6.0.0: version "6.3.0" resolved "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz" integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== chalk@^1.0.0, chalk@^1.1.1: version "1.1.3" resolved "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz" integrity sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A== dependencies: ansi-styles "^2.2.1" escape-string-regexp "^1.0.2" has-ansi "^2.0.0" strip-ansi "^3.0.0" supports-color "^2.0.0" chalk@^2.4.2: version "2.4.2" resolved "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== dependencies: ansi-styles "^3.2.1" escape-string-regexp "^1.0.5" supports-color "^5.3.0" chalk@^4.1.0, chalk@~4.1.0: version "4.1.2" resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz" integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== dependencies: ansi-styles "^4.1.0" supports-color "^7.1.0" chalk@^5.4.1: version "5.4.1" resolved "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz" integrity sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w== chalk@^5.6.2: version "5.6.2" resolved "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz" integrity sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA== chokidar@^3.5.2, chokidar@^3.5.3: version "3.6.0" resolved "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz" integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw== dependencies: anymatch "~3.1.2" braces "~3.0.2" glob-parent "~5.1.2" is-binary-path "~2.1.0" is-glob "~4.0.1" normalize-path "~3.0.0" readdirp "~3.6.0" optionalDependencies: fsevents "~2.3.2" chownr@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz" integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== chownr@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz" integrity sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g== ci-info@^4.0.0, ci-info@^4.1.0: version "4.1.0" resolved "https://registry.npmjs.org/ci-info/-/ci-info-4.1.0.tgz" integrity sha512-HutrvTNsF48wnxkzERIXOe5/mlcfFcbfCmwcg6CJnizbSue78AbDt+1cgl26zwn61WFxhcPykPfZrbqjGmBb4A== cidr-regex@^4.1.1: version "4.1.1" resolved "https://registry.npmjs.org/cidr-regex/-/cidr-regex-4.1.1.tgz" integrity sha512-ekKcVp+iRB9zlKFXyx7io7nINgb0oRjgRdXNEodp1OuxRui8FXr/CA40Tz1voWUp9DPPrMyQKy01vJhDo4N1lw== dependencies: ip-regex "^5.0.0" clean-css@^4.2.1: version "4.2.4" resolved "https://registry.npmjs.org/clean-css/-/clean-css-4.2.4.tgz" integrity sha512-EJUDT7nDVFDvaQgAo2G/PJvxmp1o/c6iXLbswsBbUFXi1Nr+AjA2cKmfbKDMjMvzEe75g3P6JkaDDAKk96A85A== dependencies: source-map "~0.6.0" cli-columns@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/cli-columns/-/cli-columns-4.0.0.tgz" integrity sha512-XW2Vg+w+L9on9wtwKpyzluIPCWXjaBahI7mTcYjx+BVIYD9c3yqcv/yKC7CmdCZat4rq2yiE1UMSJC5ivKfMtQ== dependencies: string-width "^4.2.3" strip-ansi "^6.0.1" cliui@^8.0.1: version "8.0.1" resolved "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz" integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ== dependencies: string-width "^4.2.0" strip-ansi "^6.0.1" wrap-ansi "^7.0.0" clone-response@1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz" integrity sha512-yjLXh88P599UOyPTFX0POsd7WxnbsVsGohcwzHOLspIhhpalPw1BcqED8NblyZLKcGrL8dTgMlcaZxV2jAD41Q== dependencies: mimic-response "^1.0.0" cluster-key-slot@^1.1.0: version "1.1.2" resolved "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz" integrity sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA== cmd-shim@^7.0.0: version "7.0.0" resolved "https://registry.npmjs.org/cmd-shim/-/cmd-shim-7.0.0.tgz" integrity sha512-rtpaCbr164TPPh+zFdkWpCyZuKkjpAzODfaZCf/SVJZzJN+4bHQb/LP3Jzq5/+84um3XXY8r548XiWKSborwVw== color-convert@^1.9.0: version "1.9.3" resolved "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz" integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== dependencies: color-name "1.1.3" color-convert@^2.0.1: version "2.0.1" resolved "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz" integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== dependencies: color-name "~1.1.4" color-name@1.1.3: version "1.1.3" resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz" integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== color-name@~1.1.4: version "1.1.4" resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== colors@~1.1.2: version "1.1.2" resolved "https://registry.npmjs.org/colors/-/colors-1.1.2.tgz" integrity sha512-ENwblkFQpqqia6b++zLD/KUWafYlVY/UNnAp7oz7LY7E924wmpye416wBOmvv/HMWzl8gL1kJlfvId/1Dg176w== commander@^14.0.3: version "14.0.3" resolved "https://registry.npmjs.org/commander/-/commander-14.0.3.tgz" integrity sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw== commander@^2.19.0, commander@^2.20.0, commander@^2.8.1: version "2.20.3" resolved "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== common-ancestor-path@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/common-ancestor-path/-/common-ancestor-path-1.0.1.tgz" integrity sha512-L3sHRo1pXXEqX8VU28kfgUY+YGsk09hPqZiZmLacNib6XNTCM8ubYeT7ryXQw8asB1sKgcU5lkB7ONug08aB8w== concat-map@0.0.1: version "0.0.1" resolved "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== content-disposition@^0.5.2: version "0.5.4" resolved "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz" integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ== dependencies: safe-buffer "5.2.1" content-disposition@^1.0.0: version "1.0.1" resolved "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.1.tgz" integrity sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q== content-type@^1.0.5: version "1.0.5" resolved "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz" integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== continuable-cache@^0.3.1: version "0.3.1" resolved "https://registry.npmjs.org/continuable-cache/-/continuable-cache-0.3.1.tgz" integrity sha512-TF30kpKhTH8AGCG3dut0rdd/19B7Z+qCnrMoBLpyQu/2drZdNrrpcjPEoJeSVsQM+8KmWG5O56oPDjSSUsuTyA== cookie-signature@^1.2.1: version "1.2.2" resolved "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz" integrity sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg== cookie@^0.7.1, cookie@~0.7.2: version "0.7.2" resolved "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz" integrity sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w== core-util-is@~1.0.0: version "1.0.3" resolved "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz" integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ== corifeus-builder@^2025.4.135: version "2025.4.135" resolved "https://registry.npmjs.org/corifeus-builder/-/corifeus-builder-2025.4.135.tgz" integrity sha512-37Hl1n7tLhdtstS8+DGhwcQMhYNQnCYyZ0mZW58UzluZB0ecxR09uhWVNt/2fpLgpYh/tKHNV9TGaRvV3r+Fmw== dependencies: corifeus-utils "^2025.4.120" download "^8.0.0" extract-zip "^2.0.1" fs-extra "^11.3.0" github-api "^3.4.0" glob "^8.0.3" glob-promise "^6.0.7" grunt "^1.6.1" grunt-contrib-clean "^2.0.1" grunt-contrib-copy "^1.0.0" grunt-contrib-htmlmin "^3.1.0" grunt-contrib-watch "^1.1.0" jit-grunt "^0.10.0" lodash "^4.17.21" mkdirp "^3.0.1" mocha "^11.1.0" mz "^2.7.0" npm "^11.1.0" npm-check-updates "^17.1.14" should "^13.2.3" time-grunt "^2.0.0" yaml "^2.7.0" corifeus-utils@^2025.4.120: version "2025.4.120" resolved "https://registry.npmjs.org/corifeus-utils/-/corifeus-utils-2025.4.120.tgz" integrity sha512-Plv0NcZDIoYT2OsxzF47grWaOEGFWph3vIiPSt5gDTkqlTZ/Wy5+EneeYTzMlpMKsNrNKtAcwM98dAtmLoFg4g== dependencies: fs-extra "^11.3.0" ms "^2.1.3" mz "^2.7.0" timestring "^7.0.0" uuid "^11.0.5" corifeus-utils@^2025.4.123: version "2025.4.123" resolved "https://registry.npmjs.org/corifeus-utils/-/corifeus-utils-2025.4.123.tgz" integrity sha512-YbZZr0OzY/4sK0G08i8bOapYUC1rKk3yb1pU3pQ81cGkMdY+sZLq/B0Mzr0I795Ov3mm4LeR+L+h5qyx14T3Jg== dependencies: fs-extra "^11.3.0" ms "^2.1.3" mz "^2.7.0" timestring "^7.0.0" uuid "^11.0.5" cors@~2.8.5: version "2.8.5" resolved "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz" integrity sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g== dependencies: object-assign "^4" vary "^1" cpu-features@~0.0.10: version "0.0.10" resolved "https://registry.yarnpkg.com/cpu-features/-/cpu-features-0.0.10.tgz#9aae536db2710c7254d7ed67cb3cbc7d29ad79c5" integrity sha512-9IkYqtX3YHPCzoVg1Py+o9057a3i0fp7S530UWokCSaFVTc7CwXPRiOjRjBQQ18ZCNafx78YfnG+HALxtVmOGA== dependencies: buildcheck "~0.0.6" nan "^2.19.0" cross-spawn@^7.0.0: version "7.0.6" resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz" integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== dependencies: path-key "^3.1.0" shebang-command "^2.0.0" which "^2.0.1" cssesc@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz" integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== date-time@^1.1.0: version "1.1.0" resolved "https://registry.npmjs.org/date-time/-/date-time-1.1.0.tgz" integrity sha512-RrxZQ06cdKe7YQ5oqIxs3GMc7W3vXscy7Ds+aZIqmxA59QnVtTiCseA4jbzVUub9xCbo9GuYVZo0OrZLYXnnmw== dependencies: time-zone "^0.1.0" dateformat@~4.6.2: version "4.6.3" resolved "https://registry.npmjs.org/dateformat/-/dateformat-4.6.3.tgz" integrity sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA== debug@4, debug@^4, debug@^4.1.1, debug@^4.3.4, debug@^4.3.5, debug@^4.3.6: version "4.4.0" resolved "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz" integrity sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA== dependencies: ms "^2.1.3" debug@^2.2.0: version "2.6.9" resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz" integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== dependencies: ms "2.0.0" debug@^3.1.0: version "3.2.7" resolved "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz" integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== dependencies: ms "^2.1.1" debug@^4.4.0, debug@^4.4.3, debug@~4.4.1: version "4.4.3" resolved "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz" integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA== dependencies: ms "^2.1.3" debug@~4.3.1, debug@~4.3.4: version "4.3.7" resolved "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz" integrity sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ== dependencies: ms "^2.1.3" decamelize@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz" integrity sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ== decode-uri-component@^0.2.0: version "0.2.2" resolved "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz" integrity sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ== decompress-response@^3.3.0: version "3.3.0" resolved "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz" integrity sha512-BzRPQuY1ip+qDonAOz42gRm/pg9F768C+npV/4JOsxRC2sq+Rlk+Q4ZCAsOhnIaMrgarILY+RMUIvMmmX1qAEA== dependencies: mimic-response "^1.0.0" decompress-tar@^4.0.0, decompress-tar@^4.1.0, decompress-tar@^4.1.1: version "4.1.1" resolved "https://registry.npmjs.org/decompress-tar/-/decompress-tar-4.1.1.tgz" integrity sha512-JdJMaCrGpB5fESVyxwpCx4Jdj2AagLmv3y58Qy4GE6HMVjWz1FeVQk1Ct4Kye7PftcdOo/7U7UKzYBJgqnGeUQ== dependencies: file-type "^5.2.0" is-stream "^1.1.0" tar-stream "^1.5.2" decompress-tarbz2@^4.0.0: version "4.1.1" resolved "https://registry.npmjs.org/decompress-tarbz2/-/decompress-tarbz2-4.1.1.tgz" integrity sha512-s88xLzf1r81ICXLAVQVzaN6ZmX4A6U4z2nMbOwobxkLoIIfjVMBg7TeguTUXkKeXni795B6y5rnvDw7rxhAq9A== dependencies: decompress-tar "^4.1.0" file-type "^6.1.0" is-stream "^1.1.0" seek-bzip "^1.0.5" unbzip2-stream "^1.0.9" decompress-targz@^4.0.0: version "4.1.1" resolved "https://registry.npmjs.org/decompress-targz/-/decompress-targz-4.1.1.tgz" integrity sha512-4z81Znfr6chWnRDNfFNqLwPvm4db3WuZkqV+UgXQzSngG3CEKdBkw5jrv3axjjL96glyiiKjsxJG3X6WBZwX3w== dependencies: decompress-tar "^4.1.1" file-type "^5.2.0" is-stream "^1.1.0" decompress-unzip@^4.0.1: version "4.0.1" resolved "https://registry.npmjs.org/decompress-unzip/-/decompress-unzip-4.0.1.tgz" integrity sha512-1fqeluvxgnn86MOh66u8FjbtJpAFv5wgCT9Iw8rcBqQcCo5tO8eiJw7NNTrvt9n4CRBVq7CstiS922oPgyGLrw== dependencies: file-type "^3.8.0" get-stream "^2.2.0" pify "^2.3.0" yauzl "^2.4.2" decompress@^4.2.1: version "4.2.1" resolved "https://registry.npmjs.org/decompress/-/decompress-4.2.1.tgz" integrity sha512-e48kc2IjU+2Zw8cTb6VZcJQ3lgVbS4uuB1TfCHbiZIP/haNXm+SVyhu+87jts5/3ROpd82GSVCoNs/z8l4ZOaQ== dependencies: decompress-tar "^4.0.0" decompress-tarbz2 "^4.0.0" decompress-targz "^4.0.0" decompress-unzip "^4.0.1" graceful-fs "^4.1.10" make-dir "^1.0.0" pify "^2.3.0" strip-dirs "^2.0.0" denque@^2.1.0: version "2.1.0" resolved "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz" integrity sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw== depd@^2.0.0, depd@~2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz" integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== detect-file@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz" integrity sha512-DtCOLG98P007x7wiiOmfI0fi3eIKyWiLTGJ2MDnVi/E04lWGbf+JzrRHMm0rgIIZJGtHpKpbVgLWHrv8xXpc3Q== diff@^5.2.0: version "5.2.0" resolved "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz" integrity sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A== diff@^7.0.0: version "7.0.0" resolved "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz" integrity sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw== download@^8.0.0: version "8.0.0" resolved "https://registry.npmjs.org/download/-/download-8.0.0.tgz" integrity sha512-ASRY5QhDk7FK+XrQtQyvhpDKanLluEEQtWl/J7Lxuf/b+i8RYh997QeXvL85xitrmRKVlx9c7eTrcRdq2GS4eA== dependencies: archive-type "^4.0.0" content-disposition "^0.5.2" decompress "^4.2.1" ext-name "^5.0.0" file-type "^11.1.0" filenamify "^3.0.0" get-stream "^4.1.0" got "^8.3.1" make-dir "^2.1.0" p-event "^2.1.0" pify "^4.0.1" dunder-proto@^1.0.0: version "1.0.1" resolved "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz" integrity sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A== dependencies: call-bind-apply-helpers "^1.0.1" es-errors "^1.3.0" gopd "^1.2.0" duplexer3@^0.1.4: version "0.1.5" resolved "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.5.tgz" integrity sha512-1A8za6ws41LQgv9HrE/66jyC5yuSjQ3L/KOpFtoBilsAK2iA2wuS5rTt1OCzIvtS2V7nVmedsUU+DGRcjBmOYA== eastasianwidth@^0.2.0: version "0.2.0" resolved "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz" integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== ee-first@1.1.1: version "1.1.1" resolved "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz" integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== emoji-regex@^8.0.0: version "8.0.0" resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz" integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== emoji-regex@^9.2.2: version "9.2.2" resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz" integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== encodeurl@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz" integrity sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg== encoding@^0.1.13: version "0.1.13" resolved "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz" integrity sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A== dependencies: iconv-lite "^0.6.2" end-of-stream@^1.0.0, end-of-stream@^1.1.0: version "1.4.4" resolved "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz" integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== dependencies: once "^1.4.0" engine.io-parser@~5.2.1: version "5.2.3" resolved "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz" integrity sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q== engine.io@~6.6.0: version "6.6.2" resolved "https://registry.npmjs.org/engine.io/-/engine.io-6.6.2.tgz" integrity sha512-gmNvsYi9C8iErnZdVcJnvCpSKbWTt1E8+JZo8b+daLninywUWi5NQ5STSHZ9rFjFO7imNcvb8Pc5pe/wMR5xEw== dependencies: "@types/cookie" "^0.4.1" "@types/cors" "^2.8.12" "@types/node" ">=10.0.0" accepts "~1.3.4" base64id "2.0.0" cookie "~0.7.2" cors "~2.8.5" debug "~4.3.1" engine.io-parser "~5.2.1" ws "~8.17.1" env-paths@^2.2.0: version "2.2.1" resolved "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz" integrity sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A== err-code@^2.0.2: version "2.0.3" resolved "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz" integrity sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA== error@^7.0.0: version "7.2.1" resolved "https://registry.npmjs.org/error/-/error-7.2.1.tgz" integrity sha512-fo9HBvWnx3NGUKMvMwB/CBCMMrfEJgbDTVDEkPygA3Bdd3lM1OyCd+rbQ8BwnpF6GdVeOLDNmyL4N5Bg80ZvdA== dependencies: string-template "~0.2.1" es-define-property@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz" integrity sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g== es-errors@^1.3.0: version "1.3.0" resolved "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz" integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== es-object-atoms@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz" integrity sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw== dependencies: es-errors "^1.3.0" escalade@^3.1.1: version "3.2.0" resolved "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz" integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA== escape-html@^1.0.3: version "1.0.3" resolved "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz" integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow== escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: version "1.0.5" resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz" integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== escape-string-regexp@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz" integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== esprima@^4.0.0: version "4.0.1" resolved "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== etag@^1.8.1: version "1.8.1" resolved "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz" integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg== eventemitter2@~0.4.13: version "0.4.14" resolved "https://registry.npmjs.org/eventemitter2/-/eventemitter2-0.4.14.tgz" integrity sha512-K7J4xq5xAD5jHsGM5ReWXRTFa3JRGofHiMcVgQ8PRwgWxzjHpMWCIzsmyf60+mh8KLsqYPcjUMa0AC4hd6lPyQ== exit@~0.1.2: version "0.1.2" resolved "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz" integrity sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ== expand-tilde@^2.0.0, expand-tilde@^2.0.2: version "2.0.2" resolved "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz" integrity sha512-A5EmesHW6rfnZ9ysHQjPdJRni0SRar0tjtG5MNtm9n5TUvsYU8oozprtRD4AqHxcZWWlVuAmQo2nWKfN9oyjTw== dependencies: homedir-polyfill "^1.0.1" exponential-backoff@^3.1.1: version "3.1.1" resolved "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.1.tgz" integrity sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw== express@^5.2.1: version "5.2.1" resolved "https://registry.npmjs.org/express/-/express-5.2.1.tgz" integrity sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw== dependencies: accepts "^2.0.0" body-parser "^2.2.1" content-disposition "^1.0.0" content-type "^1.0.5" cookie "^0.7.1" cookie-signature "^1.2.1" debug "^4.4.0" depd "^2.0.0" encodeurl "^2.0.0" escape-html "^1.0.3" etag "^1.8.1" finalhandler "^2.1.0" fresh "^2.0.0" http-errors "^2.0.0" merge-descriptors "^2.0.0" mime-types "^3.0.0" on-finished "^2.4.1" once "^1.4.0" parseurl "^1.3.3" proxy-addr "^2.0.7" qs "^6.14.0" range-parser "^1.2.1" router "^2.2.0" send "^1.1.0" serve-static "^2.2.0" statuses "^2.0.1" type-is "^2.0.1" vary "^1.1.2" ext-list@^2.0.0: version "2.2.2" resolved "https://registry.npmjs.org/ext-list/-/ext-list-2.2.2.tgz" integrity sha512-u+SQgsubraE6zItfVA0tBuCBhfU9ogSRnsvygI7wht9TS510oLkBRXBsqopeUG/GBOIQyKZO9wjTqIu/sf5zFA== dependencies: mime-db "^1.28.0" ext-name@^5.0.0: version "5.0.0" resolved "https://registry.npmjs.org/ext-name/-/ext-name-5.0.0.tgz" integrity sha512-yblEwXAbGv1VQDmow7s38W77hzAgJAO50ztBLMcUyUBfxv1HC+LGwtiEN+Co6LtlqT/5uwVOxsD4TNIilWhwdQ== dependencies: ext-list "^2.0.0" sort-keys-length "^1.0.0" extend@^3.0.2: version "3.0.2" resolved "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz" integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== extract-zip@^2.0.1: version "2.0.1" resolved "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz" integrity sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg== dependencies: debug "^4.1.1" get-stream "^5.1.0" yauzl "^2.10.0" optionalDependencies: "@types/yauzl" "^2.9.1" fastest-levenshtein@^1.0.16: version "1.0.16" resolved "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz" integrity sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg== faye-websocket@~0.10.0: version "0.10.0" resolved "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz" integrity sha512-Xhj93RXbMSq8urNCUq4p9l0P6hnySJ/7YNRhYNug0bLOuii7pKO7xQFb5mx9xZXWCar88pLPb805PvUkwrLZpQ== dependencies: websocket-driver ">=0.5.1" fd-slicer@~1.1.0: version "1.1.0" resolved "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz" integrity sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g== dependencies: pend "~1.2.0" figures@^1.0.0: version "1.7.0" resolved "https://registry.npmjs.org/figures/-/figures-1.7.0.tgz" integrity sha512-UxKlfCRuCBxSXU4C6t9scbDyWZ4VlaFFdojKtzJuSkuOBQ5CNFum+zZXFwHjo+CxBC1t6zlYPgHIgFjL8ggoEQ== dependencies: escape-string-regexp "^1.0.5" object-assign "^4.1.0" file-sync-cmp@^0.1.0: version "0.1.1" resolved "https://registry.npmjs.org/file-sync-cmp/-/file-sync-cmp-0.1.1.tgz" integrity sha512-0k45oWBokCqh2MOexeYKpyqmGKG+8mQ2Wd8iawx+uWd/weWJQAZ6SoPybagdCI4xFisag8iAR77WPm4h3pTfxA== file-type@^11.1.0: version "11.1.0" resolved "https://registry.npmjs.org/file-type/-/file-type-11.1.0.tgz" integrity sha512-rM0UO7Qm9K7TWTtA6AShI/t7H5BPjDeGVDaNyg9BjHAj3PysKy7+8C8D137R88jnR3rFJZQB/tFgydl5sN5m7g== file-type@^3.8.0: version "3.9.0" resolved "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz" integrity sha512-RLoqTXE8/vPmMuTI88DAzhMYC99I8BWv7zYP4A1puo5HIjEJ5EX48ighy4ZyKMG9EDXxBgW6e++cn7d1xuFghA== file-type@^4.2.0: version "4.4.0" resolved "https://registry.npmjs.org/file-type/-/file-type-4.4.0.tgz" integrity sha512-f2UbFQEk7LXgWpi5ntcO86OeA/cC80fuDDDaX/fZ2ZGel+AF7leRQqBBW1eJNiiQkrZlAoM6P+VYP5P6bOlDEQ== file-type@^5.2.0: version "5.2.0" resolved "https://registry.npmjs.org/file-type/-/file-type-5.2.0.tgz" integrity sha512-Iq1nJ6D2+yIO4c8HHg4fyVb8mAJieo1Oloy1mLLaB2PvezNedhBVm+QU7g0qM42aiMbRXTxKKwGD17rjKNJYVQ== file-type@^6.1.0: version "6.2.0" resolved "https://registry.npmjs.org/file-type/-/file-type-6.2.0.tgz" integrity sha512-YPcTBDV+2Tm0VqjybVd32MHdlEGAtuxS3VAYsumFokDSMG+ROT5wawGlnHDoz7bfMcMDt9hxuXvXwoKUx2fkOg== filename-reserved-regex@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz" integrity sha512-lc1bnsSr4L4Bdif8Xb/qrtokGbq5zlsms/CYH8PP+WtCkGNF65DPiQY8vG3SakEdRn8Dlnm+gW/qWKKjS5sZzQ== filenamify@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/filenamify/-/filenamify-3.0.0.tgz" integrity sha512-5EFZ//MsvJgXjBAFJ+Bh2YaCTRF/VP1YOmGrgt+KJ4SFRLjI87EIdwLLuT6wQX0I4F9W41xutobzczjsOKlI/g== dependencies: filename-reserved-regex "^2.0.0" strip-outer "^1.0.0" trim-repeated "^1.0.0" fill-range@^7.1.1: version "7.1.1" resolved "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz" integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== dependencies: to-regex-range "^5.0.1" finalhandler@^2.1.0: version "2.1.1" resolved "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz" integrity sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA== dependencies: debug "^4.4.0" encodeurl "^2.0.0" escape-html "^1.0.3" on-finished "^2.4.1" parseurl "^1.3.3" statuses "^2.0.1" find-up@^5.0.0: version "5.0.0" resolved "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz" integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== dependencies: locate-path "^6.0.0" path-exists "^4.0.0" findup-sync@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/findup-sync/-/findup-sync-4.0.0.tgz" integrity sha512-6jvvn/12IC4quLBL1KNokxC7wWTvYncaVUYSoxWw7YykPLuRrnv4qdHcSOywOI5RpkOVGeQRtWM8/q+G6W6qfQ== dependencies: detect-file "^1.0.0" is-glob "^4.0.0" micromatch "^4.0.2" resolve-dir "^1.0.1" findup-sync@~5.0.0: version "5.0.0" resolved "https://registry.npmjs.org/findup-sync/-/findup-sync-5.0.0.tgz" integrity sha512-MzwXju70AuyflbgeOhzvQWAvvQdo1XL0A9bVvlXsYcFEBM87WR4OakL4OfZq+QRmr+duJubio+UtNQCPsVESzQ== dependencies: detect-file "^1.0.0" is-glob "^4.0.3" micromatch "^4.0.4" resolve-dir "^1.0.1" fined@^1.2.0: version "1.2.0" resolved "https://registry.npmjs.org/fined/-/fined-1.2.0.tgz" integrity sha512-ZYDqPLGxDkDhDZBjZBb+oD1+j0rA4E0pXY50eplAAOPg2N/gUBSSk5IM1/QhPfyVo19lJ+CvXpqfvk+b2p/8Ng== dependencies: expand-tilde "^2.0.2" is-plain-object "^2.0.3" object.defaults "^1.1.0" object.pick "^1.2.0" parse-filepath "^1.0.1" flagged-respawn@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/flagged-respawn/-/flagged-respawn-1.0.1.tgz" integrity sha512-lNaHNVymajmk0OJMBn8fVUAU1BtDeKIqKoVhk4xAALB57aALg6b4W0MfJ/cUE0g9YBXy5XhSlPIpYIJ7HaY/3Q== flat@^5.0.2: version "5.0.2" resolved "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz" integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== follow-redirects@^1.14.0: version "1.15.9" resolved "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz" integrity sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ== for-in@^1.0.1: version "1.0.2" resolved "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz" integrity sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ== for-own@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz" integrity sha512-0OABksIGrxKK8K4kynWkQ7y1zounQxP+CWnyclVwj81KW3vlLlGUx57DKGcP/LH216GzqnstnPocF16Nxs0Ycg== dependencies: for-in "^1.0.1" foreground-child@^3.1.0: version "3.3.0" resolved "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz" integrity sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg== dependencies: cross-spawn "^7.0.0" signal-exit "^4.0.1" forwarded@0.2.0: version "0.2.0" resolved "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz" integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow== fresh@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz" integrity sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A== from2@^2.1.1: version "2.3.0" resolved "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz" integrity sha512-OMcX/4IC/uqEPVgGeyfN22LJk6AZrMkRZHxcHBMBvHScDGgwTm2GT2Wkgtocyd3JfZffjj2kYUDXXII0Fk9W0g== dependencies: inherits "^2.0.1" readable-stream "^2.0.0" fs-constants@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz" integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== fs-extra@^11.3.0: version "11.3.0" resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.0.tgz" integrity sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew== dependencies: graceful-fs "^4.2.0" jsonfile "^6.0.1" universalify "^2.0.0" fs-minipass@^2.0.0: version "2.1.0" resolved "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz" integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg== dependencies: minipass "^3.0.0" fs-minipass@^3.0.0, fs-minipass@^3.0.3: version "3.0.3" resolved "https://registry.npmjs.org/fs-minipass/-/fs-minipass-3.0.3.tgz" integrity sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw== dependencies: minipass "^7.0.3" fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== fsevents@~2.3.2: version "2.3.3" resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== function-bind@^1.1.2: version "1.1.2" resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz" integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== fzstd@^0.1.1: version "0.1.1" resolved "https://registry.npmjs.org/fzstd/-/fzstd-0.1.1.tgz" integrity sha512-dkuVSOKKwh3eas5VkJy1AW1vFpet8TA/fGmVA5krThl8YcOVE/8ZIoEA1+U1vEn5ckxxhLirSdY837azmbaNHA== gaze@^1.1.0: version "1.1.3" resolved "https://registry.npmjs.org/gaze/-/gaze-1.1.3.tgz" integrity sha512-BRdNm8hbWzFzWHERTrejLqwHDfS4GibPoq5wjTPIoJHoBtKGPg3xAFfxmM+9ztbXelxcf2hwQcaz1PtmFeue8g== dependencies: globule "^1.0.0" get-caller-file@^2.0.5: version "2.0.5" resolved "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== get-intrinsic@^1.2.5, get-intrinsic@^1.2.6: version "1.2.6" resolved "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.6.tgz" integrity sha512-qxsEs+9A+u85HhllWJJFicJfPDhRmjzoYdl64aMWW9yRIJmSyxdn8IEkuIM530/7T+lv0TIHd8L6Q/ra0tEoeA== dependencies: call-bind-apply-helpers "^1.0.1" dunder-proto "^1.0.0" es-define-property "^1.0.1" es-errors "^1.3.0" es-object-atoms "^1.0.0" function-bind "^1.1.2" gopd "^1.2.0" has-symbols "^1.1.0" hasown "^2.0.2" math-intrinsics "^1.0.0" get-stream@3.0.0, get-stream@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz" integrity sha512-GlhdIUuVakc8SJ6kK0zAFbiGzRFzNnY4jUuEbV9UROo4Y+0Ny4fjvcZFVTeDA4odpFyOQzaw6hXukJSq/f28sQ== get-stream@^2.2.0: version "2.3.1" resolved "https://registry.npmjs.org/get-stream/-/get-stream-2.3.1.tgz" integrity sha512-AUGhbbemXxrZJRD5cDvKtQxLuYaIbNtDTK8YqupCI393Q2KSTreEsLUN3ZxAWFGiKTzL6nKuzfcIvieflUX9qA== dependencies: object-assign "^4.0.1" pinkie-promise "^2.0.0" get-stream@^4.1.0: version "4.1.0" resolved "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz" integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w== dependencies: pump "^3.0.0" get-stream@^5.1.0: version "5.2.0" resolved "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz" integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== dependencies: pump "^3.0.0" getobject@~1.0.0: version "1.0.2" resolved "https://registry.npmjs.org/getobject/-/getobject-1.0.2.tgz" integrity sha512-2zblDBaFcb3rB4rF77XVnuINOE2h2k/OnqXAiy0IrTxUfV1iFp3la33oAQVY9pCpWU268WFYVt2t71hlMuLsOg== github-api@^3.4.0: version "3.4.0" resolved "https://registry.npmjs.org/github-api/-/github-api-3.4.0.tgz" integrity sha512-2yYqYS6Uy4br1nw0D3VrlYWxtGTkUhIZrumBrcBwKdBOzMT8roAe8IvI6kjIOkxqxapKR5GkEsHtz3Du/voOpA== dependencies: axios "^0.21.1" debug "^2.2.0" js-base64 "^2.1.9" utf8 "^2.1.1" glob-parent@~5.1.2: version "5.1.2" resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz" integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== dependencies: is-glob "^4.0.1" glob-promise@^6.0.7: version "6.0.7" resolved "https://registry.npmjs.org/glob-promise/-/glob-promise-6.0.7.tgz" integrity sha512-DEAe6br1w8ZF+y6KM2pzgdfhpreladtNvyNNVgSkxxkFWzXTJFXxQrJQQbAnc7kL0EUd7w5cR8u4K0P4+/q+Gw== glob@^10.2.2, glob@^10.3.10, glob@^10.3.7, glob@^10.4.5: version "10.4.5" resolved "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz" integrity sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg== dependencies: foreground-child "^3.1.0" jackspeak "^3.1.2" minimatch "^9.0.4" minipass "^7.1.2" package-json-from-dist "^1.0.0" path-scurry "^1.11.1" glob@^7.1.3: version "7.2.3" resolved "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz" integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== dependencies: fs.realpath "^1.0.0" inflight "^1.0.4" inherits "2" minimatch "^3.1.1" once "^1.3.0" path-is-absolute "^1.0.0" glob@^8.0.3: version "8.1.0" resolved "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz" integrity sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ== dependencies: fs.realpath "^1.0.0" inflight "^1.0.4" inherits "2" minimatch "^5.0.1" once "^1.3.0" glob@~7.1.1, glob@~7.1.6: version "7.1.7" resolved "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz" integrity sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ== dependencies: fs.realpath "^1.0.0" inflight "^1.0.4" inherits "2" minimatch "^3.0.4" once "^1.3.0" path-is-absolute "^1.0.0" global-modules@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz" integrity sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg== dependencies: global-prefix "^1.0.1" is-windows "^1.0.1" resolve-dir "^1.0.0" global-prefix@^1.0.1: version "1.0.2" resolved "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz" integrity sha512-5lsx1NUDHtSjfg0eHlmYvZKv8/nVqX4ckFbM+FrGcQ+04KWcWFo9P5MxPZYSzUvyzmdTbI7Eix8Q4IbELDqzKg== dependencies: expand-tilde "^2.0.2" homedir-polyfill "^1.0.1" ini "^1.3.4" is-windows "^1.0.1" which "^1.2.14" globule@^1.0.0: version "1.3.4" resolved "https://registry.npmjs.org/globule/-/globule-1.3.4.tgz" integrity sha512-OPTIfhMBh7JbBYDpa5b+Q5ptmMWKwcNcFSR/0c6t8V4f3ZAVBEsKNY37QdVqmLRYSMhOUGYrY0QhSoEpzGr/Eg== dependencies: glob "~7.1.1" lodash "^4.17.21" minimatch "~3.0.2" gopd@^1.2.0: version "1.2.0" resolved "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz" integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg== got@^8.3.1: version "8.3.2" resolved "https://registry.npmjs.org/got/-/got-8.3.2.tgz" integrity sha512-qjUJ5U/hawxosMryILofZCkm3C84PLJS/0grRIpjAwu+Lkxxj5cxeCU25BG0/3mDSpXKTyZr8oh8wIgLaH0QCw== dependencies: "@sindresorhus/is" "^0.7.0" cacheable-request "^2.1.1" decompress-response "^3.3.0" duplexer3 "^0.1.4" get-stream "^3.0.0" into-stream "^3.1.0" is-retry-allowed "^1.1.0" isurl "^1.0.0-alpha5" lowercase-keys "^1.0.0" mimic-response "^1.0.0" p-cancelable "^0.4.0" p-timeout "^2.0.1" pify "^3.0.0" safe-buffer "^5.1.1" timed-out "^4.0.1" url-parse-lax "^3.0.0" url-to-options "^1.0.1" graceful-fs@^4.1.10, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.11, graceful-fs@^4.2.6: version "4.2.11" resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz" integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== groq-sdk@^1.1.2: version "1.1.2" resolved "https://registry.npmjs.org/groq-sdk/-/groq-sdk-1.1.2.tgz" integrity sha512-CZO0XUQQDhn43ri1+lZHxZKpb+bGutgTvFmCJtooexiitGmPqhm1hntOT3nCoaq07e+OpeokVnfUs0i/oQuUaQ== grunt-cli@~1.4.3: version "1.4.3" resolved "https://registry.npmjs.org/grunt-cli/-/grunt-cli-1.4.3.tgz" integrity sha512-9Dtx/AhVeB4LYzsViCjUQkd0Kw0McN2gYpdmGYKtE2a5Yt7v1Q+HYZVWhqXc/kGnxlMtqKDxSwotiGeFmkrCoQ== dependencies: grunt-known-options "~2.0.0" interpret "~1.1.0" liftup "~3.0.1" nopt "~4.0.1" v8flags "~3.2.0" grunt-contrib-clean@^2.0.1: version "2.0.1" resolved "https://registry.npmjs.org/grunt-contrib-clean/-/grunt-contrib-clean-2.0.1.tgz" integrity sha512-uRvnXfhiZt8akb/ZRDHJpQQtkkVkqc/opWO4Po/9ehC2hPxgptB9S6JHDC/Nxswo4CJSM0iFPT/Iym3cEMWzKA== dependencies: async "^3.2.3" rimraf "^2.6.2" grunt-contrib-copy@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/grunt-contrib-copy/-/grunt-contrib-copy-1.0.0.tgz" integrity sha512-gFRFUB0ZbLcjKb67Magz1yOHGBkyU6uL29hiEW1tdQ9gQt72NuMKIy/kS6dsCbV0cZ0maNCb0s6y+uT1FKU7jA== dependencies: chalk "^1.1.1" file-sync-cmp "^0.1.0" grunt-contrib-htmlmin@^3.1.0: version "3.1.0" resolved "https://registry.npmjs.org/grunt-contrib-htmlmin/-/grunt-contrib-htmlmin-3.1.0.tgz" integrity sha512-Khaa+0MUuqqNroDIe9tsjZkioZnW2Y+iTGbonBkLWaG7+SkSFExfb4jLt7M6rxKV3RSqlS7NtVvu4SVIPkmKXg== dependencies: chalk "^2.4.2" html-minifier "^4.0.0" pretty-bytes "^5.1.0" grunt-contrib-watch@^1.1.0: version "1.1.0" resolved "https://registry.npmjs.org/grunt-contrib-watch/-/grunt-contrib-watch-1.1.0.tgz" integrity sha512-yGweN+0DW5yM+oo58fRu/XIRrPcn3r4tQx+nL7eMRwjpvk+rQY6R8o94BPK0i2UhTg9FN21hS+m8vR8v9vXfeg== dependencies: async "^2.6.0" gaze "^1.1.0" lodash "^4.17.10" tiny-lr "^1.1.1" grunt-known-options@~2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/grunt-known-options/-/grunt-known-options-2.0.0.tgz" integrity sha512-GD7cTz0I4SAede1/+pAbmJRG44zFLPipVtdL9o3vqx9IEyb7b4/Y3s7r6ofI3CchR5GvYJ+8buCSioDv5dQLiA== grunt-legacy-log-utils@~2.1.0: version "2.1.0" resolved "https://registry.npmjs.org/grunt-legacy-log-utils/-/grunt-legacy-log-utils-2.1.0.tgz" integrity sha512-lwquaPXJtKQk0rUM1IQAop5noEpwFqOXasVoedLeNzaibf/OPWjKYvvdqnEHNmU+0T0CaReAXIbGo747ZD+Aaw== dependencies: chalk "~4.1.0" lodash "~4.17.19" grunt-legacy-log@~3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/grunt-legacy-log/-/grunt-legacy-log-3.0.0.tgz" integrity sha512-GHZQzZmhyq0u3hr7aHW4qUH0xDzwp2YXldLPZTCjlOeGscAOWWPftZG3XioW8MasGp+OBRIu39LFx14SLjXRcA== dependencies: colors "~1.1.2" grunt-legacy-log-utils "~2.1.0" hooker "~0.2.3" lodash "~4.17.19" grunt-legacy-util@~2.0.1: version "2.0.1" resolved "https://registry.npmjs.org/grunt-legacy-util/-/grunt-legacy-util-2.0.1.tgz" integrity sha512-2bQiD4fzXqX8rhNdXkAywCadeqiPiay0oQny77wA2F3WF4grPJXCvAcyoWUJV+po/b15glGkxuSiQCK299UC2w== dependencies: async "~3.2.0" exit "~0.1.2" getobject "~1.0.0" hooker "~0.2.3" lodash "~4.17.21" underscore.string "~3.3.5" which "~2.0.2" grunt@^1.6.1: version "1.6.1" resolved "https://registry.npmjs.org/grunt/-/grunt-1.6.1.tgz" integrity sha512-/ABUy3gYWu5iBmrUSRBP97JLpQUm0GgVveDCp6t3yRNIoltIYw7rEj3g5y1o2PGPR2vfTRGa7WC/LZHLTXnEzA== dependencies: dateformat "~4.6.2" eventemitter2 "~0.4.13" exit "~0.1.2" findup-sync "~5.0.0" glob "~7.1.6" grunt-cli "~1.4.3" grunt-known-options "~2.0.0" grunt-legacy-log "~3.0.0" grunt-legacy-util "~2.0.1" iconv-lite "~0.6.3" js-yaml "~3.14.0" minimatch "~3.0.4" nopt "~3.0.6" has-ansi@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz" integrity sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg== dependencies: ansi-regex "^2.0.0" has-flag@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz" integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== has-flag@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== has-symbol-support-x@^1.4.1: version "1.4.2" resolved "https://registry.npmjs.org/has-symbol-support-x/-/has-symbol-support-x-1.4.2.tgz" integrity sha512-3ToOva++HaW+eCpgqZrCfN51IPB+7bJNVT6CUATzueB5Heb8o6Nam0V3HG5dlDvZU1Gn5QLcbahiKw/XVk5JJw== has-symbols@^1.1.0: version "1.1.0" resolved "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz" integrity sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ== has-to-string-tag-x@^1.2.0: version "1.4.1" resolved "https://registry.npmjs.org/has-to-string-tag-x/-/has-to-string-tag-x-1.4.1.tgz" integrity sha512-vdbKfmw+3LoOYVr+mtxHaX5a96+0f3DljYd8JOqvOLsf5mw2Otda2qCDT9qRqLAhrjyQ0h7ual5nOiASpsGNFw== dependencies: has-symbol-support-x "^1.4.1" hasown@^2.0.2: version "2.0.2" resolved "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz" integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== dependencies: function-bind "^1.1.2" he@^1.2.0: version "1.2.0" resolved "https://registry.npmjs.org/he/-/he-1.2.0.tgz" integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== homedir-polyfill@^1.0.1: version "1.0.3" resolved "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz" integrity sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA== dependencies: parse-passwd "^1.0.0" hooker@^0.2.3, hooker@~0.2.3: version "0.2.3" resolved "https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz" integrity sha512-t+UerCsQviSymAInD01Pw+Dn/usmz1sRO+3Zk1+lx8eg+WKpD2ulcwWqHHL0+aseRBr+3+vIhiG1K1JTwaIcTA== hosted-git-info@^8.0.0, hosted-git-info@^8.0.2: version "8.0.2" resolved "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-8.0.2.tgz" integrity sha512-sYKnA7eGln5ov8T8gnYlkSOxFJvywzEx9BueN6xo/GKO8PGiI6uK6xx+DIGe45T3bdVjLAQDQW1aicT8z8JwQg== dependencies: lru-cache "^10.0.1" html-minifier@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/html-minifier/-/html-minifier-4.0.0.tgz" integrity sha512-aoGxanpFPLg7MkIl/DDFYtb0iWz7jMFGqFhvEDZga6/4QTjneiD8I/NXL1x5aaoCp7FSIT6h/OhykDdPsbtMig== dependencies: camel-case "^3.0.0" clean-css "^4.2.1" commander "^2.19.0" he "^1.2.0" param-case "^2.1.1" relateurl "^0.2.7" uglify-js "^3.5.1" http-cache-semantics@3.8.1: version "3.8.1" resolved "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz" integrity sha512-5ai2iksyV8ZXmnZhHH4rWPoxxistEexSi5936zIQ1bnNTW5VnA85B6P/VpXiRM017IgRvb2kKo1a//y+0wSp3w== http-cache-semantics@^4.1.1: version "4.1.1" resolved "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz" integrity sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ== http-errors@^2.0.0, http-errors@^2.0.1, http-errors@~2.0.1: version "2.0.1" resolved "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz" integrity sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ== dependencies: depd "~2.0.0" inherits "~2.0.4" setprototypeof "~1.2.0" statuses "~2.0.2" toidentifier "~1.0.1" http-parser-js@>=0.5.1: version "0.5.10" resolved "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.10.tgz" integrity sha512-Pysuw9XpUq5dVc/2SMHpuTY01RFl8fttgcyunjL7eEMhGM3cI4eOmiCycJDVCo/7O7ClfQD3SaI6ftDzqOXYMA== http-proxy-agent@^7.0.0: version "7.0.2" resolved "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz" integrity sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig== dependencies: agent-base "^7.1.0" debug "^4.3.4" https-proxy-agent@^7.0.1: version "7.0.6" resolved "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz" integrity sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw== dependencies: agent-base "^7.1.2" debug "4" iconv-lite@^0.6.2, iconv-lite@~0.6.3: version "0.6.3" resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz" integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== dependencies: safer-buffer ">= 2.1.2 < 3.0.0" iconv-lite@^0.7.0, iconv-lite@~0.7.0: version "0.7.2" resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz" integrity sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw== dependencies: safer-buffer ">= 2.1.2 < 3.0.0" ieee754@^1.1.13: version "1.2.1" resolved "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== ignore-by-default@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz" integrity sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA== ignore-walk@^7.0.0: version "7.0.0" resolved "https://registry.npmjs.org/ignore-walk/-/ignore-walk-7.0.0.tgz" integrity sha512-T4gbf83A4NH95zvhVYZc+qWocBBGlpzUXLPGurJggw/WIOwicfXJChLDP/iBZnN5WqROSu5Bm3hhle4z8a8YGQ== dependencies: minimatch "^9.0.0" imurmurhash@^0.1.4: version "0.1.4" resolved "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz" integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== inflight@^1.0.4: version "1.0.6" resolved "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz" integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== dependencies: once "^1.3.0" wrappy "1" inherits@2, inherits@^2.0.1, inherits@~2.0.3, inherits@~2.0.4: version "2.0.4" resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== ini@^1.3.4: version "1.3.8" resolved "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz" integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== ini@^5.0.0: version "5.0.0" resolved "https://registry.npmjs.org/ini/-/ini-5.0.0.tgz" integrity sha512-+N0ngpO3e7cRUWOJAS7qw0IZIVc6XPrW4MlFBdD066F2L4k1L6ker3hLqSq7iXxU5tgS4WGkIUElWn5vogAEnw== init-package-json@^8.0.0: version "8.0.0" resolved "https://registry.npmjs.org/init-package-json/-/init-package-json-8.0.0.tgz" integrity sha512-zKgxfaGt6Zzi8VBSInOK0CYDigA9gzDCWPnSzGIoUlTU/5w7qIyi+6MyJYX96mMlxDGrIR85FhQszVyodYfB9g== dependencies: "@npmcli/package-json" "^6.1.0" npm-package-arg "^12.0.0" promzard "^2.0.0" read "^4.0.0" semver "^7.3.5" validate-npm-package-license "^3.0.4" validate-npm-package-name "^6.0.0" interpret@~1.1.0: version "1.1.0" resolved "https://registry.npmjs.org/interpret/-/interpret-1.1.0.tgz" integrity sha512-CLM8SNMDu7C5psFCn6Wg/tgpj/bKAg7hc2gWqcuR9OD5Ft9PhBpIu8PLicPeis+xDd6YX2ncI8MCA64I9tftIA== into-stream@^3.1.0: version "3.1.0" resolved "https://registry.npmjs.org/into-stream/-/into-stream-3.1.0.tgz" integrity sha512-TcdjPibTksa1NQximqep2r17ISRiNE9fwlfbg3F8ANdvP5/yrFTew86VcO//jk4QTaMlbjypPBq76HN2zaKfZQ== dependencies: from2 "^2.1.1" p-is-promise "^1.1.0" ioredis@^5.10.1: version "5.10.1" resolved "https://registry.npmjs.org/ioredis/-/ioredis-5.10.1.tgz" integrity sha512-HuEDBTI70aYdx1v6U97SbNx9F1+svQKBDo30o0b9fw055LMepzpOOd0Ccg9Q6tbqmBSJaMuY0fB7yw9/vjBYCA== dependencies: "@ioredis/commands" "1.5.1" cluster-key-slot "^1.1.0" debug "^4.3.4" denque "^2.1.0" lodash.defaults "^4.2.0" lodash.isarguments "^3.1.0" redis-errors "^1.2.0" redis-parser "^3.0.0" standard-as-callback "^2.1.0" ip-address@^9.0.5: version "9.0.5" resolved "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz" integrity sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g== dependencies: jsbn "1.1.0" sprintf-js "^1.1.3" ip-regex@^5.0.0: version "5.0.0" resolved "https://registry.npmjs.org/ip-regex/-/ip-regex-5.0.0.tgz" integrity sha512-fOCG6lhoKKakwv+C6KdsOnGvgXnmgfmp0myi3bcNwj3qfwPAxRKWEuFhvEFF7ceYIz6+1jRZ+yguLFAmUNPEfw== ipaddr.js@1.9.1: version "1.9.1" resolved "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz" integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== is-absolute@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/is-absolute/-/is-absolute-1.0.0.tgz" integrity sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA== dependencies: is-relative "^1.0.0" is-windows "^1.0.1" is-binary-path@~2.1.0: version "2.1.0" resolved "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz" integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== dependencies: binary-extensions "^2.0.0" is-cidr@^5.1.0: version "5.1.0" resolved "https://registry.npmjs.org/is-cidr/-/is-cidr-5.1.0.tgz" integrity sha512-OkVS+Ht2ssF27d48gZdB+ho1yND1VbkJRKKS6Pc1/Cw7uqkd9IOJg8/bTwBDQL6tfBhSdguPRnlGiE8pU/X5NQ== dependencies: cidr-regex "^4.1.1" is-core-module@^2.16.0: version "2.16.1" resolved "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz" integrity sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w== dependencies: hasown "^2.0.2" is-extglob@^2.1.1: version "2.1.1" resolved "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz" integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== is-finite@^1.0.1: version "1.1.0" resolved "https://registry.npmjs.org/is-finite/-/is-finite-1.1.0.tgz" integrity sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w== is-fullwidth-code-point@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz" integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: version "4.0.3" resolved "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz" integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== dependencies: is-extglob "^2.1.1" is-natural-number@^4.0.1: version "4.0.1" resolved "https://registry.npmjs.org/is-natural-number/-/is-natural-number-4.0.1.tgz" integrity sha512-Y4LTamMe0DDQIIAlaer9eKebAlDSV6huy+TWhJVPlzZh2o4tRP5SQWFlLn5N0To4mDD22/qdOq+veo1cSISLgQ== is-number@^7.0.0: version "7.0.0" resolved "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz" integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== is-object@^1.0.1: version "1.0.2" resolved "https://registry.npmjs.org/is-object/-/is-object-1.0.2.tgz" integrity sha512-2rRIahhZr2UWb45fIOuvZGpFtz0TyOZLf32KxBbSoUCeZR495zCKlWUKKUByk3geS2eAs7ZAABt0Y/Rx0GiQGA== is-plain-obj@^1.0.0: version "1.1.0" resolved "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz" integrity sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg== is-plain-obj@^2.1.0: version "2.1.0" resolved "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz" integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA== is-plain-object@^2.0.3, is-plain-object@^2.0.4: version "2.0.4" resolved "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz" integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== dependencies: isobject "^3.0.1" is-promise@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz" integrity sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ== is-relative@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/is-relative/-/is-relative-1.0.0.tgz" integrity sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA== dependencies: is-unc-path "^1.0.0" is-retry-allowed@^1.1.0: version "1.2.0" resolved "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz" integrity sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg== is-stream@^1.1.0: version "1.1.0" resolved "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz" integrity sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ== is-unc-path@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/is-unc-path/-/is-unc-path-1.0.0.tgz" integrity sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ== dependencies: unc-path-regex "^0.1.2" is-unicode-supported@^0.1.0: version "0.1.0" resolved "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz" integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== is-windows@^1.0.1: version "1.0.2" resolved "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz" integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== isarray@~1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz" integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== isexe@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz" integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== isexe@^3.1.1: version "3.1.1" resolved "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz" integrity sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ== isobject@^3.0.0, isobject@^3.0.1: version "3.0.1" resolved "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz" integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg== isurl@^1.0.0-alpha5: version "1.0.0" resolved "https://registry.npmjs.org/isurl/-/isurl-1.0.0.tgz" integrity sha512-1P/yWsxPlDtn7QeRD+ULKQPaIaN6yF368GZ2vDfv0AL0NwpStafjWCDDdn0k8wgFMWpVAqG7oJhxHnlud42i9w== dependencies: has-to-string-tag-x "^1.2.0" is-object "^1.0.1" jackspeak@^3.1.2: version "3.4.3" resolved "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz" integrity sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw== dependencies: "@isaacs/cliui" "^8.0.2" optionalDependencies: "@pkgjs/parseargs" "^0.11.0" jit-grunt@^0.10.0: version "0.10.0" resolved "https://registry.npmjs.org/jit-grunt/-/jit-grunt-0.10.0.tgz" integrity sha512-eT/f4c9wgZ3buXB7X1JY1w6uNtAV0bhrbOGf/mFmBb0CDNLUETJ/VRoydayWOI54tOoam0cz9RooVCn3QY1WoA== js-base64@^2.1.9: version "2.6.4" resolved "https://registry.npmjs.org/js-base64/-/js-base64-2.6.4.tgz" integrity sha512-pZe//GGmwJndub7ZghVHz7vjb2LgC1m8B07Au3eYqeqv9emhESByMXxaEgkUkEqJe87oBbSniGYoQNIBklc7IQ== js-yaml@^4.1.0: version "4.1.0" resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz" integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== dependencies: argparse "^2.0.1" js-yaml@~3.14.0: version "3.14.1" resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz" integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== dependencies: argparse "^1.0.7" esprima "^4.0.0" jsbn@1.1.0: version "1.1.0" resolved "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz" integrity sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A== json-buffer@3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz" integrity sha512-CuUqjv0FUZIdXkHPI8MezCnFCdaTAacej1TZYulLoAg1h/PhwkdXFN4V/gzY4g+fMBCOV2xF+rp7t2XD2ns/NQ== json-parse-even-better-errors@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-4.0.0.tgz" integrity sha512-lR4MXjGNgkJc7tkQ97kb2nuEMnNCyU//XYVH0MKTGcXEiSudQ5MKGKen3C5QubYy0vmq+JGitUg92uuywGEwIA== json-stringify-nice@^1.1.4: version "1.1.4" resolved "https://registry.npmjs.org/json-stringify-nice/-/json-stringify-nice-1.1.4.tgz" integrity sha512-5Z5RFW63yxReJ7vANgW6eZFGWaQvnPE3WNmZoOJrSkGju2etKA2L5rrOa1sm877TVTFt57A80BH1bArcmlLfPw== jsonfile@^6.0.1: version "6.1.0" resolved "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz" integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ== dependencies: universalify "^2.0.0" optionalDependencies: graceful-fs "^4.1.6" jsonparse@^1.3.1: version "1.3.1" resolved "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz" integrity sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg== just-diff-apply@^5.2.0: version "5.5.0" resolved "https://registry.npmjs.org/just-diff-apply/-/just-diff-apply-5.5.0.tgz" integrity sha512-OYTthRfSh55WOItVqwpefPtNt2VdKsq5AnAK6apdtR6yCH8pr0CmSr710J0Mf+WdQy7K/OzMy7K2MgAfdQURDw== just-diff@^6.0.0: version "6.0.2" resolved "https://registry.npmjs.org/just-diff/-/just-diff-6.0.2.tgz" integrity sha512-S59eriX5u3/QhMNq3v/gm8Kd0w8OS6Tz2FS1NG4blv+z0MuQcBRJyFWjdovM0Rad4/P4aUPFtnkNjMjyMlMSYA== keyv@3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/keyv/-/keyv-3.0.0.tgz" integrity sha512-eguHnq22OE3uVoSYG0LVWNP+4ppamWr9+zWBe1bsNcovIMy6huUJFPgy4mGwCd/rnl3vOLGW1MTlu4c57CT1xA== dependencies: json-buffer "3.0.0" kind-of@^6.0.2: version "6.0.3" resolved "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz" integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== libnpmaccess@^10.0.0: version "10.0.0" resolved "https://registry.npmjs.org/libnpmaccess/-/libnpmaccess-10.0.0.tgz" integrity sha512-Nz9Lolajvh6nPA5ixdKNfN2BJS0N7LvqTXPqy3+F37i3T4mcped24JCjwnp5KCPCB0ewX3ccopwUnhaTS1/yXg== dependencies: npm-package-arg "^12.0.0" npm-registry-fetch "^18.0.1" libnpmdiff@^8.0.0: version "8.0.0" resolved "https://registry.npmjs.org/libnpmdiff/-/libnpmdiff-8.0.0.tgz" integrity sha512-Ul1oGHnlQ9+bpNdaKDXZEqolJxg81xp2KG5QgVZThSS/ypLFpkcQjHeOHR99ZdxK483s1z2vdiCUMghpDo+0pg== dependencies: "@npmcli/arborist" "^9.0.0" "@npmcli/installed-package-contents" "^3.0.0" binary-extensions "^3.0.0" diff "^7.0.0" minimatch "^9.0.4" npm-package-arg "^12.0.0" pacote "^21.0.0" tar "^6.2.1" libnpmexec@^10.0.0: version "10.0.0" resolved "https://registry.npmjs.org/libnpmexec/-/libnpmexec-10.0.0.tgz" integrity sha512-kB1d44LhwvUT/0Ln+rwM8Yw2QmHZ2wSCJaqXnw5K17Iuzqy+TKSlNJNWUlLSTAD/WSGLT6lruZO09H+uRLAAdw== dependencies: "@npmcli/arborist" "^9.0.0" "@npmcli/run-script" "^9.0.1" ci-info "^4.0.0" npm-package-arg "^12.0.0" pacote "^21.0.0" proc-log "^5.0.0" read "^4.0.0" read-package-json-fast "^4.0.0" semver "^7.3.7" walk-up-path "^4.0.0" libnpmfund@^7.0.0: version "7.0.0" resolved "https://registry.npmjs.org/libnpmfund/-/libnpmfund-7.0.0.tgz" integrity sha512-uiL9lsC2/E0JX/+gKfwlsGj2W5Cd0MHss5w0yZ7gSIyYn+KhytOBdD/1GB6Kwg5V7LAm07XEEbnWYwzUBS0Tjw== dependencies: "@npmcli/arborist" "^9.0.0" libnpmorg@^8.0.0: version "8.0.0" resolved "https://registry.npmjs.org/libnpmorg/-/libnpmorg-8.0.0.tgz" integrity sha512-VO/mxds3Qu67S7/3TsFbykN+7kzpes14P/RiO3ECtLtUYQdlE5ddXGArRgU2tP4hUHZRvyBhc4sSiAXEzTA4eQ== dependencies: aproba "^2.0.0" npm-registry-fetch "^18.0.1" libnpmpack@^9.0.0: version "9.0.0" resolved "https://registry.npmjs.org/libnpmpack/-/libnpmpack-9.0.0.tgz" integrity sha512-6UBeHebxorKuFZq6qQDV1PIjfZo6OvyzXMfI2UsRtrhlFDsOQsg9H2mZQwPryQXTOO/ifHxnjpmZmJdVn5INTg== dependencies: "@npmcli/arborist" "^9.0.0" "@npmcli/run-script" "^9.0.1" npm-package-arg "^12.0.0" pacote "^21.0.0" libnpmpublish@^11.0.0: version "11.0.0" resolved "https://registry.npmjs.org/libnpmpublish/-/libnpmpublish-11.0.0.tgz" integrity sha512-c+cBWLWXafHzmSEQwRVKjHP6KkWntvqvAAT83agwmWrOwRpEXWDtiIlkopwzPcLRau6BcS6BwOttTlAWboH3BQ== dependencies: ci-info "^4.0.0" normalize-package-data "^7.0.0" npm-package-arg "^12.0.0" npm-registry-fetch "^18.0.1" proc-log "^5.0.0" semver "^7.3.7" sigstore "^3.0.0" ssri "^12.0.0" libnpmsearch@^9.0.0: version "9.0.0" resolved "https://registry.npmjs.org/libnpmsearch/-/libnpmsearch-9.0.0.tgz" integrity sha512-uMUbX5ynU/imuXlijCPathemyi1EZVtka9PEbaIqghdrjdHmMJITbyTsmSB+muzBWm1NUUFwRRKdpwktEmvipg== dependencies: npm-registry-fetch "^18.0.1" libnpmteam@^8.0.0: version "8.0.0" resolved "https://registry.npmjs.org/libnpmteam/-/libnpmteam-8.0.0.tgz" integrity sha512-GfbxITlY4rVe3PKUU6wBjfNNc4Xho9Jv03N0sdzqho9H+9hynFjiwJpfWGwfVBdtimH+kPQW58qRUMott/Bkyg== dependencies: aproba "^2.0.0" npm-registry-fetch "^18.0.1" libnpmversion@^8.0.0: version "8.0.0" resolved "https://registry.npmjs.org/libnpmversion/-/libnpmversion-8.0.0.tgz" integrity sha512-nqHD/YQtC/xLRquvFj2W2hvTNAIWSssJdz5ULCV0jAGBxjlQaPS9s8FNIiJ3w+iina+pCJo5AmlBjA7oWew0JQ== dependencies: "@npmcli/git" "^6.0.1" "@npmcli/run-script" "^9.0.1" json-parse-even-better-errors "^4.0.0" proc-log "^5.0.0" semver "^7.3.7" liftup@~3.0.1: version "3.0.1" resolved "https://registry.npmjs.org/liftup/-/liftup-3.0.1.tgz" integrity sha512-yRHaiQDizWSzoXk3APcA71eOI/UuhEkNN9DiW2Tt44mhYzX4joFoCZlxsSOF7RyeLlfqzFLQI1ngFq3ggMPhOw== dependencies: extend "^3.0.2" findup-sync "^4.0.0" fined "^1.2.0" flagged-respawn "^1.0.1" is-plain-object "^2.0.4" object.map "^1.0.1" rechoir "^0.7.0" resolve "^1.19.0" livereload-js@^2.3.0: version "2.4.0" resolved "https://registry.npmjs.org/livereload-js/-/livereload-js-2.4.0.tgz" integrity sha512-XPQH8Z2GDP/Hwz2PCDrh2mth4yFejwA1OZ/81Ti3LgKyhDcEjsSsqFWZojHG0va/duGd+WyosY7eXLDoOyqcPw== locate-path@^6.0.0: version "6.0.0" resolved "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz" integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== dependencies: p-locate "^5.0.0" lodash-es@^4.18.1: version "4.18.1" resolved "https://registry.npmjs.org/lodash-es/-/lodash-es-4.18.1.tgz" integrity sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A== lodash.defaults@^4.2.0: version "4.2.0" resolved "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz" integrity sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ== lodash.isarguments@^3.1.0: version "3.1.0" resolved "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz" integrity sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg== lodash@^4.17.10, lodash@^4.17.14, lodash@^4.17.21, lodash@~4.17.19, lodash@~4.17.21: version "4.17.21" resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== log-symbols@^4.1.0: version "4.1.0" resolved "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz" integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== dependencies: chalk "^4.1.0" is-unicode-supported "^0.1.0" lower-case@^1.1.1: version "1.1.4" resolved "https://registry.npmjs.org/lower-case/-/lower-case-1.1.4.tgz" integrity sha512-2Fgx1Ycm599x+WGpIYwJOvsjmXFzTSc34IwDWALRA/8AopUKAVPwfJ+h5+f85BCp0PWmmJcWzEpxOpoXycMpdA== lowercase-keys@1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.0.tgz" integrity sha512-RPlX0+PHuvxVDZ7xX+EBVAp4RsVxP/TdDSN2mJYdiq1Lc4Hz7EUSjUI7RZrKKlmrIzVhf6Jo2stj7++gVarS0A== lowercase-keys@^1.0.0: version "1.0.1" resolved "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz" integrity sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA== lru-cache@^10.0.1, lru-cache@^10.2.0, lru-cache@^10.2.2: version "10.4.3" resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz" integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ== lz4js@^0.2.0: version "0.2.0" resolved "https://registry.npmjs.org/lz4js/-/lz4js-0.2.0.tgz" integrity sha512-gY2Ia9Lm7Ep8qMiuGRhvUq0Q7qUereeldZPP1PMEJxPtEWHJLqw9pgX68oHajBH0nzJK4MaZEA/YNV3jT8u8Bg== make-dir@^1.0.0: version "1.3.0" resolved "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz" integrity sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ== dependencies: pify "^3.0.0" make-dir@^2.1.0: version "2.1.0" resolved "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz" integrity sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA== dependencies: pify "^4.0.1" semver "^5.6.0" make-fetch-happen@^14.0.0, make-fetch-happen@^14.0.1, make-fetch-happen@^14.0.3: version "14.0.3" resolved "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-14.0.3.tgz" integrity sha512-QMjGbFTP0blj97EeidG5hk/QhKQ3T4ICckQGLgz38QF7Vgbk6e6FTARN8KhKxyBbWn8R0HU+bnw8aSoFPD4qtQ== dependencies: "@npmcli/agent" "^3.0.0" cacache "^19.0.1" http-cache-semantics "^4.1.1" minipass "^7.0.2" minipass-fetch "^4.0.0" minipass-flush "^1.0.5" minipass-pipeline "^1.2.4" negotiator "^1.0.0" proc-log "^5.0.0" promise-retry "^2.0.1" ssri "^12.0.0" make-iterator@^1.0.0: version "1.0.1" resolved "https://registry.npmjs.org/make-iterator/-/make-iterator-1.0.1.tgz" integrity sha512-pxiuXh0iVEq7VM7KMIhs5gxsfxCux2URptUQaXo4iZZJxBAzTPOLE2BumO5dbfVYq/hBJFBR/a1mFDmOx5AGmw== dependencies: kind-of "^6.0.2" map-cache@^0.2.0: version "0.2.2" resolved "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz" integrity sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg== math-intrinsics@^1.0.0: version "1.1.0" resolved "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz" integrity sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g== media-typer@^1.1.0: version "1.1.0" resolved "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz" integrity sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw== merge-descriptors@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz" integrity sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g== micromatch@^4.0.2, micromatch@^4.0.4: version "4.0.8" resolved "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz" integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA== dependencies: braces "^3.0.3" picomatch "^2.3.1" mime-db@1.52.0: version "1.52.0" resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz" integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== mime-db@^1.28.0: version "1.53.0" resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.53.0.tgz" integrity sha512-oHlN/w+3MQ3rba9rqFr6V/ypF10LSkdwUysQL7GkXoTgIWeV+tcXGA852TBxH+gsh8UWoyhR1hKcoMJTuWflpg== mime-db@^1.54.0: version "1.54.0" resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz" integrity sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ== mime-types@^3.0.0, mime-types@^3.0.2: version "3.0.2" resolved "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz" integrity sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A== dependencies: mime-db "^1.54.0" mime-types@~2.1.34: version "2.1.35" resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz" integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== dependencies: mime-db "1.52.0" mimic-response@^1.0.0: version "1.0.1" resolved "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz" integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== minimatch@^10.2.1: version "10.2.4" resolved "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz" integrity sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg== dependencies: brace-expansion "^5.0.2" minimatch@^3.0.4, minimatch@^3.1.1: version "3.1.2" resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz" integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== dependencies: brace-expansion "^1.1.7" minimatch@^5.0.1, minimatch@^5.1.6: version "5.1.6" resolved "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz" integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g== dependencies: brace-expansion "^2.0.1" minimatch@^9.0.0, minimatch@^9.0.4, minimatch@^9.0.5: version "9.0.5" resolved "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz" integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow== dependencies: brace-expansion "^2.0.1" minimatch@~3.0.2, minimatch@~3.0.4: version "3.0.8" resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.0.8.tgz" integrity sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q== dependencies: brace-expansion "^1.1.7" minipass-collect@^2.0.1: version "2.0.1" resolved "https://registry.npmjs.org/minipass-collect/-/minipass-collect-2.0.1.tgz" integrity sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw== dependencies: minipass "^7.0.3" minipass-fetch@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-4.0.0.tgz" integrity sha512-2v6aXUXwLP1Epd/gc32HAMIWoczx+fZwEPRHm/VwtrJzRGwR1qGZXEYV3Zp8ZjjbwaZhMrM6uHV4KVkk+XCc2w== dependencies: minipass "^7.0.3" minipass-sized "^1.0.3" minizlib "^3.0.1" optionalDependencies: encoding "^0.1.13" minipass-flush@^1.0.5: version "1.0.5" resolved "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz" integrity sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw== dependencies: minipass "^3.0.0" minipass-pipeline@^1.2.4: version "1.2.4" resolved "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz" integrity sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A== dependencies: minipass "^3.0.0" minipass-sized@^1.0.3: version "1.0.3" resolved "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz" integrity sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g== dependencies: minipass "^3.0.0" minipass@^3.0.0: version "3.3.6" resolved "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz" integrity sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw== dependencies: yallist "^4.0.0" minipass@^5.0.0: version "5.0.0" resolved "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz" integrity sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ== "minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.0.2, minipass@^7.0.3, minipass@^7.0.4, minipass@^7.1.1, minipass@^7.1.2: version "7.1.2" resolved "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz" integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw== minizlib@^2.1.1: version "2.1.2" resolved "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz" integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg== dependencies: minipass "^3.0.0" yallist "^4.0.0" minizlib@^3.0.1: version "3.0.1" resolved "https://registry.npmjs.org/minizlib/-/minizlib-3.0.1.tgz" integrity sha512-umcy022ILvb5/3Djuu8LWeqUa8D68JaBzlttKeMWen48SjabqS3iY5w/vzeMzMUNhLDifyhbOwKDSznB1vvrwg== dependencies: minipass "^7.0.4" rimraf "^5.0.5" mkdirp@^1.0.3: version "1.0.4" resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== mkdirp@^3.0.1: version "3.0.1" resolved "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz" integrity sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg== mocha@^11.1.0: version "11.1.0" resolved "https://registry.npmjs.org/mocha/-/mocha-11.1.0.tgz" integrity sha512-8uJR5RTC2NgpY3GrYcgpZrsEd9zKbPDpob1RezyR2upGHRQtHWofmzTMzTMSV6dru3tj5Ukt0+Vnq1qhFEEwAg== dependencies: ansi-colors "^4.1.3" browser-stdout "^1.3.1" chokidar "^3.5.3" debug "^4.3.5" diff "^5.2.0" escape-string-regexp "^4.0.0" find-up "^5.0.0" glob "^10.4.5" he "^1.2.0" js-yaml "^4.1.0" log-symbols "^4.1.0" minimatch "^5.1.6" ms "^2.1.3" serialize-javascript "^6.0.2" strip-json-comments "^3.1.1" supports-color "^8.1.1" workerpool "^6.5.1" yargs "^17.7.2" yargs-parser "^21.1.1" yargs-unparser "^2.0.0" ms@2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz" integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== ms@^2.1.1, ms@^2.1.2, ms@^2.1.3: version "2.1.3" resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== mute-stream@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/mute-stream/-/mute-stream-2.0.0.tgz" integrity sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA== mz@^2.7.0: version "2.7.0" resolved "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz" integrity sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q== dependencies: any-promise "^1.0.0" object-assign "^4.0.1" thenify-all "^1.0.0" nan@^2.19.0: version "2.26.2" resolved "https://registry.yarnpkg.com/nan/-/nan-2.26.2.tgz#2e5e25764224c737b9897790b57c3294d4dcee9c" integrity sha512-0tTvBTYkt3tdGw22nrAy50x7gpbGCCFH3AFcyS5WiUu7Eu4vWlri1woE6qHBSfy11vksDqkiwjOnlR7WV8G1Hw== nan@^2.20.0: version "2.22.0" resolved "https://registry.npmjs.org/nan/-/nan-2.22.0.tgz" integrity sha512-nbajikzWTMwsW+eSsNm3QwlOs7het9gGJU5dDZzRTQGk03vyBOauxgI4VakDzE0PtsGTmXPsXTbbjVhRwR5mpw== negotiator@0.6.3: version "0.6.3" resolved "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz" integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== negotiator@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz" integrity sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg== no-case@^2.2.0: version "2.3.2" resolved "https://registry.npmjs.org/no-case/-/no-case-2.3.2.tgz" integrity sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ== dependencies: lower-case "^1.1.1" node-gyp@^11.0.0: version "11.0.0" resolved "https://registry.npmjs.org/node-gyp/-/node-gyp-11.0.0.tgz" integrity sha512-zQS+9MTTeCMgY0F3cWPyJyRFAkVltQ1uXm+xXu/ES6KFgC6Czo1Seb9vQW2wNxSX2OrDTiqL0ojtkFxBQ0ypIw== dependencies: env-paths "^2.2.0" exponential-backoff "^3.1.1" glob "^10.3.10" graceful-fs "^4.2.6" make-fetch-happen "^14.0.3" nopt "^8.0.0" proc-log "^5.0.0" semver "^7.3.5" tar "^7.4.3" which "^5.0.0" nodemon@^3.1.14: version "3.1.14" resolved "https://registry.npmjs.org/nodemon/-/nodemon-3.1.14.tgz" integrity sha512-jakjZi93UtB3jHMWsXL68FXSAosbLfY0In5gtKq3niLSkrWznrVBzXFNOEMJUfc9+Ke7SHWoAZsiMkNP3vq6Jw== dependencies: chokidar "^3.5.2" debug "^4" ignore-by-default "^1.0.1" minimatch "^10.2.1" pstree.remy "^1.1.8" semver "^7.5.3" simple-update-notifier "^2.0.0" supports-color "^5.5.0" touch "^3.1.0" undefsafe "^2.0.5" nopt@^8.0.0: version "8.0.0" resolved "https://registry.npmjs.org/nopt/-/nopt-8.0.0.tgz" integrity sha512-1L/fTJ4UmV/lUxT2Uf006pfZKTvAgCF+chz+0OgBHO8u2Z67pE7AaAUUj7CJy0lXqHmymUvGFt6NE9R3HER0yw== dependencies: abbrev "^2.0.0" nopt@~3.0.6: version "3.0.6" resolved "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz" integrity sha512-4GUt3kSEYmk4ITxzB/b9vaIDfUVWN/Ml1Fwl11IlnIG2iaJ9O6WXZ9SrYM9NLI8OCBieN2Y8SWC2oJV0RQ7qYg== dependencies: abbrev "1" nopt@~4.0.1: version "4.0.3" resolved "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz" integrity sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg== dependencies: abbrev "1" osenv "^0.1.4" normalize-package-data@^7.0.0: version "7.0.0" resolved "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-7.0.0.tgz" integrity sha512-k6U0gKRIuNCTkwHGZqblCfLfBRh+w1vI6tBo+IeJwq2M8FUiOqhX7GH+GArQGScA7azd1WfyRCvxoXDO3hQDIA== dependencies: hosted-git-info "^8.0.0" semver "^7.3.5" validate-npm-package-license "^3.0.4" normalize-path@^3.0.0, normalize-path@~3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== normalize-url@2.0.1: version "2.0.1" resolved "https://registry.npmjs.org/normalize-url/-/normalize-url-2.0.1.tgz" integrity sha512-D6MUW4K/VzoJ4rJ01JFKxDrtY1v9wrgzCX5f2qj/lzH1m/lW6MhUZFKerVsnyjOhOsYzI9Kqqak+10l4LvLpMw== dependencies: prepend-http "^2.0.0" query-string "^5.0.1" sort-keys "^2.0.0" npm-audit-report@^6.0.0: version "6.0.0" resolved "https://registry.npmjs.org/npm-audit-report/-/npm-audit-report-6.0.0.tgz" integrity sha512-Ag6Y1irw/+CdSLqEEAn69T8JBgBThj5mw0vuFIKeP7hATYuQuS5jkMjK6xmVB8pr7U4g5Audbun0lHhBDMIBRA== npm-bundled@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/npm-bundled/-/npm-bundled-4.0.0.tgz" integrity sha512-IxaQZDMsqfQ2Lz37VvyyEtKLe8FsRZuysmedy/N06TU1RyVppYKXrO4xIhR0F+7ubIBox6Q7nir6fQI3ej39iA== dependencies: npm-normalize-package-bin "^4.0.0" npm-check-updates@^17.1.14: version "17.1.14" resolved "https://registry.npmjs.org/npm-check-updates/-/npm-check-updates-17.1.14.tgz" integrity sha512-dr4bXIxETubLI1tFGeock5hN8yVjahvaVpx+lPO4/O2md3zJuxB7FgH3MIoTvQSCgsgkIRpe0skti01IEAA5tA== npm-install-checks@^7.1.0, npm-install-checks@^7.1.1: version "7.1.1" resolved "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-7.1.1.tgz" integrity sha512-u6DCwbow5ynAX5BdiHQ9qvexme4U3qHW3MWe5NqH+NeBm0LbiH6zvGjNNew1fY+AZZUtVHbOPF3j7mJxbUzpXg== dependencies: semver "^7.1.1" npm-normalize-package-bin@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-4.0.0.tgz" integrity sha512-TZKxPvItzai9kN9H/TkmCtx/ZN/hvr3vUycjlfmH0ootY9yFBzNOpiXAdIn1Iteqsvk4lQn6B5PTrt+n6h8k/w== npm-package-arg@^12.0.0, npm-package-arg@^12.0.1: version "12.0.1" resolved "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-12.0.1.tgz" integrity sha512-aDxjFfPV3Liw0WOBWlyZLMBqtbgbg03rmGvHDJa2Ttv7tIz+1oB5qWec4psCDFZcZi9b5XdGkPdQiJxOPzvQRQ== dependencies: hosted-git-info "^8.0.0" proc-log "^5.0.0" semver "^7.3.5" validate-npm-package-name "^6.0.0" npm-packlist@^10.0.0: version "10.0.0" resolved "https://registry.npmjs.org/npm-packlist/-/npm-packlist-10.0.0.tgz" integrity sha512-rht9U6nS8WOBDc53eipZNPo5qkAV4X2rhKE2Oj1DYUQ3DieXfj0mKkVmjnf3iuNdtMd8WfLdi2L6ASkD/8a+Kg== dependencies: ignore-walk "^7.0.0" npm-pick-manifest@^10.0.0: version "10.0.0" resolved "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-10.0.0.tgz" integrity sha512-r4fFa4FqYY8xaM7fHecQ9Z2nE9hgNfJR+EmoKv0+chvzWkBcORX3r0FpTByP+CbOVJDladMXnPQGVN8PBLGuTQ== dependencies: npm-install-checks "^7.1.0" npm-normalize-package-bin "^4.0.0" npm-package-arg "^12.0.0" semver "^7.3.5" npm-profile@^11.0.1: version "11.0.1" resolved "https://registry.npmjs.org/npm-profile/-/npm-profile-11.0.1.tgz" integrity sha512-HP5Cw9WHwFS9vb4fxVlkNAQBUhVL5BmW6rAR+/JWkpwqcFJid7TihKUdYDWqHl0NDfLd0mpucheGySqo8ysyfw== dependencies: npm-registry-fetch "^18.0.0" proc-log "^5.0.0" npm-registry-fetch@^18.0.0, npm-registry-fetch@^18.0.1, npm-registry-fetch@^18.0.2: version "18.0.2" resolved "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-18.0.2.tgz" integrity sha512-LeVMZBBVy+oQb5R6FDV9OlJCcWDU+al10oKpe+nsvcHnG24Z3uM3SvJYKfGJlfGjVU8v9liejCrUR/M5HO5NEQ== dependencies: "@npmcli/redact" "^3.0.0" jsonparse "^1.3.1" make-fetch-happen "^14.0.0" minipass "^7.0.2" minipass-fetch "^4.0.0" minizlib "^3.0.1" npm-package-arg "^12.0.0" proc-log "^5.0.0" npm-user-validate@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/npm-user-validate/-/npm-user-validate-3.0.0.tgz" integrity sha512-9xi0RdSmJ4mPYTC393VJPz1Sp8LyCx9cUnm/L9Qcb3cFO8gjT4mN20P9FAsea8qDHdQ7LtcN8VLh2UT47SdKCw== npm@^11.1.0: version "11.1.0" resolved "https://registry.npmjs.org/npm/-/npm-11.1.0.tgz" integrity sha512-rPMBrZud26lI/LcjQeLw/K5Hf1apXMKgkpNNEzp0YQYmM877+T1ZNKPcB2hnTi7e6fBNz8xLtMMn/w46fVUqGw== dependencies: "@isaacs/string-locale-compare" "^1.1.0" "@npmcli/arborist" "^9.0.0" "@npmcli/config" "^10.0.1" "@npmcli/fs" "^4.0.0" "@npmcli/map-workspaces" "^4.0.2" "@npmcli/package-json" "^6.1.1" "@npmcli/promise-spawn" "^8.0.2" "@npmcli/redact" "^3.0.0" "@npmcli/run-script" "^9.0.1" "@sigstore/tuf" "^3.0.0" abbrev "^3.0.0" archy "~1.0.0" cacache "^19.0.1" chalk "^5.4.1" ci-info "^4.1.0" cli-columns "^4.0.0" fastest-levenshtein "^1.0.16" fs-minipass "^3.0.3" glob "^10.4.5" graceful-fs "^4.2.11" hosted-git-info "^8.0.2" ini "^5.0.0" init-package-json "^8.0.0" is-cidr "^5.1.0" json-parse-even-better-errors "^4.0.0" libnpmaccess "^10.0.0" libnpmdiff "^8.0.0" libnpmexec "^10.0.0" libnpmfund "^7.0.0" libnpmorg "^8.0.0" libnpmpack "^9.0.0" libnpmpublish "^11.0.0" libnpmsearch "^9.0.0" libnpmteam "^8.0.0" libnpmversion "^8.0.0" make-fetch-happen "^14.0.3" minimatch "^9.0.5" minipass "^7.1.1" minipass-pipeline "^1.2.4" ms "^2.1.2" node-gyp "^11.0.0" nopt "^8.0.0" normalize-package-data "^7.0.0" npm-audit-report "^6.0.0" npm-install-checks "^7.1.1" npm-package-arg "^12.0.1" npm-pick-manifest "^10.0.0" npm-profile "^11.0.1" npm-registry-fetch "^18.0.2" npm-user-validate "^3.0.0" p-map "^7.0.3" pacote "^21.0.0" parse-conflict-json "^4.0.0" proc-log "^5.0.0" qrcode-terminal "^0.12.0" read "^4.0.0" semver "^7.6.3" spdx-expression-parse "^4.0.0" ssri "^12.0.0" supports-color "^9.4.0" tar "^6.2.1" text-table "~0.2.0" tiny-relative-date "^1.3.0" treeverse "^3.0.0" validate-npm-package-name "^6.0.0" which "^5.0.0" number-is-nan@^1.0.0: version "1.0.1" resolved "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz" integrity sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ== object-assign@^4, object-assign@^4.0.1, object-assign@^4.1.0: version "4.1.1" resolved "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz" integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== object-inspect@^1.13.3: version "1.13.3" resolved "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.3.tgz" integrity sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA== object.defaults@^1.1.0: version "1.1.0" resolved "https://registry.npmjs.org/object.defaults/-/object.defaults-1.1.0.tgz" integrity sha512-c/K0mw/F11k4dEUBMW8naXUuBuhxRCfG7W+yFy8EcijU/rSmazOUd1XAEEe6bC0OuXY4HUKjTJv7xbxIMqdxrA== dependencies: array-each "^1.0.1" array-slice "^1.0.0" for-own "^1.0.0" isobject "^3.0.0" object.map@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/object.map/-/object.map-1.0.1.tgz" integrity sha512-3+mAJu2PLfnSVGHwIWubpOFLscJANBKuB/6A4CxBstc4aqwQY0FWcsppuy4jU5GSB95yES5JHSI+33AWuS4k6w== dependencies: for-own "^1.0.0" make-iterator "^1.0.0" object.pick@^1.2.0: version "1.3.0" resolved "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz" integrity sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ== dependencies: isobject "^3.0.1" on-finished@^2.4.1: version "2.4.1" resolved "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz" integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== dependencies: ee-first "1.1.1" once@^1.3.0, once@^1.3.1, once@^1.4.0: version "1.4.0" resolved "https://registry.npmjs.org/once/-/once-1.4.0.tgz" integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== dependencies: wrappy "1" os-homedir@^1.0.0: version "1.0.2" resolved "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz" integrity sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ== os-tmpdir@^1.0.0: version "1.0.2" resolved "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz" integrity sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g== osenv@^0.1.4: version "0.1.5" resolved "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz" integrity sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g== dependencies: os-homedir "^1.0.0" os-tmpdir "^1.0.0" p-cancelable@^0.4.0: version "0.4.1" resolved "https://registry.npmjs.org/p-cancelable/-/p-cancelable-0.4.1.tgz" integrity sha512-HNa1A8LvB1kie7cERyy21VNeHb2CWJJYqyyC2o3klWFfMGlFmWv2Z7sFgZH8ZiaYL95ydToKTFVXgMV/Os0bBQ== p-event@^2.1.0: version "2.3.1" resolved "https://registry.npmjs.org/p-event/-/p-event-2.3.1.tgz" integrity sha512-NQCqOFhbpVTMX4qMe8PF8lbGtzZ+LCiN7pcNrb/413Na7+TRoe1xkKUzuWa/YEJdGQ0FvKtj35EEbDoVPO2kbA== dependencies: p-timeout "^2.0.1" p-finally@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz" integrity sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow== p-is-promise@^1.1.0: version "1.1.0" resolved "https://registry.npmjs.org/p-is-promise/-/p-is-promise-1.1.0.tgz" integrity sha512-zL7VE4JVS2IFSkR2GQKDSPEVxkoH43/p7oEnwpdCndKYJO0HVeRB7fA8TJwuLOTBREtK0ea8eHaxdwcpob5dmg== p-limit@^3.0.2: version "3.1.0" resolved "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz" integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== dependencies: yocto-queue "^0.1.0" p-locate@^5.0.0: version "5.0.0" resolved "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz" integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== dependencies: p-limit "^3.0.2" p-map@^7.0.2, p-map@^7.0.3: version "7.0.3" resolved "https://registry.npmjs.org/p-map/-/p-map-7.0.3.tgz" integrity sha512-VkndIv2fIB99swvQoA65bm+fsmt6UNdGeIB0oxBs+WhAhdh08QA04JXpI7rbB9r08/nkbysKoya9rtDERYOYMA== p-timeout@^2.0.1: version "2.0.1" resolved "https://registry.npmjs.org/p-timeout/-/p-timeout-2.0.1.tgz" integrity sha512-88em58dDVB/KzPEx1X0N3LwFfYZPyDc4B6eF38M1rk9VTZMbxXXgjugz8mmwpS9Ox4BDZ+t6t3QP5+/gazweIA== dependencies: p-finally "^1.0.0" package-json-from-dist@^1.0.0: version "1.0.1" resolved "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz" integrity sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw== pacote@^21.0.0: version "21.0.0" resolved "https://registry.npmjs.org/pacote/-/pacote-21.0.0.tgz" integrity sha512-lcqexq73AMv6QNLo7SOpz0JJoaGdS3rBFgF122NZVl1bApo2mfu+XzUBU/X/XsiJu+iUmKpekRayqQYAs+PhkA== dependencies: "@npmcli/git" "^6.0.0" "@npmcli/installed-package-contents" "^3.0.0" "@npmcli/package-json" "^6.0.0" "@npmcli/promise-spawn" "^8.0.0" "@npmcli/run-script" "^9.0.0" cacache "^19.0.0" fs-minipass "^3.0.0" minipass "^7.0.2" npm-package-arg "^12.0.0" npm-packlist "^10.0.0" npm-pick-manifest "^10.0.0" npm-registry-fetch "^18.0.0" proc-log "^5.0.0" promise-retry "^2.0.1" sigstore "^3.0.0" ssri "^12.0.0" tar "^6.1.11" param-case@^2.1.1: version "2.1.1" resolved "https://registry.npmjs.org/param-case/-/param-case-2.1.1.tgz" integrity sha512-eQE845L6ot89sk2N8liD8HAuH4ca6Vvr7VWAWwt7+kvvG5aBcPmmphQ68JsEG2qa9n1TykS2DLeMt363AAH8/w== dependencies: no-case "^2.2.0" parse-conflict-json@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/parse-conflict-json/-/parse-conflict-json-4.0.0.tgz" integrity sha512-37CN2VtcuvKgHUs8+0b1uJeEsbGn61GRHz469C94P5xiOoqpDYJYwjg4RY9Vmz39WyZAVkR5++nbJwLMIgOCnQ== dependencies: json-parse-even-better-errors "^4.0.0" just-diff "^6.0.0" just-diff-apply "^5.2.0" parse-filepath@^1.0.1: version "1.0.2" resolved "https://registry.npmjs.org/parse-filepath/-/parse-filepath-1.0.2.tgz" integrity sha512-FwdRXKCohSVeXqwtYonZTXtbGJKrn+HNyWDYVcp5yuJlesTwNH4rsmRZ+GrKAPJ5bLpRxESMeS+Rl0VCHRvB2Q== dependencies: is-absolute "^1.0.0" map-cache "^0.2.0" path-root "^0.1.1" parse-ms@^1.0.0: version "1.0.1" resolved "https://registry.npmjs.org/parse-ms/-/parse-ms-1.0.1.tgz" integrity sha512-LpH1Cf5EYuVjkBvCDBYvkUPh+iv2bk3FHflxHkpCYT0/FZ1d3N3uJaLiHr4yGuMcFUhv6eAivitTvWZI4B/chg== parse-passwd@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz" integrity sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q== parseurl@^1.3.3: version "1.3.3" resolved "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz" integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== path-exists@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz" integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== path-is-absolute@^1.0.0: version "1.0.1" resolved "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz" integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== path-key@^3.1.0: version "3.1.1" resolved "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz" integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== path-parse@^1.0.7: version "1.0.7" resolved "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== path-root-regex@^0.1.0: version "0.1.2" resolved "https://registry.npmjs.org/path-root-regex/-/path-root-regex-0.1.2.tgz" integrity sha512-4GlJ6rZDhQZFE0DPVKh0e9jmZ5egZfxTkp7bcRDuPlJXbAwhxcl2dINPUAsjLdejqaLsCeg8axcLjIbvBjN4pQ== path-root@^0.1.1: version "0.1.1" resolved "https://registry.npmjs.org/path-root/-/path-root-0.1.1.tgz" integrity sha512-QLcPegTHF11axjfojBIoDygmS2E3Lf+8+jI6wOVmNVenrKSo3mFdSGiIgdSHenczw3wPtlVMQaFVwGmM7BJdtg== dependencies: path-root-regex "^0.1.0" path-scurry@^1.11.1: version "1.11.1" resolved "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz" integrity sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA== dependencies: lru-cache "^10.2.0" minipass "^5.0.0 || ^6.0.2 || ^7.0.0" path-to-regexp@^8.0.0: version "8.3.0" resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz" integrity sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA== pend@~1.2.0: version "1.2.0" resolved "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz" integrity sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg== picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1: version "2.3.1" resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== pify@^2.3.0: version "2.3.0" resolved "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz" integrity sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog== pify@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz" integrity sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg== pify@^4.0.1: version "4.0.1" resolved "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz" integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== pinkie-promise@^2.0.0: version "2.0.1" resolved "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz" integrity sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw== dependencies: pinkie "^2.0.0" pinkie@^2.0.0: version "2.0.4" resolved "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz" integrity sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg== plur@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/plur/-/plur-1.0.0.tgz" integrity sha512-qSnKBSZeDY8ApxwhfVIwKwF36KVJqb1/9nzYYq3j3vdwocULCXT8f8fQGkiw1Nk9BGfxiDagEe/pwakA+bOBqw== postcss-selector-parser@^6.1.2: version "6.1.2" resolved "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz" integrity sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg== dependencies: cssesc "^3.0.0" util-deprecate "^1.0.2" prepend-http@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz" integrity sha512-ravE6m9Atw9Z/jjttRUZ+clIXogdghyZAuWJ3qEzjT+jI/dL1ifAqhZeC5VHzQp1MSt1+jxKkFNemj/iO7tVUA== pretty-bytes@^5.1.0: version "5.6.0" resolved "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-5.6.0.tgz" integrity sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg== pretty-ms@^2.1.0: version "2.1.0" resolved "https://registry.npmjs.org/pretty-ms/-/pretty-ms-2.1.0.tgz" integrity sha512-H2enpsxzDhuzRl3zeSQpQMirn8dB0Z/gxW96j06tMfTviUWvX14gjKb7qd1gtkUyYhDPuoNe00K5PqNvy2oQNg== dependencies: is-finite "^1.0.1" parse-ms "^1.0.0" plur "^1.0.0" proc-log@^5.0.0: version "5.0.0" resolved "https://registry.npmjs.org/proc-log/-/proc-log-5.0.0.tgz" integrity sha512-Azwzvl90HaF0aCz1JrDdXQykFakSSNPaPoiZ9fm5qJIMHioDZEi7OAdRwSm6rSoPtY3Qutnm3L7ogmg3dc+wbQ== process-nextick-args@~2.0.0: version "2.0.1" resolved "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz" integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== proggy@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/proggy/-/proggy-3.0.0.tgz" integrity sha512-QE8RApCM3IaRRxVzxrjbgNMpQEX6Wu0p0KBeoSiSEw5/bsGwZHsshF4LCxH2jp/r6BU+bqA3LrMDEYNfJnpD8Q== promise-all-reject-late@^1.0.0: version "1.0.1" resolved "https://registry.npmjs.org/promise-all-reject-late/-/promise-all-reject-late-1.0.1.tgz" integrity sha512-vuf0Lf0lOxyQREH7GDIOUMLS7kz+gs8i6B+Yi8dC68a2sychGrHTJYghMBD6k7eUcH0H5P73EckCA48xijWqXw== promise-call-limit@^3.0.1: version "3.0.2" resolved "https://registry.npmjs.org/promise-call-limit/-/promise-call-limit-3.0.2.tgz" integrity sha512-mRPQO2T1QQVw11E7+UdCJu7S61eJVWknzml9sC1heAdj1jxl0fWMBypIt9ZOcLFf8FkG995ZD7RnVk7HH72fZw== promise-inflight@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz" integrity sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g== promise-retry@^2.0.1: version "2.0.1" resolved "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz" integrity sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g== dependencies: err-code "^2.0.2" retry "^0.12.0" promzard@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/promzard/-/promzard-2.0.0.tgz" integrity sha512-Ncd0vyS2eXGOjchIRg6PVCYKetJYrW1BSbbIo+bKdig61TB6nH2RQNF2uP+qMpsI73L/jURLWojcw8JNIKZ3gg== dependencies: read "^4.0.0" proxy-addr@^2.0.7: version "2.0.7" resolved "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz" integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg== dependencies: forwarded "0.2.0" ipaddr.js "1.9.1" pstree.remy@^1.1.8: version "1.1.8" resolved "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz" integrity sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w== pump@^3.0.0: version "3.0.2" resolved "https://registry.npmjs.org/pump/-/pump-3.0.2.tgz" integrity sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw== dependencies: end-of-stream "^1.1.0" once "^1.3.1" qrcode-terminal@^0.12.0: version "0.12.0" resolved "https://registry.npmjs.org/qrcode-terminal/-/qrcode-terminal-0.12.0.tgz" integrity sha512-EXtzRZmC+YGmGlDFbXKxQiMZNwCLEO6BANKXG4iCtSIM0yqc/pappSx3RIKr4r0uh5JsBckOXeKrB3Iz7mdQpQ== qs@^6.14.0, qs@^6.14.1: version "6.15.0" resolved "https://registry.npmjs.org/qs/-/qs-6.15.0.tgz" integrity sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ== dependencies: side-channel "^1.1.0" qs@^6.4.0: version "6.13.1" resolved "https://registry.npmjs.org/qs/-/qs-6.13.1.tgz" integrity sha512-EJPeIn0CYrGu+hli1xilKAPXODtJ12T0sP63Ijx2/khC2JtuaN3JyNIpvmnkmaEtha9ocbG4A4cMcr+TvqvwQg== dependencies: side-channel "^1.0.6" query-string@^5.0.1: version "5.1.1" resolved "https://registry.npmjs.org/query-string/-/query-string-5.1.1.tgz" integrity sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw== dependencies: decode-uri-component "^0.2.0" object-assign "^4.1.0" strict-uri-encode "^1.0.0" randombytes@^2.1.0: version "2.1.0" resolved "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz" integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== dependencies: safe-buffer "^5.1.0" range-parser@^1.2.1: version "1.2.1" resolved "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz" integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== raw-body@^3.0.1: version "3.0.2" resolved "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz" integrity sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA== dependencies: bytes "~3.1.2" http-errors "~2.0.1" iconv-lite "~0.7.0" unpipe "~1.0.0" raw-body@~1.1.0: version "1.1.7" resolved "https://registry.npmjs.org/raw-body/-/raw-body-1.1.7.tgz" integrity sha512-WmJJU2e9Y6M5UzTOkHaM7xJGAPQD8PNzx3bAd2+uhZAim6wDk6dAZxPVYLF67XhbR4hmKGh33Lpmh4XWrCH5Mg== dependencies: bytes "1" string_decoder "0.10" read-cmd-shim@^5.0.0: version "5.0.0" resolved "https://registry.npmjs.org/read-cmd-shim/-/read-cmd-shim-5.0.0.tgz" integrity sha512-SEbJV7tohp3DAAILbEMPXavBjAnMN0tVnh4+9G8ihV4Pq3HYF9h8QNez9zkJ1ILkv9G2BjdzwctznGZXgu/HGw== read-package-json-fast@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/read-package-json-fast/-/read-package-json-fast-4.0.0.tgz" integrity sha512-qpt8EwugBWDw2cgE2W+/3oxC+KTez2uSVR8JU9Q36TXPAGCaozfQUs59v4j4GFpWTaw0i6hAZSvOmu1J0uOEUg== dependencies: json-parse-even-better-errors "^4.0.0" npm-normalize-package-bin "^4.0.0" read@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/read/-/read-4.0.0.tgz" integrity sha512-nbYGT3cec3J5NPUeJia7l72I3oIzMIB6yeNyDqi8CVHr3WftwjrCUqR0j13daoHEMVaZ/rxCpmHKrbods3hI2g== dependencies: mute-stream "^2.0.0" readable-stream@^2.0.0, readable-stream@^2.3.0, readable-stream@^2.3.5: version "2.3.8" resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz" integrity sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA== dependencies: core-util-is "~1.0.0" inherits "~2.0.3" isarray "~1.0.0" process-nextick-args "~2.0.0" safe-buffer "~5.1.1" string_decoder "~1.1.1" util-deprecate "~1.0.1" readdirp@~3.6.0: version "3.6.0" resolved "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz" integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== dependencies: picomatch "^2.2.1" rechoir@^0.7.0: version "0.7.1" resolved "https://registry.npmjs.org/rechoir/-/rechoir-0.7.1.tgz" integrity sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg== dependencies: resolve "^1.9.0" redis-errors@^1.0.0, redis-errors@^1.2.0: version "1.2.0" resolved "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz" integrity sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w== redis-parser@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz" integrity sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A== dependencies: redis-errors "^1.0.0" relateurl@^0.2.7: version "0.2.7" resolved "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz" integrity sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog== require-directory@^2.1.1: version "2.1.1" resolved "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz" integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== resolve-dir@^1.0.0, resolve-dir@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz" integrity sha512-R7uiTjECzvOsWSfdM0QKFNBVFcK27aHOUwdvK53BcW8zqnGdYp0Fbj82cy54+2A4P2tFM22J5kRfe1R+lM/1yg== dependencies: expand-tilde "^2.0.0" global-modules "^1.0.0" resolve@^1.19.0, resolve@^1.9.0: version "1.22.10" resolved "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz" integrity sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w== dependencies: is-core-module "^2.16.0" path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" responselike@1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz" integrity sha512-/Fpe5guzJk1gPqdJLJR5u7eG/gNY4nImjbRDaVWVMRhne55TCmj2i9Q+54PBRfatRC8v/rIiv9BN0pMd9OV5EQ== dependencies: lowercase-keys "^1.0.0" retry@^0.12.0: version "0.12.0" resolved "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz" integrity sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow== rimraf@^2.6.2: version "2.7.1" resolved "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz" integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== dependencies: glob "^7.1.3" rimraf@^5.0.5: version "5.0.10" resolved "https://registry.npmjs.org/rimraf/-/rimraf-5.0.10.tgz" integrity sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ== dependencies: glob "^10.3.7" router@^2.2.0: version "2.2.0" resolved "https://registry.npmjs.org/router/-/router-2.2.0.tgz" integrity sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ== dependencies: debug "^4.4.0" depd "^2.0.0" is-promise "^4.0.0" parseurl "^1.3.3" path-to-regexp "^8.0.0" safe-buffer@5.2.1, safe-buffer@>=5.1.0, safe-buffer@^5.1.0, safe-buffer@^5.1.1: version "5.2.1" resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== safe-json-parse@~1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/safe-json-parse/-/safe-json-parse-1.0.1.tgz" integrity sha512-o0JmTu17WGUaUOHa1l0FPGXKBfijbxK6qoHzlkihsDXxzBHvJcA7zgviKR92Xs841rX9pK16unfphLq0/KqX7A== "safer-buffer@>= 2.1.2 < 3.0.0", safer-buffer@~2.1.0: version "2.1.2" resolved "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== seek-bzip@^1.0.5: version "1.0.6" resolved "https://registry.npmjs.org/seek-bzip/-/seek-bzip-1.0.6.tgz" integrity sha512-e1QtP3YL5tWww8uKaOCQ18UxIT2laNBXHjV/S2WYCiK4udiv8lkG89KRIoCjUagnAmCBurjF4zEVX2ByBbnCjQ== dependencies: commander "^2.8.1" semver@^5.6.0: version "5.7.2" resolved "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz" integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== semver@^7.1.1, semver@^7.3.5, semver@^7.3.7, semver@^7.5.3, semver@^7.6.3: version "7.6.3" resolved "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz" integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== send@^1.1.0, send@^1.2.0: version "1.2.1" resolved "https://registry.npmjs.org/send/-/send-1.2.1.tgz" integrity sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ== dependencies: debug "^4.4.3" encodeurl "^2.0.0" escape-html "^1.0.3" etag "^1.8.1" fresh "^2.0.0" http-errors "^2.0.1" mime-types "^3.0.2" ms "^2.1.3" on-finished "^2.4.1" range-parser "^1.2.1" statuses "^2.0.2" serialize-javascript@^6.0.2: version "6.0.2" resolved "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz" integrity sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g== dependencies: randombytes "^2.1.0" serve-static@^2.2.0: version "2.2.1" resolved "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz" integrity sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw== dependencies: encodeurl "^2.0.0" escape-html "^1.0.3" parseurl "^1.3.3" send "^1.2.0" setprototypeof@~1.2.0: version "1.2.0" resolved "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz" integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== shebang-command@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz" integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== dependencies: shebang-regex "^3.0.0" shebang-regex@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz" integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== should-equal@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/should-equal/-/should-equal-2.0.0.tgz" integrity sha512-ZP36TMrK9euEuWQYBig9W55WPC7uo37qzAEmbjHz4gfyuXrEUgF8cUvQVO+w+d3OMfPvSRQJ22lSm8MQJ43LTA== dependencies: should-type "^1.4.0" should-format@^3.0.3: version "3.0.3" resolved "https://registry.npmjs.org/should-format/-/should-format-3.0.3.tgz" integrity sha512-hZ58adtulAk0gKtua7QxevgUaXTTXxIi8t41L3zo9AHvjXO1/7sdLECuHeIN2SRtYXpNkmhoUP2pdeWgricQ+Q== dependencies: should-type "^1.3.0" should-type-adaptors "^1.0.1" should-type-adaptors@^1.0.1: version "1.1.0" resolved "https://registry.npmjs.org/should-type-adaptors/-/should-type-adaptors-1.1.0.tgz" integrity sha512-JA4hdoLnN+kebEp2Vs8eBe9g7uy0zbRo+RMcU0EsNy+R+k049Ki+N5tT5Jagst2g7EAja+euFuoXFCa8vIklfA== dependencies: should-type "^1.3.0" should-util "^1.0.0" should-type@^1.3.0, should-type@^1.4.0: version "1.4.0" resolved "https://registry.npmjs.org/should-type/-/should-type-1.4.0.tgz" integrity sha512-MdAsTu3n25yDbIe1NeN69G4n6mUnJGtSJHygX3+oN0ZbO3DTiATnf7XnYJdGT42JCXurTb1JI0qOBR65shvhPQ== should-util@^1.0.0: version "1.0.1" resolved "https://registry.npmjs.org/should-util/-/should-util-1.0.1.tgz" integrity sha512-oXF8tfxx5cDk8r2kYqlkUJzZpDBqVY/II2WhvU0n9Y3XYvAYRmeaf1PvvIvTgPnv4KJ+ES5M0PyDq5Jp+Ygy2g== should@^13.2.3: version "13.2.3" resolved "https://registry.npmjs.org/should/-/should-13.2.3.tgz" integrity sha512-ggLesLtu2xp+ZxI+ysJTmNjh2U0TsC+rQ/pfED9bUZZ4DKefP27D+7YJVVTvKsmjLpIi9jAa7itwDGkDDmt1GQ== dependencies: should-equal "^2.0.0" should-format "^3.0.3" should-type "^1.4.0" should-type-adaptors "^1.0.1" should-util "^1.0.0" side-channel-list@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz" integrity sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA== dependencies: es-errors "^1.3.0" object-inspect "^1.13.3" side-channel-map@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz" integrity sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA== dependencies: call-bound "^1.0.2" es-errors "^1.3.0" get-intrinsic "^1.2.5" object-inspect "^1.13.3" side-channel-weakmap@^1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz" integrity sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A== dependencies: call-bound "^1.0.2" es-errors "^1.3.0" get-intrinsic "^1.2.5" object-inspect "^1.13.3" side-channel-map "^1.0.1" side-channel@^1.0.6, side-channel@^1.1.0: version "1.1.0" resolved "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz" integrity sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw== dependencies: es-errors "^1.3.0" object-inspect "^1.13.3" side-channel-list "^1.0.0" side-channel-map "^1.0.1" side-channel-weakmap "^1.0.2" signal-exit@^4.0.1: version "4.1.0" resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz" integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== sigstore@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/sigstore/-/sigstore-3.0.0.tgz" integrity sha512-PHMifhh3EN4loMcHCz6l3v/luzgT3za+9f8subGgeMNjbJjzH4Ij/YoX3Gvu+kaouJRIlVdTHHCREADYf+ZteA== dependencies: "@sigstore/bundle" "^3.0.0" "@sigstore/core" "^2.0.0" "@sigstore/protobuf-specs" "^0.3.2" "@sigstore/sign" "^3.0.0" "@sigstore/tuf" "^3.0.0" "@sigstore/verify" "^2.0.0" simple-update-notifier@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz" integrity sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w== dependencies: semver "^7.5.3" smart-buffer@^4.2.0: version "4.2.0" resolved "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz" integrity sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg== snappyjs@^0.7.0: version "0.7.0" resolved "https://registry.npmjs.org/snappyjs/-/snappyjs-0.7.0.tgz" integrity sha512-u5iEEXkMe2EInQio6Wv9LWHOQYRDbD2O9hzS27GpT/lwfIQhTCnHCTqedqHIHe9ZcvQo+9au6vngQayipz1NYw== socket.io-adapter@~2.5.2: version "2.5.5" resolved "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.5.tgz" integrity sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg== dependencies: debug "~4.3.4" ws "~8.17.1" socket.io-parser@~4.2.4: version "4.2.4" resolved "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz" integrity sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew== dependencies: "@socket.io/component-emitter" "~3.1.0" debug "~4.3.1" socket.io@^4.8.3: version "4.8.3" resolved "https://registry.npmjs.org/socket.io/-/socket.io-4.8.3.tgz" integrity sha512-2Dd78bqzzjE6KPkD5fHZmDAKRNe3J15q+YHDrIsy9WEkqttc7GY+kT9OBLSMaPbQaEd0x1BjcmtMtXkfpc+T5A== dependencies: accepts "~1.3.4" base64id "~2.0.0" cors "~2.8.5" debug "~4.4.1" engine.io "~6.6.0" socket.io-adapter "~2.5.2" socket.io-parser "~4.2.4" socks-proxy-agent@^8.0.3: version "8.0.5" resolved "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz" integrity sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw== dependencies: agent-base "^7.1.2" debug "^4.3.4" socks "^2.8.3" socks@^2.8.3: version "2.8.3" resolved "https://registry.npmjs.org/socks/-/socks-2.8.3.tgz" integrity sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw== dependencies: ip-address "^9.0.5" smart-buffer "^4.2.0" sort-keys-length@^1.0.0: version "1.0.1" resolved "https://registry.npmjs.org/sort-keys-length/-/sort-keys-length-1.0.1.tgz" integrity sha512-GRbEOUqCxemTAk/b32F2xa8wDTs+Z1QHOkbhJDQTvv/6G3ZkbJ+frYWsTcc7cBB3Fu4wy4XlLCuNtJuMn7Gsvw== dependencies: sort-keys "^1.0.0" sort-keys@^1.0.0: version "1.1.2" resolved "https://registry.npmjs.org/sort-keys/-/sort-keys-1.1.2.tgz" integrity sha512-vzn8aSqKgytVik0iwdBEi+zevbTYZogewTUM6dtpmGwEcdzbub/TX4bCzRhebDCRC3QzXgJsLRKB2V/Oof7HXg== dependencies: is-plain-obj "^1.0.0" sort-keys@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/sort-keys/-/sort-keys-2.0.0.tgz" integrity sha512-/dPCrG1s3ePpWm6yBbxZq5Be1dXGLyLn9Z791chDC3NFrpkVbWGzkBwPN1knaciexFXgRJ7hzdnwZ4stHSDmjg== dependencies: is-plain-obj "^1.0.0" source-map-support@~0.5.20: version "0.5.21" resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz" integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== dependencies: buffer-from "^1.0.0" source-map "^0.6.0" source-map@^0.6.0, source-map@~0.6.0: version "0.6.1" resolved "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== spdx-correct@^3.0.0: version "3.2.0" resolved "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz" integrity sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA== dependencies: spdx-expression-parse "^3.0.0" spdx-license-ids "^3.0.0" spdx-exceptions@^2.1.0: version "2.5.0" resolved "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz" integrity sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w== spdx-expression-parse@^3.0.0: version "3.0.1" resolved "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz" integrity sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q== dependencies: spdx-exceptions "^2.1.0" spdx-license-ids "^3.0.0" spdx-expression-parse@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-4.0.0.tgz" integrity sha512-Clya5JIij/7C6bRR22+tnGXbc4VKlibKSVj2iHvVeX5iMW7s1SIQlqu699JkODJJIhh/pUu8L0/VLh8xflD+LQ== dependencies: spdx-exceptions "^2.1.0" spdx-license-ids "^3.0.0" spdx-license-ids@^3.0.0: version "3.0.23" resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.23.tgz#b069e687b1291a32f126893ed76a27a745ee2133" integrity sha512-CWLcCCH7VLu13TgOH+r8p1O/Znwhqv/dbb6lqWy67G+pT1kHmeD/+V36AVb/vq8QMIQwVShJ6Ssl5FPh0fuSdw== sprintf-js@^1.1.1, sprintf-js@^1.1.3: version "1.1.3" resolved "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz" integrity sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA== sprintf-js@~1.0.2: version "1.0.3" resolved "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz" integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== ssh2@^1.15.0: version "1.16.0" resolved "https://registry.npmjs.org/ssh2/-/ssh2-1.16.0.tgz" integrity sha512-r1X4KsBGedJqo7h8F5c4Ybpcr5RjyP+aWIG007uBPRjmdQWfEiVLzSK71Zji1B9sKxwaCvD8y8cwSkYrlLiRRg== dependencies: asn1 "^0.2.6" bcrypt-pbkdf "^1.0.2" optionalDependencies: cpu-features "~0.0.10" nan "^2.20.0" ssri@^12.0.0: version "12.0.0" resolved "https://registry.npmjs.org/ssri/-/ssri-12.0.0.tgz" integrity sha512-S7iGNosepx9RadX82oimUkvr0Ct7IjJbEbs4mJcTxst8um95J3sDYU1RBEOvdu6oL1Wek2ODI5i4MAw+dZ6cAQ== dependencies: minipass "^7.0.3" standard-as-callback@^2.1.0: version "2.1.0" resolved "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz" integrity sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A== statuses@^2.0.1, statuses@^2.0.2, statuses@~2.0.2: version "2.0.2" resolved "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz" integrity sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw== strict-uri-encode@^1.0.0: version "1.1.0" resolved "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz" integrity sha512-R3f198pcvnB+5IpnBlRkphuE9n46WyVl8I39W/ZUTZLz4nqSP/oLYUrcnJrw462Ds8he4YKMov2efsTIw1BDGQ== string-template@~0.2.1: version "0.2.1" resolved "https://registry.npmjs.org/string-template/-/string-template-0.2.1.tgz" integrity sha512-Yptehjogou2xm4UJbxJ4CxgZx12HBfeystp0y3x7s4Dj32ltVVG1Gg8YhKjHZkHicuKpZX/ffilA8505VbUbpw== "string-width-cjs@npm:string-width@^4.2.0": version "4.2.3" resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== dependencies: emoji-regex "^8.0.0" is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.1" string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== dependencies: emoji-regex "^8.0.0" is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.1" string-width@^5.0.1, string-width@^5.1.2: version "5.1.2" resolved "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz" integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== dependencies: eastasianwidth "^0.2.0" emoji-regex "^9.2.2" strip-ansi "^7.0.1" string_decoder@0.10: version "0.10.31" resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz" integrity sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ== string_decoder@~1.1.1: version "1.1.1" resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz" integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== dependencies: safe-buffer "~5.1.0" "strip-ansi-cjs@npm:strip-ansi@^6.0.1": version "6.0.1" resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== dependencies: ansi-regex "^5.0.1" strip-ansi@^3.0.0: version "3.0.1" resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz" integrity sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg== dependencies: ansi-regex "^2.0.0" strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== dependencies: ansi-regex "^5.0.1" strip-ansi@^7.0.1: version "7.1.0" resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz" integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ== dependencies: ansi-regex "^6.0.1" strip-dirs@^2.0.0: version "2.1.0" resolved "https://registry.npmjs.org/strip-dirs/-/strip-dirs-2.1.0.tgz" integrity sha512-JOCxOeKLm2CAS73y/U4ZeZPTkE+gNVCzKt7Eox84Iej1LT/2pTWYpZKJuxwQpvX1LiZb1xokNR7RLfuBAa7T3g== dependencies: is-natural-number "^4.0.1" strip-json-comments@^3.1.1: version "3.1.1" resolved "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== strip-outer@^1.0.0: version "1.0.1" resolved "https://registry.npmjs.org/strip-outer/-/strip-outer-1.0.1.tgz" integrity sha512-k55yxKHwaXnpYGsOzg4Vl8+tDrWylxDEpknGjhTiZB8dFRU5rTo9CAzeycivxV3s+zlTKwrs6WxMxR95n26kwg== dependencies: escape-string-regexp "^1.0.2" supports-color@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz" integrity sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g== supports-color@^5.3.0, supports-color@^5.5.0: version "5.5.0" resolved "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz" integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== dependencies: has-flag "^3.0.0" supports-color@^7.1.0: version "7.2.0" resolved "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz" integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== dependencies: has-flag "^4.0.0" supports-color@^8.1.1: version "8.1.1" resolved "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz" integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== dependencies: has-flag "^4.0.0" supports-color@^9.4.0: version "9.4.0" resolved "https://registry.npmjs.org/supports-color/-/supports-color-9.4.0.tgz" integrity sha512-VL+lNrEoIXww1coLPOmiEmK/0sGigko5COxI09KzHc2VJXJsQ37UaQ+8quuxjDeA7+KnLGTWRyOXSLLR2Wb4jw== supports-preserve-symlinks-flag@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz" integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== tar-stream@^1.5.2: version "1.6.2" resolved "https://registry.npmjs.org/tar-stream/-/tar-stream-1.6.2.tgz" integrity sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A== dependencies: bl "^1.0.0" buffer-alloc "^1.2.0" end-of-stream "^1.0.0" fs-constants "^1.0.0" readable-stream "^2.3.0" to-buffer "^1.1.1" xtend "^4.0.0" tar@^6.1.11, tar@^6.2.1: version "6.2.1" resolved "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz" integrity sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A== dependencies: chownr "^2.0.0" fs-minipass "^2.0.0" minipass "^5.0.0" minizlib "^2.1.1" mkdirp "^1.0.3" yallist "^4.0.0" tar@^7.4.3: version "7.4.3" resolved "https://registry.npmjs.org/tar/-/tar-7.4.3.tgz" integrity sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw== dependencies: "@isaacs/fs-minipass" "^4.0.0" chownr "^3.0.0" minipass "^7.1.2" minizlib "^3.0.1" mkdirp "^3.0.1" yallist "^5.0.0" terser@^5.46.1: version "5.46.1" resolved "https://registry.npmjs.org/terser/-/terser-5.46.1.tgz" integrity sha512-vzCjQO/rgUuK9sf8VJZvjqiqiHFaZLnOiimmUuOKODxWL8mm/xua7viT7aqX7dgPY60otQjUotzFMmCB4VdmqQ== dependencies: "@jridgewell/source-map" "^0.3.3" acorn "^8.15.0" commander "^2.20.0" source-map-support "~0.5.20" text-table@^0.2.0, text-table@~0.2.0: version "0.2.0" resolved "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz" integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== thenify-all@^1.0.0: version "1.6.0" resolved "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz" integrity sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA== dependencies: thenify ">= 3.1.0 < 4" "thenify@>= 3.1.0 < 4": version "3.3.1" resolved "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz" integrity sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw== dependencies: any-promise "^1.0.0" through@^2.3.8: version "2.3.8" resolved "https://registry.npmjs.org/through/-/through-2.3.8.tgz" integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== time-grunt@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/time-grunt/-/time-grunt-2.0.0.tgz" integrity sha512-iQD2AeDYCAJrsPC/eUsfYZD9UT7TuBOmUIgFV5zeTQgRk6yLJKoc3aYR0gusJ0m+bG13B6qrDZ0SwPLe0/htHw== dependencies: chalk "^1.0.0" date-time "^1.1.0" figures "^1.0.0" hooker "^0.2.3" number-is-nan "^1.0.0" pretty-ms "^2.1.0" text-table "^0.2.0" time-zone@^0.1.0: version "0.1.0" resolved "https://registry.npmjs.org/time-zone/-/time-zone-0.1.0.tgz" integrity sha512-S5CjtVIkeBTnlsaZP3gjsTb78ClBe74sEcgEoBwAVUKnTRDAGqUtLLIZHMsIyqOWjt9DGQpLMMoD8ZKIfP2ddQ== timed-out@^4.0.1: version "4.0.1" resolved "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz" integrity sha512-G7r3AhovYtr5YKOWQkta8RKAPb+J9IsO4uVmzjl8AZwfhs8UcUwTiD6gcJYSgOtzyjvQKrKYn41syHbUWMkafA== timestring@^7.0.0: version "7.0.0" resolved "https://registry.npmjs.org/timestring/-/timestring-7.0.0.tgz" integrity sha512-U7ttxEdKWqHYJ96OGoJJR5gU8Nwkl3tlY0n7Jr4vcpLD2RkVZLE1Ph9k8ZRrZ7LYX9QCtd3M9OUaR9P8Z37QNg== tiny-lr@^1.1.1: version "1.1.1" resolved "https://registry.npmjs.org/tiny-lr/-/tiny-lr-1.1.1.tgz" integrity sha512-44yhA3tsaRoMOjQQ+5v5mVdqef+kH6Qze9jTpqtVufgYjYt08zyZAwNwwVBj3i1rJMnR52IxOW0LK0vBzgAkuA== dependencies: body "^5.1.0" debug "^3.1.0" faye-websocket "~0.10.0" livereload-js "^2.3.0" object-assign "^4.1.0" qs "^6.4.0" tiny-relative-date@^1.3.0: version "1.3.0" resolved "https://registry.npmjs.org/tiny-relative-date/-/tiny-relative-date-1.3.0.tgz" integrity sha512-MOQHpzllWxDCHHaDno30hhLfbouoYlOI8YlMNtvKe1zXbjEVhbcEovQxvZrPvtiYW630GQDoMMarCnjfyfHA+A== to-buffer@^1.1.1: version "1.1.1" resolved "https://registry.npmjs.org/to-buffer/-/to-buffer-1.1.1.tgz" integrity sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg== to-regex-range@^5.0.1: version "5.0.1" resolved "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz" integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== dependencies: is-number "^7.0.0" toidentifier@~1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz" integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== touch@^3.1.0: version "3.1.1" resolved "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz" integrity sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA== treeverse@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/treeverse/-/treeverse-3.0.0.tgz" integrity sha512-gcANaAnd2QDZFmHFEOF4k7uc1J/6a6z3DJMd/QwEyxLoKGiptJRwid582r7QIsFlFMIZ3SnxfS52S4hm2DHkuQ== trim-repeated@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/trim-repeated/-/trim-repeated-1.0.0.tgz" integrity sha512-pkonvlKk8/ZuR0D5tLW8ljt5I8kmxp2XKymhepUeOdCEfKpZaktSArkLHZt76OB1ZvO9bssUsDty4SWhLvZpLg== dependencies: escape-string-regexp "^1.0.2" tuf-js@^3.0.1: version "3.0.1" resolved "https://registry.npmjs.org/tuf-js/-/tuf-js-3.0.1.tgz" integrity sha512-+68OP1ZzSF84rTckf3FA95vJ1Zlx/uaXyiiKyPd1pA4rZNkpEvDAKmsu1xUSmbF/chCRYgZ6UZkDwC7PmzmAyA== dependencies: "@tufjs/models" "3.0.1" debug "^4.3.6" make-fetch-happen "^14.0.1" tunnel-ssh@^5.2.0: version "5.2.0" resolved "https://registry.npmjs.org/tunnel-ssh/-/tunnel-ssh-5.2.0.tgz" integrity sha512-IGiyhE2RSt3NVvZ7aKH3ykziAxKNPe/z97Rab/lrIXslif/cq7J/m6EXfERlDITiFyGGYMqqi5SSrt/mk1VbEg== dependencies: ssh2 "^1.15.0" tweetnacl@^0.14.3: version "0.14.5" resolved "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz" integrity sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA== type-is@^2.0.1: version "2.0.1" resolved "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz" integrity sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw== dependencies: content-type "^1.0.5" media-typer "^1.1.0" mime-types "^3.0.0" uglify-js@^3.5.1: version "3.19.3" resolved "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz" integrity sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ== unbzip2-stream@^1.0.9: version "1.4.3" resolved "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz" integrity sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg== dependencies: buffer "^5.2.1" through "^2.3.8" unc-path-regex@^0.1.2: version "0.1.2" resolved "https://registry.npmjs.org/unc-path-regex/-/unc-path-regex-0.1.2.tgz" integrity sha512-eXL4nmJT7oCpkZsHZUOJo8hcX3GbsiDOa0Qu9F646fi8dT3XuSVopVqAcEiVzSKKH7UoDti23wNX3qGFxcW5Qg== undefsafe@^2.0.5: version "2.0.5" resolved "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz" integrity sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA== underscore.string@~3.3.5: version "3.3.6" resolved "https://registry.npmjs.org/underscore.string/-/underscore.string-3.3.6.tgz" integrity sha512-VoC83HWXmCrF6rgkyxS9GHv8W9Q5nhMKho+OadDJGzL2oDYbYEppBaCMH6pFlwLeqj2QS+hhkw2kpXkSdD1JxQ== dependencies: sprintf-js "^1.1.1" util-deprecate "^1.0.2" undici-types@~6.20.0: version "6.20.0" resolved "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz" integrity sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg== unique-filename@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/unique-filename/-/unique-filename-4.0.0.tgz" integrity sha512-XSnEewXmQ+veP7xX2dS5Q4yZAvO40cBN2MWkJ7D/6sW4Dg6wYBNwM1Vrnz1FhH5AdeLIlUXRI9e28z1YZi71NQ== dependencies: unique-slug "^5.0.0" unique-slug@^5.0.0: version "5.0.0" resolved "https://registry.npmjs.org/unique-slug/-/unique-slug-5.0.0.tgz" integrity sha512-9OdaqO5kwqR+1kVgHAhsp5vPNU0hnxRa26rBFNfNgM7M6pNtgzeBn3s/xbyCQL3dcjzOatcef6UUHpB/6MaETg== dependencies: imurmurhash "^0.1.4" universalify@^2.0.0: version "2.0.1" resolved "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz" integrity sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw== unpipe@~1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz" integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== upper-case@^1.1.1: version "1.1.3" resolved "https://registry.npmjs.org/upper-case/-/upper-case-1.1.3.tgz" integrity sha512-WRbjgmYzgXkCV7zNVpy5YgrHgbBv126rMALQQMrmzOVC4GM2waQ9x7xtm8VU+1yF2kWyPzI9zbZ48n4vSxwfSA== url-parse-lax@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz" integrity sha512-NjFKA0DidqPa5ciFcSrXnAltTtzz84ogy+NebPvfEgAck0+TNg4UJ4IN+fB7zRZfbgUf0syOo9MDxFkDSMuFaQ== dependencies: prepend-http "^2.0.0" url-to-options@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/url-to-options/-/url-to-options-1.0.1.tgz" integrity sha512-0kQLIzG4fdk/G5NONku64rSH/x32NOA39LVQqlK8Le6lvTF6GGRJpqaQFGgU+CLwySIqBSMdwYM0sYcW9f6P4A== utf8@^2.1.1: version "2.1.2" resolved "https://registry.npmjs.org/utf8/-/utf8-2.1.2.tgz" integrity sha512-QXo+O/QkLP/x1nyi54uQiG0XrODxdysuQvE5dtVqv7F5K2Qb6FsN+qbr6KhF5wQ20tfcV3VQp0/2x1e1MRSPWg== util-deprecate@^1.0.2, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== uuid@^11.0.5: version "11.0.5" resolved "https://registry.npmjs.org/uuid/-/uuid-11.0.5.tgz" integrity sha512-508e6IcKLrhxKdBbcA2b4KQZlLVp2+J5UwQ6F7Drckkc5N9ZJwFa4TgWtsww9UG8fGHbm6gbV19TdM5pQ4GaIA== v8flags@~3.2.0: version "3.2.0" resolved "https://registry.npmjs.org/v8flags/-/v8flags-3.2.0.tgz" integrity sha512-mH8etigqMfiGWdeXpaaqGfs6BndypxusHHcv2qSHyZkGEznCd/qAXCWWRzeowtL54147cktFOC4P5y+kl8d8Jg== dependencies: homedir-polyfill "^1.0.1" validate-npm-package-license@^3.0.4: version "3.0.4" resolved "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz" integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== dependencies: spdx-correct "^3.0.0" spdx-expression-parse "^3.0.0" validate-npm-package-name@^6.0.0: version "6.0.0" resolved "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-6.0.0.tgz" integrity sha512-d7KLgL1LD3U3fgnvWEY1cQXoO/q6EQ1BSz48Sa149V/5zVTAbgmZIpyI8TRi6U9/JNyeYLlTKsEMPtLC27RFUg== vary@^1, vary@^1.1.2: version "1.1.2" resolved "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz" integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== walk-up-path@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/walk-up-path/-/walk-up-path-4.0.0.tgz" integrity sha512-3hu+tD8YzSLGuFYtPRb48vdhKMi0KQV5sn+uWr8+7dMEq/2G/dtLrdDinkLjqq5TIbIBjYJ4Ax/n3YiaW7QM8A== websocket-driver@>=0.5.1: version "0.7.4" resolved "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz" integrity sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg== dependencies: http-parser-js ">=0.5.1" safe-buffer ">=5.1.0" websocket-extensions ">=0.1.1" websocket-extensions@>=0.1.1: version "0.1.4" resolved "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz" integrity sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg== which@^1.2.14: version "1.3.1" resolved "https://registry.npmjs.org/which/-/which-1.3.1.tgz" integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== dependencies: isexe "^2.0.0" which@^2.0.1, which@~2.0.2: version "2.0.2" resolved "https://registry.npmjs.org/which/-/which-2.0.2.tgz" integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== dependencies: isexe "^2.0.0" which@^5.0.0: version "5.0.0" resolved "https://registry.npmjs.org/which/-/which-5.0.0.tgz" integrity sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ== dependencies: isexe "^3.1.1" workerpool@^6.5.1: version "6.5.1" resolved "https://registry.npmjs.org/workerpool/-/workerpool-6.5.1.tgz" integrity sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA== "wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": version "7.0.0" resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== dependencies: ansi-styles "^4.0.0" string-width "^4.1.0" strip-ansi "^6.0.0" wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== dependencies: ansi-styles "^4.0.0" string-width "^4.1.0" strip-ansi "^6.0.0" wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz" integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ== dependencies: ansi-styles "^6.1.0" string-width "^5.0.1" strip-ansi "^7.0.1" wrappy@1: version "1.0.2" resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== write-file-atomic@^6.0.0: version "6.0.0" resolved "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-6.0.0.tgz" integrity sha512-GmqrO8WJ1NuzJ2DrziEI2o57jKAVIQNf8a18W3nCYU3H7PNWqCCVTeH6/NQE93CIllIgQS98rrmVkYgTX9fFJQ== dependencies: imurmurhash "^0.1.4" signal-exit "^4.0.1" ws@~8.17.1: version "8.17.1" resolved "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz" integrity sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ== xtend@^4.0.0: version "4.0.2" resolved "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz" integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== y18n@^5.0.5: version "5.0.8" resolved "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz" integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== yallist@^4.0.0: version "4.0.0" resolved "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz" integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== yallist@^5.0.0: version "5.0.0" resolved "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz" integrity sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw== yaml@^2.7.0: version "2.7.0" resolved "https://registry.npmjs.org/yaml/-/yaml-2.7.0.tgz" integrity sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA== yargs-parser@^21.1.1: version "21.1.1" resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz" integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== yargs-unparser@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz" integrity sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA== dependencies: camelcase "^6.0.0" decamelize "^4.0.0" flat "^5.0.2" is-plain-obj "^2.1.0" yargs@^17.7.2: version "17.7.2" resolved "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz" integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== dependencies: cliui "^8.0.1" escalade "^3.1.1" get-caller-file "^2.0.5" require-directory "^2.1.1" string-width "^4.2.3" y18n "^5.0.5" yargs-parser "^21.1.1" yauzl@^2.10.0, yauzl@^2.4.2: version "2.10.0" resolved "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz" integrity sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g== dependencies: buffer-crc32 "~0.2.3" fd-slicer "~1.1.0" yocto-queue@^0.1.0: version "0.1.0" resolved "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz" integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==