diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000..86678f2 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,62 @@ +{ + "root": true, + "parser": "@typescript-eslint/parser", + "parserOptions": { + "project": "./tsconfig.json" + }, + "plugins": [ + "@typescript-eslint", + "eslint-plugin-tsdoc" + ], + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/eslint-recommended", + "plugin:@typescript-eslint/recommended" + ], + "overrides": [ + { + "files": ["**.ts"], + "excludedFiles": ["**.js"], + "rules": { + "no-prototype-builtins": "off", + + "prefer-const": "off", + "block-spacing": "warn", + "brace-style": ["warn", "1tbs"], + "camelcase": "warn", + "comma-dangle": ["warn", "never"], + "comma-spacing": ["warn", { "before": false, "after": true }], + "comma-style": ["warn", "last"], + "consistent-return": "error", + "eol-last": ["warn", "always"], + "func-call-spacing": ["warn", "never"], + "indent": ["warn", 4, { "SwitchCase": 1 }], + "keyword-spacing": ["warn", { "before": true, "after": true }], + "lines-between-class-members": [ "warn", "always", { "exceptAfterSingleLine": true } ], + "no-trailing-spaces": "warn", + "quotes": ["warn", "double"], + "semi": ["error", "always"], + "semi-style": ["warn", "last"], + "space-before-blocks": "warn", + "no-var": "warn", + + "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/no-empty-interface": "off", + "@typescript-eslint/explicit-module-boundary-types": ["warn", { "allowArgumentsExplicitlyTypedAsAny": true }], + "@typescript-eslint/no-inferrable-types": "off", + "@typescript-eslint/member-ordering": "warn", + "@typescript-eslint/explicit-function-return-type": "error", + "@typescript-eslint/no-unsafe-return": "error", + "@typescript-eslint/naming-convention": [ + "warn", + { "selector": "objectLiteralProperty", "modifiers": [ "requiresQuotes" ], "format": null }, + { "selector": "memberLike", "modifiers": [ "public" ], "format": ["camelCase"], "leadingUnderscore": "allow" }, + { "selector": "memberLike", "modifiers": [ "protected" ], "format": ["camelCase"], "leadingUnderscore": "require" }, + { "selector": "memberLike", "modifiers": [ "private" ], "format": ["camelCase"], "leadingUnderscore": "require" } + ], + + "tsdoc/syntax": "warn" + } + } + ] +} \ No newline at end of file diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..1807baf --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,30 @@ +name: CI + +on: + push: + branches: [ main ] + +jobs: + build-win32: + runs-on: windows-latest + + steps: + - uses: actions/checkout@v2 + - name: Build httpiness on Windows + run: | + npm install + npm run lint + npm run test + npm run build:prod + + build-macos: + runs-on: macos-latest + + steps: + - uses: actions/checkout@v2 + - name: Build httpiness on macOS + run: | + npm install + npm run lint + npm run test + npm run build:prod diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f8ee100 --- /dev/null +++ b/.gitignore @@ -0,0 +1,74 @@ +# Logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Runtime data +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env + +# next.js build output +.next + +# OS X temporary files +.DS_Store + +# Rush temporary files +common/deploy/ +common/temp/ +common/autoinstallers/*/.npmrc +**/.rush/temp/ + +# Build output +**/dist/electron-workspace/*.js +**/dist/electron-workspace/*.node +**/dist/electron-workspace/*.LICENSE.txt +**/dist/bin +**/temp/** +**/test/bin/** +dummyCollection.json diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..a337a6c --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,6 @@ +{ + "recommendations": [ + "davidanson.vscode-markdownlint", + "streetsidesoftware.code-spell-checker" + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..1c71850 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,9 @@ +{ + "[markdown]": { + "editor.tabSize": 2, + "editor.detectIndentation": false, + "editor.wordWrap": "wordWrapColumn", + "editor.defaultFormatter": "DavidAnson.vscode-markdownlint", + "editor.rulers": [80] + } +} \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..daf6238 --- /dev/null +++ b/LICENSE @@ -0,0 +1,7 @@ +Copyright (c) 2023-2024 httpiness contributors + +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. diff --git a/README.md b/README.md new file mode 100644 index 0000000..b59a4ba --- /dev/null +++ b/README.md @@ -0,0 +1,37 @@ +# httpiness + +httpiness is a developer-oriented parametric HTTP client for slalom API testing. + +![httpiness preview](./docs/images/screenshot.png) + +## How to run httpiness? + +### Build from source (preferred) + +```bash +# TL;DR +git clone https://github.com/bognikol/httpiness.git +cd httpiness +npm run start:prod:clean +``` + +1. Make sure Node.js is installed on your machine. +2. Navigate to root directory (where package.json is located) and run `npm run start:prod:clean`. This script will clean the repo (if any garbage), install the dependencies, build and finally run httpiness. The script will also build the installers (though not signed). + +### Download binaries + +Alternatively, you can download Windows and macOS installers at [httpiness.com](https://www.httpiness.com). Current version of binaries is 1.4.2. Be aware that they almost certainly outdated because new binaries are not pushed to the httpiness.com due to signing issues. + +## How to use httpiness? + +After starting httpiness, click **Help** button in the upper-right corner and help window will be shown. Alternatively, the same help sections can be accessed on [httpiness.com](https://www.httpiness.com/#/docs). + +## How is httpiness architectured? + +httpiness is an [electron](https://electronjs.org) application which currently acts as a GUI shell for [curl](https://curl.se). curl may be substituted to some other HTTP client in the future. + +HTML-based GUI in written using [aflon](https://github.com/bognikol/aflonstack). aflon is a strongly-typed object-oriented UI framework for web. + +## Contributing + +You are welcome to contribute. Refer to `docs` directory for technical details. diff --git a/dist/electron-workspace/entitlements.mac.plist b/dist/electron-workspace/entitlements.mac.plist new file mode 100644 index 0000000..3c09da4 --- /dev/null +++ b/dist/electron-workspace/entitlements.mac.plist @@ -0,0 +1,12 @@ + + + + + com.apple.security.cs.allow-jit + + com.apple.security.cs.debugger + + com.apple.security.cs.disable-library-validation + + + \ No newline at end of file diff --git a/dist/electron-workspace/index.html b/dist/electron-workspace/index.html new file mode 100644 index 0000000..041a250 --- /dev/null +++ b/dist/electron-workspace/index.html @@ -0,0 +1,45 @@ + + + + + httpiness + + + + + + + + \ No newline at end of file diff --git a/dist/electron-workspace/package-lock.json b/dist/electron-workspace/package-lock.json new file mode 100644 index 0000000..85502bf --- /dev/null +++ b/dist/electron-workspace/package-lock.json @@ -0,0 +1,1525 @@ +{ + "name": "httpiness", + "version": "1.4.3", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "httpiness", + "version": "1.4.3", + "license": "MIT", + "devDependencies": { + "electron": "30.0.9" + } + }, + "node_modules/@electron/get": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@electron/get/-/get-2.0.3.tgz", + "integrity": "sha512-Qkzpg2s9GnVV2I2BjRksUi43U5e6+zaQMcjoJy0C+C5oxaKl+fmckGDQFtRpZpZV0NQekuZZ+tGz7EA9TVnQtQ==", + "dev": true, + "dependencies": { + "debug": "^4.1.1", + "env-paths": "^2.2.0", + "fs-extra": "^8.1.0", + "got": "^11.8.5", + "progress": "^2.0.3", + "semver": "^6.2.0", + "sumchecker": "^3.0.1" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "global-agent": "^3.0.0" + } + }, + "node_modules/@sindresorhus/is": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", + "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" + } + }, + "node_modules/@szmarczak/http-timer": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", + "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==", + "dev": true, + "dependencies": { + "defer-to-connect": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@types/cacheable-request": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz", + "integrity": "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==", + "dev": true, + "dependencies": { + "@types/http-cache-semantics": "*", + "@types/keyv": "^3.1.4", + "@types/node": "*", + "@types/responselike": "^1.0.0" + } + }, + "node_modules/@types/http-cache-semantics": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz", + "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==", + "dev": true + }, + "node_modules/@types/keyv": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", + "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/node": { + "version": "20.12.12", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.12.tgz", + "integrity": "sha512-eWLDGF/FOSPtAvEqeRAQ4C8LSA7M1I7i0ky1I8U7kD1J5ITyW3AsRhQrKVoWf5pFKZ2kILsEGJhsI9r93PYnOw==", + "dev": true, + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@types/responselike": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.3.tgz", + "integrity": "sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw==", + "dev": true, + "optional": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/boolean": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.2.0.tgz", + "integrity": "sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==", + "dev": true, + "optional": true + }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/cacheable-lookup": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", + "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==", + "dev": true, + "engines": { + "node": ">=10.6.0" + } + }, + "node_modules/cacheable-request": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.4.tgz", + "integrity": "sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==", + "dev": true, + "dependencies": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^4.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^6.0.1", + "responselike": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/clone-response": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", + "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==", + "dev": true, + "dependencies": { + "mimic-response": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "dev": true, + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decompress-response/node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/defer-to-connect": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", + "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "optional": true, + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "optional": true, + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", + "dev": true, + "optional": true + }, + "node_modules/electron": { + "version": "30.0.9", + "resolved": "https://registry.npmjs.org/electron/-/electron-30.0.9.tgz", + "integrity": "sha512-ArxgdGHVu3o5uaP+Tqj8cJDvU03R6vrGrOqiMs7JXLnvQHMqXJIIxmFKQAIdJW8VoT3ac3hD21tA7cPO10RLow==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "@electron/get": "^2.0.0", + "@types/node": "^20.9.0", + "extract-zip": "^2.0.1" + }, + "bin": { + "electron": "cli.js" + }, + "engines": { + "node": ">= 12.20.55" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "dev": true, + "optional": true, + "dependencies": { + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "optional": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es6-error": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", + "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", + "dev": true, + "optional": true + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "optional": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/extract-zip": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", + "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", + "dev": true, + "dependencies": { + "debug": "^4.1.1", + "get-stream": "^5.1.0", + "yauzl": "^2.10.0" + }, + "bin": { + "extract-zip": "cli.js" + }, + "engines": { + "node": ">= 10.17.0" + }, + "optionalDependencies": { + "@types/yauzl": "^2.9.1" + } + }, + "node_modules/fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", + "dev": true, + "dependencies": { + "pend": "~1.2.0" + } + }, + "node_modules/fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "optional": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "dev": true, + "optional": true, + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/global-agent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-agent/-/global-agent-3.0.0.tgz", + "integrity": "sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==", + "dev": true, + "optional": true, + "dependencies": { + "boolean": "^3.0.1", + "es6-error": "^4.1.1", + "matcher": "^3.0.0", + "roarr": "^2.15.3", + "semver": "^7.3.2", + "serialize-error": "^7.0.1" + }, + "engines": { + "node": ">=10.0" + } + }, + "node_modules/global-agent/node_modules/semver": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", + "dev": true, + "optional": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "dev": true, + "optional": true, + "dependencies": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dev": true, + "optional": true, + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/got": { + "version": "11.8.6", + "resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz", + "integrity": "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==", + "dev": true, + "dependencies": { + "@sindresorhus/is": "^4.0.0", + "@szmarczak/http-timer": "^4.0.5", + "@types/cacheable-request": "^6.0.1", + "@types/responselike": "^1.0.0", + "cacheable-lookup": "^5.0.3", + "cacheable-request": "^7.0.2", + "decompress-response": "^6.0.0", + "http2-wrapper": "^1.0.0-beta.5.2", + "lowercase-keys": "^2.0.0", + "p-cancelable": "^2.0.0", + "responselike": "^2.0.0" + }, + "engines": { + "node": ">=10.19.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/got?sponsor=1" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "optional": true, + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", + "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", + "dev": true, + "optional": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "dev": true, + "optional": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "optional": true, + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/http-cache-semantics": { + "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==", + "dev": true + }, + "node_modules/http2-wrapper": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", + "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", + "dev": true, + "dependencies": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.0.0" + }, + "engines": { + "node": ">=10.19.0" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", + "dev": true, + "optional": true + }, + "node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/matcher": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz", + "integrity": "sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==", + "dev": true, + "optional": true, + "dependencies": { + "escape-string-regexp": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/normalize-url": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "optional": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/p-cancelable": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", + "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", + "dev": true + }, + "node_modules/progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/resolve-alpn": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", + "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==", + "dev": true + }, + "node_modules/responselike": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz", + "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==", + "dev": true, + "dependencies": { + "lowercase-keys": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/roarr": { + "version": "2.15.4", + "resolved": "https://registry.npmjs.org/roarr/-/roarr-2.15.4.tgz", + "integrity": "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==", + "dev": true, + "optional": true, + "dependencies": { + "boolean": "^3.0.1", + "detect-node": "^2.0.4", + "globalthis": "^1.0.1", + "json-stringify-safe": "^5.0.1", + "semver-compare": "^1.0.0", + "sprintf-js": "^1.1.2" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/semver-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", + "integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==", + "dev": true, + "optional": true + }, + "node_modules/serialize-error": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz", + "integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==", + "dev": true, + "optional": true, + "dependencies": { + "type-fest": "^0.13.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/sprintf-js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", + "dev": true, + "optional": true + }, + "node_modules/sumchecker": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-3.0.1.tgz", + "integrity": "sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg==", + "dev": true, + "dependencies": { + "debug": "^4.1.0" + }, + "engines": { + "node": ">= 8.0" + } + }, + "node_modules/type-fest": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", + "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", + "dev": true, + "optional": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true + }, + "node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "dev": true, + "dependencies": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + } + }, + "dependencies": { + "@electron/get": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@electron/get/-/get-2.0.3.tgz", + "integrity": "sha512-Qkzpg2s9GnVV2I2BjRksUi43U5e6+zaQMcjoJy0C+C5oxaKl+fmckGDQFtRpZpZV0NQekuZZ+tGz7EA9TVnQtQ==", + "dev": true, + "requires": { + "debug": "^4.1.1", + "env-paths": "^2.2.0", + "fs-extra": "^8.1.0", + "global-agent": "^3.0.0", + "got": "^11.8.5", + "progress": "^2.0.3", + "semver": "^6.2.0", + "sumchecker": "^3.0.1" + } + }, + "@sindresorhus/is": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", + "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", + "dev": true + }, + "@szmarczak/http-timer": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", + "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==", + "dev": true, + "requires": { + "defer-to-connect": "^2.0.0" + } + }, + "@types/cacheable-request": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz", + "integrity": "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==", + "dev": true, + "requires": { + "@types/http-cache-semantics": "*", + "@types/keyv": "^3.1.4", + "@types/node": "*", + "@types/responselike": "^1.0.0" + } + }, + "@types/http-cache-semantics": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz", + "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==", + "dev": true + }, + "@types/keyv": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", + "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/node": { + "version": "20.12.12", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.12.tgz", + "integrity": "sha512-eWLDGF/FOSPtAvEqeRAQ4C8LSA7M1I7i0ky1I8U7kD1J5ITyW3AsRhQrKVoWf5pFKZ2kILsEGJhsI9r93PYnOw==", + "dev": true, + "requires": { + "undici-types": "~5.26.4" + } + }, + "@types/responselike": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.3.tgz", + "integrity": "sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw==", + "dev": true, + "optional": true, + "requires": { + "@types/node": "*" + } + }, + "boolean": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.2.0.tgz", + "integrity": "sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==", + "dev": true, + "optional": true + }, + "buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "dev": true + }, + "cacheable-lookup": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", + "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==", + "dev": true + }, + "cacheable-request": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.4.tgz", + "integrity": "sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==", + "dev": true, + "requires": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^4.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^6.0.1", + "responselike": "^2.0.0" + } + }, + "clone-response": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", + "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==", + "dev": true, + "requires": { + "mimic-response": "^1.0.0" + } + }, + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "dev": true, + "requires": { + "mimic-response": "^3.1.0" + }, + "dependencies": { + "mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "dev": true + } + } + }, + "defer-to-connect": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", + "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", + "dev": true + }, + "define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "optional": true, + "requires": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + } + }, + "define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "optional": true, + "requires": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + } + }, + "detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", + "dev": true, + "optional": true + }, + "electron": { + "version": "30.0.9", + "resolved": "https://registry.npmjs.org/electron/-/electron-30.0.9.tgz", + "integrity": "sha512-ArxgdGHVu3o5uaP+Tqj8cJDvU03R6vrGrOqiMs7JXLnvQHMqXJIIxmFKQAIdJW8VoT3ac3hD21tA7cPO10RLow==", + "dev": true, + "requires": { + "@electron/get": "^2.0.0", + "@types/node": "^20.9.0", + "extract-zip": "^2.0.1" + } + }, + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, + "requires": { + "once": "^1.4.0" + } + }, + "env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "dev": true + }, + "es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "dev": true, + "optional": true, + "requires": { + "get-intrinsic": "^1.2.4" + } + }, + "es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "optional": true + }, + "es6-error": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", + "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", + "dev": true, + "optional": true + }, + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "optional": true + }, + "extract-zip": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", + "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", + "dev": true, + "requires": { + "@types/yauzl": "^2.9.1", + "debug": "^4.1.1", + "get-stream": "^5.1.0", + "yauzl": "^2.10.0" + } + }, + "fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", + "dev": true, + "requires": { + "pend": "~1.2.0" + } + }, + "fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "optional": true + }, + "get-intrinsic": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "dev": true, + "optional": true, + "requires": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + } + }, + "get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "global-agent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-agent/-/global-agent-3.0.0.tgz", + "integrity": "sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==", + "dev": true, + "optional": true, + "requires": { + "boolean": "^3.0.1", + "es6-error": "^4.1.1", + "matcher": "^3.0.0", + "roarr": "^2.15.3", + "semver": "^7.3.2", + "serialize-error": "^7.0.1" + }, + "dependencies": { + "semver": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", + "dev": true, + "optional": true + } + } + }, + "globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "dev": true, + "optional": true, + "requires": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + } + }, + "gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dev": true, + "optional": true, + "requires": { + "get-intrinsic": "^1.1.3" + } + }, + "got": { + "version": "11.8.6", + "resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz", + "integrity": "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==", + "dev": true, + "requires": { + "@sindresorhus/is": "^4.0.0", + "@szmarczak/http-timer": "^4.0.5", + "@types/cacheable-request": "^6.0.1", + "@types/responselike": "^1.0.0", + "cacheable-lookup": "^5.0.3", + "cacheable-request": "^7.0.2", + "decompress-response": "^6.0.0", + "http2-wrapper": "^1.0.0-beta.5.2", + "lowercase-keys": "^2.0.0", + "p-cancelable": "^2.0.0", + "responselike": "^2.0.0" + } + }, + "graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, + "has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "optional": true, + "requires": { + "es-define-property": "^1.0.0" + } + }, + "has-proto": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", + "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", + "dev": true, + "optional": true + }, + "has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "dev": true, + "optional": true + }, + "hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "optional": true, + "requires": { + "function-bind": "^1.1.2" + } + }, + "http-cache-semantics": { + "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==", + "dev": true + }, + "http2-wrapper": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", + "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", + "dev": true, + "requires": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.0.0" + } + }, + "json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", + "dev": true, + "optional": true + }, + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6" + } + }, + "keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "requires": { + "json-buffer": "3.0.1" + } + }, + "lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "dev": true + }, + "matcher": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz", + "integrity": "sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==", + "dev": true, + "optional": true, + "requires": { + "escape-string-regexp": "^4.0.0" + } + }, + "mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", + "dev": true + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "normalize-url": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", + "dev": true + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "optional": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "p-cancelable": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", + "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==", + "dev": true + }, + "pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", + "dev": true + }, + "progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", + "dev": true + }, + "resolve-alpn": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", + "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==", + "dev": true + }, + "responselike": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz", + "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==", + "dev": true, + "requires": { + "lowercase-keys": "^2.0.0" + } + }, + "roarr": { + "version": "2.15.4", + "resolved": "https://registry.npmjs.org/roarr/-/roarr-2.15.4.tgz", + "integrity": "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==", + "dev": true, + "optional": true, + "requires": { + "boolean": "^3.0.1", + "detect-node": "^2.0.4", + "globalthis": "^1.0.1", + "json-stringify-safe": "^5.0.1", + "semver-compare": "^1.0.0", + "sprintf-js": "^1.1.2" + } + }, + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true + }, + "semver-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", + "integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==", + "dev": true, + "optional": true + }, + "serialize-error": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz", + "integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==", + "dev": true, + "optional": true, + "requires": { + "type-fest": "^0.13.1" + } + }, + "sprintf-js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", + "dev": true, + "optional": true + }, + "sumchecker": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-3.0.1.tgz", + "integrity": "sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg==", + "dev": true, + "requires": { + "debug": "^4.1.0" + } + }, + "type-fest": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", + "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", + "dev": true, + "optional": true + }, + "undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true + }, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "dev": true, + "requires": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + } + } +} diff --git a/dist/electron-workspace/package.json b/dist/electron-workspace/package.json new file mode 100644 index 0000000..d898ec3 --- /dev/null +++ b/dist/electron-workspace/package.json @@ -0,0 +1,36 @@ +{ + "name": "httpiness", + "version": "1.4.3", + "description": "httpiness in developer-oriented parametric HTTP client for slalom API testing.", + "main": "./main.js", + "scripts": {}, + "author": "Bogdan Nikolic", + "license": "MIT", + "build": { + "appId": "org.httpiness", + "productName": "httpiness", + "artifactName": "${productName}_${os}_${arch}_${version}.${ext}", + "copyright": "Copyright (c) 2023-2024 httpiness contributors", + "directories": { + "output": "../bin" + }, + "win": { + "icon": "resources/images/HttpinessIconWin.png" + }, + "nsis": { + "oneClick": false + }, + "mac": { + "icon": "resources/images/HttpinessIconMacOS.png", + "hardenedRuntime": true, + "entitlements": "entitlements.mac.plist", + "entitlementsInherit": "entitlements.mac.plist", + "target": [ + { "target": "default", "arch": [ "x64" ] } + ] + } + }, + "devDependencies": { + "electron": "30.0.9" + } +} diff --git a/dist/electron-workspace/resources/bin/macOS/curl/curl b/dist/electron-workspace/resources/bin/macOS/curl/curl new file mode 100644 index 0000000..6045505 Binary files /dev/null and b/dist/electron-workspace/resources/bin/macOS/curl/curl differ diff --git a/dist/electron-workspace/resources/bin/win32/curl/curl.exe b/dist/electron-workspace/resources/bin/win32/curl/curl.exe new file mode 100644 index 0000000..1120260 Binary files /dev/null and b/dist/electron-workspace/resources/bin/win32/curl/curl.exe differ diff --git a/dist/electron-workspace/resources/faq.txt b/dist/electron-workspace/resources/faq.txt new file mode 100644 index 0000000..b5ac36e --- /dev/null +++ b/dist/electron-workspace/resources/faq.txt @@ -0,0 +1,383 @@ +>>section- +Getting started + +>>question- +What is the easiest way to get started? + +>>answer- +When you start httpiness, Welcome screen will be shown. Click 'Send http request' label on Welcome screen and a simple predefined request will appear in the Workspace. Press 'Send' button to send request. + +>>question- +Why is httpiness unique? + +>>answer- +Software engineers, while developing APIs, often need to send multiple requests before they can send request they want to test. For example, if developers want to test request which deletes entry in database, they need to authenticate, create entity, query entity to validate its creation, delete entity (which is a request to be tested), and then query entity again (to validate that delete did its job). This is what we call slalom testing. Httpiness is created with this workflow in mind. Httpiness offers simplicity and effective parametrization strategy to maximize development efficiency. For example, httpiness is one of rare clients which render whole request in the single view (no tabs for every part of request). + +>>section- +Collections + +>>question- +How to create new collection? + +>>answer- +Click 'Create new collection' button in header of Request Browser; then specify where to create collection file. +Collections are stored in files. All changes are automatically saved to files. + +>>question- +How to import existing collection? + +>>answer- +Click 'Import httpiness or third-party collection' button in header of Request Browser; then specify collection file which you would like to load. Collections which were opened at the moment when httpiness was closed will be automatically loaded at the next startup. + +>>question- +How to import third-party collection? + +>>answer- +Postman collections v2.1 can be converted and imported to httpiness. +To import Postman collection, you first need to export it from Postman. Consult Postman documentation for details. +Once Postman collection is exported, click 'Import httpiness or third-party collection' button in header of Request Browser; then specify Postman collection file which you would like to import. Message box will appear informing you about result of conversion, and then you will be asked to specify name and location of httpiness collection created from Postman collection. +Once you do that, new httpiness collection will be automatically opened. + +>>question- +What is the Experiment collection? + +>>answer- +Experiment collection is the collection for sketching and experimenting with requests. This special collection is not stored in file and cannot be shared. There is only single Experiment collection. Show Experiment collection in Request Browser by clicking 'Experiment' button in Request Browser's header. + +>>question- +Should I save collection after making change? + +>>answer- +No. All changes are automatically saved. + +>>question- +Is it possible to work with multiple collections in the same time? + +>>answer- +Yes. Just import multiple collections. + +>>question- +How to track changes in collection? + +>>answer- +If you are an API developer, commit your collection file to repository; then update collection file as your API evolves. Release collection file with each API release. Use your existing versioning system to track and merge changes. +If you are API consumer, use collection file which is released together with API version you are using. + +>>question- +What if loaded collection file is modified outside httpiness? + +>>answer- +If a loaded collection file is modified outside httpiness there might be a conflict. Httpiness will detect that the file has been modified and will prompt the user asking whether external changes should be discarded or integrated. + +>>section- +HTTP requests + +>>question- +How to create and send new request? + +>>answer- +Once collection is open, hover your mouse cursor over it in Request Browser. Three buttons will appear, including 'Create new request' button, which should be clicked. Default GET request will be created. Specify URL you want to get and press 'Send' button right from the request name. + +>>question- +Is it possible to send a request directly from Request Browser? + +>>answer- +Yes. Hover mouse cursor over request in Request browser. Three buttons will appear, including 'Send' button. Click the 'Send' button. + +>>question- +How to reorder requests in Request Browser? + +>>answer- +Just drag the request up or down and then drop it in desired location. + +>>question- +How to preview request? + +>>answer- +In Request Browser, click request you want to preview. Request will appear in the Workspace. Request-in-preview is always on the top of the Workspace. There can be only single request-in-preview. Click another request in Request Browser to preview another request. Request-in-preview has a bullet left from the HTTP method and its name is written in italics. + +>>question- +What is pinned request? + +>>answer- +Only single request can be in preview at any time. If you want to have multiple requests in the Workspace in the same time, you have to pin them. Pinned request has a triangle left from HTTP method and its name is not in italics. + +>>question- +How to pin request? + +>>answer- +Either click pin icon on previewed request on the top of the Workspace, or click pin icon on request in Request Browser (it will appear when you hover mouse). + +>>question- +Is it possible to collapse pinned request? + +>>answer- +Yes, just click pinned request method or triangle left to method. + +>>question- +How to hide request labels? + +>>answer- +Click the 'Settings' button in the upper right corner of the window, and toggle 'Hide request labels' checkbox. + +>>section- +URL, headers and Body + +>>question- +How to configure url? + +>>answer- +Once request is shown in the Workspace (it is either in-preview or pinned), paste or type url in URL section of request, bellow method. It will be automatically formatted for optimal readability. + +>>question- +How to prevent URL query key-value pair from being sent? + +>>answer- +Any key-value pair in query section of URL or form body will not be sent if its value is empty string. If '=' character is missing though, key alone will be sent. On UI, key-values which are not going to be sent are grayed out. + +>>question- +How to show URL in single line? + +>>answer- +To show URL in single line, click the 'URL' label left from it. + +If you want URL to be shown in the single line all the time, click 'Settings' button in the upper right corner of the window, and toggle 'Prefer single-line URL' checkbox. + +>>question- +How to configure request to match cURL command? + +>>answer- +Just paste cURL command to URL text box. The pasted command will be automatically parsed and the request will be modified to match it. + +>>question- +How to configure headers? + +>>answer- +Just type or paste header name and value in last empty header pair on the bottom of Headers section of request. Header name and value are separated by blue colon. Empty headers are not sent and are automatically removed. + +>>question- +How to configure body? + +>>answer- +If you change method to POST, PUT, PATCH or DELETE, body section of request will automatically be shown. Configure it by selecting appropriate body type and optional formatting, and then specify body content. + +>>question- +Is it possible to specify relative file paths in body? + +>>answer- +Yes. All relative file paths will be resolved relative to the location of the collection file. + +>>question- +Is it possible to collapse section of request? + +>>answer- +Yes. Sections URL, Headers, Body and Authentication can be collapsed by clicking section name. + +>>question- +What is 'Body type' dropdown? + +>>answer- +It specifies general type of body you want to send. According to selected body type, UI for configuring body content is updated. Have in mind that body type is not necessarily dependent on 'Content-Type' header which the user may need to configure separately. + +>>question- +What is 'Encode as' dropdown? + +>>answer- +When body type is set to Form, 'Encode as' dropdown specifies how key-value pairs of the form will be encoded in the body. It is either application/x-www-form-urlencoded or multipart/form-data. + +When body type is set to Form, httpiness will automatically add appropriate Content-Type header, which can be overwritten. + +>>question- +What is the concept of default body in httpiness? + +>>answer- +Bodies are often changed, but usually there is a single reference body for single request. Httpiness supports saving default body for certain request. Body then can be freely modified only to be later restored to the default. +To save current body of a request as the default, click 'Save' button in the 'Body' section of the request. To revert current body to the default one, click 'Revert' button next to 'Save'. + +>>section- +Authentication + +>>question- +What does 'Auth' section of request do? + +>>answer- +Auth section represents authentication middleware for that request. In httpiness, authentication middleware does two things: +(1) obtains authentication data (token in OAuth2, for example), and +(2) modifies request to insert that data at the moment of sending request. +Be sure to save any sensitive authentication attributes as locked parameters. + +>>question- +What authentication schemes are supported by httpiness? + +>>answer- +Httpiness currently supports ApiKey, Basic, Bearer and OAuth2 authentication schemes. + +>>question- +What does 'Inherit authentication' do? + +>>answer- +When authentication middleware is set to 'Inherit authentication', which is the default value, httpiness will try to find first standalone authentication definition with name 'Default Auth' in some ancestor directory. If there is such definition, it will be used. Otherwise, there will be no authentication. + +>>question- +How to work with OAuth2? + +>>answer- +Select OAuth2 authentication type in authentication definition and populate attributes. Click 'Execute auth flow' button to execute authentication flow and obtain token. Token will be automatically inserted to 'Authorization' header. Once token expires, it will not be automatically refreshed; you need to execute authentication flow again to obtain new token. + +>>question- +Can httpiness work with signing-based authentication schemes (OAuth1, AWS, etc.)? + +>>answer- +No. Complex authentication protocols which involve signing HTTP request are not supported in httpiness. Support for them is coming soon. + +>>section- +parameters + +>>question- +How to define parameter? + +>>answer- +Any string between parameter opening '${' and parameter closing '}' is consider parameter. If you want to create parameter BASE_URL, just add ${BASE_URL} (to URL string for example). Parameters can be inserted in URL, headers, body and authentication. Parameters are shown in red. + +>>question- +How to specify parameter value? + +>>answer- +All parameters which are used in all requests in the Workspace are listed in Parameters panel. Parameters are grouped per collection. Modify parameter value in Parameters panel. +If you want to set a parameter to value from some HTTP response, select text in the response, make a right click and then choose parameter you want to set value to. + +>>question- +How do parameters work? + +>>answer- +For example: if parameter SOME_PARAM has value 123, every occurrence of ${SOME_PARAM} will be replaced with string 123. + +>>question- +Are parameters shared between requests in same collection? + +>>answer- +Yes. All requests in same collection share parameters. This way multiple requests can be simultaneously reconfigured by changing value of common parameter. + +>>question- +How to know what parameters are used in any pinned request? + +>>answer- +Move your cursor across requests in the Workspace (either expanded or collapsed). Parameters which are not used in request over which cursor is located will be grayed out. + +>>question- +What is locked parameter? + +>>answer- +Value of locked parameter are not stored in collection file but are encrypted in system's default credential manager (Credential Store on Windows and Keychain Access on macOS), and is read only when needed. + +This way values of locked parameters cannot be accidentally shared by sharing collection file. + +>>question- +How to lock parameter? + +>>answer- +Hover mouse over parameter in Parameters panel you want to lock. Lock button will appear. Click lock button to lock the parameter. + +>>question- +How to preview locked parameter? + +>>answer- +Hover mouse over locked parameter you want to preview in Parameters panel. Preview (eye) button will appear. Click preview button to preview parameter. Parameter will be automatically hidden after mouse is moved out. + +>>question- +What are presets? + +>>answer- +Presets simultaneously configure multiple parameters to predefined values. Httpiness allows to define any number of presets, each configuring any number of parameters. Presets are configured and applied per collection. Every collection has two default empty presets: Development and Production. + +>>question- +How to apply presets? + +>>answer- +Parameters panel show all parameters which appear in all requests in the Workspace, grouped by parent collection. Left from the name of collection in Parameters panel, there is the Presets button. Click the button to show context menu, and select the presets you want to apply. By applying preset you configure each parameter in the collection to the value from the preset, if any. + +>>question- +How to configure presets? + +>>answer- +Right click on the collection in Request Browser and select 'Configure presets' button in the context menu. Alternatively, click the Presets button left from the name of collection in Parameters panel (when Parameters panel is not empty), and select 'Configure presets' button in the context menu. + +>>question- +Is it possible to change parameter's value when it is configured by applying preset? + +>>answer- +Yes. Applying a preset does not bound parameters to the values from the preset. Parameters can be changed at any time. Presets can be reapplied at any time as well. + +>>question- +Is it possible to auto-hide parameters panel? + +>>answer- +Yes, when responses are shown in the workspace (not in the console). Click the 'Settings' button in the upper right corner of the window, and toggle 'Auto-hide parameters' checkbox. + +>>section- +Responses and History + +>>question- +Where is the response shown? + +>>answer- +There are two places where the response can be shown: (1) in the 'Responses & History' console which expands when a request is sent, or (2) in the Workspace, next to pinned requests. + +By default, the response will be shown in the Workspace if there is enough space on the screen. Otherwise, it will be shown in the console. Click 'Settings' button in the upper right corner of the window to explicitly configure location of the response - console, workspace or automatic. + +>>question- +How to see history of sent requests? + +>>answer- +Previously sent requests are shown in the 'History' panel. When the response is shown in the console, 'History' panel is located left from the response. When the response is shown in the Workspace, click on the request name above the response to show the history. + +>>question- +How to see request execution details? + +>>answer- +In left panel of Responses & History console, click request execution which you want to examine. On the top of right panel, right from the response status, execution details will be shown, including IP addresses, download and upload size, HTTP version and timing. + +>>question- +How to hide response labels? + +>>answer- +Click the 'Settings' button in the upper right corner of the window, and toggle 'Hide response labels' checkbox. + +>>question- +How to resend request from 'History' list of sent requests? + +>>answer- +When 'History' is shown, move you cursor over one of executed requests, and resend button will appear. Click the button to resend the request. + +>>question- +How to clear request history? + +>>answer- +Click 'Clear history' button in header of Responses & History console. + +>>section- +Miscellaneous + +>>question- +How to switch between light and dark themes? + +>>answer- +Click 'Settings' button in the upper right corner of the window, and then click 'Switch to light theme' or 'Switch to dark theme'. (Which text is shown depends on current theme.) + +>>question- +How to zoom in and zoom out? + +>>answer- +Click 'Settings' button in the upper right corner of the window, and then click 'Zoom in', 'Zoom out' or 'Reset zoom'. + +>>question- +How to resize Request Browser and Parameters panels? + +>>answer- +Move cursor over right edge of Request Browser or left edge of Parameters panel. The edge will become blue. Drag the edge to resize panels. + +>>question- +How to send feedback or report an issue? + +>>answer- +Click 'Settings' button in the upper right corner of the window, and then click 'Give feedback' or 'Report an issue'. diff --git a/dist/electron-workspace/resources/fonts/OpenSans-Bold.ttf b/dist/electron-workspace/resources/fonts/OpenSans-Bold.ttf new file mode 100644 index 0000000..efdd5e8 Binary files /dev/null and b/dist/electron-workspace/resources/fonts/OpenSans-Bold.ttf differ diff --git a/dist/electron-workspace/resources/fonts/OpenSans-ExtraBold.ttf b/dist/electron-workspace/resources/fonts/OpenSans-ExtraBold.ttf new file mode 100644 index 0000000..67fcf0f Binary files /dev/null and b/dist/electron-workspace/resources/fonts/OpenSans-ExtraBold.ttf differ diff --git a/dist/electron-workspace/resources/fonts/OpenSans-Italic.ttf b/dist/electron-workspace/resources/fonts/OpenSans-Italic.ttf new file mode 100644 index 0000000..1178567 Binary files /dev/null and b/dist/electron-workspace/resources/fonts/OpenSans-Italic.ttf differ diff --git a/dist/electron-workspace/resources/fonts/OpenSans-Regular.ttf b/dist/electron-workspace/resources/fonts/OpenSans-Regular.ttf new file mode 100644 index 0000000..29bfd35 Binary files /dev/null and b/dist/electron-workspace/resources/fonts/OpenSans-Regular.ttf differ diff --git a/dist/electron-workspace/resources/fonts/UbuntuMono-Bold.ttf b/dist/electron-workspace/resources/fonts/UbuntuMono-Bold.ttf new file mode 100644 index 0000000..0ac7e58 Binary files /dev/null and b/dist/electron-workspace/resources/fonts/UbuntuMono-Bold.ttf differ diff --git a/dist/electron-workspace/resources/fonts/UbuntuMono-Regular.ttf b/dist/electron-workspace/resources/fonts/UbuntuMono-Regular.ttf new file mode 100644 index 0000000..4977028 Binary files /dev/null and b/dist/electron-workspace/resources/fonts/UbuntuMono-Regular.ttf differ diff --git a/dist/electron-workspace/resources/icons/fonts/rapids-icons.eot b/dist/electron-workspace/resources/icons/fonts/rapids-icons.eot new file mode 100644 index 0000000..ae22242 Binary files /dev/null and b/dist/electron-workspace/resources/icons/fonts/rapids-icons.eot differ diff --git a/dist/electron-workspace/resources/icons/fonts/rapids-icons.svg b/dist/electron-workspace/resources/icons/fonts/rapids-icons.svg new file mode 100644 index 0000000..457335e --- /dev/null +++ b/dist/electron-workspace/resources/icons/fonts/rapids-icons.svg @@ -0,0 +1,52 @@ + + + +Generated by IcoMoon + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/dist/electron-workspace/resources/icons/fonts/rapids-icons.ttf b/dist/electron-workspace/resources/icons/fonts/rapids-icons.ttf new file mode 100644 index 0000000..c5ee37a Binary files /dev/null and b/dist/electron-workspace/resources/icons/fonts/rapids-icons.ttf differ diff --git a/dist/electron-workspace/resources/icons/fonts/rapids-icons.woff b/dist/electron-workspace/resources/icons/fonts/rapids-icons.woff new file mode 100644 index 0000000..620cbaa Binary files /dev/null and b/dist/electron-workspace/resources/icons/fonts/rapids-icons.woff differ diff --git a/dist/electron-workspace/resources/icons/icomoon-project.json b/dist/electron-workspace/resources/icons/icomoon-project.json new file mode 100644 index 0000000..dea942d --- /dev/null +++ b/dist/electron-workspace/resources/icons/icomoon-project.json @@ -0,0 +1 @@ +{"IcoMoonType":"selection","icons":[{"icon":{"paths":["M267.815 512c0 70.892-55.138 126.031-126.031 126.031s-126.031-55.138-126.031-126.031 63.015-126.031 126.031-126.031 126.031 55.138 126.031 126.031zM645.908 512c0 70.892-55.138 126.031-126.031 126.031s-126.031-55.138-126.031-126.031 55.138-126.031 126.031-126.031 126.031 55.138 126.031 126.031zM1008.246 512c0 70.892-55.138 126.031-126.031 126.031s-126.031-55.138-126.031-126.031 55.138-126.031 126.031-126.031 126.031 55.138 126.031 126.031z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["options"]},"attrs":[{}],"properties":{"order":106,"id":41,"name":"options","prevSize":32,"code":59689},"setIdx":0,"setId":2,"iconIdx":0},{"icon":{"paths":["M307.2 131.657v-87.771h270.629v87.771h-270.629zM394.971 621.714h87.771v-263.314h-87.771v263.314zM438.857 980.114c-58.514 0-109.714-7.314-153.6-29.257-51.2-21.943-87.771-51.2-124.343-87.771s-65.829-80.457-87.771-124.343c-29.257-51.2-36.571-102.4-36.571-160.914s7.314-109.714 29.257-153.6c21.943-51.2 51.2-95.086 87.771-131.657s80.457-65.829 124.343-87.771c51.2-14.629 102.4-29.257 160.914-29.257 43.886 0 87.771 7.314 131.657 21.943s80.457 36.571 117.029 65.829l65.829-65.829 65.829 65.829-65.829 65.829c29.257 36.571 51.2 73.143 65.829 117.029s21.943 87.771 21.943 131.657c0 58.514-7.314 109.714-29.257 153.6-21.943 51.2-51.2 87.771-87.771 124.343s-80.457 65.829-124.343 87.771c-51.2 29.257-102.4 36.571-160.914 36.571zM438.857 892.343c87.771 0 160.914-29.257 219.429-95.086 58.514-58.514 95.086-131.657 95.086-219.429s-29.257-160.914-95.086-219.429c-58.514-58.514-131.657-95.086-219.429-95.086s-160.914 36.571-219.429 95.086c-58.514 58.514-95.086 131.657-95.086 219.429s29.257 160.914 95.086 219.429c58.514 65.829 131.657 95.086 219.429 95.086z"],"attrs":[{}],"width":878,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["timer"]},"attrs":[{}],"properties":{"order":104,"id":40,"name":"timer","prevSize":32,"code":59688},"setIdx":0,"setId":2,"iconIdx":1},{"icon":{"paths":["M162.133 981.333c-34.133 0-59.733-8.533-85.333-34.133s-34.133-51.2-34.133-85.333v-170.667h119.467v179.2h708.267v-179.2h119.467v179.2c0 34.133-8.533 59.733-34.133 85.333s-51.2 34.133-85.333 34.133h-708.267zM452.267 750.933v-486.4l-153.6 153.6-85.333-85.333 298.667-290.133 290.133 290.133-85.333 85.333-145.067-153.6v477.867h-119.467z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["upload"]},"attrs":[{}],"properties":{"order":103,"id":39,"name":"upload","prevSize":32,"code":59685},"setIdx":0,"setId":2,"iconIdx":2},{"icon":{"paths":["M290.133 981.333l-256-264.533 264.533-264.533 76.8 76.8-145.067 136.533h793.6v102.4h-793.6l136.533 136.533-76.8 76.8zM819.2 563.2l-76.8-68.267 136.533-136.533h-793.6v-110.933h793.6l-136.533-128 76.8-76.8 264.533 264.533-264.533 256z"],"attrs":[{}],"width":1109,"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["sync"]},"attrs":[{}],"properties":{"order":102,"id":38,"name":"sync","prevSize":32,"code":59686},"setIdx":0,"setId":2,"iconIdx":3},{"icon":{"paths":["M162.133 981.333c-34.133 0-59.733-8.533-85.333-34.133s-34.133-51.2-34.133-85.333v-170.667h119.467v179.2h708.267v-179.2h119.467v179.2c0 34.133-8.533 59.733-34.133 85.333s-51.2 34.133-85.333 34.133h-708.267zM512 750.933l-290.133-298.667 85.333-85.333 153.6 153.6v-477.867h119.467v477.867l153.6-153.6 85.333 85.333-307.2 298.667z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"grid":0,"tags":["download"]},"attrs":[{}],"properties":{"order":101,"id":37,"name":"download","prevSize":32,"code":59687},"setIdx":0,"setId":2,"iconIdx":4},{"icon":{"paths":["M311.216 602.353c-20.078-10.039-40.157-30.118-60.235-40.157v-491.922c0-40.157-30.118-70.275-70.275-70.275s-70.275 30.118-70.275 70.275v491.922c-20.078 10.039-40.157 30.118-60.235 40.157-10.039 20.078-30.118 40.157-40.157 60.235s-10.039 50.196-10.039 70.275 20.078 90.353 50.196 130.51c20.078 10.039 40.157 30.118 60.235 40.157v50.196c0 40.157 30.118 70.275 70.275 70.275s70.275-30.118 70.275-70.275v-50.196c20.078-10.039 40.157-20.078 60.235-40.157 30.118-30.118 50.196-80.314 50.196-130.51 0-20.078 0-50.196-10.039-70.275s-30.118-40.157-40.157-60.235z","M692.706 120.471v-50.196c0-40.157-30.118-70.275-70.275-70.275s-70.275 30.118-70.275 70.275v50.196c-20.078 10.039-40.157 30.118-60.235 40.157-30.118 40.157-50.196 80.314-50.196 130.51s20.078 90.353 50.196 130.51c20.078 20.078 40.157 30.118 60.235 40.157v491.922c0 40.157 30.118 70.275 70.275 70.275s70.275-30.118 70.275-70.275v-491.922c20.078-10.039 40.157-20.078 60.235-40.157 30.118-40.157 50.196-80.314 50.196-130.51s-20.078-90.353-50.196-130.51c-20.078-10.039-40.157-30.118-60.235-40.157z","M1244.863 732.863c0-20.078 0-50.196-10.039-70.275s-30.118-40.157-40.157-60.235c-20.078-20.078-40.157-30.118-60.235-40.157v-491.922c0-40.157-30.118-70.275-70.275-70.275s-70.275 30.118-70.275 70.275v491.922c-20.078 10.039-40.157 30.118-60.235 40.157-10.039 20.078-30.118 40.157-40.157 60.235s-10.039 50.196-10.039 70.275 20.078 90.353 50.196 130.51c20.078 10.039 40.157 30.118 60.235 40.157v50.196c0 40.157 30.118 70.275 70.275 70.275s70.275-30.118 70.275-70.275v-50.196c20.078-10.039 40.157-20.078 60.235-40.157 30.118-40.157 50.196-80.314 50.196-130.51z"],"width":1245,"attrs":[{},{},{}],"isMulticolor":false,"isMulticolor2":false,"tags":["preset"],"grid":0},"attrs":[{},{},{}],"properties":{"order":100,"id":0,"name":"preset","prevSize":32,"code":59684},"setIdx":0,"setId":2,"iconIdx":5},{"icon":{"paths":["M260.655 0v214.109c0 37.236 9.309 65.164 37.236 93.091s55.855 37.236 93.091 37.236h167.564c37.236 0 65.164-9.309 93.091-37.236s37.236-55.855 37.236-93.091v-214.109h27.927c46.545 0 93.091 18.618 121.018 46.545l139.636 139.636c27.927 37.236 46.545 74.473 46.545 121.018v539.927c0 46.545-18.618 93.091-46.545 121.018-37.236 37.236-74.473 55.855-121.018 55.855v-381.673c0-37.236-9.309-65.164-37.236-93.091s-55.855-37.236-93.091-37.236h-428.218c-74.473 0-130.327 55.855-130.327 130.327v381.673c-46.545 0-93.091-18.618-121.018-46.545-27.927-37.236-46.545-74.473-46.545-121.018v-688.873c0-46.545 18.618-93.091 46.545-121.018 37.236-27.927 74.473-46.545 121.018-46.545h93.091zM344.436 0v214.109c0 9.309 0 18.618 9.309 27.927s18.618 18.618 27.927 18.618h176.873c9.309 0 18.618 0 27.927-9.309s9.309-18.618 9.309-27.927v-223.418h-251.345zM260.655 1024h512v-381.673c0-9.309 0-18.618-9.309-27.927-18.618-9.309-27.927-18.618-37.236-18.618h-428.218c-18.618 0-37.236 18.618-37.236 46.545v381.673z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"tags":["save"],"grid":0},"attrs":[{}],"properties":{"order":63,"id":1,"name":"save","prevSize":32,"code":59677},"setIdx":0,"setId":2,"iconIdx":6},{"icon":{"paths":["M0 65.164c0-18.618 9.309-37.236 18.618-46.545s27.927-18.618 46.545-18.618c18.618 0 27.927 9.309 46.545 18.618 9.309 9.309 18.618 27.927 18.618 46.545v260.655l223.418-223.418c65.164-65.164 148.945-93.091 242.036-93.091s176.873 37.236 242.036 102.4c65.164 55.855 93.091 139.636 93.091 232.727s-37.236 176.873-93.091 242.036l-418.909 418.909c-9.309 9.309-9.309 9.309-18.618 18.618-9.309 0-18.618 0-27.927 0s-18.618 0-27.927 0c-9.309 0-18.618-9.309-18.618-18.618-9.309-9.309-9.309-9.309-18.618-18.618 0-9.309-9.309-18.618 0-27.927 0-9.309 0-18.618 9.309-27.927 0-9.309 9.309-18.618 18.618-18.618l409.6-418.909c18.618-18.618 37.236-46.545 46.545-65.164 9.309-27.927 18.618-55.855 18.618-83.782s-9.309-55.855-18.618-83.782c-9.309-27.927-27.927-46.545-46.545-74.473-18.618-18.618-37.236-27.927-65.164-46.545-27.927-9.309-55.855-9.309-83.782-9.309s-55.855 9.309-83.782 18.618c-18.618 9.309-46.545 18.618-65.164 37.236l-195.491 195.491h195.491c18.618 0 37.236 9.309 46.545 18.618s18.618 27.927 18.618 46.545c0 18.618-9.309 37.236-18.618 46.545s-27.927 18.618-46.545 18.618h-372.364c-18.618 0-37.236-9.309-55.855-18.618-9.309-18.618-18.618-37.236-18.618-55.855v-372.364z"],"width":931,"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"tags":["undo"],"grid":0},"attrs":[{}],"properties":{"order":64,"id":2,"name":"undo","prevSize":32,"code":59682},"setIdx":0,"setId":2,"iconIdx":7},{"icon":{"paths":["M140.8 563.2v192h-140.8v-512h166.4c140.8-12.8 217.6 51.2 217.6 153.6 0 64-25.6 115.2-89.6 140.8l153.6 230.4h-153.6l-115.2-179.2h-38.4v-25.6zM140.8 460.8h25.6c51.2 0 76.8-12.8 76.8-64 0-38.4-25.6-51.2-76.8-51.2h-25.6v115.2zM806.4 768h-307.2v-537.6h307.2v115.2h-166.4v89.6h153.6v115.2h-153.6v102.4h166.4v115.2zM1395.2 499.2c0 128-38.4 192-115.2 243.2l128 153.6h-179.2l-89.6-128c-89.6 0-153.6-12.8-192-64-51.2-51.2-64-115.2-64-204.8s12.8-153.6 64-192c51.2-51.2 102.4-64 192-64s153.6 12.8 192 64c38.4 38.4 64 102.4 64 192zM1036.8 499.2c0 102.4 38.4 153.6 102.4 153.6 38.4 0 64-12.8 76.8-38.4s25.6-64 25.6-115.2-12.8-89.6-25.6-115.2c-25.6-25.6-51.2-38.4-76.8-38.4-64 0-102.4 51.2-102.4 153.6z"],"width":1408,"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"tags":["req"],"grid":0},"attrs":[{}],"properties":{"order":107,"id":3,"name":"req","prevSize":32,"code":59681},"setIdx":0,"setId":2,"iconIdx":8},{"icon":{"paths":["M501.76 1003.52l-450.56-440.32 122.88-122.88 307.2 307.2 614.4-686.080 133.12 122.88-727.040 819.2z"],"width":1331,"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"tags":["checkmark"],"grid":0},"attrs":[{}],"properties":{"order":66,"id":4,"name":"checkmark","prevSize":32,"code":59680},"setIdx":0,"setId":2,"iconIdx":9},{"icon":{"paths":["M68.267 17.067c-8.533 0-8.533 0-17.067 0s-8.533 0-17.067 8.533c-8.533 0-8.533 8.533-8.533 17.067s0 8.533 0 17.067l93.867 341.333c0 8.533 8.533 8.533 8.533 17.067 8.533 8.533 8.533 8.533 17.067 8.533l401.067 68.267c17.067 0 17.067 34.133 0 34.133l-409.6 51.2c-8.533 0-8.533 0-17.067 8.533-0 8.533-8.533 8.533-8.533 17.067l-93.867 341.333c0 8.533 0 17.067 0 17.067 0 8.533 8.533 8.533 8.533 17.067 8.533 0 8.533 8.533 17.067 8.533s17.067 0 17.067 0l904.533-452.267c8.533 0 8.533-8.533 17.067-17.067 0-8.533 8.533-8.533 8.533-17.067s0-17.067-8.533-17.067c0-8.533-8.533-8.533-17.067-17.067l-896-452.267z"],"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"tags":["send"],"grid":0},"attrs":[{}],"properties":{"order":67,"id":5,"name":"send","prevSize":32,"code":59679},"setIdx":0,"setId":2,"iconIdx":10},{"icon":{"paths":["M194.56 563.2v0c0 10.24 0 10.24-10.24 20.48 0 10.24-10.24 10.24-20.48 10.24s-10.24 10.24-20.48 10.24c-10.24 0-10.24 0-20.48 0-40.96-10.24-30.72-71.68-30.72-71.68v-10.24c0 0 0-10.24 10.24-20.48 30.72-81.92 71.68-153.6 122.88-215.040 102.4-122.88 266.24-235.52 522.24-235.52 266.24 0 430.080 122.88 522.24 235.52 61.44 71.68 102.4 143.36 122.88 225.28 0 10.24 0 10.24 10.24 20.48v0 0 0c0 10.24 0 30.72-10.24 40.96s-20.48 20.48-30.72 20.48c-10.24 0-30.72 0-40.96-10.24s-20.48-20.48-30.72-30.72v0 0c-10.24-20.48-10.24-40.96-20.48-61.44-20.48-51.2-51.2-92.16-81.92-143.36-81.92-102.4-215.040-194.56-440.32-194.56s-358.4 102.4-440.32 194.56c-51.2 71.68-81.92 133.12-102.4 204.8-10.24 0-10.24 0-10.24 10.24v0zM471.040 655.36c0-71.68 30.72-143.36 81.92-194.56s122.88-71.68 194.56-71.68 143.36 20.48 194.56 71.68c51.2 61.44 81.92 122.88 81.92 194.56s-30.72 143.36-81.92 194.56c-51.2 51.2-122.88 81.92-194.56 81.92s-143.36-30.72-194.56-81.92c-51.2-51.2-81.92-112.64-81.92-194.56z"],"width":1434,"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"tags":["eye"],"grid":0},"attrs":[{}],"properties":{"order":68,"id":6,"name":"eye","prevSize":32,"code":59678},"setIdx":0,"setId":2,"iconIdx":11},{"icon":{"paths":["M290.133 34.133c0-17.067 17.067-34.133 34.133-34.133h273.067c17.067 0 34.133 17.067 34.133 34.133v238.933l264.533 546.133c8.533 17.067 8.533 34.133 8.533 51.2s-8.533 34.133-17.067 51.2c-8.533 17.067-25.6 25.6-42.667 34.133s-34.133 17.067-51.2 17.067h-665.6c-17.067 0-34.133 0-51.2-8.533-25.6-8.533-42.667-34.133-51.2-59.733s-8.533-59.733 8.533-85.333l264.533-546.133v-238.933zM691.2 554.667l-119.467-256c0-8.533 0-8.533 0-17.067v-213.333h-213.333v213.333c0 8.533 0 8.533 0 17.067l-153.6 315.733c17.067 8.533 34.133 17.067 59.733 25.6 51.2 8.533 110.933 8.533 170.667-34.133 76.8-68.267 170.667-68.267 230.4-51.2 8.533 0 17.067 0 25.6 0z"],"width":939,"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"tags":["experiment"],"grid":0},"attrs":[{}],"properties":{"order":98,"id":7,"name":"experiment","prevSize":32,"code":59683},"setIdx":0,"setId":2,"iconIdx":12},{"icon":{"paths":["M133.908 70.892c-31.508 0-70.892 7.877-94.523 39.385-23.631 23.631-39.385 55.138-39.385 86.646v653.785c0 31.508 15.754 70.892 39.385 94.523s63.015 39.385 94.523 39.385h409.6c39.385 0 70.892-15.754 94.523-39.385s39.385-55.138 39.385-94.523v-496.246c0-23.631-7.877-47.262-31.508-70.892l-196.923-189.046c-7.877-7.877-23.631-15.754-31.508-15.754-15.754-7.877-23.631-7.877-39.385-7.877h-244.185zM70.892 196.923c0-15.754 7.877-31.508 23.631-47.262s31.508-15.754 47.262-15.754h204.8v165.415c0 23.631 7.877 47.262 31.508 70.892 7.877 15.754 31.508 23.631 63.015 23.631h165.415v456.862c0 15.754-7.877 31.508-23.631 47.262s-31.508 15.754-47.262 15.754h-401.723c-15.754 0-31.508-7.877-47.262-15.754-15.754-15.754-23.631-31.508-23.631-47.262v-653.785zM590.769 330.831h-157.538c-7.877 0-15.754 0-23.631-7.877 0-7.877 0-15.754 0-23.631v-149.662l181.169 181.169zM149.662 779.815c0-15.754 15.754-31.508 31.508-31.508h307.2c15.754 0 31.508 15.754 31.508 31.508s-15.754 31.508-31.508 31.508h-307.2c-15.754 0-31.508-15.754-31.508-31.508zM149.662 653.785c0-15.754 15.754-31.508 31.508-31.508h307.2c15.754 0 31.508 15.754 31.508 31.508s-15.754 31.508-31.508 31.508h-307.2c-15.754-0-31.508-15.754-31.508-31.508zM149.662 527.754c0-15.754 15.754-31.508 31.508-31.508h307.2c15.754 0 31.508 15.754 31.508 31.508s-15.754 31.508-31.508 31.508h-307.2c-15.754 0-31.508-15.754-31.508-31.508z"],"width":709,"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"tags":["docs"],"grid":0},"attrs":[{}],"properties":{"order":69,"id":8,"name":"docs","prevSize":32,"code":59675},"setIdx":0,"setId":2,"iconIdx":13},{"icon":{"paths":["M1392.64 20.48c10.24 0 20.48 0 30.72 10.24s10.24 20.48 20.48 30.72v860.16c0 10.24 0 20.48-10.24 30.72s-20.48 10.24-30.72 20.48c-10.24 0-20.48 0-30.72-10.24s-20.48-20.48-20.48-30.72v-860.16c0-10.24 10.24-20.48 10.24-30.72s20.48-20.48 30.72-20.48zM829.44 143.36v0c10.24-10.24 20.48-20.48 40.96-20.48 10.24 0 20.48 0 30.72 10.24h10.24l317.44 327.68c10.24 10.24 10.24 20.48 10.24 30.72s0 20.48-10.24 30.72v10.24l-327.68 327.68c-10.24 10.24-20.48 10.24-30.72 10.24s-20.48 0-30.72-10.24c-10.24-10.24-20.48-20.48-20.48-30.72s0-20.48 10.24-30.72v-10.24l245.76-245.76h-901.12c-10.24 0-20.48 0-30.72-10.24s-10.24-20.48-10.24-30.72v-10.24c0-10.24 0-20.48 10.24-30.72s20.48-10.24 30.72-20.48h911.36l-256-235.52c-10.24-10.24-10.24-20.48-10.24-30.72s0-20.48 10.24-30.72zM235.52 747.52c0 20.48-10.24 30.72-20.48 51.2-10.24 0-20.48 10.24-40.96 20.48v0c20.48 0 40.96 10.24 51.2 20.48s20.48 30.72 20.48 40.96c0 30.72-10.24 51.2-30.72 61.44-20.48 20.48-51.2 30.72-92.16 30.72-30.72 0-61.44-10.24-81.92-20.48v-40.96c10.24 0 30.72 10.24 40.96 10.24s20.48 0 40.96 0c20.48 0 30.72 0 40.96-10.24s10.24-20.48 10.24-30.72c0-10.24-10.24-20.48-20.48-30.72 0 0-20.48-10.24-40.96-10.24h-20.48v-40.96h20.48c20.48 0 40.96 0 51.2-10.24s20.48-20.48 20.48-30.72c0-20.48-10.24-30.72-40.96-30.72-10.24 0-20.48 0-30.72 0s-20.48 10.24-30.72 20.48l-40.96-40.96c30.72-20.48 61.44-30.72 102.4-30.72 30.72 0 51.2 10.24 71.68 20.48 10.24 10.24 20.48 30.72 20.48 51.2zM419.84 747.52c10.24 0 10.24 0 20.48 0v61.44c0 0-10.24 0-20.48 0-20.48 0-30.72 0-40.96 10.24-20.48 10.24-20.48 20.48-20.48 40.96v112.64h-61.44v-225.28h51.2l10.24 40.96c10.24-10.24 20.48-20.48 30.72-30.72 0-10.24 10.24-10.24 30.72-10.24zM552.96 972.8c-30.72 0-51.2-10.24-61.44-30.72-20.48-20.48-30.72-40.96-30.72-81.92s10.24-61.44 20.48-81.92c10.24-20.48 40.96-30.72 61.44-30.72 30.72 0 51.2 10.24 61.44 30.72v0c0-20.48 0-30.72 0-40.96v-81.92h61.44v307.2h-40.96l-10.24-20.48c-10.24 20.48-40.96 30.72-61.44 30.72zM573.44 931.84c20.48 0 30.72 0 30.72-10.24 10.24-20.48 10.24-30.72 10.24-51.2v-10.24c0-20.48 0-40.96-10.24-51.2 0-10.24-20.48-20.48-30.72-20.48s-20.48 10.24-30.72 20.48c-10.24 10.24-10.24 30.72-10.24 51.2s0 40.96 10.24 51.2c0 10.24 10.24 20.48 30.72 20.48z"],"width":1536,"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"tags":["import-3rd"],"grid":0},"attrs":[{}],"properties":{"order":70,"id":9,"name":"import-3rd","prevSize":32,"code":59676},"setIdx":0,"setId":2,"iconIdx":14},{"icon":{"paths":["M581.424 8.678c-234.305 0-416.542 190.915-416.542 416.542 0 104.136 43.39 208.271 104.136 277.695l-251.661 251.661 60.746 60.746 251.661-251.661c69.424 52.068 156.203 78.102 242.983 78.102 234.305 0 416.542-190.915 416.542-416.542 8.678-225.627-182.237-416.542-407.864-416.542zM581.424 754.983c-182.237 0-329.763-147.525-329.763-329.763s147.525-329.763 329.763-329.763 329.763 147.525 329.763 329.763-147.525 329.763-329.763 329.763z"],"width":1007,"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"tags":["search"],"grid":0},"attrs":[{}],"properties":{"order":71,"id":10,"name":"search","prevSize":32,"code":59674},"setIdx":0,"setId":2,"iconIdx":15},{"icon":{"paths":["M791.273 772.655c46.545-37.236 74.473-83.782 93.091-130.327l83.782 27.927c-27.927 65.164-65.164 121.018-121.018 167.564-186.182 176.873-484.073 158.255-660.945-27.927s-158.255-484.073 27.927-660.945c186.182-176.873 484.073-158.255 660.945 27.927 55.855 55.855 93.091 130.327 111.709 195.491l65.164-74.473 74.473 55.855-167.564 204.8-204.8-139.636 55.855-74.473 93.091 65.164c-18.618-65.164-46.545-121.018-93.091-167.564-139.636-148.945-372.364-158.255-521.309-18.618-158.255 139.636-167.564 372.364-27.927 521.309 139.636 158.255 372.364 167.564 530.618 27.927z"],"width":1210,"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"tags":["restart"],"grid":0},"attrs":[{}],"properties":{"order":72,"id":11,"name":"restart","prevSize":32,"code":59673},"setIdx":0,"setId":2,"iconIdx":16},{"icon":{"paths":["M433.231 512l-338.708 441.108-63.015-47.262 275.692-409.6-275.692-409.6 63.015-47.262 338.708 441.108v31.508z"],"width":473,"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"tags":["more"],"grid":0},"attrs":[{}],"properties":{"order":73,"id":12,"name":"more","prevSize":32,"code":59672},"setIdx":0,"setId":2,"iconIdx":17},{"icon":{"paths":["M455.763 8.55l13.571-0.404c56.567-0.003 111.003 21.575 152.207 60.331s66.069 91.773 69.524 148.234l0.404 13.571v40.388h60.582c32.135 0 62.954 12.766 85.676 35.488s35.488 53.542 35.488 85.676v484.658c0 32.137-12.766 62.95-35.488 85.675s-53.542 35.49-85.676 35.49h-565.436c-32.135 0-62.954-12.766-85.676-35.49s-35.489-53.538-35.489-85.675v-484.658c0-32.135 12.766-62.954 35.489-85.676s53.542-35.488 85.676-35.488h60.582v-40.388c-0.003-56.567 21.575-111.004 60.332-152.207s91.773-66.069 148.233-69.525zM469.333 553.388c-21.424 0-41.969 8.51-57.118 23.659s-23.659 35.695-23.659 57.118c0 21.423 8.51 41.969 23.659 57.118s35.694 23.659 57.118 23.659c21.423 0 41.969-8.51 57.118-23.659s23.659-35.695 23.659-57.118c0-21.423-8.51-41.969-23.659-57.118s-35.695-23.659-57.118-23.659zM479.672 129.795l-10.339-0.485c-24.997-0.011-49.109 9.25-67.672 25.992s-30.255 39.774-32.814 64.639l-0.485 10.34v40.388h201.941v-40.388c0.011-24.997-9.25-49.109-25.992-67.672s-39.774-30.255-64.64-32.814z"],"width":939,"attrs":[{}],"isMulticolor":false,"isMulticolor2":false,"tags":["lock"],"grid":0},"attrs":[{}],"properties":{"order":74,"id":13,"name":"lock","prevSize":32,"code":59648},"setIdx":0,"setId":2,"iconIdx":18},{"icon":{"paths":["M269.964 83.782h828.509c9.309 0 18.618 0 27.927-9.309s9.309-18.618 9.309-27.927c0-9.309 0-18.618-9.309-27.927s-18.618-18.618-27.927-18.618h-828.509c-9.309 0-18.618 0-27.927 9.309s-9.309 18.618-18.618 27.927c0 9.309 0 18.618 9.309 27.927s18.618 18.618 37.236 18.618v0zM269.964 940.218h828.509c9.309 0 18.618 0 27.927-9.309s9.309-18.618 9.309-27.927c0-9.309 0-18.618-9.309-27.927s-18.618-9.309-27.927-18.618h-828.509c-9.309 0-18.618 0-27.927 9.309s-9.309 18.618-18.618 27.927 0 18.618 9.309 27.927c9.309 9.309 18.618 18.618 37.236 18.618v0zM670.255 288.582c-9.309 0-18.618 0-27.927 9.309s-18.618 18.618-18.618 27.927c0 9.309 0 18.618 9.309 27.927s27.927 18.618 37.236 18.618h428.218c9.309 0 18.618 0 27.927-9.309s9.309-18.618 9.309-27.927c0-9.309 0-18.618-9.309-27.927s-18.618-9.309-27.927-9.309h-428.218zM623.709 614.4c0-9.309 0-18.618 9.309-27.927s18.618-9.309 27.927-9.309h428.218c9.309 0 18.618 0 27.927 9.309s9.309 18.618 9.309 27.927c0 9.309 0 18.618-9.309 27.927 0 9.309-9.309 9.309-18.618 9.309h-428.218c-9.309 0-27.927 0-27.927-9.309-9.309-9.309-18.618-18.618-18.618-27.927zM512 484.073c0 37.236-9.309 65.164-18.618 102.4-18.618 27.927-37.236 55.855-55.855 74.473s-55.855 46.545-83.782 55.855c-27.927 18.618-65.164 18.618-102.4 18.618-27.927 0-65.164 0-93.091-18.618-27.927-9.309-55.855-27.927-83.782-55.855-27.927-18.618-46.545-46.545-55.855-83.782-9.309-27.927-18.618-55.855-18.618-93.091 0-65.164 27.927-130.327 74.473-176.873s111.709-83.782 176.873-83.782c65.164 0 130.327 27.927 176.873 74.473s83.782 121.018 83.782 186.182zM400.291 484.073c0-9.309 0-18.618-9.309-18.618-9.309-9.309-18.618-9.309-18.618-9.309h-232.727c-9.309 0-18.618 0-18.618 9.309 0 0-9.309 9.309-9.309 18.618s0 18.618 9.309 18.618 9.309 9.309 18.618 9.309h232.727c9.309 0 18.618 0 18.618-9.309s9.309-9.309 9.309-18.618z"],"width":1210,"attrs":[],"isMulticolor":false,"isMulticolor2":false,"tags":["collapse-all"],"defaultCode":59648,"grid":0},"attrs":[],"properties":{"id":14,"order":75,"ligatures":"","prevSize":32,"code":59649,"name":"collapse-all"},"setIdx":0,"setId":2,"iconIdx":19},{"icon":{"paths":["M640 170.667h256c11.315 0 22.17 4.495 30.174 12.497 7.996 8.001 12.493 18.854 12.493 30.17s-4.497 22.168-12.493 30.17c-8.004 8.002-18.859 12.497-30.174 12.497h-47.189l-64.171 578.219c-5.797 52.181-30.635 100.395-69.759 135.415-39.124 35.012-89.789 54.374-142.294 54.366h-206.507c-52.506 0.009-103.17-19.354-142.294-54.366-39.124-35.021-63.962-83.234-69.759-135.415l-64.256-578.219h-47.104c-11.316 0-22.168-4.495-30.17-12.497s-12.497-18.854-12.497-30.17c0-11.316 4.495-22.169 12.497-30.17s18.854-12.497 30.17-12.497h256c0-45.263 17.981-88.673 49.987-120.68s75.416-49.987 120.679-49.987c45.263 0 88.673 17.981 120.679 49.987s49.987 75.416 49.987 120.68zM469.333 85.333c-22.632 0-44.337 8.991-60.34 24.993s-24.993 37.708-24.993 60.34h170.667c0-22.632-8.991-44.337-24.993-60.34s-37.708-24.993-60.34-24.993zM341.333 426.667v341.333c0 11.316 4.495 22.168 12.497 30.17s18.854 12.497 30.17 12.497c11.316 0 22.169-4.495 30.17-12.497s12.497-18.854 12.497-30.17v-341.333c0-11.316-4.495-22.169-12.497-30.17s-18.854-12.497-30.17-12.497c-11.316 0-22.169 4.495-30.17 12.497s-12.497 18.854-12.497 30.17zM554.667 384c-11.316 0-22.169 4.495-30.17 12.497s-12.497 18.854-12.497 30.17v341.333c0 11.316 4.495 22.168 12.497 30.17s18.854 12.497 30.17 12.497c11.316 0 22.169-4.495 30.17-12.497s12.497-18.854 12.497-30.17v-341.333c0-11.316-4.495-22.169-12.497-30.17s-18.854-12.497-30.17-12.497z"],"width":939,"attrs":[],"isMulticolor":false,"isMulticolor2":false,"tags":["delete"],"defaultCode":59649,"grid":0},"attrs":[],"properties":{"id":15,"order":76,"ligatures":"","prevSize":32,"code":59650,"name":"delete"},"setIdx":0,"setId":2,"iconIdx":20},{"icon":{"paths":["M366.933 0v247.467c0 25.6 8.533 51.2 34.133 76.8 17.067 17.067 42.667 25.6 76.8 25.6h256v529.067c0 25.6-8.533 51.2-34.133 76.8-17.067 17.067-51.2 34.133-76.8 34.133h-512c-25.6 0-59.733-8.533-76.8-34.133s-34.133-42.667-34.133-68.267v-785.067c0-25.6 8.533-51.2 34.133-76.8 17.067-17.067 42.667-25.6 76.8-25.6h256zM443.733 17.067v230.4c0 8.533 0 17.067 8.533 25.6s17.067 8.533 25.6 8.533h238.933l-273.067-264.533z"],"width":768,"attrs":[],"isMulticolor":false,"isMulticolor2":false,"tags":["file-solid"],"defaultCode":59650,"grid":0},"attrs":[],"properties":{"id":16,"order":90,"ligatures":"","prevSize":32,"code":59651,"name":"file-solid"},"setIdx":0,"setId":2,"iconIdx":21},{"icon":{"paths":["M146.539 0c-38.864 0-76.137 14.893-103.619 41.403s-42.92 62.465-42.92 99.956v706.794c0 37.488 15.439 73.447 42.92 99.952 27.481 26.513 64.754 41.404 103.619 41.404h439.616c38.864 0 76.137-14.891 103.618-41.404 27.482-26.505 42.92-62.464 42.92-99.952v-536.174c-0.026-28.109-11.622-55.057-32.238-74.92l-213.433-206.030c-10.211-9.843-22.332-17.65-35.67-22.974s-27.633-8.061-42.068-8.055h-262.744zM73.269 141.359c0-18.745 7.72-36.723 21.46-49.978s32.377-20.702 51.809-20.702h219.808v176.699c0 28.118 11.58 55.084 32.19 74.967s48.566 31.052 77.714 31.052h183.173v494.756c0 18.749-7.719 36.72-21.46 49.981-13.74 13.252-32.376 20.702-51.808 20.702h-439.616c-19.432 0-38.069-7.45-51.809-20.702-13.741-13.261-21.46-31.232-21.46-49.981v-706.794zM644.257 282.718h-168.007c-9.716 0-19.034-3.724-25.905-10.351s-10.73-15.617-10.73-24.989v-162.068l204.641 197.408z"],"width":768,"attrs":[],"isMulticolor":false,"isMulticolor2":false,"tags":["file-void"],"defaultCode":59651,"grid":0},"attrs":[],"properties":{"id":17,"order":89,"ligatures":"","prevSize":32,"code":59652,"name":"file-void"},"setIdx":0,"setId":2,"iconIdx":22},{"icon":{"paths":["M1261.568 0c11.878 0.001 23.357 4.302 32.307 12.109s14.776 18.591 16.384 30.358l0.461 6.685v852.23c0 12.454-4.731 24.441-13.24 33.541-8.499 9.099-20.132 14.633-32.563 15.482-12.421 0.849-24.709-3.050-34.365-10.909s-15.974-19.092-17.674-31.43l-0.461-6.685v-852.23c0-13.036 5.181-25.538 14.397-34.756s21.719-14.396 34.755-14.396zM697.762 118.227l4.784-5.505c8.326-8.342 19.373-13.413 31.126-14.292s23.43 2.496 32.903 9.507l5.505 4.784 327.42 327.483c8.325 8.322 13.384 19.356 14.264 31.094s-2.488 23.401-9.482 32.869l-4.721 5.571-327.481 327.877c-8.759 8.818-20.533 13.989-32.952 14.473s-24.561-3.753-33.98-11.86-15.419-19.483-16.79-31.835c-1.37-12.354 1.991-24.766 9.404-34.741l4.784-5.505 243.14-243.597h-896.534c-11.868 0.002-23.335-4.291-32.285-12.084s-14.778-18.562-16.408-30.318l-0.459-6.685c0.003-11.888 4.313-23.371 12.134-32.324s18.62-14.769 30.399-16.37l6.619-0.459 897.451-0.066-244.057-244.056c-8.324-8.322-13.385-19.356-14.263-31.094s2.486-23.401 9.479-32.869z"],"width":1331,"attrs":[],"isMulticolor":false,"isMulticolor2":false,"tags":["import"],"defaultCode":59652,"grid":0},"attrs":[],"properties":{"id":18,"order":88,"ligatures":"","prevSize":32,"code":59653,"name":"import"},"setIdx":0,"setId":2,"iconIdx":23},{"icon":{"paths":["M30.72 491.52l460.8-460.8c20.48-20.48 51.2-30.72 71.68-30.72 30.72 0 51.2 10.24 71.68 30.72l460.8 460.8c20.48 20.48 30.72 40.96 30.72 71.68 0 20.48-10.24 51.2-30.72 71.68s-40.96 30.72-71.68 30.72c-20.48 0-51.2-10.24-71.68-30.72l-286.72-296.96v512c0 20.48-10.24 51.2-20.48 61.44-20.48 20.48-40.96 30.72-61.44 30.72h-10.24c-30.72 0-51.2-10.24-71.68-20.48-20.48-20.48-30.72-51.2-30.72-71.68v-512l-296.96 296.96c-10.24 10.24-20.48 20.48-30.72 20.48-10.24 10.24-30.72 10.24-40.96 10.24s-30.72 0-40.96-10.24-20.48-10.24-30.72-20.48c-10.24-10.24-20.48-20.48-20.48-30.72-10.24-10.24-10.24-20.48-10.24-40.96 0-10.24 0-30.72 10.24-40.96s10.24-20.48 20.48-30.72v0z"],"width":1126,"attrs":[],"isMulticolor":false,"isMulticolor2":false,"tags":["inherit"],"defaultCode":59653,"grid":0},"attrs":[],"properties":{"id":19,"order":87,"ligatures":"","prevSize":32,"code":59654,"name":"inherit"},"setIdx":0,"setId":2,"iconIdx":24},{"icon":{"paths":["M465.455 0l-251.345 819.2c-18.618 46.545-18.618 74.473-18.618 93.091 0 9.309 0 9.309 9.309 18.618 0 0 9.309 9.309 18.618 9.309s18.618 0 27.927-9.309c27.927-18.618 46.545-46.545 83.782-93.091h18.618c-46.545 74.473-93.091 130.327-139.636 158.255-37.236 18.618-74.473 27.927-102.4 27.927-37.236 0-55.855-9.309-74.473-27.927-27.927-18.618-37.236-37.236-37.236-55.855s9.309-46.545 18.618-83.782l204.8-670.255c9.309-37.236 18.618-65.164 18.618-74.473s-9.309-18.618-18.618-27.927c-18.618-9.309-37.236-18.618-65.164-9.309l9.309-27.927 251.345-46.545h46.545z"],"width":465,"attrs":[],"isMulticolor":false,"isMulticolor2":false,"tags":["local"],"defaultCode":59654,"grid":0},"attrs":[],"properties":{"id":20,"order":86,"ligatures":"","prevSize":32,"code":59655,"name":"local"},"setIdx":0,"setId":2,"iconIdx":25},{"icon":{"paths":["M451.672 78.887l-80.721-60.541c-15.879-11.908-35.19-18.346-55.037-18.346h-163.033c-40.547 0-79.433 16.107-108.103 44.778s-44.778 67.557-44.778 108.103v61.152h310.043c4.155 0.001 8.266-0.844 12.081-2.485s7.259-4.040 10.117-7.055l119.431-125.607zM523.771 91.727l-147.194 154.9c-8.564 9.018-18.874 16.2-30.302 21.109s-23.734 7.444-36.17 7.448h-310.104v428.068c0 40.547 16.107 79.433 44.778 108.104s67.557 44.778 108.103 44.778h311.878c-35.195-68.897-45.562-147.819-29.356-223.468s57.998-143.396 118.33-191.827c60.331-48.431 135.514-74.581 212.877-74.045s152.177 27.726 211.832 76.988v-199.174l-0.307-10.030c-2.541-38.73-19.726-75.043-48.063-101.571-28.334-26.528-65.698-41.286-104.512-41.28h-301.788zM959.004 897.84c51.6-51.607 80.598-121.602 80.598-194.586s-28.998-142.978-80.598-194.586c-51.611-51.607-121.606-80.6-194.59-80.6s-142.978 28.993-194.586 80.6c-51.607 51.608-80.6 121.603-80.6 194.586s28.993 142.979 80.6 194.586c51.608 51.612 121.603 80.601 194.586 80.601s142.979-28.989 194.59-80.601zM786.035 559.329c5.733 5.733 8.955 13.511 8.955 21.62v91.729h91.729c8.109 0 15.886 3.221 21.62 8.955s8.955 13.511 8.955 21.62c0 8.109-3.221 15.887-8.955 21.621s-13.511 8.955-21.62 8.955h-91.729v91.729c0 8.109-3.222 15.886-8.955 21.62s-13.512 8.955-21.621 8.955c-8.109 0-15.886-3.221-21.62-8.955s-8.955-13.511-8.955-21.62v-91.729h-91.729c-8.109 0-15.887-3.222-21.62-8.955s-8.956-13.512-8.956-21.621c0-8.109 3.222-15.886 8.956-21.62s13.511-8.955 21.62-8.955h91.729v-91.729c0-8.109 3.221-15.887 8.955-21.62s13.511-8.956 21.62-8.956c8.109 0 15.887 3.222 21.621 8.956z"],"width":1117,"attrs":[],"isMulticolor":false,"isMulticolor2":false,"tags":["new-dir"],"defaultCode":59655,"grid":0},"attrs":[],"properties":{"id":21,"order":85,"ligatures":"","prevSize":32,"code":59656,"name":"new-dir"},"setIdx":0,"setId":2,"iconIdx":26},{"icon":{"paths":["M349.867 0c-34.133 0-68.267 17.067-93.867 42.667s-42.667 59.733-42.667 102.4v153.6c25.6-8.533 42.667-8.533 68.267-17.067v-136.533c0-17.067 8.533-34.133 17.067-51.2 17.067-17.067 34.133-17.067 51.2-17.067h213.333v179.2c0 25.6 8.533 51.2 34.133 76.8 17.067 8.533 42.667 17.067 76.8 17.067h179.2v494.933c0 17.067-8.533 34.133-17.067 51.2-17.067 17.067-34.133 17.067-51.2 17.067h-170.667c-25.6 25.6-51.2 51.2-76.8 68.267h238.933c34.133 0 76.8-17.067 102.4-42.667s42.667-59.733 42.667-102.4v-520.533c0-25.6-8.533-51.2-34.133-76.8l-204.8-204.8c-25.6-25.6-51.2-34.133-76.8-34.133h-256zM836.267 281.6h-162.133c-8.533 0-17.067 0-25.6-8.533s-17.067-17.067-17.067-25.6v-162.133l204.8 196.267zM631.467 674.133c0 85.333-34.133 162.133-93.867 221.867s-136.533 93.867-221.867 93.867c-85.333 0-162.133-34.133-221.867-93.867s-93.867-145.067-93.867-221.867 34.133-162.133 93.867-221.867c59.733-59.733 136.533-93.867 221.867-93.867s162.133 34.133 221.867 93.867c68.267 51.2 93.867 136.533 93.867 221.867zM349.867 529.067c0-8.533 0-17.067-8.533-25.6s-17.067-8.533-25.6-8.533c-8.533 0-17.067 0-25.6 8.533 0 8.533-8.533 17.067-8.533 25.6v102.4h-102.4c-8.533 0-17.067 0-25.6 8.533s-8.533 17.067-8.533 34.133c0 8.533 0 17.067 8.533 25.6 8.533 0 17.067 8.533 25.6 8.533h102.4v102.4c0 8.533 0 17.067 8.533 25.6s17.067 8.533 25.6 8.533c8.533 0 17.067 0 25.6-8.533s8.533-17.067 8.533-25.6v-102.4h102.4c8.533 0 17.067 0 25.6-8.533s17.067-17.067 17.067-25.6c0-8.533 0-17.067-8.533-25.6s-17.067-17.067-25.6-17.067h-110.933v-102.4z"],"width":939,"attrs":[],"isMulticolor":false,"isMulticolor2":false,"tags":["new-file"],"defaultCode":59656,"grid":0},"attrs":[],"properties":{"id":22,"order":84,"ligatures":"","prevSize":32,"code":59657,"name":"new-file"},"setIdx":0,"setId":2,"iconIdx":27},{"icon":{"paths":["M1003.52 665.6c0 92.16-40.96 184.32-102.4 245.76s-153.6 102.4-245.76 102.4-184.32-40.96-245.76-112.64c-61.44-61.44-102.4-153.6-102.4-235.52 0-92.16 40.96-184.32 102.4-245.76s153.6-102.4 245.76-102.4 184.32 40.96 245.76 102.4c61.44 61.44 102.4 153.6 102.4 245.76zM696.32 512c0-10.24 0-20.48-10.24-30.72s-20.48-10.24-30.72-10.24-20.48 0-30.72 10.24-10.24 20.48-10.24 30.72v112.64h-112.64c-10.24 0-20.48 0-30.72 10.24s-10.24 20.48-10.24 30.72c0 10.24 0 20.48 10.24 30.72s20.48 10.24 30.72 10.24h112.64v112.64c0 10.24 0 20.48 10.24 30.72s20.48 10.24 30.72 10.24 20.48 0 30.72-10.24c0-20.48 10.24-20.48 10.24-30.72v-122.88h112.64c10.24 0 20.48 0 30.72-10.24s10.24-20.48 10.24-30.72c0-10.24 0-20.48-10.24-30.72-10.24 0-20.48 0-30.72 0h-112.64v-112.64zM1249.28 501.76c71.68-40.96 112.64-122.88 112.64-235.52 0-81.92-20.48-153.6-61.44-194.56s-102.4-71.68-184.32-71.68c-92.16 0-153.6 20.48-194.56 71.68-40.96 40.96-61.44 102.4-61.44 194.56 0 20.48 0 30.72 0 40.96 81.92 51.2 143.36 122.88 174.080 215.040 20.48 0 40.96 10.24 71.68 10.24v0 0 0l92.16 112.64h174.080l-122.88-143.36zM1105.92 409.6c-71.68 0-102.4-51.2-102.4-143.36 0-102.4 30.72-153.6 102.4-153.6 30.72 0 61.44 10.24 71.68 40.96 20.48 20.48 30.72 61.44 30.72 112.64s-10.24 92.16-20.48 112.64c-20.48 20.48-40.96 30.72-81.92 30.72zM348.16 389.12c-30.72 30.72-61.44 71.68-71.68 112.64l-102.4-174.080h-30.72v184.32h-143.36v-512h163.84c143.36 0 204.8 51.2 204.8 153.6 0 61.44-30.72 102.4-81.92 133.12l61.44 102.4zM143.36 225.28h30.72c51.2 0 71.68-20.48 71.68-61.44 0-30.72-20.48-51.2-71.68-51.2h-30.72v112.64zM778.24 266.24v-71.68h-153.6v-71.68h163.84v-112.64h-307.2v276.48c51.2-20.48 112.64-30.72 163.84-30.72s92.16 0 133.12 10.24z"],"width":1434,"attrs":[],"isMulticolor":false,"isMulticolor2":false,"tags":["new-req"],"defaultCode":59657,"grid":0},"attrs":[],"properties":{"id":23,"order":83,"ligatures":"","prevSize":32,"code":59658,"name":"new-req"},"setIdx":0,"setId":2,"iconIdx":28},{"icon":{"paths":["M51.2 0h486.4v93.867l-102.4 51.2v247.467l145.067 145.067v145.067h-238.933v196.267l-51.2 145.067-51.2-145.067v-196.267h-238.933v-145.067l145.067-145.067v-247.467l-93.867-51.2v-93.867z"],"width":597,"attrs":[],"isMulticolor":false,"isMulticolor2":false,"tags":["pin"],"defaultCode":59658,"grid":0},"attrs":[],"properties":{"id":24,"order":82,"ligatures":"","prevSize":32,"code":59659,"name":"pin"},"setIdx":0,"setId":2,"iconIdx":29},{"icon":{"paths":["M604.16 61.44c20.48-20.48 51.2-20.48 71.68 0l419.84 419.84c10.24 10.24 10.24 20.48 10.24 40.96 0 10.24-10.24 30.72-10.24 40.96l-419.84 409.6c-20.48 20.48-51.2 20.48-71.68 0s-20.48-51.2 0-71.68l327.68-327.68h-829.44c-30.72 0-51.2-20.48-51.2-51.2s20.48-51.2 51.2-51.2h829.44l-327.68-327.68c-20.48-20.48-20.48-61.44 0-81.92z"],"width":1126,"attrs":[],"isMulticolor":false,"isMulticolor2":false,"tags":["result"],"defaultCode":59659,"grid":0},"attrs":[],"properties":{"id":25,"order":81,"ligatures":"","prevSize":32,"code":59660,"name":"result"},"setIdx":0,"setId":2,"iconIdx":30},{"icon":{"paths":["M130.327 0c-37.236 0-65.164 9.309-93.091 37.236s-37.236 55.855-37.236 93.091v642.327c0 37.236 9.309 65.164 37.236 93.091s55.855 37.236 93.091 37.236v-353.745c0-27.927 9.309-46.545 27.927-65.164s46.545-27.927 65.164-27.927h418.909l74.473-74.473c27.927-27.927 55.855-46.545 83.782-55.855 37.236-9.309 65.164-9.309 102.4 0v-93.091c0-37.236-9.309-65.164-37.236-93.091l-102.4-102.4c-27.927-27.927-55.855-37.236-93.091-37.236h-27.927v223.418c0 27.927-9.309 46.545-27.927 65.164-18.618 27.927-37.236 37.236-65.164 37.236h-260.655c-27.927 0-46.545-9.309-65.164-27.927s-27.927-46.545-27.927-74.473v-223.418h-65.164zM577.164 521.309l-167.564 167.564c-27.927 27.927-46.545 55.855-55.855 93.091l-27.927 93.091c0 9.309 0 18.618 0 18.618h-130.327v-344.436c0-9.309 0-18.618 9.309-18.618 9.309-9.309 18.618-9.309 18.618-9.309h353.745zM260.655 0h325.818v223.418c0 9.309 0 18.618-9.309 18.618-9.309 9.309-18.618 18.618-27.927 18.618h-260.655c-9.309 0-18.618 0-18.618-9.309-9.309-9.309-9.309-18.618-9.309-27.927v-223.418zM763.345 418.909l-316.509 316.509c-9.309 18.618-27.927 37.236-37.236 65.164l-27.927 93.091c0 9.309 0 18.618 0 27.927s9.309 18.618 18.618 27.927c9.309 9.309 18.618 9.309 27.927 18.618 9.309 0 18.618 0 27.927 0l93.091-27.927c27.927-9.309 46.545-18.618 65.164-37.236l316.509-307.2c18.618-18.618 37.236-55.855 37.236-83.782s-9.309-65.164-37.236-83.782c-18.618-27.927-46.545-37.236-83.782-37.236-27.927 0-65.164 9.309-83.782 27.927z"],"attrs":[],"isMulticolor":false,"isMulticolor2":false,"tags":["save"],"defaultCode":59660,"grid":0},"attrs":[],"properties":{"id":26,"order":80,"ligatures":"","prevSize":32,"code":59661,"name":"save1"},"setIdx":0,"setId":2,"iconIdx":31},{"icon":{"paths":["M1.349 340.79c21.4-66.078 56.554-126.872 103.148-178.381 3.852-4.257 8.88-7.272 14.449-8.664s11.425-1.098 16.826 0.845l111.083 39.73c7.908 2.824 16.336 3.894 24.699 3.137s16.46-3.323 23.733-7.522c7.272-4.198 13.544-9.927 18.382-16.79s8.126-14.697 9.635-22.957l21.139-116.18c1.024-5.656 3.707-10.879 7.709-15.004s9.14-6.967 14.762-8.162c67.869-14.457 138.022-14.457 205.891 0 5.622 1.196 10.761 4.037 14.762 8.162s6.686 9.348 7.709 15.004l21.198 116.18c1.509 8.26 4.797 16.093 9.635 22.957s11.11 12.593 18.382 16.79c7.272 4.198 15.37 6.765 23.733 7.522s16.79-0.314 24.698-3.137l111.138-39.73c5.404-1.924 11.248-2.202 16.809-0.8 5.553 1.402 10.571 4.422 14.407 8.676 46.568 51.497 81.707 112.271 103.093 178.323 1.764 5.456 1.875 11.313 0.307 16.829s-4.742 10.443-9.114 14.156l-90.057 76.449c-6.396 5.437-11.532 12.2-15.061 19.82-3.521 7.62-5.348 15.916-5.348 24.312s1.827 16.691 5.348 24.311c3.529 7.62 8.665 14.383 15.061 19.82l90.057 76.449c4.372 3.712 7.546 8.639 9.114 14.156s1.457 11.373-0.307 16.829c-21.386 66.072-56.517 126.867-103.093 178.382-3.844 4.254-8.877 7.27-14.446 8.665-5.569 1.386-11.422 1.095-16.825-0.851l-111.083-39.723c-7.908-2.827-16.335-3.898-24.698-3.141-8.363 0.754-16.461 3.322-23.733 7.52s-13.545 9.925-18.382 16.794c-4.838 6.861-8.126 14.698-9.635 22.953l-21.255 116.24c-1.030 5.64-3.71 10.839-7.7 14.958-3.99 4.112-9.11 6.947-14.714 8.153-67.868 14.454-138.023 14.454-205.891 0-5.622-1.197-10.761-4.041-14.762-8.168-4.002-4.12-6.685-9.342-7.709-14.998l-21.139-116.185c-1.509-8.255-4.797-16.093-9.635-22.953-4.838-6.869-11.11-12.595-18.382-16.794s-15.37-6.766-23.733-7.52c-8.363-0.757-16.79 0.314-24.699 3.141l-111.14 39.723c-5.399 1.93-11.247 2.206-16.804 0.803s-10.573-4.419-14.413-8.672c-46.567-51.5-81.702-112.275-103.090-178.327-1.765-5.456-1.871-11.313-0.304-16.829s4.737-10.443 9.107-14.156l90.059-76.449c6.398-5.437 11.537-12.2 15.061-19.82s5.35-15.916 5.35-24.311c0-8.396-1.825-16.692-5.35-24.312s-8.663-14.383-15.061-19.82l-90.059-76.449c-4.37-3.712-7.54-8.639-9.107-14.156s-1.519-11.373 0.246-16.829zM354.057 492.356c0 30.72 12.204 60.182 33.927 81.905s51.185 33.927 81.905 33.927c30.721 0 60.183-12.204 81.906-33.927s33.926-51.185 33.926-81.905c0-30.721-12.203-60.183-33.926-81.906s-51.185-33.927-81.906-33.927c-30.72 0-60.183 12.204-81.905 33.927s-33.927 51.185-33.927 81.906z"],"width":945,"attrs":[],"isMulticolor":false,"isMulticolor2":false,"tags":["settings"],"defaultCode":59661,"grid":0},"attrs":[],"properties":{"id":27,"order":79,"ligatures":"","prevSize":32,"code":59662,"name":"settings"},"setIdx":0,"setId":2,"iconIdx":32},{"icon":{"paths":["M591.644 512l409.6-409.6c22.756-22.756 22.756-34.133 22.756-45.511s-11.378-34.133-11.378-34.133c-11.378-11.378-34.133-22.756-45.511-22.756s-22.756 0-34.133 11.378l-420.978 420.978-409.6-420.978c-11.378-11.378-22.756-11.378-45.511-11.378s-22.756 11.378-34.133 11.378-11.378 34.133-11.378 45.511 0 22.756 11.378 34.133l409.6 420.978-409.6 409.6c-11.378 11.378-11.378 11.378-22.756 22.756 0 11.378 0 11.378 0 22.756s0 11.378 0 22.756 11.378 11.378 11.378 22.756c0 0 11.378 11.378 22.756 11.378s11.378 0 22.756 0 11.378 0 22.756 0 11.378-11.378 22.756-11.378l409.6-409.6 409.6 409.6c11.378 11.378 22.756 11.378 34.133 11.378s22.756-11.378 34.133-11.378c22.756-22.756 34.133-34.133 34.133-45.511s0-22.756-11.378-34.133l-420.978-420.978z"],"attrs":[],"isMulticolor":false,"isMulticolor2":false,"tags":["close"],"defaultCode":59662,"grid":0},"attrs":[],"properties":{"id":28,"order":78,"ligatures":"","prevSize":32,"code":59663,"name":"close"},"setIdx":0,"setId":2,"iconIdx":33},{"icon":{"paths":["M472.615 0v472.615h-472.615l472.615-472.615zM551.385 1024v-472.615h472.615l-472.615 472.615z"],"attrs":[],"isMulticolor":false,"isMulticolor2":false,"tags":["exitfullscreen"],"defaultCode":59663,"grid":0},"attrs":[],"properties":{"id":29,"order":77,"ligatures":"","prevSize":32,"code":59664,"name":"exitfullscreen"},"setIdx":0,"setId":2,"iconIdx":34},{"icon":{"paths":["M0 768v-768h768l-768 768zM1024 256v768h-768l768-768z"],"attrs":[],"isMulticolor":false,"isMulticolor2":false,"tags":["fullscreen"],"defaultCode":59664,"grid":0},"attrs":[],"properties":{"id":30,"order":91,"ligatures":"","prevSize":32,"code":59665,"name":"fullscreen"},"setIdx":0,"setId":2,"iconIdx":35},{"icon":{"paths":["M0 0v1024h1024v-1024h-1024zM910.222 910.222h-796.444v-796.444h796.444v796.444z"],"attrs":[],"isMulticolor":false,"isMulticolor2":false,"tags":["maximize"],"defaultCode":59665,"grid":0},"attrs":[],"properties":{"id":31,"order":92,"ligatures":"","prevSize":32,"code":59666,"name":"maximize"},"setIdx":0,"setId":2,"iconIdx":36},{"icon":{"paths":["M1024 568.889h-1024v-113.778h1024v113.778z"],"attrs":[],"isMulticolor":false,"isMulticolor2":false,"tags":["minimize"],"defaultCode":59666,"grid":0},"attrs":[],"properties":{"id":32,"order":93,"ligatures":"","prevSize":32,"code":59667,"name":"minimize"},"setIdx":0,"setId":2,"iconIdx":37},{"icon":{"paths":["M341.333 0v341.333h-341.333v682.667h682.667v-341.333h341.333v-682.667h-682.667zM568.889 910.222h-455.111v-455.111h455.111v455.111zM910.222 568.889h-227.556v-227.556h-227.556v-227.556h455.111v455.111z"],"attrs":[],"isMulticolor":false,"isMulticolor2":false,"tags":["restore"],"defaultCode":59667,"grid":0},"attrs":[],"properties":{"id":33,"order":94,"ligatures":"","prevSize":32,"code":59668,"name":"restore"},"setIdx":0,"setId":2,"iconIdx":38},{"icon":{"paths":["M512 0c-284.444 0-512 227.556-512 512s227.556 512 512 512 512-227.556 512-512-227.556-512-512-512zM113.778 512c0-216.178 182.044-398.222 398.222-398.222 91.022 0 170.667 34.133 238.933 79.644l-557.511 557.511c-45.511-68.267-79.644-147.911-79.644-238.933zM512 910.222c-91.022 0-170.667-34.133-238.933-79.644l557.511-557.511c45.511 68.267 79.644 147.911 79.644 238.933 0 216.178-182.044 398.222-398.222 398.222z"],"attrs":[],"isMulticolor":false,"isMulticolor2":false,"tags":["clear"],"defaultCode":59668,"grid":0},"attrs":[],"properties":{"id":34,"order":95,"ligatures":"","prevSize":32,"code":59669,"name":"clear"},"setIdx":0,"setId":2,"iconIdx":39},{"icon":{"paths":["M269.964 1024c-9.309 0-9.309 0 0 0-9.309 0-9.309 0-9.309 0-18.618 0-27.927 0-46.545 0h-65.164l37.236-46.545c9.309-9.309 18.618-27.927 18.618-37.236 9.309-27.927 18.618-55.855 27.927-93.091-148.945-83.782-232.727-214.109-232.727-400.291 0-269.964 195.491-446.836 512-446.836s512 176.873 512 446.836c0 279.273-195.491 446.836-512 446.836h-18.618c-46.545 55.855-111.709 111.709-223.418 130.327zM316.509 912.291c46.545-9.309 83.782-46.545 102.4-74.473l27.927-37.236h65.164c148.945 0 251.345-37.236 316.509-93.091s102.4-139.636 102.4-260.655c0-111.709-37.236-195.491-102.4-260.655-74.473-55.855-176.873-93.091-316.509-93.091s-242.036 37.236-316.509 102.4c-65.164 55.855-102.4 139.636-102.4 251.345 0 148.945 65.164 251.345 186.182 307.2l65.164 27.927-18.618 74.473c0 18.618-9.309 37.236-9.309 55.855z"],"attrs":[],"isMulticolor":false,"isMulticolor2":false,"tags":["say"],"defaultCode":59669,"grid":0},"attrs":[],"properties":{"id":35,"order":96,"ligatures":"","prevSize":32,"code":59670,"name":"say"},"setIdx":0,"setId":2,"iconIdx":40},{"icon":{"paths":["M153.6 307.2c-30.72 61.44-40.96 133.12-40.96 204.8 0 286.72 225.28 512 512 512 102.4 0 194.56-30.72 276.48-81.92l-81.92-71.68c-61.44 30.72-122.88 51.2-194.56 51.2-225.28 0-409.6-184.32-409.6-409.6 0-51.2 10.24-92.16 20.48-143.36l30.72 92.16 92.16-30.72-81.92-256-276.48 71.68 20.48 102.4 133.12-40.96zM1187.84 727.040l-122.88 30.72c40.96-71.68 61.44-153.6 61.44-245.76 0-286.72-225.28-512-512-512-112.64 0-225.28 40.96-307.2 102.4l71.68 71.68c71.68-40.96 153.6-71.68 245.76-71.68 225.28 0 409.6 184.32 409.6 409.6 0 71.68-20.48 133.12-51.2 194.56l-30.72-102.4-102.4 30.72 92.16 256 276.48-71.68-30.72-92.16z"],"width":1219,"attrs":[],"isMulticolor":false,"isMulticolor2":false,"tags":["update"],"defaultCode":59670,"grid":0},"attrs":[],"properties":{"id":36,"order":97,"ligatures":"","prevSize":32,"code":59671,"name":"update"},"setIdx":0,"setId":2,"iconIdx":41}],"height":1024,"metadata":{"name":"rapids-icons"},"preferences":{"showGlyphs":true,"showQuickUse":true,"showQuickUse2":true,"showSVGs":true,"fontPref":{"prefix":"ri-","metadata":{"fontFamily":"rapids-icons","majorVersion":1,"minorVersion":0},"metrics":{"emSize":1024,"baseline":6.25,"whitespace":50},"embed":false},"imagePref":{"prefix":"icon-","png":true,"useClassSelector":true,"color":0,"bgColor":16777215,"classSelector":".icon"},"historySize":50,"showCodes":true,"gridSize":16}} \ No newline at end of file diff --git a/dist/electron-workspace/resources/icons/style.css b/dist/electron-workspace/resources/icons/style.css new file mode 100644 index 0000000..6167070 --- /dev/null +++ b/dist/electron-workspace/resources/icons/style.css @@ -0,0 +1,153 @@ +@font-face { + font-family: 'rapids-icons'; + src: url('fonts/rapids-icons.eot?rhn1ft'); + src: url('fonts/rapids-icons.eot?rhn1ft#iefix') format('embedded-opentype'), + url('fonts/rapids-icons.ttf?rhn1ft') format('truetype'), + url('fonts/rapids-icons.woff?rhn1ft') format('woff'), + url('fonts/rapids-icons.svg?rhn1ft#rapids-icons') format('svg'); + font-weight: normal; + font-style: normal; + font-display: block; +} + +[class^="ri-"], [class*=" ri-"] { + /* use !important to prevent issues with browser extensions that change fonts */ + font-family: 'rapids-icons' !important; + speak: never; + font-style: normal; + font-weight: normal; + font-variant: normal; + text-transform: none; + line-height: 1; + + /* Better Font Rendering =========== */ + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +.ri-options:before { + content: "\e929"; +} +.ri-timer:before { + content: "\e928"; +} +.ri-upload:before { + content: "\e925"; +} +.ri-sync:before { + content: "\e926"; +} +.ri-download:before { + content: "\e927"; +} +.ri-preset:before { + content: "\e924"; +} +.ri-save:before { + content: "\e91d"; +} +.ri-undo:before { + content: "\e922"; +} +.ri-req:before { + content: "\e921"; +} +.ri-checkmark:before { + content: "\e920"; +} +.ri-send:before { + content: "\e91f"; +} +.ri-eye:before { + content: "\e91e"; +} +.ri-experiment:before { + content: "\e923"; +} +.ri-docs:before { + content: "\e91b"; +} +.ri-import-3rd:before { + content: "\e91c"; +} +.ri-search:before { + content: "\e91a"; +} +.ri-restart:before { + content: "\e919"; +} +.ri-more:before { + content: "\e918"; +} +.ri-lock:before { + content: "\e900"; +} +.ri-collapse-all:before { + content: "\e901"; +} +.ri-delete:before { + content: "\e902"; +} +.ri-file-solid:before { + content: "\e903"; +} +.ri-file-void:before { + content: "\e904"; +} +.ri-import:before { + content: "\e905"; +} +.ri-inherit:before { + content: "\e906"; +} +.ri-local:before { + content: "\e907"; +} +.ri-new-dir:before { + content: "\e908"; +} +.ri-new-file:before { + content: "\e909"; +} +.ri-new-req:before { + content: "\e90a"; +} +.ri-pin:before { + content: "\e90b"; +} +.ri-result:before { + content: "\e90c"; +} +.ri-save1:before { + content: "\e90d"; +} +.ri-settings:before { + content: "\e90e"; +} +.ri-close:before { + content: "\e90f"; +} +.ri-exitfullscreen:before { + content: "\e910"; +} +.ri-fullscreen:before { + content: "\e911"; +} +.ri-maximize:before { + content: "\e912"; +} +.ri-minimize:before { + content: "\e913"; +} +.ri-restore:before { + content: "\e914"; +} +.ri-clear:before { + content: "\e915"; +} +.ri-say:before { + content: "\e916"; +} +.ri-update:before { + content: "\e917"; +} diff --git a/dist/electron-workspace/resources/images/HttpinessIconMacOS.png b/dist/electron-workspace/resources/images/HttpinessIconMacOS.png new file mode 100644 index 0000000..dae28cc Binary files /dev/null and b/dist/electron-workspace/resources/images/HttpinessIconMacOS.png differ diff --git a/dist/electron-workspace/resources/images/HttpinessIconPlain.png b/dist/electron-workspace/resources/images/HttpinessIconPlain.png new file mode 100644 index 0000000..6c3b74e Binary files /dev/null and b/dist/electron-workspace/resources/images/HttpinessIconPlain.png differ diff --git a/dist/electron-workspace/resources/images/HttpinessIconWin.png b/dist/electron-workspace/resources/images/HttpinessIconWin.png new file mode 100644 index 0000000..f10dfb2 Binary files /dev/null and b/dist/electron-workspace/resources/images/HttpinessIconWin.png differ diff --git a/dist/electron-workspace/resources/images/HttpinessWordMark.darktheme.svg b/dist/electron-workspace/resources/images/HttpinessWordMark.darktheme.svg new file mode 100644 index 0000000..df5271f --- /dev/null +++ b/dist/electron-workspace/resources/images/HttpinessWordMark.darktheme.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/dist/electron-workspace/resources/images/HttpinessWordMark.lighttheme.svg b/dist/electron-workspace/resources/images/HttpinessWordMark.lighttheme.svg new file mode 100644 index 0000000..93a03fb --- /dev/null +++ b/dist/electron-workspace/resources/images/HttpinessWordMark.lighttheme.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/dist/electron-workspace/resources/images/HttpinessWordMark.png b/dist/electron-workspace/resources/images/HttpinessWordMark.png new file mode 100644 index 0000000..dbeb0c5 Binary files /dev/null and b/dist/electron-workspace/resources/images/HttpinessWordMark.png differ diff --git a/dist/electron-workspace/resources/images/StatusError.png b/dist/electron-workspace/resources/images/StatusError.png new file mode 100644 index 0000000..07b4fa6 Binary files /dev/null and b/dist/electron-workspace/resources/images/StatusError.png differ diff --git a/dist/electron-workspace/resources/images/WelcomeIcon.svg b/dist/electron-workspace/resources/images/WelcomeIcon.svg new file mode 100644 index 0000000..d3d8692 --- /dev/null +++ b/dist/electron-workspace/resources/images/WelcomeIcon.svg @@ -0,0 +1,3 @@ + + + diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md new file mode 100644 index 0000000..20169fc --- /dev/null +++ b/docs/ARCHITECTURE.md @@ -0,0 +1,56 @@ +# Architecture of httpiness + +![httpiness architecture](images/httpiness-architecture.png "Schematic presentation of httpiness architecture") + +httpiness is written using Electron, an extension of Chromium-based browser which allows access to node APIs from within loaded HTML page. This way a simple HTML page escapes its browser-enforced sandbox and gains access to system resources so it can act as a full-fledged desktop app. + +Electron runs two independent processes: `main` process and `renderer` process. This architecture is inherited from Chromium. + +Each of these processes have independent compilation targets and codebases - which are located in appropriate directories in `./src` folder. Main process is a node application, while renderer process is a classical browser-hosted application with optional access to node APIs and ability to communicate with the main process. Read more about [Electron process model](https://www.electronjs.org/docs/latest/tutorial/process-model). + +`./src/renderer` directory, which host codebase for renderer process, is consisted of two modules: `lib` and `ui`. `ui` module contains httpiness GUI written in [aflon](https://github.com/bognikol/aflonstack). `ui` module should use only standard browser JavaScript APIs. `lib` module, on the other hand, should host self-contained business models which depend on node APIs. Goal of this approach is to segregate all non-browser code in `lib` directory, so the code changes would be localized if httpiness is to be ported to the browser. + +## Overview of `lib/http` + +Classes `HttpRequest` and `HttpResponse` represent plain HTTP request and response respectively. They are essentially plain-old data objects, except they contain simple serialization and caching utility methods. + +`HttpRequest` object is passed to method `execute` of `IHttpExecutor` which sends the request and returns instance of `HttpExecution`. `HttpExecution` represents HTTP transaction where a request is sent and a response is received (or is missing). `HttpExecution` contains two fields: `HttpResponse`, which represents actual HTTP response, and `HttpExecutionMetadata`, which contains additional details about HTTP transaction. Implementation of `IHttpExecutor` currently used is `CurlHttpExecutor`. Implementations of `IHttpExecutor`s are stored in `lib/http/executors` directory. + +Directory `lib/http/templates` contains a number of classes which are used for implementing httpiness collections - persistable tree-like structures of configurable and parametrizable HTTP requests. There are four main classes in terms of which httpiness collections are implemented: + +- **`HttpReqt`** - Reqt is short for Request Template. `HttpReqt` is eventually resolved to a valid sendable `HttpRequest` by resolving all macros (parameters) and inserting authentication-specific details. +- **`HttpAuth`** - Represents authentication approach applicable to particular HTTP request. +- **`HttpDir`** - Represents a node in tree-like structure which is associated with lists of `HttpReqt`s, `HttpAuth`s and other `HttpDir`s. +- **`HttpCollection`** - Represent root `HttpDir` which can be persisted (usually on the file system). Also, it stores values of all macros used in the collection and acts as a dictionary when resolving their values (implements `IMacroContext` interface). + +![sending http request in httpiness](images/httpiness-sending-request.png "Schematic presentation of HTTP transaction in httpiness") + +## Macro infrastructure + +Internally, httpiness refers to parameters as macros. Thus all interfaces and classes which contains word `Macro` actually refer to infrastructure responsible for handling parameters in httpiness. + +Macros behave in search-and-replace fashion: all macro-patterns in string fields are replaced by actual macro values. Macro-patterns have following structure: `${MACRO_NAME}`, where MACRO_NAME is name of the macro. + +Values of unlocked macros are stored in plain text in the collection file. Macro can be locked - in which case value of the macro is stored in system's credential manager. For that purpose [keytar](https://www.npmjs.com/package/keytar) library is used. Keytar is a native node module and webpack requires [native-ext-loader](https://www.npmjs.com/package/native-ext-loader) in order to process it. + +## Icons font + +RapidsIcons is an icon font family specially bundled for httpiness. It is created using [IcoMoon App](https://icomoon.io/app). To add new icon to the font, open icomoon project in the IcoMoon app; the project file is located in `./dist/electron-workspace/resources/icons/fonts/icomoon-project.json`. Once the font is generated and downloaded, update all relevant files in `./dist/electron-workspace/resources/icons` directory. + +Icons are referenced in the code using `Icon` element located in `./src/renderer/ui/Icon.ts`. + +## curl + +httpiness is using curl to send HTTP requests, by spawning new curl process for each HTTP request it is sending. curl binaries are bundled into httpiness distributable. However, majority of OS today (including latest macOS and Windows 10) have curl preinstalled. Therefore, if httpiness cannot load curl from the resources, it will switch to using preinstalled curl. For more details, investigate `src\renderer\lib\http\executors\CurlHttpExecutor.ts`. + +## Telemetry + +httpiness is reporting heart-bests to the telemetry server. Heart-beats are used to estimate popularity of httpiness. Telemetry server is a simple counter of all reported heart-beats. + +A heart-beat is an event that httpiness instance has been run at least once during 24-hour period. Heart-beats are completely anonymous and contain **NO** data. + +To disable sending telemetry, add key `DO_NOT_SEND_TELEMETRY` to httpiness.env file in home directory. Read about httpiness.env bellow. + +## httpiness.env file + +To configure httpiness behavior on user level, create httpiness.env file in home directory. Add `=`-separated key-values to this file. For implementation details, refer to `src\renderer\lib\Environment.ts`. diff --git a/docs/DEVELOPMENT_GUIDE.md b/docs/DEVELOPMENT_GUIDE.md new file mode 100644 index 0000000..9a35491 --- /dev/null +++ b/docs/DEVELOPMENT_GUIDE.md @@ -0,0 +1,83 @@ +# Development guide + +## Quick start + +1. Install [Node.js](https://nodejs.org/en). +2. Navigate to the repo root and run `npm run start:dev:clean`. This command will clean the repo (if any garbage), install dependencies, build httpiness and start electron in development mode. +3. For changes in *renderer process* codebase to be visible, run `npm run build:dev` and refresh the electron application (by pressing `Ctrl+R` for example). For changes in *main process* codebase to be visible, electron instance needs to be restarted. + +## Directory structure + +Currently, the repo contains following directories: + +- **.github** - Contains GitHub Actions workflows YAML specifications. +- **.vscode** - Contains VSCode configuration files. +- **dist** - Contains build output and files which are direct input for build process (static resources, for example). + - **electron-workspace** - This a directory in which `electron-builder` or `electron` can be run. It contains special package.json, as well as all resources which are needed as input for `electron-builder` or `electron`. `webpack` outputs for main and renderer processes (`main.js` and `renderer.js`) are generated in this directory. This directory is committed to git (as resources are stored here), except for ephemeral files (like `main.js` and `renderer.js`). + - **bin** - This directory contains actual output of `electron-builder`, i.e. httpiness build artifacts. This is ephemeral directory which is created during build process and is not committed to git. +- **docs** - Contains internal documentation. +- **src** - Contains actual TypeScript source. + - **main** - Contains `main` process entry point and its dependencies. + - **renderer** - Contains `renderer` process entry point and its dependencies. For details about renderer and main process in Chromium and electron, please check electron documentation. + - **ui** - This directory contains code of UI layer. Code in this directory, when properly compiled, should be loadable in the browser. It means that it **MUST** exclusively depend on standard browser APIs. If it needs to access non-browser APIs (for example, *node* APIs like `fs`), these APIs **MUST** be wrapped within proper abstraction layer within *lib* directory. The reason for this explicit segregation is to allow easy porting of httpiness from electron to browser; if *ui* directory contains browser-loadable code, then only files in *lib* directory needs to be reimplemented for browser. + - **lib** - This directory contains files which operate with *non-browser* APIs (node APIs, for example). +- **test** - Contains test-related files. + - **manual** - Contains manual testing plan with companion resources. + - **unit** - Contains small collection of unit tests where sensitive algorithmic logic is tested. + +## Command-line scripts for repository management + +All commands are intended to be run from the repo root. + +### `build:*` commands + +- `npm run build:dev` - Runs dev build only for renderer process. For main process to be build, run `start:dev` command. This command is used during development of renderer codebase, after electron dev instance is started using `start:dev`. After change in render code in created, run this command and refresh the electron app (by pressing `Ctrl+R`, for example). +- `npm run build:prod` - Runs production build for renderer and main processes and then builds httpiness executable and installers using electron-builder. +- `npm run build:prod:clean` - This is a convenience command that runs sequentially: `npm run clean`, `npm install` and `npm run build:prod`. + +### `start:*` commands + +- `npm run start:dev` - Builds renderer and main codebase and runs development electron instance. Use this command during development. +- `npm run start:dev:clean` - This is a convenience command that runs sequentially: `npm run clean`, `npm install` and `npm run start:dev`. +- `npm run start:prod` - Starts production httpiness executable in `dist/bin` (if it has been previously built). Use this command if you want to use httpiness as an end product. +- `npm run start:prod:clean` - This is a convenience command that runs sequentially: `npm run clean`, `npm install`, `npm run build:prod` and `npm run start:prod`. + +### Other commands + +- `npm run clean` - Cleans the repo by calling `git clean -dfX`. Deletes all git-ignored files, including `node_module` directories and build artifacts. This command will delete all *git-ignored* files but will leave all *non-git-ignored but untracked* files (new source code files won't be deleted). +- `npm run test` - Runs unit tests. +- `npm run lint` - Runs linter. + +## Development workflow + +### Dev cycle + +During development, electron dev instance should be used. It is started by running `npm run start:dev`. + +Developers are modifying either code base of (1) renderer process, or (2) main process. For difference between main and renderer process, check out [Electron documentation](https://www.electronjs.org/docs/latest/tutorial/process-model). These code bases do not overlap and do not depend on each other; they are segregated in their respective directories as explained above. + +When renderer process code-base is changed, it is sufficient to rebuild it by running `npm run build:dev` and refresh the web-page loaded in the dev instance of electron (by pressing `Ctrl+R` for example). httpiness caches its state, so, for example, it should show load the same list of requests that were opened before refresh. + +When main process code-base is changed, it is necessary to restart electron dev instance, for example by ending existing process in command line (press `Ctrl+C`) and starting it again (by calling `npm run start:dev`). + +### Debugging + +It is possible to open Chrome dev tools in Electron instance by pressing `Ctrl+Shift+I`. There's no debugging configuration prepared for VSCode, so resort to using console for debugging data. + +### Tests + +Directory `test/manual` contains modest manual testing plan for manual testing. `httpiness-test.json` collection contains predefined requests where majority of httpiness features are utilized and can be tested. + +Directory `test/unit` contains small collection of unit tests for sensitive algorithmic logic. + +### Linting + +TypeScript linter is configured. Run it by `npm run lint`. + +## Distributing binaries + +Binaries need to be properly signed and notarized in order to be run on modern operating systems without security warnings. This is true for both httpiness executable as well as installers (NSIS for Windows, dmg for macOS). Have in mind that these warnings are not usually shown on the machine where binaries are build even if they are *not* signed and notarized. + +In order to sign binaries, it is necessary to buy proper code-signing certificate. Overview of code-signing certificates is beyond of scope of this readme. Once you obtain certificate, follow [electron-builder documentation](https://www.electron.build/code-signing) to configure signing using your certificate. + +However, signing alone is not always sufficient to avoid security warnings on some operating systems; it is also necessary to notarize application (submit application to OS vendor for pre-distribution scan). Different OS vendors are handling notarization differently; for example, macOS uses `notarytool` command-line tool which is installed as part of XCode tool-set. diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md new file mode 100644 index 0000000..71ef47d --- /dev/null +++ b/docs/ROADMAP.md @@ -0,0 +1,39 @@ +# Roadmap + +This document contains list of candidate features which are to be implemented in the future. + +## 1. Switch to external text editor for body editing + +`TokenTextEditor`, which is currently used as text editor throughout httpiness has distinct limitations, including but not limited to: + +- Performance issues when working with large text. +- Lack of support for simple text-based formats like JSON or YAML. +- Bad support for undo and redo. + +Its only true application is `UrlTextEditor` in `HttpRequestControl` as it supports custom text wrapping based on text's syntactic structure. + +In *almost* all other contexts, `TokenTextEditor` should be replaced with industry-proven web-based code editor like Monaco or CodeMirror. Main challenge here is that new text editor needs to be configured to support highlighting of httpiness-style parameters (`${PARAMETER}`) in context of existing text-based formats like JSON. + +## 2. Add support for Ubuntu + +Almost all code is platform independent, though there are certain customization that needs to be introduced: + + 1. [node-keytar](https://github.com/atom/node-keytar) on Linux depends on `libsecret` library which needs to be installed independently. Other platforms do not have explicit dependencies. + 2. `curl` compiled for Linux is not packaged in the executable. However, given the fact that `curl` is preinstalled on Ubuntu, it may not need to be packaged at all. + 3. `src\renderer\ui\ControlBar\ControlBar.ts` needs to be updated in order to facilitate native Ubuntu experience. + 4. OS-specific behavior has been implemented on multiple places; these places can be identified by searching for `currentPlatform()` function; proper support for Ubuntu needs to be introduced. + +## 3. Create custom HTTP-client library and deprecating use of curl + +Currently, httpiness sends HTTP request by spawning new curl process with appropriate command line arguments. This means that httpiness effectively acts as a GUI shell for curl. + +Ideally, curl should be replaced by custom lightweight HTTP client library which has native interoperability with node. + +## 4. Add support for global undo and redo + +Current support for undo is insufficient and buggy. + +## 5. Improve URL UI + +- Allow URL query to be shown as a table nested within URL string +- Colorize parts of URL with different colors. diff --git a/docs/images/httpiness-architecture.png b/docs/images/httpiness-architecture.png new file mode 100644 index 0000000..a7d60c8 Binary files /dev/null and b/docs/images/httpiness-architecture.png differ diff --git a/docs/images/httpiness-sending-request.png b/docs/images/httpiness-sending-request.png new file mode 100644 index 0000000..57274ed Binary files /dev/null and b/docs/images/httpiness-sending-request.png differ diff --git a/docs/images/screenshot.png b/docs/images/screenshot.png new file mode 100644 index 0000000..95514d4 Binary files /dev/null and b/docs/images/screenshot.png differ diff --git a/jest.config.js b/jest.config.js new file mode 100644 index 0000000..a1dd58e --- /dev/null +++ b/jest.config.js @@ -0,0 +1,12 @@ +/** @type {import('ts-jest').JestConfigWithTsJest} */ +module.exports = { + preset: "ts-jest", + testEnvironment: "jsdom", + moduleNameMapper: { + "uuid": require.resolve("uuid"), + }, + globals: { + DIST: false, + APP_ID: "test" + } +}; \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..800ea11 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,17233 @@ +{ + "name": "httpiness", + "version": "1.4.3", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "httpiness", + "version": "1.4.3", + "license": "MIT", + "dependencies": { + "aflon": "^2.0.10", + "keytar": "^7.9.0", + "mime-db": "^1.52.0", + "uuid": "^8.3.2" + }, + "devDependencies": { + "@marshallofsound/webpack-asset-relocator-loader": "^0.5.0", + "@types/jest": "^29.5.12", + "@typescript-eslint/eslint-plugin": "^5.41.0", + "@typescript-eslint/parser": "^5.41.0", + "cross-env": "^7.0.3", + "electron": "^30.0.9", + "electron-builder": "^24.13.3", + "eslint": "^8.26.0", + "eslint-plugin-tsdoc": "^0.2.17", + "formidable": "^2.0.1", + "jest-environment-jsdom": "^29.7.0", + "native-ext-loader": "^2.3.0", + "node-loader": "^2.0.0", + "run-script-os": "^1.1.6", + "ts-jest": "^29.1.2", + "ts-loader": "^9.4.1", + "typescript": "^4.8.4", + "webpack": "^5.74.0", + "webpack-cli": "^4.10.0" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, + "peer": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.24.2", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.2.tgz", + "integrity": "sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.24.2", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.24.4", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.4.tgz", + "integrity": "sha512-vg8Gih2MLK+kOkHJp4gBEIkyaIi00jgWot2D9QOmmfLC8jINSOzmCLta6Bvz/JSBCqnegV0L80jhxkol5GWNfQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.5.tgz", + "integrity": "sha512-tVQRucExLQ02Boi4vdPp49svNGcfL2GhdTCT9aldhXgCJVAI21EtRfBettiuLUwce/7r6bFdgs6JFkcdTiFttA==", + "dev": true, + "peer": true, + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.24.2", + "@babel/generator": "^7.24.5", + "@babel/helper-compilation-targets": "^7.23.6", + "@babel/helper-module-transforms": "^7.24.5", + "@babel/helpers": "^7.24.5", + "@babel/parser": "^7.24.5", + "@babel/template": "^7.24.0", + "@babel/traverse": "^7.24.5", + "@babel/types": "^7.24.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "peer": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.5.tgz", + "integrity": "sha512-x32i4hEXvr+iI0NEoEfDKzlemF8AmtOP8CcrRaEcpzysWuoEb1KknpcvMsHKPONoKZiDuItklgWhB18xEhr9PA==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/types": "^7.24.5", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^2.5.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz", + "integrity": "sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/compat-data": "^7.23.5", + "@babel/helper-validator-option": "^7.23.5", + "browserslist": "^4.22.2", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "peer": true, + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "peer": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "peer": true + }, + "node_modules/@babel/helper-environment-visitor": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-function-name": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-hoist-variables": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.24.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.3.tgz", + "integrity": "sha512-viKb0F9f2s0BCS22QSF308z/+1YWKV/76mwt61NBzS5izMzDPwdq1pTrzf+Li3npBWX9KdQbkeCt1jSAM7lZqg==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/types": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.24.5.tgz", + "integrity": "sha512-9GxeY8c2d2mdQUP1Dye0ks3VDyIMS98kt/llQ2nUId8IsWqTF0l1LkSX0/uP7l7MCDrzXS009Hyhe2gzTiGW8A==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-module-imports": "^7.24.3", + "@babel/helper-simple-access": "^7.24.5", + "@babel/helper-split-export-declaration": "^7.24.5", + "@babel/helper-validator-identifier": "^7.24.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.5.tgz", + "integrity": "sha512-xjNLDopRzW2o6ba0gKbkZq5YWEBaK3PCyTOY1K2P/O07LGMhMqlMXPxwN4S5/RhWuCobT8z0jrlKGlYmeR1OhQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.24.5.tgz", + "integrity": "sha512-uH3Hmf5q5n7n8mz7arjUlDOCbttY/DW4DYhE6FUsjKJ/oYC1kQQUvwEQWxRwUpX9qQKRXeqLwWxrqilMrf32sQ==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/types": "^7.24.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.5.tgz", + "integrity": "sha512-5CHncttXohrHk8GWOFCcCl4oRD9fKosWlIRgWm4ql9VYioKm52Mk2xsmoohvm7f3JoiLSM5ZgJuRaf5QZZYd3Q==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/types": "^7.24.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.1.tgz", + "integrity": "sha512-2ofRCjnnA9y+wk8b9IAREroeUP02KHp431N2mhKniy2yKIDKpbrHv9eXwm8cBeWQYcJmzv5qKCu65P47eCF7CQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.5.tgz", + "integrity": "sha512-3q93SSKX2TWCG30M2G2kwaKeTYgEUp5Snjuj8qm729SObL6nbtUldAi37qbxkD5gg3xnBio+f9nqpSepGZMvxA==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz", + "integrity": "sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.5.tgz", + "integrity": "sha512-CiQmBMMpMQHwM5m01YnrM6imUG1ebgYJ+fAIW4FZe6m4qHTPaRHti+R8cggAwkdz4oXhtO4/K9JWlh+8hIfR2Q==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/template": "^7.24.0", + "@babel/traverse": "^7.24.5", + "@babel/types": "^7.24.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.5.tgz", + "integrity": "sha512-8lLmua6AVh/8SLJRRVD6V8p73Hir9w5mJrhE+IPpILG31KKlI9iz5zmBYKcWPS59qSfgP9RaSBQSHHE81WKuEw==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.24.5", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "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==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/@babel/highlight/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/parser": { + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.5.tgz", + "integrity": "sha512-EOv5IK8arwh3LI47dz1b0tKUb/1uhHAnHJOrjgtQMIpu1uXd9mlFrJg9IUgGUgZ41Ch0K8REPTYpO7B76b4vJg==", + "dev": true, + "peer": true, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.24.1.tgz", + "integrity": "sha512-2eCtxZXf+kbkMIsXS4poTvT4Yu5rXiRa+9xGVT56raghjmBTKMpFNc9R4IDiB4emao9eO22Ox7CxuJG7BgExqA==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.24.1.tgz", + "integrity": "sha512-Yhnmvy5HZEnHUty6i++gcfH1/l68AHnItFHnaCv6hn9dNh0hQvvQJsxpi4BMBFN5DLeHBuucT/0DgzXif/OyRw==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.0.tgz", + "integrity": "sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/code-frame": "^7.23.5", + "@babel/parser": "^7.24.0", + "@babel/types": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.5.tgz", + "integrity": "sha512-7aaBLeDQ4zYcUFDUD41lJc1fG8+5IU9DaNSJAgal866FGvmD5EbWQgnEC6kO1gGLsX0esNkfnJSndbTXA3r7UA==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/code-frame": "^7.24.2", + "@babel/generator": "^7.24.5", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.24.5", + "@babel/parser": "^7.24.5", + "@babel/types": "^7.24.5", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/types": { + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.5.tgz", + "integrity": "sha512-6mQNsaLeXTw0nxYUYu+NSa4Hx4BlF1x1x8/PMFbiR+GBSr+2DkECc69b8hgy2frEodNcvPffeH8YfWd3LI6jhQ==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/helper-string-parser": "^7.24.1", + "@babel/helper-validator-identifier": "^7.24.5", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true, + "peer": true + }, + "node_modules/@develar/schema-utils": { + "version": "2.6.5", + "resolved": "https://registry.npmjs.org/@develar/schema-utils/-/schema-utils-2.6.5.tgz", + "integrity": "sha512-0cp4PsWQ/9avqTVMCtZ+GirikIA36ikvjtHweU4/j8yLtgObI0+JUPhYFScgwlteveGB1rt3Cm8UhN04XayDig==", + "dev": true, + "dependencies": { + "ajv": "^6.12.0", + "ajv-keywords": "^3.4.1" + }, + "engines": { + "node": ">= 8.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/@discoveryjs/json-ext": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", + "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", + "dev": true, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/@electron/asar": { + "version": "3.2.10", + "resolved": "https://registry.npmjs.org/@electron/asar/-/asar-3.2.10.tgz", + "integrity": "sha512-mvBSwIBUeiRscrCeJE1LwctAriBj65eUDm0Pc11iE5gRwzkmsdbS7FnZ1XUWjpSeQWL1L5g12Fc/SchPM9DUOw==", + "dev": true, + "dependencies": { + "commander": "^5.0.0", + "glob": "^7.1.6", + "minimatch": "^3.0.4" + }, + "bin": { + "asar": "bin/asar.js" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/@electron/asar/node_modules/commander": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", + "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@electron/get": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@electron/get/-/get-2.0.3.tgz", + "integrity": "sha512-Qkzpg2s9GnVV2I2BjRksUi43U5e6+zaQMcjoJy0C+C5oxaKl+fmckGDQFtRpZpZV0NQekuZZ+tGz7EA9TVnQtQ==", + "dev": true, + "dependencies": { + "debug": "^4.1.1", + "env-paths": "^2.2.0", + "fs-extra": "^8.1.0", + "got": "^11.8.5", + "progress": "^2.0.3", + "semver": "^6.2.0", + "sumchecker": "^3.0.1" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "global-agent": "^3.0.0" + } + }, + "node_modules/@electron/get/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@electron/notarize": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@electron/notarize/-/notarize-2.2.1.tgz", + "integrity": "sha512-aL+bFMIkpR0cmmj5Zgy0LMKEpgy43/hw5zadEArgmAMWWlKc5buwFvFT9G/o/YJkvXAJm5q3iuTuLaiaXW39sg==", + "dev": true, + "dependencies": { + "debug": "^4.1.1", + "fs-extra": "^9.0.1", + "promise-retry": "^2.0.1" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@electron/notarize/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@electron/notarize/node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/@electron/notarize/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@electron/osx-sign": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@electron/osx-sign/-/osx-sign-1.0.5.tgz", + "integrity": "sha512-k9ZzUQtamSoweGQDV2jILiRIHUu7lYlJ3c6IEmjv1hC17rclE+eb9U+f6UFlOOETo0JzY1HNlXy4YOlCvl+Lww==", + "dev": true, + "dependencies": { + "compare-version": "^0.1.2", + "debug": "^4.3.4", + "fs-extra": "^10.0.0", + "isbinaryfile": "^4.0.8", + "minimist": "^1.2.6", + "plist": "^3.0.5" + }, + "bin": { + "electron-osx-flat": "bin/electron-osx-flat.js", + "electron-osx-sign": "bin/electron-osx-sign.js" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@electron/osx-sign/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@electron/osx-sign/node_modules/isbinaryfile": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.10.tgz", + "integrity": "sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==", + "dev": true, + "engines": { + "node": ">= 8.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/gjtorikian/" + } + }, + "node_modules/@electron/osx-sign/node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/@electron/osx-sign/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@electron/universal": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@electron/universal/-/universal-1.5.1.tgz", + "integrity": "sha512-kbgXxyEauPJiQQUNG2VgUeyfQNFk6hBF11ISN2PNI6agUgPl55pv4eQmaqHzTAzchBvqZ2tQuRVaPStGf0mxGw==", + "dev": true, + "dependencies": { + "@electron/asar": "^3.2.1", + "@malept/cross-spawn-promise": "^1.1.0", + "debug": "^4.3.1", + "dir-compare": "^3.0.0", + "fs-extra": "^9.0.1", + "minimatch": "^3.0.4", + "plist": "^3.0.4" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/@electron/universal/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@electron/universal/node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/@electron/universal/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.3.tgz", + "integrity": "sha512-uj3pT6Mg+3t39fvLrj8iuCIJ38zKO9FpGtJ4BBJebJhEwjoT+KLVNCcHT5QC9NGRIEi7fZ0ZR8YRb884auB4Lg==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.4.0", + "globals": "^13.15.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.11.7", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.7.tgz", + "integrity": "sha512-kBbPWzN8oVMLb0hOUYXhmxggL/1cJE6ydvjDIGi9EnAGUyA7cLVKQg+d/Dsm+KZwx2czGHrCmMVLiyg8s5JPKw==", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "peer": true, + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "peer": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "peer": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true, + "peer": true + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/console": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", + "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", + "dev": true, + "peer": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/core": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", + "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", + "dev": true, + "peer": true, + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/reporters": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-changed-files": "^29.7.0", + "jest-config": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-resolve-dependencies": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "jest-watcher": "^29.7.0", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/environment": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", + "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", + "dev": true, + "dependencies": { + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", + "dev": true, + "peer": true, + "dependencies": { + "expect": "^29.7.0", + "jest-snapshot": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", + "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", + "dev": true, + "dependencies": { + "jest-get-type": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/fake-timers": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", + "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@sinonjs/fake-timers": "^10.0.2", + "@types/node": "*", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/globals": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", + "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", + "dev": true, + "peer": true, + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/types": "^29.6.3", + "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/reporters": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", + "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", + "dev": true, + "peer": true, + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^6.0.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "slash": "^3.0.0", + "string-length": "^4.0.1", + "strip-ansi": "^6.0.0", + "v8-to-istanbul": "^9.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/reporters/node_modules/jest-worker": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", + "dev": true, + "peer": true, + "dependencies": { + "@types/node": "*", + "jest-util": "^29.7.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/reporters/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "peer": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dev": true, + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/source-map": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", + "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", + "dev": true, + "peer": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.18", + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-result": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", + "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", + "dev": true, + "peer": true, + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-sequencer": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", + "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", + "dev": true, + "peer": true, + "dependencies": { + "@jest/test-result": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/transform": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", + "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.2" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", + "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@malept/cross-spawn-promise": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@malept/cross-spawn-promise/-/cross-spawn-promise-1.1.1.tgz", + "integrity": "sha512-RTBGWL5FWQcg9orDOCcp4LvItNzUPcyEU9bwaeJX0rJ1IQxzucC48Y0/sQLp/g6t99IQgAlGIaesJS+gTn7tVQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/malept" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/subscription/pkg/npm-.malept-cross-spawn-promise?utm_medium=referral&utm_source=npm_fund" + } + ], + "dependencies": { + "cross-spawn": "^7.0.1" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@malept/flatpak-bundler": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@malept/flatpak-bundler/-/flatpak-bundler-0.4.0.tgz", + "integrity": "sha512-9QOtNffcOF/c1seMCDnjckb3R9WHcG34tky+FHpNKKCW0wc/scYLwMtO+ptyGUfMW0/b/n4qRiALlaFHc9Oj7Q==", + "dev": true, + "dependencies": { + "debug": "^4.1.1", + "fs-extra": "^9.0.0", + "lodash": "^4.17.15", + "tmp-promise": "^3.0.2" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@malept/flatpak-bundler/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@malept/flatpak-bundler/node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/@malept/flatpak-bundler/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@marshallofsound/webpack-asset-relocator-loader": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@marshallofsound/webpack-asset-relocator-loader/-/webpack-asset-relocator-loader-0.5.0.tgz", + "integrity": "sha512-X50R97SiDNTpOckiplghBo63Vo8GxSsr98s3VTwEu3qyVr+TY4I91KRtKelEj2OAfgMnkTymw89+psFVq8aB1g==", + "dev": true, + "dependencies": { + "sourcemap-codec": "^1.4.4" + } + }, + "node_modules/@microsoft/tsdoc": { + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.14.2.tgz", + "integrity": "sha512-9b8mPpKrfeGRuhFH5iO1iwCLeIIsV6+H1sRfxbkoGXIyQE2BTsPd9zqSqQJ+pv5sJ/hT5M1zvOFL02MnEezFug==", + "dev": true + }, + "node_modules/@microsoft/tsdoc-config": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc-config/-/tsdoc-config-0.16.2.tgz", + "integrity": "sha512-OGiIzzoBLgWWR0UdRJX98oYO+XKGf7tiK4Zk6tQ/E4IJqGCe7dvkTvgDZV5cFJUzLGDOjeAXrnZoA6QkVySuxw==", + "dev": true, + "dependencies": { + "@microsoft/tsdoc": "0.14.2", + "ajv": "~6.12.6", + "jju": "~1.4.0", + "resolve": "~1.19.0" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@popmotion/easing": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@popmotion/easing/-/easing-1.0.2.tgz", + "integrity": "sha512-IkdW0TNmRnWTeWI7aGQIVDbKXPWHVEYdGgd5ZR4SH/Ty/61p63jCjrPxX1XrR7IGkl08bjhJROStD7j+RKgoIw==" + }, + "node_modules/@popmotion/popcorn": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/@popmotion/popcorn/-/popcorn-0.4.4.tgz", + "integrity": "sha512-jYO/8319fKoNLMlY4ZJPiPu8Ea8occYwRZhxpaNn/kZsK4QG2E7XFlXZMJBsTWDw7I1i0uaqyC4zn1nwEezLzg==", + "dependencies": { + "@popmotion/easing": "^1.0.1", + "framesync": "^4.0.1", + "hey-listen": "^1.0.8", + "style-value-types": "^3.1.7", + "tslib": "^1.10.0" + } + }, + "node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "dev": true + }, + "node_modules/@sindresorhus/is": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", + "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" + } + }, + "node_modules/@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "dev": true, + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", + "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^3.0.0" + } + }, + "node_modules/@szmarczak/http-timer": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", + "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==", + "dev": true, + "dependencies": { + "defer-to-connect": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@tootallnate/once": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", + "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.6.8", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", + "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.5.tgz", + "integrity": "sha512-WXCyOcRtH37HAUkpXhUduaxdm82b4GSlyTqajXviN4EfiuPgNYR109xMCKvpl6zPIpua0DGlMEDCq+g8EdoheQ==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/types": "^7.20.7" + } + }, + "node_modules/@types/cacheable-request": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz", + "integrity": "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==", + "dev": true, + "dependencies": { + "@types/http-cache-semantics": "*", + "@types/keyv": "^3.1.4", + "@types/node": "*", + "@types/responselike": "^1.0.0" + } + }, + "node_modules/@types/debug": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", + "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", + "dev": true, + "dependencies": { + "@types/ms": "*" + } + }, + "node_modules/@types/eslint": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.2.tgz", + "integrity": "sha512-Z1nseZON+GEnFjJc04sv4NSALGjhFwy6K0HXt7qsn5ArfAKtb63dXNJHf+1YW6IpOIYRBGUbu3GwJdj8DGnCjA==", + "dev": true, + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/@types/eslint-scope": { + "version": "3.7.3", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.3.tgz", + "integrity": "sha512-PB3ldyrcnAicT35TWPs5IcwKD8S333HMaa2VVv4+wdvebJkjWuW/xESoB8IwRcog8HYVYamb1g/R31Qv5Bx03g==", + "dev": true, + "dependencies": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", + "dev": true + }, + "node_modules/@types/fs-extra": { + "version": "9.0.13", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.13.tgz", + "integrity": "sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/graceful-fs": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", + "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", + "dev": true, + "peer": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/http-cache-semantics": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz", + "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==", + "dev": true + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "dev": true + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/jest": { + "version": "29.5.12", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.12.tgz", + "integrity": "sha512-eDC8bTvT/QhYdxJAulQikueigY5AsdBRH2yDKW3yveW7svY3+DzN84/2NUgkw10RTiJbWqZrTtoGVdYlvFJdLw==", + "dev": true, + "dependencies": { + "expect": "^29.0.0", + "pretty-format": "^29.0.0" + } + }, + "node_modules/@types/jsdom": { + "version": "20.0.1", + "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-20.0.1.tgz", + "integrity": "sha512-d0r18sZPmMQr1eG35u12FZfhIXNrnsPU/g5wvRKCUf/tOGilKKwYMYGqh33BNR6ba+2gkHw1EUiHoN3mn7E5IQ==", + "dev": true, + "dependencies": { + "@types/node": "*", + "@types/tough-cookie": "*", + "parse5": "^7.0.0" + } + }, + "node_modules/@types/json-schema": { + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", + "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", + "dev": true + }, + "node_modules/@types/keyv": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", + "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/ms": { + "version": "0.7.34", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.34.tgz", + "integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==", + "dev": true + }, + "node_modules/@types/node": { + "version": "20.12.12", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.12.tgz", + "integrity": "sha512-eWLDGF/FOSPtAvEqeRAQ4C8LSA7M1I7i0ky1I8U7kD1J5ITyW3AsRhQrKVoWf5pFKZ2kILsEGJhsI9r93PYnOw==", + "dev": true, + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@types/plist": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/plist/-/plist-3.0.5.tgz", + "integrity": "sha512-E6OCaRmAe4WDmWNsL/9RMqdkkzDCY1etutkflWk4c+AcjDU07Pcz1fQwTX0TQz+Pxqn9i4L1TU3UFpjnrcDgxA==", + "dev": true, + "optional": true, + "dependencies": { + "@types/node": "*", + "xmlbuilder": ">=11.0.1" + } + }, + "node_modules/@types/responselike": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.3.tgz", + "integrity": "sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/semver": { + "version": "7.3.13", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.13.tgz", + "integrity": "sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==", + "dev": true + }, + "node_modules/@types/stack-utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", + "dev": true + }, + "node_modules/@types/tough-cookie": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", + "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==", + "dev": true + }, + "node_modules/@types/verror": { + "version": "1.10.10", + "resolved": "https://registry.npmjs.org/@types/verror/-/verror-1.10.10.tgz", + "integrity": "sha512-l4MM0Jppn18hb9xmM6wwD1uTdShpf9Pn80aXTStnK1C94gtPvJcV2FrDmbOQUAQfJ1cKZHktkQUDwEqaAKXMMg==", + "dev": true, + "optional": true + }, + "node_modules/@types/yargs": { + "version": "17.0.13", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.13.tgz", + "integrity": "sha512-9sWaruZk2JGxIQU+IhI1fhPYRcQ0UuTNuKuCW9bR5fp7qi2Llf7WDzNa17Cy7TKnh3cdxDOiyTu6gaLS0eDatg==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", + "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", + "dev": true + }, + "node_modules/@types/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw==", + "dev": true, + "optional": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "5.41.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.41.0.tgz", + "integrity": "sha512-DXUS22Y57/LAFSg3x7Vi6RNAuLpTXwxB9S2nIA7msBb/Zt8p7XqMwdpdc1IU7CkOQUPgAqR5fWvxuKCbneKGmA==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "5.41.0", + "@typescript-eslint/type-utils": "5.41.0", + "@typescript-eslint/utils": "5.41.0", + "debug": "^4.3.4", + "ignore": "^5.2.0", + "regexpp": "^3.2.0", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^5.0.0", + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "5.41.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.41.0.tgz", + "integrity": "sha512-HQVfix4+RL5YRWZboMD1pUfFN8MpRH4laziWkkAzyO1fvNOY/uinZcvo3QiFJVS/siNHupV8E5+xSwQZrl6PZA==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "5.41.0", + "@typescript-eslint/types": "5.41.0", + "@typescript-eslint/typescript-estree": "5.41.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "5.41.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.41.0.tgz", + "integrity": "sha512-xOxPJCnuktUkY2xoEZBKXO5DBCugFzjrVndKdUnyQr3+9aDWZReKq9MhaoVnbL+maVwWJu/N0SEtrtEUNb62QQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.41.0", + "@typescript-eslint/visitor-keys": "5.41.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "5.41.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.41.0.tgz", + "integrity": "sha512-L30HNvIG6A1Q0R58e4hu4h+fZqaO909UcnnPbwKiN6Rc3BUEx6ez2wgN7aC0cBfcAjZfwkzE+E2PQQ9nEuoqfA==", + "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "5.41.0", + "@typescript-eslint/utils": "5.41.0", + "debug": "^4.3.4", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/types": { + "version": "5.41.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.41.0.tgz", + "integrity": "sha512-5BejraMXMC+2UjefDvrH0Fo/eLwZRV6859SXRg+FgbhA0R0l6lDqDGAQYhKbXhPN2ofk2kY5sgGyLNL907UXpA==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "5.41.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.41.0.tgz", + "integrity": "sha512-SlzFYRwFSvswzDSQ/zPkIWcHv8O5y42YUskko9c4ki+fV6HATsTODUPbRbcGDFYP86gaJL5xohUEytvyNNcXWg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.41.0", + "@typescript-eslint/visitor-keys": "5.41.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "5.41.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.41.0.tgz", + "integrity": "sha512-QlvfwaN9jaMga9EBazQ+5DDx/4sAdqDkcs05AsQHMaopluVCUyu1bTRUVKzXbgjDlrRAQrYVoi/sXJ9fmG+KLQ==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "@types/semver": "^7.3.12", + "@typescript-eslint/scope-manager": "5.41.0", + "@typescript-eslint/types": "5.41.0", + "@typescript-eslint/typescript-estree": "5.41.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0", + "semver": "^7.3.7" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "5.41.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.41.0.tgz", + "integrity": "sha512-vilqeHj267v8uzzakbm13HkPMl7cbYpKVjgFWZPIOHIJHZtinvypUhJ5xBXfWYg4eFKqztbMMpOgFpT9Gfx4fw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.41.0", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@webassemblyjs/ast": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.12.1.tgz", + "integrity": "sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg==", + "dev": true, + "dependencies": { + "@webassemblyjs/helper-numbers": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6" + } + }, + "node_modules/@webassemblyjs/floating-point-hex-parser": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz", + "integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-api-error": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz", + "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-buffer": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.12.1.tgz", + "integrity": "sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-numbers": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz", + "integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==", + "dev": true, + "dependencies": { + "@webassemblyjs/floating-point-hex-parser": "1.11.6", + "@webassemblyjs/helper-api-error": "1.11.6", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/helper-wasm-bytecode": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz", + "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==", + "dev": true + }, + "node_modules/@webassemblyjs/helper-wasm-section": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.12.1.tgz", + "integrity": "sha512-Jif4vfB6FJlUlSbgEMHUyk1j234GTNG9dBJ4XJdOySoj518Xj0oGsNi59cUQF4RRMS9ouBUxDDdyBVfPTypa5g==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-buffer": "1.12.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/wasm-gen": "1.12.1" + } + }, + "node_modules/@webassemblyjs/ieee754": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz", + "integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==", + "dev": true, + "dependencies": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "node_modules/@webassemblyjs/leb128": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz", + "integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==", + "dev": true, + "dependencies": { + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webassemblyjs/utf8": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz", + "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==", + "dev": true + }, + "node_modules/@webassemblyjs/wasm-edit": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.12.1.tgz", + "integrity": "sha512-1DuwbVvADvS5mGnXbE+c9NfA8QRcZ6iKquqjjmR10k6o+zzsRVesil54DKexiowcFCPdr/Q0qaMgB01+SQ1u6g==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-buffer": "1.12.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/helper-wasm-section": "1.12.1", + "@webassemblyjs/wasm-gen": "1.12.1", + "@webassemblyjs/wasm-opt": "1.12.1", + "@webassemblyjs/wasm-parser": "1.12.1", + "@webassemblyjs/wast-printer": "1.12.1" + } + }, + "node_modules/@webassemblyjs/wasm-gen": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.12.1.tgz", + "integrity": "sha512-TDq4Ojh9fcohAw6OIMXqiIcTq5KUXTGRkVxbSo1hQnSy6lAM5GSdfwWeSxpAo0YzgsgF182E/U0mDNhuA0tW7w==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/ieee754": "1.11.6", + "@webassemblyjs/leb128": "1.11.6", + "@webassemblyjs/utf8": "1.11.6" + } + }, + "node_modules/@webassemblyjs/wasm-opt": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.12.1.tgz", + "integrity": "sha512-Jg99j/2gG2iaz3hijw857AVYekZe2SAskcqlWIZXjji5WStnOpVoat3gQfT/Q5tb2djnCjBtMocY/Su1GfxPBg==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-buffer": "1.12.1", + "@webassemblyjs/wasm-gen": "1.12.1", + "@webassemblyjs/wasm-parser": "1.12.1" + } + }, + "node_modules/@webassemblyjs/wasm-parser": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.12.1.tgz", + "integrity": "sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-api-error": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/ieee754": "1.11.6", + "@webassemblyjs/leb128": "1.11.6", + "@webassemblyjs/utf8": "1.11.6" + } + }, + "node_modules/@webassemblyjs/wast-printer": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.12.1.tgz", + "integrity": "sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA==", + "dev": true, + "dependencies": { + "@webassemblyjs/ast": "1.12.1", + "@xtuc/long": "4.2.2" + } + }, + "node_modules/@webpack-cli/configtest": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.2.0.tgz", + "integrity": "sha512-4FB8Tj6xyVkyqjj1OaTqCjXYULB9FMkqQ8yGrZjRDrYh0nOE+7Lhs45WioWQQMV+ceFlE368Ukhe6xdvJM9Egg==", + "dev": true, + "peerDependencies": { + "webpack": "4.x.x || 5.x.x", + "webpack-cli": "4.x.x" + } + }, + "node_modules/@webpack-cli/info": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-1.5.0.tgz", + "integrity": "sha512-e8tSXZpw2hPl2uMJY6fsMswaok5FdlGNRTktvFk2sD8RjH0hE2+XistawJx1vmKteh4NmGmNUrp+Tb2w+udPcQ==", + "dev": true, + "dependencies": { + "envinfo": "^7.7.3" + }, + "peerDependencies": { + "webpack-cli": "4.x.x" + } + }, + "node_modules/@webpack-cli/serve": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.7.0.tgz", + "integrity": "sha512-oxnCNGj88fL+xzV+dacXs44HcDwf1ovs3AuEzvP7mqXw7fQntqIhQ1BRmynh4qEKQSSSRSWVyXRjmTbZIX9V2Q==", + "dev": true, + "peerDependencies": { + "webpack-cli": "4.x.x" + }, + "peerDependenciesMeta": { + "webpack-dev-server": { + "optional": true + } + } + }, + "node_modules/@xmldom/xmldom": { + "version": "0.8.10", + "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.10.tgz", + "integrity": "sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==", + "dev": true, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "dev": true + }, + "node_modules/@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "dev": true + }, + "node_modules/7zip-bin": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/7zip-bin/-/7zip-bin-5.2.0.tgz", + "integrity": "sha512-ukTPVhqG4jNzMro2qA9HSCSSVJN3aN7tlb+hfqYCt3ER0yWroeA2VR38MNrOHLQ/cVj+DaIMad0kFCtWWowh/A==", + "dev": true + }, + "node_modules/abab": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", + "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", + "deprecated": "Use your platform's native atob() and btoa() methods instead", + "dev": true + }, + "node_modules/acorn": { + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-globals": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-7.0.1.tgz", + "integrity": "sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q==", + "dev": true, + "dependencies": { + "acorn": "^8.1.0", + "acorn-walk": "^8.0.2" + } + }, + "node_modules/acorn-import-assertions": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz", + "integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==", + "dev": true, + "peerDependencies": { + "acorn": "^8" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz", + "integrity": "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/aflon": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/aflon/-/aflon-2.0.10.tgz", + "integrity": "sha512-jPpwapiNyBuWfTmTLJcd0HakzSFO/hGUxwNfZgo223KvKg7iRrFosWdqvr9ejbFxome66rcktp0VCD8Bw8DClQ==", + "dependencies": { + "popmotion": "^8.7.5", + "stylefire": "^7.0.3", + "typestyle": "^2.1.0" + } + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "peer": true, + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-escapes/node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "peer": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/app-builder-bin": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/app-builder-bin/-/app-builder-bin-4.0.0.tgz", + "integrity": "sha512-xwdG0FJPQMe0M0UA4Tz0zEB8rBJTRA5a476ZawAqiBkMv16GRK5xpXThOjMaEOFnZ6zabejjG4J3da0SXG63KA==", + "dev": true + }, + "node_modules/app-builder-lib": { + "version": "24.13.3", + "resolved": "https://registry.npmjs.org/app-builder-lib/-/app-builder-lib-24.13.3.tgz", + "integrity": "sha512-FAzX6IBit2POXYGnTCT8YHFO/lr5AapAII6zzhQO3Rw4cEDOgK+t1xhLc5tNcKlicTHlo9zxIwnYCX9X2DLkig==", + "dev": true, + "dependencies": { + "@develar/schema-utils": "~2.6.5", + "@electron/notarize": "2.2.1", + "@electron/osx-sign": "1.0.5", + "@electron/universal": "1.5.1", + "@malept/flatpak-bundler": "^0.4.0", + "@types/fs-extra": "9.0.13", + "async-exit-hook": "^2.0.1", + "bluebird-lst": "^1.0.9", + "builder-util": "24.13.1", + "builder-util-runtime": "9.2.4", + "chromium-pickle-js": "^0.2.0", + "debug": "^4.3.4", + "ejs": "^3.1.8", + "electron-publish": "24.13.1", + "form-data": "^4.0.0", + "fs-extra": "^10.1.0", + "hosted-git-info": "^4.1.0", + "is-ci": "^3.0.0", + "isbinaryfile": "^5.0.0", + "js-yaml": "^4.1.0", + "lazy-val": "^1.0.5", + "minimatch": "^5.1.1", + "read-config-file": "6.3.2", + "sanitize-filename": "^1.6.3", + "semver": "^7.3.8", + "tar": "^6.1.12", + "temp-file": "^3.4.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "dmg-builder": "24.13.3", + "electron-builder-squirrel-windows": "24.13.3" + } + }, + "node_modules/app-builder-lib/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/app-builder-lib/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/app-builder-lib/node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/app-builder-lib/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/app-builder-lib/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" + }, + "node_modules/archiver": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/archiver/-/archiver-5.3.2.tgz", + "integrity": "sha512-+25nxyyznAXF7Nef3y0EbBeqmGZgeN/BxHX29Rs39djAfaFalmQ89SE6CWyDCHzGL0yt/ycBtNOmGTW0FyGWNw==", + "dev": true, + "peer": true, + "dependencies": { + "archiver-utils": "^2.1.0", + "async": "^3.2.4", + "buffer-crc32": "^0.2.1", + "readable-stream": "^3.6.0", + "readdir-glob": "^1.1.2", + "tar-stream": "^2.2.0", + "zip-stream": "^4.1.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/archiver-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-2.1.0.tgz", + "integrity": "sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==", + "dev": true, + "peer": true, + "dependencies": { + "glob": "^7.1.4", + "graceful-fs": "^4.2.0", + "lazystream": "^1.0.0", + "lodash.defaults": "^4.2.0", + "lodash.difference": "^4.5.0", + "lodash.flatten": "^4.4.0", + "lodash.isplainobject": "^4.0.6", + "lodash.union": "^4.6.0", + "normalize-path": "^3.0.0", + "readable-stream": "^2.0.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/archiver/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "peer": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/are-we-there-yet": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.7.tgz", + "integrity": "sha512-nxwy40TuMiUGqMyRHgCSWZ9FM4VAoRP4xUYSTv5ImRog+h9yISPbVH7H8fASCIzYn9wlEv4zvFL7uKDMCFQm3g==", + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", + "dev": true + }, + "node_modules/assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", + "dev": true, + "optional": true, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true, + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/async": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", + "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==", + "dev": true + }, + "node_modules/async-exit-hook": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/async-exit-hook/-/async-exit-hook-2.0.1.tgz", + "integrity": "sha512-NW2cX8m1Q7KPA7a5M2ULQeZ2wR5qI5PAbw5L0UOMxdioVk9PMZ0h1TmyZEkPYrCvYjDlFICusOu1dlEKAAeXBw==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true + }, + "node_modules/at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "dev": true, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/babel-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", + "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", + "dev": true, + "peer": true, + "dependencies": { + "@jest/transform": "^29.7.0", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^29.6.3", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.8.0" + } + }, + "node_modules/babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-istanbul/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "peer": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/babel-plugin-jest-hoist": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", + "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.1.14", + "@types/babel__traverse": "^7.0.6" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/babel-preset-current-node-syntax": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", + "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.8.3", + "@babel/plugin-syntax-import-meta": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.8.3", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-top-level-await": "^7.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/babel-preset-jest": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", + "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", + "dev": true, + "peer": true, + "dependencies": { + "babel-plugin-jest-hoist": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/bl/node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", + "dev": true + }, + "node_modules/bluebird-lst": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/bluebird-lst/-/bluebird-lst-1.0.9.tgz", + "integrity": "sha512-7B1Rtx82hjnSD4PGLAjVWeYH3tHAcVUmChh85a3lltKQm6FresXh9ErQo6oAv6CqxttczC3/kEg8SY5NluPuUw==", + "dev": true, + "dependencies": { + "bluebird": "^3.5.5" + } + }, + "node_modules/boolean": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.2.0.tgz", + "integrity": "sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==", + "dev": true, + "optional": true + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.23.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz", + "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001587", + "electron-to-chromium": "^1.4.668", + "node-releases": "^2.0.14", + "update-browserslist-db": "^1.0.13" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/bs-logger": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", + "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", + "dev": true, + "dependencies": { + "fast-json-stable-stringify": "2.x" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "peer": true, + "dependencies": { + "node-int64": "^0.4.0" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/buffer-equal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-1.0.1.tgz", + "integrity": "sha512-QoV3ptgEaQpvVwbXdSO39iqPQTCxSF7A5U99AxbHYqUdCizL/lH2Z0A2y6nbZucxMEOtNyZfG2s6gsVugGpKkg==", + "dev": true, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, + "node_modules/builder-util": { + "version": "24.13.1", + "resolved": "https://registry.npmjs.org/builder-util/-/builder-util-24.13.1.tgz", + "integrity": "sha512-NhbCSIntruNDTOVI9fdXz0dihaqX2YuE1D6zZMrwiErzH4ELZHE6mdiB40wEgZNprDia+FghRFgKoAqMZRRjSA==", + "dev": true, + "dependencies": { + "@types/debug": "^4.1.6", + "7zip-bin": "~5.2.0", + "app-builder-bin": "4.0.0", + "bluebird-lst": "^1.0.9", + "builder-util-runtime": "9.2.4", + "chalk": "^4.1.2", + "cross-spawn": "^7.0.3", + "debug": "^4.3.4", + "fs-extra": "^10.1.0", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.1", + "is-ci": "^3.0.0", + "js-yaml": "^4.1.0", + "source-map-support": "^0.5.19", + "stat-mode": "^1.0.0", + "temp-file": "^3.4.0" + } + }, + "node_modules/builder-util-runtime": { + "version": "9.2.4", + "resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-9.2.4.tgz", + "integrity": "sha512-upp+biKpN/XZMLim7aguUyW8s0FUpDvOtK6sbanMFDAMBzpHDqdhgVYm6zc9HJ6nWo7u2Lxk60i2M6Jd3aiNrA==", + "dev": true, + "dependencies": { + "debug": "^4.3.4", + "sax": "^1.2.4" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/builder-util/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/builder-util/node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/builder-util/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/cacheable-lookup": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", + "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==", + "dev": true, + "engines": { + "node": ">=10.6.0" + } + }, + "node_modules/cacheable-request": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.4.tgz", + "integrity": "sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==", + "dev": true, + "dependencies": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^4.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^6.0.1", + "responselike": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/call-bind": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "dev": true, + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001618", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001618.tgz", + "integrity": "sha512-p407+D1tIkDvsEAPS22lJxLQQaG8OTBEqo0KhzfABGk0TU4juBNDSfH0hyAp/HRyx+M8L17z/ltyhxh27FTfQg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true, + "peer": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" + }, + "node_modules/chrome-trace-event": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", + "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", + "dev": true, + "engines": { + "node": ">=6.0" + } + }, + "node_modules/chromium-pickle-js": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/chromium-pickle-js/-/chromium-pickle-js-0.2.0.tgz", + "integrity": "sha512-1R5Fho+jBq0DDydt+/vHWj5KJNJCKdARKOCwZUen84I5BreWoLqRLANH1U87eJy1tiASPtMnGqJJq0ZsLoRPOw==", + "dev": true + }, + "node_modules/ci-info": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.5.0.tgz", + "integrity": "sha512-yH4RezKOGlOhxkmhbeNuC4eYZKAUsEaGtBuBzDDP1eFUKiccDWzBABxBfOx31IDwDIXMTxWuwAxUGModvkbuVw==", + "dev": true + }, + "node_modules/cjs-module-lexer": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.3.1.tgz", + "integrity": "sha512-a3KdPAANPbNE4ZUv9h6LckSl9zLsYOP4MBmhIPkRaeyybt+r4UghLvq+xw/YwUcC1gqylCkL4rdVs3Lwupjm4Q==", + "dev": true, + "peer": true + }, + "node_modules/cli-truncate": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", + "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", + "dev": true, + "optional": true, + "dependencies": { + "slice-ansi": "^3.0.0", + "string-width": "^4.2.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "dev": true, + "dependencies": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/clone-response": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", + "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==", + "dev": true, + "dependencies": { + "mimic-response": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "dev": true, + "peer": true, + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/collect-v8-coverage": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", + "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", + "dev": true, + "peer": true + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/colorette": { + "version": "2.0.16", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.16.tgz", + "integrity": "sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g==", + "dev": true + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "node_modules/compare-version": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/compare-version/-/compare-version-0.1.2.tgz", + "integrity": "sha512-pJDh5/4wrEnXX/VWRZvruAGHkzKdr46z11OlTPN+VrATlWWhSKewNCJ1futCO5C7eJB3nPMFZA1LeYtcFboZ2A==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/compress-commons": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-4.1.2.tgz", + "integrity": "sha512-D3uMHtGc/fcO1Gt1/L7i1e33VOvD4A9hfQLP+6ewd+BvG/gQ84Yh4oftEhAdjSMgBgwGL+jsppT7JYNpo6MHHg==", + "dev": true, + "peer": true, + "dependencies": { + "buffer-crc32": "^0.2.13", + "crc32-stream": "^4.0.2", + "normalize-path": "^3.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/compress-commons/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "peer": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/config-file-ts": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/config-file-ts/-/config-file-ts-0.2.6.tgz", + "integrity": "sha512-6boGVaglwblBgJqGyxm4+xCmEGcWgnWHSWHY5jad58awQhB6gftq0G8HbzU39YqCIYHMLAiL1yjwiZ36m/CL8w==", + "dev": true, + "dependencies": { + "glob": "^10.3.10", + "typescript": "^5.3.3" + } + }, + "node_modules/config-file-ts/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/config-file-ts/node_modules/glob": { + "version": "10.3.16", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.16.tgz", + "integrity": "sha512-JDKXl1DiuuHJ6fVS2FXjownaavciiHNUU4mOvV/B793RLh05vZL1rcPnCSaOgv1hDT6RDlY7AB7ZUvFYAtPgAw==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.1", + "minipass": "^7.0.4", + "path-scurry": "^1.11.0" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/config-file-ts/node_modules/minimatch": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/config-file-ts/node_modules/minipass": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.1.tgz", + "integrity": "sha512-UZ7eQ+h8ywIRAW1hIEl2AqdwzJucU/Kp59+8kkZeSvafXhZjul247BvIJjEVFVeON6d7lM46XX1HXCduKAS8VA==", + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/config-file-ts/node_modules/typescript": { + "version": "5.4.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", + "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "peer": true + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + }, + "node_modules/crc": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/crc/-/crc-3.8.0.tgz", + "integrity": "sha512-iX3mfgcTMIq3ZKLIsVFAbv7+Mc10kxabAGQb8HvjA1o3T1PIYprbakQ65d3I+2HGHt6nSKkM9PYjgoJO2KcFBQ==", + "dev": true, + "optional": true, + "dependencies": { + "buffer": "^5.1.0" + } + }, + "node_modules/crc-32": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", + "dev": true, + "peer": true, + "bin": { + "crc32": "bin/crc32.njs" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/crc32-stream": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-4.0.3.tgz", + "integrity": "sha512-NT7w2JVU7DFroFdYkeq8cywxrgjPHWkdX1wjpRQXPX5Asews3tA+Ght6lddQO5Mkumffp3X7GEqku3epj2toIw==", + "dev": true, + "peer": true, + "dependencies": { + "crc-32": "^1.2.0", + "readable-stream": "^3.4.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/crc32-stream/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "peer": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/create-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", + "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", + "dev": true, + "peer": true, + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "prompts": "^2.0.1" + }, + "bin": { + "create-jest": "bin/create-jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/cross-env": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", + "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.1" + }, + "bin": { + "cross-env": "src/bin/cross-env.js", + "cross-env-shell": "src/bin/cross-env-shell.js" + }, + "engines": { + "node": ">=10.14", + "npm": ">=6", + "yarn": ">=1" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/cssom": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz", + "integrity": "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==", + "dev": true + }, + "node_modules/cssstyle": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", + "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", + "dev": true, + "dependencies": { + "cssom": "~0.3.6" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cssstyle/node_modules/cssom": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", + "dev": true + }, + "node_modules/csstype": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.10.tgz", + "integrity": "sha512-2u44ZG2OcNUO9HDp/Jl8C07x6pU/eTR3ncV91SiK3dhG9TWvRVsCoJw14Ckx5DgWkzGA3waZWO3d7pgqpUI/XA==" + }, + "node_modules/data-urls": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-3.0.2.tgz", + "integrity": "sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==", + "dev": true, + "dependencies": { + "abab": "^2.0.6", + "whatwg-mimetype": "^3.0.0", + "whatwg-url": "^11.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decimal.js": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", + "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==", + "dev": true + }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decompress-response/node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/dedent": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.3.tgz", + "integrity": "sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ==", + "dev": true, + "peer": true, + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" + }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/defer-to-connect": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", + "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "optional": true, + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" + }, + "node_modules/detect-libc": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz", + "integrity": "sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==", + "engines": { + "node": ">=8" + } + }, + "node_modules/detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", + "dev": true, + "optional": true + }, + "node_modules/dezalgo": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz", + "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==", + "dev": true, + "dependencies": { + "asap": "^2.0.0", + "wrappy": "1" + } + }, + "node_modules/diff-sequences": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/dir-compare": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/dir-compare/-/dir-compare-3.3.0.tgz", + "integrity": "sha512-J7/et3WlGUCxjdnD3HAAzQ6nsnc0WL6DD7WcwJb7c39iH1+AWfg+9OqzJNaI6PkBwBvm1mhZNL9iY/nRiZXlPg==", + "dev": true, + "dependencies": { + "buffer-equal": "^1.0.0", + "minimatch": "^3.0.4" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dmg-builder": { + "version": "24.13.3", + "resolved": "https://registry.npmjs.org/dmg-builder/-/dmg-builder-24.13.3.tgz", + "integrity": "sha512-rcJUkMfnJpfCboZoOOPf4L29TRtEieHNOeAbYPWPxlaBw/Z1RKrRA86dOI9rwaI4tQSc/RD82zTNHprfUHXsoQ==", + "dev": true, + "dependencies": { + "app-builder-lib": "24.13.3", + "builder-util": "24.13.1", + "builder-util-runtime": "9.2.4", + "fs-extra": "^10.1.0", + "iconv-lite": "^0.6.2", + "js-yaml": "^4.1.0" + }, + "optionalDependencies": { + "dmg-license": "^1.0.11" + } + }, + "node_modules/dmg-builder/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/dmg-builder/node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/dmg-builder/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/dmg-license": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/dmg-license/-/dmg-license-1.0.11.tgz", + "integrity": "sha512-ZdzmqwKmECOWJpqefloC5OJy1+WZBBse5+MR88z9g9Zn4VY+WYUkAyojmhzJckH5YbbZGcYIuGAkY5/Ys5OM2Q==", + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "dependencies": { + "@types/plist": "^3.0.1", + "@types/verror": "^1.10.3", + "ajv": "^6.10.0", + "crc": "^3.8.0", + "iconv-corefoundation": "^1.1.7", + "plist": "^3.0.4", + "smart-buffer": "^4.0.2", + "verror": "^1.10.0" + }, + "bin": { + "dmg-license": "bin/dmg-license.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/domexception": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz", + "integrity": "sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==", + "deprecated": "Use your platform's native DOMException instead", + "dev": true, + "dependencies": { + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/dotenv": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-9.0.2.tgz", + "integrity": "sha512-I9OvvrHp4pIARv4+x9iuewrWycX6CcZtoAu1XrzPxc5UygMJXJZYmBsynku8IkrJwgypE5DGNjDPmPRhDCptUg==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/dotenv-expand": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz", + "integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==", + "dev": true + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, + "node_modules/ejs": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", + "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", + "dev": true, + "dependencies": { + "jake": "^10.8.5" + }, + "bin": { + "ejs": "bin/cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/electron": { + "version": "30.0.9", + "resolved": "https://registry.npmjs.org/electron/-/electron-30.0.9.tgz", + "integrity": "sha512-ArxgdGHVu3o5uaP+Tqj8cJDvU03R6vrGrOqiMs7JXLnvQHMqXJIIxmFKQAIdJW8VoT3ac3hD21tA7cPO10RLow==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "@electron/get": "^2.0.0", + "@types/node": "^20.9.0", + "extract-zip": "^2.0.1" + }, + "bin": { + "electron": "cli.js" + }, + "engines": { + "node": ">= 12.20.55" + } + }, + "node_modules/electron-builder": { + "version": "24.13.3", + "resolved": "https://registry.npmjs.org/electron-builder/-/electron-builder-24.13.3.tgz", + "integrity": "sha512-yZSgVHft5dNVlo31qmJAe4BVKQfFdwpRw7sFp1iQglDRCDD6r22zfRJuZlhtB5gp9FHUxCMEoWGq10SkCnMAIg==", + "dev": true, + "dependencies": { + "app-builder-lib": "24.13.3", + "builder-util": "24.13.1", + "builder-util-runtime": "9.2.4", + "chalk": "^4.1.2", + "dmg-builder": "24.13.3", + "fs-extra": "^10.1.0", + "is-ci": "^3.0.0", + "lazy-val": "^1.0.5", + "read-config-file": "6.3.2", + "simple-update-notifier": "2.0.0", + "yargs": "^17.6.2" + }, + "bin": { + "electron-builder": "cli.js", + "install-app-deps": "install-app-deps.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/electron-builder-squirrel-windows": { + "version": "24.13.3", + "resolved": "https://registry.npmjs.org/electron-builder-squirrel-windows/-/electron-builder-squirrel-windows-24.13.3.tgz", + "integrity": "sha512-oHkV0iogWfyK+ah9ZIvMDpei1m9ZRpdXcvde1wTpra2U8AFDNNpqJdnin5z+PM1GbQ5BoaKCWas2HSjtR0HwMg==", + "dev": true, + "peer": true, + "dependencies": { + "app-builder-lib": "24.13.3", + "archiver": "^5.3.1", + "builder-util": "24.13.1", + "fs-extra": "^10.1.0" + } + }, + "node_modules/electron-builder-squirrel-windows/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "peer": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/electron-builder-squirrel-windows/node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "peer": true, + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/electron-builder-squirrel-windows/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "peer": true, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/electron-builder/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/electron-builder/node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/electron-builder/node_modules/universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "dev": true, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/electron-publish": { + "version": "24.13.1", + "resolved": "https://registry.npmjs.org/electron-publish/-/electron-publish-24.13.1.tgz", + "integrity": "sha512-2ZgdEqJ8e9D17Hwp5LEq5mLQPjqU3lv/IALvgp+4W8VeNhryfGhYEQC/PgDPMrnWUp+l60Ou5SJLsu+k4mhQ8A==", + "dev": true, + "dependencies": { + "@types/fs-extra": "^9.0.11", + "builder-util": "24.13.1", + "builder-util-runtime": "9.2.4", + "chalk": "^4.1.2", + "fs-extra": "^10.1.0", + "lazy-val": "^1.0.5", + "mime": "^2.5.2" + } + }, + "node_modules/electron-publish/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/electron-publish/node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/electron-publish/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.4.767", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.767.tgz", + "integrity": "sha512-nzzHfmQqBss7CE3apQHkHjXW77+8w3ubGCIoEijKCJebPufREaFETgGXWTkh32t259F3Kcq+R8MZdFdOJROgYw==", + "dev": true + }, + "node_modules/emittery": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", + "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "node_modules/emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/enhanced-resolve": { + "version": "5.16.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.16.1.tgz", + "integrity": "sha512-4U5pNsuDl0EhuZpq46M5xPslstkviJuhrdobaRDBk2Jy2KO37FDAJl4lb2KlNabxT0m4MTK2UHNrsAcphE8nyw==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/envinfo": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.8.1.tgz", + "integrity": "sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw==", + "dev": true, + "bin": { + "envinfo": "dist/cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/err-code": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", + "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", + "dev": true + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "peer": true, + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-module-lexer": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.5.2.tgz", + "integrity": "sha512-l60ETUTmLqbVbVHv1J4/qj+M8nq7AwMzEcg3kmJDt9dCNrTk+yHcYFf/Kw75pMDwd9mPcIGCG5LcS20SxYRzFA==", + "dev": true + }, + "node_modules/es6-error": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", + "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", + "dev": true, + "optional": true + }, + "node_modules/escalade": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", + "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/escodegen": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", + "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", + "dev": true, + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=6.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, + "node_modules/escodegen/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/eslint": { + "version": "8.26.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.26.0.tgz", + "integrity": "sha512-kzJkpaw1Bfwheq4VXUezFriD1GxszX6dUekM7Z3aC2o4hju+tsR/XyTC3RcoSD7jmy9VkPU3+N6YjVU2e96Oyg==", + "dev": true, + "dependencies": { + "@eslint/eslintrc": "^1.3.3", + "@humanwhocodes/config-array": "^0.11.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.1.1", + "eslint-utils": "^3.0.0", + "eslint-visitor-keys": "^3.3.0", + "espree": "^9.4.0", + "esquery": "^1.4.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.15.0", + "grapheme-splitter": "^1.0.4", + "ignore": "^5.2.0", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-sdsl": "^4.1.4", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "regexpp": "^3.2.0", + "strip-ansi": "^6.0.1", + "strip-json-comments": "^3.1.0", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-plugin-tsdoc": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/eslint-plugin-tsdoc/-/eslint-plugin-tsdoc-0.2.17.tgz", + "integrity": "sha512-xRmVi7Zx44lOBuYqG8vzTXuL6IdGOeF9nHX17bjJ8+VE6fsxpdGem0/SBTmAwgYMKYB1WBkqRJVQ+n8GK041pA==", + "dev": true, + "dependencies": { + "@microsoft/tsdoc": "0.14.2", + "@microsoft/tsdoc-config": "0.16.2" + } + }, + "node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^2.0.0" + }, + "engines": { + "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=5" + } + }, + "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", + "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/eslint/node_modules/eslint-scope": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", + "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/eslint/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/eslint/node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/eslint/node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/espree": { + "version": "9.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.0.tgz", + "integrity": "sha512-DQmnRpLj7f6TgN/NYb0MTzJXL+vJF9h3pHy4JhCIs3zwcgez8xmGg3sXHcEO97BrmO2OSvCwMdfdlyl+E9KjOw==", + "dev": true, + "dependencies": { + "acorn": "^8.8.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esquery": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", + "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esquery/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "dev": true, + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "peer": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/execa/node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", + "dev": true, + "dependencies": { + "@jest/expect-utils": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/extract-zip": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", + "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", + "dev": true, + "dependencies": { + "debug": "^4.1.1", + "get-stream": "^5.1.0", + "yauzl": "^2.10.0" + }, + "bin": { + "extract-zip": "cli.js" + }, + "engines": { + "node": ">= 10.17.0" + }, + "optionalDependencies": { + "@types/yauzl": "^2.9.1" + } + }, + "node_modules/extsprintf": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.4.1.tgz", + "integrity": "sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA==", + "dev": true, + "engines": [ + "node >=0.6.0" + ], + "optional": true + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-glob": { + "version": "3.2.12", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", + "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "node_modules/fastest-levenshtein": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.12.tgz", + "integrity": "sha512-On2N+BpYJ15xIC974QNVuYGMOlEVt4s0EOI3wwMqOmK1fdDY+FN/zltPV8vosq4ad4c/gJ1KHScUn/6AWIgiow==", + "dev": true + }, + "node_modules/fastq": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", + "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "dev": true, + "peer": true, + "dependencies": { + "bser": "2.1.1" + } + }, + "node_modules/fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", + "dev": true, + "dependencies": { + "pend": "~1.2.0" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/filelist": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", + "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", + "dev": true, + "dependencies": { + "minimatch": "^5.0.1" + } + }, + "node_modules/filelist/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/filelist/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "dependencies": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", + "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", + "dev": true + }, + "node_modules/foreground-child": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", + "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/foreground-child/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dev": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/formidable": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-2.1.2.tgz", + "integrity": "sha512-CM3GuJ57US06mlpQ47YcunuUZ9jpm8Vx+P2CGt2j7HpgkKZO/DJYQ0Bobim8G6PFQmK5lOqOOdUXboU+h73A4g==", + "dev": true, + "dependencies": { + "dezalgo": "^1.0.4", + "hexoid": "^1.0.0", + "once": "^1.4.0", + "qs": "^6.11.0" + }, + "funding": { + "url": "https://ko-fi.com/tunnckoCore/commissions" + } + }, + "node_modules/framesync": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/framesync/-/framesync-4.1.0.tgz", + "integrity": "sha512-MmgZ4wCoeVxNbx2xp5hN/zPDCbLSKiDt4BbbslK7j/pM2lg5S0vhTNv1v8BCVb99JPIo6hXBFdwzU7Q4qcAaoQ==", + "dependencies": { + "hey-listen": "^1.0.5" + } + }, + "node_modules/free-style": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/free-style/-/free-style-3.1.0.tgz", + "integrity": "sha512-vJujYSIyT30iDoaoeigNAxX4yB1RUrh+N2ZMhIElMr3BvCuGXOw7XNJMEEJkDUeamK2Rnb/IKFGKRKlTWIGRWA==" + }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" + }, + "node_modules/fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dev": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/fs-minipass/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "peer": true, + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gauge": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", + "dependencies": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + } + }, + "node_modules/gauge/node_modules/ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gauge/node_modules/is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dependencies": { + "number-is-nan": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gauge/node_modules/string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dependencies": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gauge/node_modules/strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dependencies": { + "ansi-regex": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4=" + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "dev": true + }, + "node_modules/global-agent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-agent/-/global-agent-3.0.0.tgz", + "integrity": "sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==", + "dev": true, + "optional": true, + "dependencies": { + "boolean": "^3.0.1", + "es6-error": "^4.1.1", + "matcher": "^3.0.0", + "roarr": "^2.15.3", + "semver": "^7.3.2", + "serialize-error": "^7.0.1" + }, + "engines": { + "node": ">=10.0" + } + }, + "node_modules/globals": { + "version": "13.17.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.17.0.tgz", + "integrity": "sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globals/node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "dev": true, + "optional": true, + "dependencies": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/got": { + "version": "11.8.6", + "resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz", + "integrity": "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==", + "dev": true, + "dependencies": { + "@sindresorhus/is": "^4.0.0", + "@szmarczak/http-timer": "^4.0.5", + "@types/cacheable-request": "^6.0.1", + "@types/responselike": "^1.0.0", + "cacheable-lookup": "^5.0.3", + "cacheable-request": "^7.0.2", + "decompress-response": "^6.0.0", + "http2-wrapper": "^1.0.0-beta.5.2", + "lowercase-keys": "^2.0.0", + "p-cancelable": "^2.0.0", + "responselike": "^2.0.0" + }, + "engines": { + "node": ">=10.19.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/got?sponsor=1" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, + "node_modules/grapheme-splitter": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", + "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", + "dev": true + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", + "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hexoid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/hexoid/-/hexoid-1.0.0.tgz", + "integrity": "sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/hey-listen": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/hey-listen/-/hey-listen-1.0.8.tgz", + "integrity": "sha512-COpmrF2NOg4TBWUJ5UVyaCU2A88wEMkUPK4hNqyCkqHbxT92BbvfjoSozkAIIm6XhicGlJHhFdullInrdhwU8Q==" + }, + "node_modules/hosted-git-info": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", + "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/html-encoding-sniffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", + "integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==", + "dev": true, + "dependencies": { + "whatwg-encoding": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true, + "peer": true + }, + "node_modules/http-cache-semantics": { + "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==", + "dev": true + }, + "node_modules/http-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "dev": true, + "dependencies": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/http2-wrapper": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", + "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", + "dev": true, + "dependencies": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.0.0" + }, + "engines": { + "node": ">=10.19.0" + } + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "peer": true, + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/iconv-corefoundation": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/iconv-corefoundation/-/iconv-corefoundation-1.1.7.tgz", + "integrity": "sha512-T10qvkw0zz4wnm560lOEg0PovVqUXuOFhhHAkixw8/sycy7TJt7v/RrkEKEQnAw2viPSJu6iAkErxnzR0g8PpQ==", + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "dependencies": { + "cli-truncate": "^2.1.0", + "node-addon-api": "^1.6.3" + }, + "engines": { + "node": "^8.11.2 || >=10" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/ignore": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-local": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", + "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", + "dev": true, + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" + }, + "node_modules/interpret": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz", + "integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true, + "peer": true + }, + "node_modules/is-ci": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.1.tgz", + "integrity": "sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==", + "dev": true, + "dependencies": { + "ci-info": "^3.2.0" + }, + "bin": { + "is-ci": "bin.js" + } + }, + "node_modules/is-core-module": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", + "dev": true, + "dependencies": { + "hasown": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "dev": true + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "node_modules/isbinaryfile": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-5.0.2.tgz", + "integrity": "sha512-GvcjojwonMjWbTkfMpnVHVqXW/wKMYDfEpY94/8zy8HFMOqb/VL6oeONq9v87q4ttVlaTLnGXnJD4B5B1OTGIg==", + "dev": true, + "engines": { + "node": ">= 18.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/gjtorikian/" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.2.tgz", + "integrity": "sha512-1WUsZ9R1lA0HtBSohTkm39WTPlNKSJ5iFk7UwqXkBLoHQT+hfqPsfsTDVuZdKGaBwn7din9bS7SsnoAr943hvw==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/core": "^7.23.9", + "@babel/parser": "^7.23.9", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "peer": true, + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "peer": true, + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-reports": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", + "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", + "dev": true, + "peer": true, + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jackspeak": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.1.2.tgz", + "integrity": "sha512-kWmLKn2tRtfYMF/BakihVVRzBKOxz4gJMiL2Rj91WnAB5TPZumSH99R/Yf1qE1u4uRimvCSJfm6hnxohXeEXjQ==", + "dev": true, + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jake": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.1.tgz", + "integrity": "sha512-61btcOHNnLnsOdtLgA5efqQWjnSi/vow5HbI7HMdKKWqvrKR1bLK3BPlJn9gcSaP2ewuamUSMB5XEy76KUIS2w==", + "dev": true, + "dependencies": { + "async": "^3.2.3", + "chalk": "^4.0.2", + "filelist": "^1.0.4", + "minimatch": "^3.1.2" + }, + "bin": { + "jake": "bin/cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", + "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", + "dev": true, + "peer": true, + "dependencies": { + "@jest/core": "^29.7.0", + "@jest/types": "^29.6.3", + "import-local": "^3.0.2", + "jest-cli": "^29.7.0" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-changed-files": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", + "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", + "dev": true, + "peer": true, + "dependencies": { + "execa": "^5.0.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-changed-files/node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "peer": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-circus": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", + "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", + "dev": true, + "peer": true, + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^1.0.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^29.7.0", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0", + "pretty-format": "^29.7.0", + "pure-rand": "^6.0.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-circus/node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "peer": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-cli": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", + "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", + "dev": true, + "peer": true, + "dependencies": { + "@jest/core": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "create-jest": "^29.7.0", + "exit": "^0.1.2", + "import-local": "^3.0.2", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "yargs": "^17.3.1" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-config": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", + "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/test-sequencer": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-jest": "^29.7.0", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-circus": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@types/node": "*", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/jest-config/node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-diff": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", + "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-docblock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", + "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", + "dev": true, + "peer": true, + "dependencies": { + "detect-newline": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-each": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", + "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", + "dev": true, + "peer": true, + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "jest-util": "^29.7.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-environment-jsdom": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-29.7.0.tgz", + "integrity": "sha512-k9iQbsf9OyOfdzWH8HDmrRT0gSIcX+FLNW7IQq94tFX0gynPwqDTW0Ho6iMVNjGz/nb+l/vW3dWM2bbLLpkbXA==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/jsdom": "^20.0.0", + "@types/node": "*", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0", + "jsdom": "^20.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "canvas": "^2.5.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/jest-environment-node": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", + "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", + "dev": true, + "peer": true, + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-get-type": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-haste-map": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", + "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", + "dev": true, + "peer": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + } + }, + "node_modules/jest-haste-map/node_modules/jest-worker": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", + "dev": true, + "peer": true, + "dependencies": { + "@types/node": "*", + "jest-util": "^29.7.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-haste-map/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "peer": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/jest-leak-detector": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", + "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", + "dev": true, + "peer": true, + "dependencies": { + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-matcher-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", + "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-message-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", + "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.6.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-mock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", + "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-pnp-resolver": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "jest-resolve": "*" + }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } + } + }, + "node_modules/jest-regex-util": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", + "dev": true, + "peer": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", + "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", + "dev": true, + "peer": true, + "dependencies": { + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "resolve": "^1.20.0", + "resolve.exports": "^2.0.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve-dependencies": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", + "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", + "dev": true, + "peer": true, + "dependencies": { + "jest-regex-util": "^29.6.3", + "jest-snapshot": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve/node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "dev": true, + "peer": true, + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/jest-runner": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", + "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", + "dev": true, + "peer": true, + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/environment": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "graceful-fs": "^4.2.9", + "jest-docblock": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-leak-detector": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-resolve": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-util": "^29.7.0", + "jest-watcher": "^29.7.0", + "jest-worker": "^29.7.0", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runner/node_modules/jest-worker": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", + "dev": true, + "peer": true, + "dependencies": { + "@types/node": "*", + "jest-util": "^29.7.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runner/node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "peer": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-runner/node_modules/source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "dev": true, + "peer": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/jest-runner/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "peer": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/jest-runtime": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", + "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", + "dev": true, + "peer": true, + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/globals": "^29.7.0", + "@jest/source-map": "^29.6.3", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-snapshot": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", + "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-jsx": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/types": "^7.3.3", + "@jest/expect-utils": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "natural-compare": "^1.4.0", + "pretty-format": "^29.7.0", + "semver": "^7.5.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", + "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", + "dev": true, + "peer": true, + "dependencies": { + "@jest/types": "^29.6.3", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "leven": "^3.1.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-watcher": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", + "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", + "dev": true, + "peer": true, + "dependencies": { + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "jest-util": "^29.7.0", + "string-length": "^4.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "dev": true, + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/jju": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/jju/-/jju-1.4.0.tgz", + "integrity": "sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==", + "dev": true + }, + "node_modules/js-sdsl": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.1.5.tgz", + "integrity": "sha512-08bOAKweV2NUC1wqTtf3qZlnpOX/R2DU9ikpjOHs0H+ibQv3zpncVQg6um4uYtRtrwIX8M4Nh3ytK4HGlYAq7Q==", + "dev": true + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsdom": { + "version": "20.0.3", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-20.0.3.tgz", + "integrity": "sha512-SYhBvTh89tTfCD/CRdSOm13mOBa42iTaTyfyEWBdKcGdPxPtLFBXuHR8XHb33YNYaP+lLbmSvBTsnoesCNJEsQ==", + "dev": true, + "dependencies": { + "abab": "^2.0.6", + "acorn": "^8.8.1", + "acorn-globals": "^7.0.0", + "cssom": "^0.5.0", + "cssstyle": "^2.3.0", + "data-urls": "^3.0.2", + "decimal.js": "^10.4.2", + "domexception": "^4.0.0", + "escodegen": "^2.0.0", + "form-data": "^4.0.0", + "html-encoding-sniffer": "^3.0.0", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.1", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.2", + "parse5": "^7.1.1", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^4.1.2", + "w3c-xmlserializer": "^4.0.0", + "webidl-conversions": "^7.0.0", + "whatwg-encoding": "^2.0.0", + "whatwg-mimetype": "^3.0.0", + "whatwg-url": "^11.0.0", + "ws": "^8.11.0", + "xml-name-validator": "^4.0.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "canvas": "^2.5.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true, + "peer": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", + "dev": true, + "optional": true + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/keytar": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/keytar/-/keytar-7.9.0.tgz", + "integrity": "sha512-VPD8mtVtm5JNtA2AErl6Chp06JBfy7diFQ7TQQhdpWOl6MrCRB+eRbvAZUsbGQS9kiMq0coJsy0W0vHpDCkWsQ==", + "hasInstallScript": true, + "dependencies": { + "node-addon-api": "^4.3.0", + "prebuild-install": "^7.0.1" + } + }, + "node_modules/keytar/node_modules/node-addon-api": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-4.3.0.tgz", + "integrity": "sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==" + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/lazy-val": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/lazy-val/-/lazy-val-1.0.5.tgz", + "integrity": "sha512-0/BnGCCfyUMkBpeDgWihanIAF9JmZhHBgUhEqzvf+adhNGLoP6TaiI5oF8oyb3I45P+PcnrqihSf01M0l0G5+Q==", + "dev": true + }, + "node_modules/lazystream": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz", + "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==", + "dev": true, + "peer": true, + "dependencies": { + "readable-stream": "^2.0.5" + }, + "engines": { + "node": ">= 0.6.3" + } + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "peer": true + }, + "node_modules/loader-runner": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", + "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", + "dev": true, + "engines": { + "node": ">=6.11.5" + } + }, + "node_modules/loader-utils": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "node_modules/lodash.defaults": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", + "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==", + "dev": true, + "peer": true + }, + "node_modules/lodash.difference": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz", + "integrity": "sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA==", + "dev": true, + "peer": true + }, + "node_modules/lodash.flatten": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", + "integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==", + "dev": true, + "peer": true + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "dev": true, + "peer": true + }, + "node_modules/lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", + "dev": true + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/lodash.union": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz", + "integrity": "sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw==", + "dev": true, + "peer": true + }, + "node_modules/lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "peer": true, + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dev": true, + "peer": true, + "dependencies": { + "tmpl": "1.0.5" + } + }, + "node_modules/matcher": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz", + "integrity": "sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==", + "dev": true, + "optional": true, + "dependencies": { + "escape-string-regexp": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "dev": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" + }, + "node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "dev": true, + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minizlib/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==" + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/napi-build-utils": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", + "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==" + }, + "node_modules/native-ext-loader": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/native-ext-loader/-/native-ext-loader-2.3.0.tgz", + "integrity": "sha512-awBQEfD25+YLi1voefLbjHiUt0yfm3Z4mBOLqbzFVR8ZOVv0MebeN9fhNiY0fxVEFiyBYtr/hF2VWfuMSCfFlw==", + "dev": true + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true + }, + "node_modules/node-abi": { + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.15.0.tgz", + "integrity": "sha512-Ic6z/j6I9RLm4ov7npo1I48UQr2BEyFCqh6p7S1dhEx9jPO0GPGq/e2Rb7x7DroQrmiVMz/Bw1vJm9sPAl2nxA==", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-addon-api": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-1.7.2.tgz", + "integrity": "sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg==", + "dev": true, + "optional": true + }, + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true, + "peer": true + }, + "node_modules/node-loader": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/node-loader/-/node-loader-2.0.0.tgz", + "integrity": "sha512-I5VN34NO4/5UYJaUBtkrODPWxbobrE4hgDqPrjB25yPkonFhCmZ146vTH+Zg417E9Iwoh1l/MbRs1apc5J295Q==", + "dev": true, + "dependencies": { + "loader-utils": "^2.0.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.0.0" + } + }, + "node_modules/node-releases": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", + "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", + "dev": true + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-url": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "peer": true, + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npmlog": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", + "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", + "dependencies": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "node_modules/number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/nwsapi": { + "version": "2.2.10", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.10.tgz", + "integrity": "sha512-QK0sRs7MKv0tKe1+5uZIQk/C8XGza4DAnztJG8iD+TpJIORARrCxczA738awHrZoHeTjSSoHqao2teO0dC/gFQ==", + "dev": true + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", + "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "optional": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "peer": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-cancelable": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", + "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "peer": true, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse5": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", + "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", + "dev": true, + "dependencies": { + "entities": "^4.4.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.2.tgz", + "integrity": "sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==", + "dev": true, + "engines": { + "node": "14 || >=16.14" + } + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", + "dev": true + }, + "node_modules/picocolors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", + "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", + "dev": true + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pirates": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", + "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", + "dev": true, + "peer": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/plist": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/plist/-/plist-3.1.0.tgz", + "integrity": "sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ==", + "dev": true, + "dependencies": { + "@xmldom/xmldom": "^0.8.8", + "base64-js": "^1.5.1", + "xmlbuilder": "^15.1.1" + }, + "engines": { + "node": ">=10.4.0" + } + }, + "node_modules/popmotion": { + "version": "8.7.6", + "resolved": "https://registry.npmjs.org/popmotion/-/popmotion-8.7.6.tgz", + "integrity": "sha512-gzU0mRAik8FIEOP4Nk5yqYptJIvHLoq/IRU+rANmKjDZ7tynAivYQ9cIJAxVaoS9h0zfXvN0cFBAg93ncmHHkA==", + "dependencies": { + "@popmotion/easing": "^1.0.1", + "@popmotion/popcorn": "^0.4.4", + "framesync": "^4.0.0", + "hey-listen": "^1.0.5", + "style-value-types": "^3.1.7", + "stylefire": "^7.0.1", + "tslib": "^1.10.0" + } + }, + "node_modules/prebuild-install": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.0.tgz", + "integrity": "sha512-CNcMgI1xBypOyGqjp3wOc8AAo1nMhZS3Cwd3iHIxOdAUbb+YxdNuM4Z5iIrZ8RLvOsf3F3bl7b7xGq6DjQoNYA==", + "dependencies": { + "detect-libc": "^2.0.0", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^1.0.1", + "node-abi": "^3.3.0", + "npmlog": "^4.0.1", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^4.0.0", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + }, + "bin": { + "prebuild-install": "bin.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "node_modules/progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/promise-retry": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", + "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", + "dev": true, + "dependencies": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, + "peer": true, + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/psl": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", + "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", + "dev": true + }, + "node_modules/pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/pure-rand": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", + "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "peer": true + }, + "node_modules/qs": { + "version": "6.12.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.12.1.tgz", + "integrity": "sha512-zWmv4RSuB9r2mYQw3zxQuHWeU+42aKi1wWig/j4ele4ygELZ7PEO6MM7rim9oAQH2A5MWfsAVf/jPvTPgCbvUQ==", + "dev": true, + "dependencies": { + "side-channel": "^1.0.6" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", + "dev": true + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true + }, + "node_modules/read-config-file": { + "version": "6.3.2", + "resolved": "https://registry.npmjs.org/read-config-file/-/read-config-file-6.3.2.tgz", + "integrity": "sha512-M80lpCjnE6Wt6zb98DoW8WHR09nzMSpu8XHtPkiTHrJ5Az9CybfeQhTJ8D7saeBHpGhLPIVyA8lcL6ZmdKwY6Q==", + "dev": true, + "dependencies": { + "config-file-ts": "^0.2.4", + "dotenv": "^9.0.2", + "dotenv-expand": "^5.1.0", + "js-yaml": "^4.1.0", + "json5": "^2.2.0", + "lazy-val": "^1.0.4" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/readdir-glob": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.3.tgz", + "integrity": "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==", + "dev": true, + "peer": true, + "dependencies": { + "minimatch": "^5.1.0" + } + }, + "node_modules/readdir-glob/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "peer": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/readdir-glob/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "peer": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/rechoir": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.1.tgz", + "integrity": "sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg==", + "dev": true, + "dependencies": { + "resolve": "^1.9.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "dev": true + }, + "node_modules/resolve": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz", + "integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==", + "dev": true, + "dependencies": { + "is-core-module": "^2.1.0", + "path-parse": "^1.0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-alpn": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", + "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==", + "dev": true + }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-cwd/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve.exports": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", + "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==", + "dev": true, + "peer": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/responselike": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz", + "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==", + "dev": true, + "dependencies": { + "lowercase-keys": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/roarr": { + "version": "2.15.4", + "resolved": "https://registry.npmjs.org/roarr/-/roarr-2.15.4.tgz", + "integrity": "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==", + "dev": true, + "optional": true, + "dependencies": { + "boolean": "^3.0.1", + "detect-node": "^2.0.4", + "globalthis": "^1.0.1", + "json-stringify-safe": "^5.0.1", + "semver-compare": "^1.0.0", + "sprintf-js": "^1.1.2" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/run-script-os": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/run-script-os/-/run-script-os-1.1.6.tgz", + "integrity": "sha512-ql6P2LzhBTTDfzKts+Qo4H94VUKpxKDFz6QxxwaUZN0mwvi7L3lpOI7BqPCq7lgDh3XLl0dpeXwfcVIitlrYrw==", + "dev": true, + "bin": { + "run-os": "index.js", + "run-script-os": "index.js" + } + }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "node_modules/sanitize-filename": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/sanitize-filename/-/sanitize-filename-1.6.3.tgz", + "integrity": "sha512-y/52Mcy7aw3gRm7IrcGDFx/bCk4AhRh2eI9luHOQM86nZsqwiRkkq2GekHXBBD+SmPidc8i2PqtYZl+pWJ8Oeg==", + "dev": true, + "dependencies": { + "truncate-utf8-bytes": "^1.0.0" + } + }, + "node_modules/sax": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.3.0.tgz", + "integrity": "sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA==", + "dev": true + }, + "node_modules/saxes": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "dev": true, + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=v12.22.7" + } + }, + "node_modules/schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/semver": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", + "integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==", + "dev": true, + "optional": true + }, + "node_modules/serialize-error": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz", + "integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==", + "dev": true, + "optional": true, + "dependencies": { + "type-fest": "^0.13.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/serialize-javascript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "dev": true, + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "dev": true, + "dependencies": { + "kind-of": "^6.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", + "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "object-inspect": "^1.13.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" + }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/simple-get": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "node_modules/simple-update-notifier": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", + "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", + "dev": true, + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true, + "peer": true + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/slice-ansi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", + "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", + "dev": true, + "optional": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "dev": true, + "optional": true, + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/sourcemap-codec": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", + "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", + "dev": true + }, + "node_modules/sprintf-js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", + "dev": true, + "optional": true + }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/stack-utils/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/stat-mode": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/stat-mode/-/stat-mode-1.0.0.tgz", + "integrity": "sha512-jH9EhtKIjuXZ2cWxmXS8ZP80XyC3iasQxMDV8jzhNJpfDb7VbQLVW4Wvsxz9QZvzV+G4YoSfBUVKDOyxLzi/sg==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dev": true, + "peer": true, + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/style-value-types": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/style-value-types/-/style-value-types-3.2.0.tgz", + "integrity": "sha512-ih0mGsrYYmVvdDi++/66O6BaQPRPRMQHoZevNNdMMcPlP/cH28Rnfsqf1UEba/Bwfuw9T8BmIMwbGdzsPwQKrQ==", + "dependencies": { + "hey-listen": "^1.0.8", + "tslib": "^1.10.0" + } + }, + "node_modules/stylefire": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/stylefire/-/stylefire-7.0.3.tgz", + "integrity": "sha512-Q0l7NSeFz/OkX+o6/7Zg3VZxSAZeQzQpYomWmIpOehFM/rJNMSLVX5fgg6Q48ut2ETNKwdhm97mPNU643EBCoQ==", + "dependencies": { + "@popmotion/popcorn": "^0.4.4", + "framesync": "^4.0.0", + "hey-listen": "^1.0.8", + "style-value-types": "^3.1.7", + "tslib": "^1.10.0" + } + }, + "node_modules/sumchecker": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-3.0.1.tgz", + "integrity": "sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg==", + "dev": true, + "dependencies": { + "debug": "^4.1.0" + }, + "engines": { + "node": ">= 8.0" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "peer": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true + }, + "node_modules/tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/tar": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", + "dev": true, + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tar-fs": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", + "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tar-stream/node_modules/readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/tar/node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/temp-file": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/temp-file/-/temp-file-3.4.0.tgz", + "integrity": "sha512-C5tjlC/HCtVUOi3KWVokd4vHVViOmGjtLwIh4MuzPo/nMYTV/p1urt3RnMz2IWXDdKEGJH3k5+KPxtqRsUYGtg==", + "dev": true, + "dependencies": { + "async-exit-hook": "^2.0.1", + "fs-extra": "^10.0.0" + } + }, + "node_modules/temp-file/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/temp-file/node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/temp-file/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/terser": { + "version": "5.31.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.31.0.tgz", + "integrity": "sha512-Q1JFAoUKE5IMfI4Z/lkE/E6+SwgzO+x4tq4v1AyBLRj8VSYvRO6A/rQrPg1yud4g0En9EKI1TvFRF2tQFcoUkg==", + "dev": true, + "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.8.2", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser-webpack-plugin": { + "version": "5.3.10", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz", + "integrity": "sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.20", + "jest-worker": "^27.4.5", + "schema-utils": "^3.1.1", + "serialize-javascript": "^6.0.1", + "terser": "^5.26.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "uglify-js": { + "optional": true + } + } + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "peer": true, + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "node_modules/tmp": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", + "integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==", + "dev": true, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/tmp-promise": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/tmp-promise/-/tmp-promise-3.0.3.tgz", + "integrity": "sha512-RwM7MoPojPxsOBYnyd2hy0bxtIlVrihNs9pj5SUvY8Zz1sQcQG2tG1hSr8PDxfgEB8RNKDhqbIlroIarSNDNsQ==", + "dev": true, + "dependencies": { + "tmp": "^0.2.0" + } + }, + "node_modules/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "dev": true, + "peer": true + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "dev": true, + "peer": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/tough-cookie": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz", + "integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==", + "dev": true, + "dependencies": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.2.0", + "url-parse": "^1.5.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tough-cookie/node_modules/universalify": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", + "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", + "dev": true, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/tr46": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", + "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==", + "dev": true, + "dependencies": { + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/truncate-utf8-bytes": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz", + "integrity": "sha512-95Pu1QXQvruGEhv62XCMO3Mm90GscOCClvrIUwCM0PYOXK3kaF3l3sIHxx71ThJfcbM2O5Au6SO3AWCSEfW4mQ==", + "dev": true, + "dependencies": { + "utf8-byte-length": "^1.0.1" + } + }, + "node_modules/ts-jest": { + "version": "29.1.2", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.1.2.tgz", + "integrity": "sha512-br6GJoH/WUX4pu7FbZXuWGKGNDuU7b8Uj77g/Sp7puZV6EXzuByl6JrECvm0MzVzSTkSHWTihsXt+5XYER5b+g==", + "dev": true, + "dependencies": { + "bs-logger": "0.x", + "fast-json-stable-stringify": "2.x", + "jest-util": "^29.0.0", + "json5": "^2.2.3", + "lodash.memoize": "4.x", + "make-error": "1.x", + "semver": "^7.5.3", + "yargs-parser": "^21.0.1" + }, + "bin": { + "ts-jest": "cli.js" + }, + "engines": { + "node": "^16.10.0 || ^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.0 <8", + "@jest/types": "^29.0.0", + "babel-jest": "^29.0.0", + "jest": "^29.0.0", + "typescript": ">=4.3 <6" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "@jest/types": { + "optional": true + }, + "babel-jest": { + "optional": true + }, + "esbuild": { + "optional": true + } + } + }, + "node_modules/ts-loader": { + "version": "9.4.1", + "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.4.1.tgz", + "integrity": "sha512-384TYAqGs70rn9F0VBnh6BPTfhga7yFNdC5gXbQpDrBj9/KsT4iRkGqKXhziofHOlE2j6YEaiTYVGKKvPhGWvw==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "enhanced-resolve": "^5.0.0", + "micromatch": "^4.0.0", + "semver": "^7.3.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "typescript": "*", + "webpack": "^5.0.0" + } + }, + "node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "dependencies": { + "tslib": "^1.8.1" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + } + }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", + "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", + "dev": true, + "optional": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typescript": { + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.4.tgz", + "integrity": "sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/typestyle": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/typestyle/-/typestyle-2.4.0.tgz", + "integrity": "sha512-/d1BL6Qi+YlMLEydnUEB8KL/CAjAN8cyt3/UyGnOyBrWf7bLGcR/6yhmsaUstO2IcYwZfagjE7AIzuI2vUW9mg==", + "dependencies": { + "csstype": "3.0.10", + "free-style": "3.1.0" + } + }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true + }, + "node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.16.tgz", + "integrity": "sha512-KVbTxlBYlckhF5wgfyZXTWnMn7MMZjMu9XG8bPlliUOP9ThaF4QnhP8qrjrH7DRzHfSk0oQv1wToW+iA5GajEQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "escalade": "^3.1.2", + "picocolors": "^1.0.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "dev": true, + "dependencies": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "node_modules/utf8-byte-length": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/utf8-byte-length/-/utf8-byte-length-1.0.5.tgz", + "integrity": "sha512-Xn0w3MtiQ6zoz2vFyUVruaCL53O/DwUvkEeOvj+uulMm0BkUGYWmBYVyElqZaSLhY6ZD0ulfU3aBra2aVT4xfA==", + "dev": true + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/v8-to-istanbul": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.2.0.tgz", + "integrity": "sha512-/EH/sDgxU2eGxajKdwLCDmQ4FWq+kpi3uCmBGpw1xJtnAxEjlD8j8PEiGWpCIMIs3ciNAgH0d3TTJiUkYzyZjA==", + "dev": true, + "peer": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/verror": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.1.tgz", + "integrity": "sha512-veufcmxri4e3XSrT0xwfUR7kguIkaxBeosDg00yDWhk49wdwkSUrvvsm7nc75e1PUyvIeZj6nS8VQRYz2/S4Xg==", + "dev": true, + "optional": true, + "dependencies": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/verror/node_modules/core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", + "dev": true, + "optional": true + }, + "node_modules/w3c-xmlserializer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz", + "integrity": "sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw==", + "dev": true, + "dependencies": { + "xml-name-validator": "^4.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "dev": true, + "peer": true, + "dependencies": { + "makeerror": "1.0.12" + } + }, + "node_modules/watchpack": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.1.tgz", + "integrity": "sha512-8wrBCMtVhqcXP2Sup1ctSkga6uc2Bx0IIvKyT7yTFier5AXHooSI+QyQQAtTb7+E0IUCCKyTFmXqdqgum2XWGg==", + "dev": true, + "dependencies": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/webpack": { + "version": "5.91.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.91.0.tgz", + "integrity": "sha512-rzVwlLeBWHJbmgTC/8TvAcu5vpJNII+MelQpylD4jNERPwpBJOE2lEcko1zJX3QJeLjTTAnQxn/OJ8bjDzVQaw==", + "dev": true, + "dependencies": { + "@types/eslint-scope": "^3.7.3", + "@types/estree": "^1.0.5", + "@webassemblyjs/ast": "^1.12.1", + "@webassemblyjs/wasm-edit": "^1.12.1", + "@webassemblyjs/wasm-parser": "^1.12.1", + "acorn": "^8.7.1", + "acorn-import-assertions": "^1.9.0", + "browserslist": "^4.21.10", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.16.0", + "es-module-lexer": "^1.2.1", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.11", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.2.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.3.10", + "watchpack": "^2.4.1", + "webpack-sources": "^3.2.3" + }, + "bin": { + "webpack": "bin/webpack.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-cli": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.10.0.tgz", + "integrity": "sha512-NLhDfH/h4O6UOy+0LSso42xvYypClINuMNBVVzX4vX98TmTaTUxwRbXdhucbFMd2qLaCTcLq/PdYrvi8onw90w==", + "dev": true, + "dependencies": { + "@discoveryjs/json-ext": "^0.5.0", + "@webpack-cli/configtest": "^1.2.0", + "@webpack-cli/info": "^1.5.0", + "@webpack-cli/serve": "^1.7.0", + "colorette": "^2.0.14", + "commander": "^7.0.0", + "cross-spawn": "^7.0.3", + "fastest-levenshtein": "^1.0.12", + "import-local": "^3.0.2", + "interpret": "^2.2.0", + "rechoir": "^0.7.0", + "webpack-merge": "^5.7.3" + }, + "bin": { + "webpack-cli": "bin/cli.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "4.x.x || 5.x.x" + }, + "peerDependenciesMeta": { + "@webpack-cli/generators": { + "optional": true + }, + "@webpack-cli/migrate": { + "optional": true + }, + "webpack-bundle-analyzer": { + "optional": true + }, + "webpack-dev-server": { + "optional": true + } + } + }, + "node_modules/webpack-cli/node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/webpack-merge": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.8.0.tgz", + "integrity": "sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q==", + "dev": true, + "dependencies": { + "clone-deep": "^4.0.1", + "wildcard": "^2.0.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/webpack-sources": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", + "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", + "dev": true, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/whatwg-encoding": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", + "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==", + "dev": true, + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-mimetype": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz", + "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-url": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz", + "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==", + "dev": true, + "dependencies": { + "tr46": "^3.0.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, + "node_modules/wildcard": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz", + "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==", + "dev": true + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "node_modules/write-file-atomic": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", + "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", + "dev": true, + "peer": true, + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/ws": { + "version": "8.17.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.0.tgz", + "integrity": "sha512-uJq6108EgZMAl20KagGkzCKfMEjxmKvZHG7Tlq0Z6nOky7YF7aq4mOx6xK8TJ/i1LeK4Qus7INktacctDgY8Ow==", + "dev": true, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xml-name-validator": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz", + "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/xmlbuilder": { + "version": "15.1.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz", + "integrity": "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==", + "dev": true, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "dev": true, + "dependencies": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zip-stream": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-4.1.1.tgz", + "integrity": "sha512-9qv4rlDiopXg4E69k+vMHjNN63YFMe9sZMrdlvKnCjlCRWeCBswPPMPUfx+ipsAWq1LXHe70RcbaHdJJpS6hyQ==", + "dev": true, + "peer": true, + "dependencies": { + "archiver-utils": "^3.0.4", + "compress-commons": "^4.1.2", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/zip-stream/node_modules/archiver-utils": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-3.0.4.tgz", + "integrity": "sha512-KVgf4XQVrTjhyWmx6cte4RxonPLR9onExufI1jhvw/MQ4BB6IsZD5gT8Lq+u/+pRkWna/6JoHpiQioaqFP5Rzw==", + "dev": true, + "peer": true, + "dependencies": { + "glob": "^7.2.3", + "graceful-fs": "^4.2.0", + "lazystream": "^1.0.0", + "lodash.defaults": "^4.2.0", + "lodash.difference": "^4.5.0", + "lodash.flatten": "^4.4.0", + "lodash.isplainobject": "^4.0.6", + "lodash.union": "^4.6.0", + "normalize-path": "^3.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/zip-stream/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "peer": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + } + }, + "dependencies": { + "@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, + "peer": true, + "requires": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "@babel/code-frame": { + "version": "7.24.2", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.2.tgz", + "integrity": "sha512-y5+tLQyV8pg3fsiln67BVLD1P13Eg4lh5RW9mF0zUuvLrv9uIQ4MCL+CRT+FTsBlBjcIan6PGsLcBN0m3ClUyQ==", + "dev": true, + "requires": { + "@babel/highlight": "^7.24.2", + "picocolors": "^1.0.0" + } + }, + "@babel/compat-data": { + "version": "7.24.4", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.4.tgz", + "integrity": "sha512-vg8Gih2MLK+kOkHJp4gBEIkyaIi00jgWot2D9QOmmfLC8jINSOzmCLta6Bvz/JSBCqnegV0L80jhxkol5GWNfQ==", + "dev": true, + "peer": true + }, + "@babel/core": { + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.5.tgz", + "integrity": "sha512-tVQRucExLQ02Boi4vdPp49svNGcfL2GhdTCT9aldhXgCJVAI21EtRfBettiuLUwce/7r6bFdgs6JFkcdTiFttA==", + "dev": true, + "peer": true, + "requires": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.24.2", + "@babel/generator": "^7.24.5", + "@babel/helper-compilation-targets": "^7.23.6", + "@babel/helper-module-transforms": "^7.24.5", + "@babel/helpers": "^7.24.5", + "@babel/parser": "^7.24.5", + "@babel/template": "^7.24.0", + "@babel/traverse": "^7.24.5", + "@babel/types": "^7.24.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "dependencies": { + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "peer": true + } + } + }, + "@babel/generator": { + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.5.tgz", + "integrity": "sha512-x32i4hEXvr+iI0NEoEfDKzlemF8AmtOP8CcrRaEcpzysWuoEb1KknpcvMsHKPONoKZiDuItklgWhB18xEhr9PA==", + "dev": true, + "peer": true, + "requires": { + "@babel/types": "^7.24.5", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^2.5.1" + } + }, + "@babel/helper-compilation-targets": { + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz", + "integrity": "sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==", + "dev": true, + "peer": true, + "requires": { + "@babel/compat-data": "^7.23.5", + "@babel/helper-validator-option": "^7.23.5", + "browserslist": "^4.22.2", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "dependencies": { + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "peer": true, + "requires": { + "yallist": "^3.0.2" + } + }, + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "peer": true + }, + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "peer": true + } + } + }, + "@babel/helper-environment-visitor": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", + "dev": true, + "peer": true + }, + "@babel/helper-function-name": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", + "dev": true, + "peer": true, + "requires": { + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" + } + }, + "@babel/helper-hoist-variables": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", + "dev": true, + "peer": true, + "requires": { + "@babel/types": "^7.22.5" + } + }, + "@babel/helper-module-imports": { + "version": "7.24.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.3.tgz", + "integrity": "sha512-viKb0F9f2s0BCS22QSF308z/+1YWKV/76mwt61NBzS5izMzDPwdq1pTrzf+Li3npBWX9KdQbkeCt1jSAM7lZqg==", + "dev": true, + "peer": true, + "requires": { + "@babel/types": "^7.24.0" + } + }, + "@babel/helper-module-transforms": { + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.24.5.tgz", + "integrity": "sha512-9GxeY8c2d2mdQUP1Dye0ks3VDyIMS98kt/llQ2nUId8IsWqTF0l1LkSX0/uP7l7MCDrzXS009Hyhe2gzTiGW8A==", + "dev": true, + "peer": true, + "requires": { + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-module-imports": "^7.24.3", + "@babel/helper-simple-access": "^7.24.5", + "@babel/helper-split-export-declaration": "^7.24.5", + "@babel/helper-validator-identifier": "^7.24.5" + } + }, + "@babel/helper-plugin-utils": { + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.5.tgz", + "integrity": "sha512-xjNLDopRzW2o6ba0gKbkZq5YWEBaK3PCyTOY1K2P/O07LGMhMqlMXPxwN4S5/RhWuCobT8z0jrlKGlYmeR1OhQ==", + "dev": true, + "peer": true + }, + "@babel/helper-simple-access": { + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.24.5.tgz", + "integrity": "sha512-uH3Hmf5q5n7n8mz7arjUlDOCbttY/DW4DYhE6FUsjKJ/oYC1kQQUvwEQWxRwUpX9qQKRXeqLwWxrqilMrf32sQ==", + "dev": true, + "peer": true, + "requires": { + "@babel/types": "^7.24.5" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.5.tgz", + "integrity": "sha512-5CHncttXohrHk8GWOFCcCl4oRD9fKosWlIRgWm4ql9VYioKm52Mk2xsmoohvm7f3JoiLSM5ZgJuRaf5QZZYd3Q==", + "dev": true, + "peer": true, + "requires": { + "@babel/types": "^7.24.5" + } + }, + "@babel/helper-string-parser": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.1.tgz", + "integrity": "sha512-2ofRCjnnA9y+wk8b9IAREroeUP02KHp431N2mhKniy2yKIDKpbrHv9eXwm8cBeWQYcJmzv5qKCu65P47eCF7CQ==", + "dev": true, + "peer": true + }, + "@babel/helper-validator-identifier": { + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.5.tgz", + "integrity": "sha512-3q93SSKX2TWCG30M2G2kwaKeTYgEUp5Snjuj8qm729SObL6nbtUldAi37qbxkD5gg3xnBio+f9nqpSepGZMvxA==", + "dev": true + }, + "@babel/helper-validator-option": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz", + "integrity": "sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==", + "dev": true, + "peer": true + }, + "@babel/helpers": { + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.5.tgz", + "integrity": "sha512-CiQmBMMpMQHwM5m01YnrM6imUG1ebgYJ+fAIW4FZe6m4qHTPaRHti+R8cggAwkdz4oXhtO4/K9JWlh+8hIfR2Q==", + "dev": true, + "peer": true, + "requires": { + "@babel/template": "^7.24.0", + "@babel/traverse": "^7.24.5", + "@babel/types": "^7.24.5" + } + }, + "@babel/highlight": { + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.5.tgz", + "integrity": "sha512-8lLmua6AVh/8SLJRRVD6V8p73Hir9w5mJrhE+IPpILG31KKlI9iz5zmBYKcWPS59qSfgP9RaSBQSHHE81WKuEw==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.24.5", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" + }, + "dependencies": { + "ansi-styles": { + "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==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "@babel/parser": { + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.5.tgz", + "integrity": "sha512-EOv5IK8arwh3LI47dz1b0tKUb/1uhHAnHJOrjgtQMIpu1uXd9mlFrJg9IUgGUgZ41Ch0K8REPTYpO7B76b4vJg==", + "dev": true, + "peer": true + }, + "@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "peer": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, + "peer": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "peer": true, + "requires": { + "@babel/helper-plugin-utils": "^7.12.13" + } + }, + "@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "peer": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "peer": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-jsx": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.24.1.tgz", + "integrity": "sha512-2eCtxZXf+kbkMIsXS4poTvT4Yu5rXiRa+9xGVT56raghjmBTKMpFNc9R4IDiB4emao9eO22Ox7CxuJG7BgExqA==", + "dev": true, + "peer": true, + "requires": { + "@babel/helper-plugin-utils": "^7.24.0" + } + }, + "@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "peer": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "peer": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "peer": true, + "requires": { + "@babel/helper-plugin-utils": "^7.10.4" + } + }, + "@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "peer": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "peer": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "peer": true, + "requires": { + "@babel/helper-plugin-utils": "^7.8.0" + } + }, + "@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "peer": true, + "requires": { + "@babel/helper-plugin-utils": "^7.14.5" + } + }, + "@babel/plugin-syntax-typescript": { + "version": "7.24.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.24.1.tgz", + "integrity": "sha512-Yhnmvy5HZEnHUty6i++gcfH1/l68AHnItFHnaCv6hn9dNh0hQvvQJsxpi4BMBFN5DLeHBuucT/0DgzXif/OyRw==", + "dev": true, + "peer": true, + "requires": { + "@babel/helper-plugin-utils": "^7.24.0" + } + }, + "@babel/template": { + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.0.tgz", + "integrity": "sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA==", + "dev": true, + "peer": true, + "requires": { + "@babel/code-frame": "^7.23.5", + "@babel/parser": "^7.24.0", + "@babel/types": "^7.24.0" + } + }, + "@babel/traverse": { + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.5.tgz", + "integrity": "sha512-7aaBLeDQ4zYcUFDUD41lJc1fG8+5IU9DaNSJAgal866FGvmD5EbWQgnEC6kO1gGLsX0esNkfnJSndbTXA3r7UA==", + "dev": true, + "peer": true, + "requires": { + "@babel/code-frame": "^7.24.2", + "@babel/generator": "^7.24.5", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.24.5", + "@babel/parser": "^7.24.5", + "@babel/types": "^7.24.5", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, + "dependencies": { + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "peer": true + } + } + }, + "@babel/types": { + "version": "7.24.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.5.tgz", + "integrity": "sha512-6mQNsaLeXTw0nxYUYu+NSa4Hx4BlF1x1x8/PMFbiR+GBSr+2DkECc69b8hgy2frEodNcvPffeH8YfWd3LI6jhQ==", + "dev": true, + "peer": true, + "requires": { + "@babel/helper-string-parser": "^7.24.1", + "@babel/helper-validator-identifier": "^7.24.5", + "to-fast-properties": "^2.0.0" + } + }, + "@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true, + "peer": true + }, + "@develar/schema-utils": { + "version": "2.6.5", + "resolved": "https://registry.npmjs.org/@develar/schema-utils/-/schema-utils-2.6.5.tgz", + "integrity": "sha512-0cp4PsWQ/9avqTVMCtZ+GirikIA36ikvjtHweU4/j8yLtgObI0+JUPhYFScgwlteveGB1rt3Cm8UhN04XayDig==", + "dev": true, + "requires": { + "ajv": "^6.12.0", + "ajv-keywords": "^3.4.1" + } + }, + "@discoveryjs/json-ext": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", + "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", + "dev": true + }, + "@electron/asar": { + "version": "3.2.10", + "resolved": "https://registry.npmjs.org/@electron/asar/-/asar-3.2.10.tgz", + "integrity": "sha512-mvBSwIBUeiRscrCeJE1LwctAriBj65eUDm0Pc11iE5gRwzkmsdbS7FnZ1XUWjpSeQWL1L5g12Fc/SchPM9DUOw==", + "dev": true, + "requires": { + "commander": "^5.0.0", + "glob": "^7.1.6", + "minimatch": "^3.0.4" + }, + "dependencies": { + "commander": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", + "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", + "dev": true + } + } + }, + "@electron/get": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@electron/get/-/get-2.0.3.tgz", + "integrity": "sha512-Qkzpg2s9GnVV2I2BjRksUi43U5e6+zaQMcjoJy0C+C5oxaKl+fmckGDQFtRpZpZV0NQekuZZ+tGz7EA9TVnQtQ==", + "dev": true, + "requires": { + "debug": "^4.1.1", + "env-paths": "^2.2.0", + "fs-extra": "^8.1.0", + "global-agent": "^3.0.0", + "got": "^11.8.5", + "progress": "^2.0.3", + "semver": "^6.2.0", + "sumchecker": "^3.0.1" + }, + "dependencies": { + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true + } + } + }, + "@electron/notarize": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@electron/notarize/-/notarize-2.2.1.tgz", + "integrity": "sha512-aL+bFMIkpR0cmmj5Zgy0LMKEpgy43/hw5zadEArgmAMWWlKc5buwFvFT9G/o/YJkvXAJm5q3iuTuLaiaXW39sg==", + "dev": true, + "requires": { + "debug": "^4.1.1", + "fs-extra": "^9.0.1", + "promise-retry": "^2.0.1" + }, + "dependencies": { + "fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "requires": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, + "jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^2.0.0" + } + }, + "universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true + } + } + }, + "@electron/osx-sign": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@electron/osx-sign/-/osx-sign-1.0.5.tgz", + "integrity": "sha512-k9ZzUQtamSoweGQDV2jILiRIHUu7lYlJ3c6IEmjv1hC17rclE+eb9U+f6UFlOOETo0JzY1HNlXy4YOlCvl+Lww==", + "dev": true, + "requires": { + "compare-version": "^0.1.2", + "debug": "^4.3.4", + "fs-extra": "^10.0.0", + "isbinaryfile": "^4.0.8", + "minimist": "^1.2.6", + "plist": "^3.0.5" + }, + "dependencies": { + "fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, + "isbinaryfile": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.10.tgz", + "integrity": "sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==", + "dev": true + }, + "jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^2.0.0" + } + }, + "universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true + } + } + }, + "@electron/universal": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@electron/universal/-/universal-1.5.1.tgz", + "integrity": "sha512-kbgXxyEauPJiQQUNG2VgUeyfQNFk6hBF11ISN2PNI6agUgPl55pv4eQmaqHzTAzchBvqZ2tQuRVaPStGf0mxGw==", + "dev": true, + "requires": { + "@electron/asar": "^3.2.1", + "@malept/cross-spawn-promise": "^1.1.0", + "debug": "^4.3.1", + "dir-compare": "^3.0.0", + "fs-extra": "^9.0.1", + "minimatch": "^3.0.4", + "plist": "^3.0.4" + }, + "dependencies": { + "fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "requires": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, + "jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^2.0.0" + } + }, + "universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true + } + } + }, + "@eslint/eslintrc": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.3.tgz", + "integrity": "sha512-uj3pT6Mg+3t39fvLrj8iuCIJ38zKO9FpGtJ4BBJebJhEwjoT+KLVNCcHT5QC9NGRIEi7fZ0ZR8YRb884auB4Lg==", + "dev": true, + "requires": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.4.0", + "globals": "^13.15.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "dependencies": { + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true + } + } + }, + "@humanwhocodes/config-array": { + "version": "0.11.7", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.7.tgz", + "integrity": "sha512-kBbPWzN8oVMLb0hOUYXhmxggL/1cJE6ydvjDIGi9EnAGUyA7cLVKQg+d/Dsm+KZwx2czGHrCmMVLiyg8s5JPKw==", + "dev": true, + "requires": { + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.5" + } + }, + "@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true + }, + "@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true + }, + "@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "requires": { + "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" + }, + "dependencies": { + "ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true + }, + "ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true + }, + "emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "requires": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + } + }, + "strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "requires": { + "ansi-regex": "^6.0.1" + } + }, + "wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "requires": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + } + } + } + }, + "@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "peer": true, + "requires": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "dependencies": { + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "peer": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "peer": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "peer": true + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true, + "peer": true + } + } + }, + "@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "peer": true + }, + "@jest/console": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", + "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", + "dev": true, + "peer": true, + "requires": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0" + } + }, + "@jest/core": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", + "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", + "dev": true, + "peer": true, + "requires": { + "@jest/console": "^29.7.0", + "@jest/reporters": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-changed-files": "^29.7.0", + "jest-config": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-resolve-dependencies": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "jest-watcher": "^29.7.0", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + } + }, + "@jest/environment": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", + "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", + "dev": true, + "requires": { + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0" + } + }, + "@jest/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", + "dev": true, + "peer": true, + "requires": { + "expect": "^29.7.0", + "jest-snapshot": "^29.7.0" + } + }, + "@jest/expect-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", + "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", + "dev": true, + "requires": { + "jest-get-type": "^29.6.3" + } + }, + "@jest/fake-timers": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", + "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", + "dev": true, + "requires": { + "@jest/types": "^29.6.3", + "@sinonjs/fake-timers": "^10.0.2", + "@types/node": "*", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + } + }, + "@jest/globals": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", + "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", + "dev": true, + "peer": true, + "requires": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/types": "^29.6.3", + "jest-mock": "^29.7.0" + } + }, + "@jest/reporters": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", + "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", + "dev": true, + "peer": true, + "requires": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^6.0.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "slash": "^3.0.0", + "string-length": "^4.0.1", + "strip-ansi": "^6.0.0", + "v8-to-istanbul": "^9.0.1" + }, + "dependencies": { + "jest-worker": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", + "dev": true, + "peer": true, + "requires": { + "@types/node": "*", + "jest-util": "^29.7.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + } + }, + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "peer": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dev": true, + "requires": { + "@sinclair/typebox": "^0.27.8" + } + }, + "@jest/source-map": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", + "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", + "dev": true, + "peer": true, + "requires": { + "@jridgewell/trace-mapping": "^0.3.18", + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9" + } + }, + "@jest/test-result": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", + "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", + "dev": true, + "peer": true, + "requires": { + "@jest/console": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + } + }, + "@jest/test-sequencer": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", + "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", + "dev": true, + "peer": true, + "requires": { + "@jest/test-result": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "slash": "^3.0.0" + } + }, + "@jest/transform": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", + "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", + "dev": true, + "peer": true, + "requires": { + "@babel/core": "^7.11.6", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.2" + } + }, + "@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "requires": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + } + }, + "@jridgewell/gen-mapping": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", + "dev": true, + "requires": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true + }, + "@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "dev": true + }, + "@jridgewell/source-map": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", + "integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==", + "dev": true, + "requires": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" + } + }, + "@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true + }, + "@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "requires": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "@malept/cross-spawn-promise": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@malept/cross-spawn-promise/-/cross-spawn-promise-1.1.1.tgz", + "integrity": "sha512-RTBGWL5FWQcg9orDOCcp4LvItNzUPcyEU9bwaeJX0rJ1IQxzucC48Y0/sQLp/g6t99IQgAlGIaesJS+gTn7tVQ==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.1" + } + }, + "@malept/flatpak-bundler": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@malept/flatpak-bundler/-/flatpak-bundler-0.4.0.tgz", + "integrity": "sha512-9QOtNffcOF/c1seMCDnjckb3R9WHcG34tky+FHpNKKCW0wc/scYLwMtO+ptyGUfMW0/b/n4qRiALlaFHc9Oj7Q==", + "dev": true, + "requires": { + "debug": "^4.1.1", + "fs-extra": "^9.0.0", + "lodash": "^4.17.15", + "tmp-promise": "^3.0.2" + }, + "dependencies": { + "fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "requires": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, + "jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^2.0.0" + } + }, + "universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true + } + } + }, + "@marshallofsound/webpack-asset-relocator-loader": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@marshallofsound/webpack-asset-relocator-loader/-/webpack-asset-relocator-loader-0.5.0.tgz", + "integrity": "sha512-X50R97SiDNTpOckiplghBo63Vo8GxSsr98s3VTwEu3qyVr+TY4I91KRtKelEj2OAfgMnkTymw89+psFVq8aB1g==", + "dev": true, + "requires": { + "sourcemap-codec": "^1.4.4" + } + }, + "@microsoft/tsdoc": { + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.14.2.tgz", + "integrity": "sha512-9b8mPpKrfeGRuhFH5iO1iwCLeIIsV6+H1sRfxbkoGXIyQE2BTsPd9zqSqQJ+pv5sJ/hT5M1zvOFL02MnEezFug==", + "dev": true + }, + "@microsoft/tsdoc-config": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/@microsoft/tsdoc-config/-/tsdoc-config-0.16.2.tgz", + "integrity": "sha512-OGiIzzoBLgWWR0UdRJX98oYO+XKGf7tiK4Zk6tQ/E4IJqGCe7dvkTvgDZV5cFJUzLGDOjeAXrnZoA6QkVySuxw==", + "dev": true, + "requires": { + "@microsoft/tsdoc": "0.14.2", + "ajv": "~6.12.6", + "jju": "~1.4.0", + "resolve": "~1.19.0" + } + }, + "@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true + }, + "@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "requires": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + } + }, + "@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true + }, + "@popmotion/easing": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@popmotion/easing/-/easing-1.0.2.tgz", + "integrity": "sha512-IkdW0TNmRnWTeWI7aGQIVDbKXPWHVEYdGgd5ZR4SH/Ty/61p63jCjrPxX1XrR7IGkl08bjhJROStD7j+RKgoIw==" + }, + "@popmotion/popcorn": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/@popmotion/popcorn/-/popcorn-0.4.4.tgz", + "integrity": "sha512-jYO/8319fKoNLMlY4ZJPiPu8Ea8occYwRZhxpaNn/kZsK4QG2E7XFlXZMJBsTWDw7I1i0uaqyC4zn1nwEezLzg==", + "requires": { + "@popmotion/easing": "^1.0.1", + "framesync": "^4.0.1", + "hey-listen": "^1.0.8", + "style-value-types": "^3.1.7", + "tslib": "^1.10.0" + } + }, + "@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "dev": true + }, + "@sindresorhus/is": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", + "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", + "dev": true + }, + "@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "dev": true, + "requires": { + "type-detect": "4.0.8" + } + }, + "@sinonjs/fake-timers": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", + "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", + "dev": true, + "requires": { + "@sinonjs/commons": "^3.0.0" + } + }, + "@szmarczak/http-timer": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", + "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==", + "dev": true, + "requires": { + "defer-to-connect": "^2.0.0" + } + }, + "@tootallnate/once": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", + "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", + "dev": true + }, + "@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "peer": true, + "requires": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "@types/babel__generator": { + "version": "7.6.8", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", + "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", + "dev": true, + "peer": true, + "requires": { + "@babel/types": "^7.0.0" + } + }, + "@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "peer": true, + "requires": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "@types/babel__traverse": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.5.tgz", + "integrity": "sha512-WXCyOcRtH37HAUkpXhUduaxdm82b4GSlyTqajXviN4EfiuPgNYR109xMCKvpl6zPIpua0DGlMEDCq+g8EdoheQ==", + "dev": true, + "peer": true, + "requires": { + "@babel/types": "^7.20.7" + } + }, + "@types/cacheable-request": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz", + "integrity": "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==", + "dev": true, + "requires": { + "@types/http-cache-semantics": "*", + "@types/keyv": "^3.1.4", + "@types/node": "*", + "@types/responselike": "^1.0.0" + } + }, + "@types/debug": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", + "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", + "dev": true, + "requires": { + "@types/ms": "*" + } + }, + "@types/eslint": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.2.tgz", + "integrity": "sha512-Z1nseZON+GEnFjJc04sv4NSALGjhFwy6K0HXt7qsn5ArfAKtb63dXNJHf+1YW6IpOIYRBGUbu3GwJdj8DGnCjA==", + "dev": true, + "requires": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "@types/eslint-scope": { + "version": "3.7.3", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.3.tgz", + "integrity": "sha512-PB3ldyrcnAicT35TWPs5IcwKD8S333HMaa2VVv4+wdvebJkjWuW/xESoB8IwRcog8HYVYamb1g/R31Qv5Bx03g==", + "dev": true, + "requires": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, + "@types/estree": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", + "dev": true + }, + "@types/fs-extra": { + "version": "9.0.13", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.13.tgz", + "integrity": "sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/graceful-fs": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", + "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", + "dev": true, + "peer": true, + "requires": { + "@types/node": "*" + } + }, + "@types/http-cache-semantics": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz", + "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==", + "dev": true + }, + "@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "dev": true + }, + "@types/istanbul-lib-report": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "*" + } + }, + "@types/istanbul-reports": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "dev": true, + "requires": { + "@types/istanbul-lib-report": "*" + } + }, + "@types/jest": { + "version": "29.5.12", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.12.tgz", + "integrity": "sha512-eDC8bTvT/QhYdxJAulQikueigY5AsdBRH2yDKW3yveW7svY3+DzN84/2NUgkw10RTiJbWqZrTtoGVdYlvFJdLw==", + "dev": true, + "requires": { + "expect": "^29.0.0", + "pretty-format": "^29.0.0" + } + }, + "@types/jsdom": { + "version": "20.0.1", + "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-20.0.1.tgz", + "integrity": "sha512-d0r18sZPmMQr1eG35u12FZfhIXNrnsPU/g5wvRKCUf/tOGilKKwYMYGqh33BNR6ba+2gkHw1EUiHoN3mn7E5IQ==", + "dev": true, + "requires": { + "@types/node": "*", + "@types/tough-cookie": "*", + "parse5": "^7.0.0" + } + }, + "@types/json-schema": { + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", + "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", + "dev": true + }, + "@types/keyv": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", + "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/ms": { + "version": "0.7.34", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.34.tgz", + "integrity": "sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==", + "dev": true + }, + "@types/node": { + "version": "20.12.12", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.12.tgz", + "integrity": "sha512-eWLDGF/FOSPtAvEqeRAQ4C8LSA7M1I7i0ky1I8U7kD1J5ITyW3AsRhQrKVoWf5pFKZ2kILsEGJhsI9r93PYnOw==", + "dev": true, + "requires": { + "undici-types": "~5.26.4" + } + }, + "@types/plist": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/plist/-/plist-3.0.5.tgz", + "integrity": "sha512-E6OCaRmAe4WDmWNsL/9RMqdkkzDCY1etutkflWk4c+AcjDU07Pcz1fQwTX0TQz+Pxqn9i4L1TU3UFpjnrcDgxA==", + "dev": true, + "optional": true, + "requires": { + "@types/node": "*", + "xmlbuilder": ">=11.0.1" + } + }, + "@types/responselike": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.3.tgz", + "integrity": "sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/semver": { + "version": "7.3.13", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.13.tgz", + "integrity": "sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==", + "dev": true + }, + "@types/stack-utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", + "dev": true + }, + "@types/tough-cookie": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", + "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==", + "dev": true + }, + "@types/verror": { + "version": "1.10.10", + "resolved": "https://registry.npmjs.org/@types/verror/-/verror-1.10.10.tgz", + "integrity": "sha512-l4MM0Jppn18hb9xmM6wwD1uTdShpf9Pn80aXTStnK1C94gtPvJcV2FrDmbOQUAQfJ1cKZHktkQUDwEqaAKXMMg==", + "dev": true, + "optional": true + }, + "@types/yargs": { + "version": "17.0.13", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.13.tgz", + "integrity": "sha512-9sWaruZk2JGxIQU+IhI1fhPYRcQ0UuTNuKuCW9bR5fp7qi2Llf7WDzNa17Cy7TKnh3cdxDOiyTu6gaLS0eDatg==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "@types/yargs-parser": { + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", + "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", + "dev": true + }, + "@types/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw==", + "dev": true, + "optional": true, + "requires": { + "@types/node": "*" + } + }, + "@typescript-eslint/eslint-plugin": { + "version": "5.41.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.41.0.tgz", + "integrity": "sha512-DXUS22Y57/LAFSg3x7Vi6RNAuLpTXwxB9S2nIA7msBb/Zt8p7XqMwdpdc1IU7CkOQUPgAqR5fWvxuKCbneKGmA==", + "dev": true, + "requires": { + "@typescript-eslint/scope-manager": "5.41.0", + "@typescript-eslint/type-utils": "5.41.0", + "@typescript-eslint/utils": "5.41.0", + "debug": "^4.3.4", + "ignore": "^5.2.0", + "regexpp": "^3.2.0", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + } + }, + "@typescript-eslint/parser": { + "version": "5.41.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.41.0.tgz", + "integrity": "sha512-HQVfix4+RL5YRWZboMD1pUfFN8MpRH4laziWkkAzyO1fvNOY/uinZcvo3QiFJVS/siNHupV8E5+xSwQZrl6PZA==", + "dev": true, + "requires": { + "@typescript-eslint/scope-manager": "5.41.0", + "@typescript-eslint/types": "5.41.0", + "@typescript-eslint/typescript-estree": "5.41.0", + "debug": "^4.3.4" + } + }, + "@typescript-eslint/scope-manager": { + "version": "5.41.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.41.0.tgz", + "integrity": "sha512-xOxPJCnuktUkY2xoEZBKXO5DBCugFzjrVndKdUnyQr3+9aDWZReKq9MhaoVnbL+maVwWJu/N0SEtrtEUNb62QQ==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.41.0", + "@typescript-eslint/visitor-keys": "5.41.0" + } + }, + "@typescript-eslint/type-utils": { + "version": "5.41.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.41.0.tgz", + "integrity": "sha512-L30HNvIG6A1Q0R58e4hu4h+fZqaO909UcnnPbwKiN6Rc3BUEx6ez2wgN7aC0cBfcAjZfwkzE+E2PQQ9nEuoqfA==", + "dev": true, + "requires": { + "@typescript-eslint/typescript-estree": "5.41.0", + "@typescript-eslint/utils": "5.41.0", + "debug": "^4.3.4", + "tsutils": "^3.21.0" + } + }, + "@typescript-eslint/types": { + "version": "5.41.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.41.0.tgz", + "integrity": "sha512-5BejraMXMC+2UjefDvrH0Fo/eLwZRV6859SXRg+FgbhA0R0l6lDqDGAQYhKbXhPN2ofk2kY5sgGyLNL907UXpA==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "5.41.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.41.0.tgz", + "integrity": "sha512-SlzFYRwFSvswzDSQ/zPkIWcHv8O5y42YUskko9c4ki+fV6HATsTODUPbRbcGDFYP86gaJL5xohUEytvyNNcXWg==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.41.0", + "@typescript-eslint/visitor-keys": "5.41.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + } + }, + "@typescript-eslint/utils": { + "version": "5.41.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.41.0.tgz", + "integrity": "sha512-QlvfwaN9jaMga9EBazQ+5DDx/4sAdqDkcs05AsQHMaopluVCUyu1bTRUVKzXbgjDlrRAQrYVoi/sXJ9fmG+KLQ==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.9", + "@types/semver": "^7.3.12", + "@typescript-eslint/scope-manager": "5.41.0", + "@typescript-eslint/types": "5.41.0", + "@typescript-eslint/typescript-estree": "5.41.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0", + "semver": "^7.3.7" + } + }, + "@typescript-eslint/visitor-keys": { + "version": "5.41.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.41.0.tgz", + "integrity": "sha512-vilqeHj267v8uzzakbm13HkPMl7cbYpKVjgFWZPIOHIJHZtinvypUhJ5xBXfWYg4eFKqztbMMpOgFpT9Gfx4fw==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.41.0", + "eslint-visitor-keys": "^3.3.0" + } + }, + "@webassemblyjs/ast": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ast/-/ast-1.12.1.tgz", + "integrity": "sha512-EKfMUOPRRUTy5UII4qJDGPpqfwjOmZ5jeGFwid9mnoqIFK+e0vqoi1qH56JpmZSzEL53jKnNzScdmftJyG5xWg==", + "dev": true, + "requires": { + "@webassemblyjs/helper-numbers": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6" + } + }, + "@webassemblyjs/floating-point-hex-parser": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz", + "integrity": "sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==", + "dev": true + }, + "@webassemblyjs/helper-api-error": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz", + "integrity": "sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==", + "dev": true + }, + "@webassemblyjs/helper-buffer": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-buffer/-/helper-buffer-1.12.1.tgz", + "integrity": "sha512-nzJwQw99DNDKr9BVCOZcLuJJUlqkJh+kVzVl6Fmq/tI5ZtEyWT1KZMyOXltXLZJmDtvLCDgwsyrkohEtopTXCw==", + "dev": true + }, + "@webassemblyjs/helper-numbers": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz", + "integrity": "sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==", + "dev": true, + "requires": { + "@webassemblyjs/floating-point-hex-parser": "1.11.6", + "@webassemblyjs/helper-api-error": "1.11.6", + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/helper-wasm-bytecode": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz", + "integrity": "sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==", + "dev": true + }, + "@webassemblyjs/helper-wasm-section": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.12.1.tgz", + "integrity": "sha512-Jif4vfB6FJlUlSbgEMHUyk1j234GTNG9dBJ4XJdOySoj518Xj0oGsNi59cUQF4RRMS9ouBUxDDdyBVfPTypa5g==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-buffer": "1.12.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/wasm-gen": "1.12.1" + } + }, + "@webassemblyjs/ieee754": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz", + "integrity": "sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==", + "dev": true, + "requires": { + "@xtuc/ieee754": "^1.2.0" + } + }, + "@webassemblyjs/leb128": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.6.tgz", + "integrity": "sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==", + "dev": true, + "requires": { + "@xtuc/long": "4.2.2" + } + }, + "@webassemblyjs/utf8": { + "version": "1.11.6", + "resolved": "https://registry.npmjs.org/@webassemblyjs/utf8/-/utf8-1.11.6.tgz", + "integrity": "sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==", + "dev": true + }, + "@webassemblyjs/wasm-edit": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-edit/-/wasm-edit-1.12.1.tgz", + "integrity": "sha512-1DuwbVvADvS5mGnXbE+c9NfA8QRcZ6iKquqjjmR10k6o+zzsRVesil54DKexiowcFCPdr/Q0qaMgB01+SQ1u6g==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-buffer": "1.12.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/helper-wasm-section": "1.12.1", + "@webassemblyjs/wasm-gen": "1.12.1", + "@webassemblyjs/wasm-opt": "1.12.1", + "@webassemblyjs/wasm-parser": "1.12.1", + "@webassemblyjs/wast-printer": "1.12.1" + } + }, + "@webassemblyjs/wasm-gen": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-gen/-/wasm-gen-1.12.1.tgz", + "integrity": "sha512-TDq4Ojh9fcohAw6OIMXqiIcTq5KUXTGRkVxbSo1hQnSy6lAM5GSdfwWeSxpAo0YzgsgF182E/U0mDNhuA0tW7w==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/ieee754": "1.11.6", + "@webassemblyjs/leb128": "1.11.6", + "@webassemblyjs/utf8": "1.11.6" + } + }, + "@webassemblyjs/wasm-opt": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-opt/-/wasm-opt-1.12.1.tgz", + "integrity": "sha512-Jg99j/2gG2iaz3hijw857AVYekZe2SAskcqlWIZXjji5WStnOpVoat3gQfT/Q5tb2djnCjBtMocY/Su1GfxPBg==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-buffer": "1.12.1", + "@webassemblyjs/wasm-gen": "1.12.1", + "@webassemblyjs/wasm-parser": "1.12.1" + } + }, + "@webassemblyjs/wasm-parser": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wasm-parser/-/wasm-parser-1.12.1.tgz", + "integrity": "sha512-xikIi7c2FHXysxXe3COrVUPSheuBtpcfhbpFj4gmu7KRLYOzANztwUU0IbsqvMqzuNK2+glRGWCEqZo1WCLyAQ==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.12.1", + "@webassemblyjs/helper-api-error": "1.11.6", + "@webassemblyjs/helper-wasm-bytecode": "1.11.6", + "@webassemblyjs/ieee754": "1.11.6", + "@webassemblyjs/leb128": "1.11.6", + "@webassemblyjs/utf8": "1.11.6" + } + }, + "@webassemblyjs/wast-printer": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/@webassemblyjs/wast-printer/-/wast-printer-1.12.1.tgz", + "integrity": "sha512-+X4WAlOisVWQMikjbcvY2e0rwPsKQ9F688lksZhBcPycBBuii3O7m8FACbDMWDojpAqvjIncrG8J0XHKyQfVeA==", + "dev": true, + "requires": { + "@webassemblyjs/ast": "1.12.1", + "@xtuc/long": "4.2.2" + } + }, + "@webpack-cli/configtest": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.2.0.tgz", + "integrity": "sha512-4FB8Tj6xyVkyqjj1OaTqCjXYULB9FMkqQ8yGrZjRDrYh0nOE+7Lhs45WioWQQMV+ceFlE368Ukhe6xdvJM9Egg==", + "dev": true, + "requires": {} + }, + "@webpack-cli/info": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-1.5.0.tgz", + "integrity": "sha512-e8tSXZpw2hPl2uMJY6fsMswaok5FdlGNRTktvFk2sD8RjH0hE2+XistawJx1vmKteh4NmGmNUrp+Tb2w+udPcQ==", + "dev": true, + "requires": { + "envinfo": "^7.7.3" + } + }, + "@webpack-cli/serve": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.7.0.tgz", + "integrity": "sha512-oxnCNGj88fL+xzV+dacXs44HcDwf1ovs3AuEzvP7mqXw7fQntqIhQ1BRmynh4qEKQSSSRSWVyXRjmTbZIX9V2Q==", + "dev": true, + "requires": {} + }, + "@xmldom/xmldom": { + "version": "0.8.10", + "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.10.tgz", + "integrity": "sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==", + "dev": true + }, + "@xtuc/ieee754": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", + "integrity": "sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==", + "dev": true + }, + "@xtuc/long": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz", + "integrity": "sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==", + "dev": true + }, + "7zip-bin": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/7zip-bin/-/7zip-bin-5.2.0.tgz", + "integrity": "sha512-ukTPVhqG4jNzMro2qA9HSCSSVJN3aN7tlb+hfqYCt3ER0yWroeA2VR38MNrOHLQ/cVj+DaIMad0kFCtWWowh/A==", + "dev": true + }, + "abab": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", + "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", + "dev": true + }, + "acorn": { + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", + "dev": true + }, + "acorn-globals": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-7.0.1.tgz", + "integrity": "sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q==", + "dev": true, + "requires": { + "acorn": "^8.1.0", + "acorn-walk": "^8.0.2" + } + }, + "acorn-import-assertions": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz", + "integrity": "sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==", + "dev": true, + "requires": {} + }, + "acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "requires": {} + }, + "acorn-walk": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz", + "integrity": "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==", + "dev": true + }, + "aflon": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/aflon/-/aflon-2.0.10.tgz", + "integrity": "sha512-jPpwapiNyBuWfTmTLJcd0HakzSFO/hGUxwNfZgo223KvKg7iRrFosWdqvr9ejbFxome66rcktp0VCD8Bw8DClQ==", + "requires": { + "popmotion": "^8.7.5", + "stylefire": "^7.0.3", + "typestyle": "^2.1.0" + } + }, + "agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "requires": { + "debug": "4" + } + }, + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "requires": {} + }, + "ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "peer": true, + "requires": { + "type-fest": "^0.21.3" + }, + "dependencies": { + "type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "peer": true + } + } + }, + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "peer": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "app-builder-bin": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/app-builder-bin/-/app-builder-bin-4.0.0.tgz", + "integrity": "sha512-xwdG0FJPQMe0M0UA4Tz0zEB8rBJTRA5a476ZawAqiBkMv16GRK5xpXThOjMaEOFnZ6zabejjG4J3da0SXG63KA==", + "dev": true + }, + "app-builder-lib": { + "version": "24.13.3", + "resolved": "https://registry.npmjs.org/app-builder-lib/-/app-builder-lib-24.13.3.tgz", + "integrity": "sha512-FAzX6IBit2POXYGnTCT8YHFO/lr5AapAII6zzhQO3Rw4cEDOgK+t1xhLc5tNcKlicTHlo9zxIwnYCX9X2DLkig==", + "dev": true, + "requires": { + "@develar/schema-utils": "~2.6.5", + "@electron/notarize": "2.2.1", + "@electron/osx-sign": "1.0.5", + "@electron/universal": "1.5.1", + "@malept/flatpak-bundler": "^0.4.0", + "@types/fs-extra": "9.0.13", + "async-exit-hook": "^2.0.1", + "bluebird-lst": "^1.0.9", + "builder-util": "24.13.1", + "builder-util-runtime": "9.2.4", + "chromium-pickle-js": "^0.2.0", + "debug": "^4.3.4", + "ejs": "^3.1.8", + "electron-publish": "24.13.1", + "form-data": "^4.0.0", + "fs-extra": "^10.1.0", + "hosted-git-info": "^4.1.0", + "is-ci": "^3.0.0", + "isbinaryfile": "^5.0.0", + "js-yaml": "^4.1.0", + "lazy-val": "^1.0.5", + "minimatch": "^5.1.1", + "read-config-file": "6.3.2", + "sanitize-filename": "^1.6.3", + "semver": "^7.3.8", + "tar": "^6.1.12", + "temp-file": "^3.4.0" + }, + "dependencies": { + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, + "jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^2.0.0" + } + }, + "minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + }, + "universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true + } + } + }, + "aproba": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==" + }, + "archiver": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/archiver/-/archiver-5.3.2.tgz", + "integrity": "sha512-+25nxyyznAXF7Nef3y0EbBeqmGZgeN/BxHX29Rs39djAfaFalmQ89SE6CWyDCHzGL0yt/ycBtNOmGTW0FyGWNw==", + "dev": true, + "peer": true, + "requires": { + "archiver-utils": "^2.1.0", + "async": "^3.2.4", + "buffer-crc32": "^0.2.1", + "readable-stream": "^3.6.0", + "readdir-glob": "^1.1.2", + "tar-stream": "^2.2.0", + "zip-stream": "^4.1.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "peer": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, + "archiver-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-2.1.0.tgz", + "integrity": "sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==", + "dev": true, + "peer": true, + "requires": { + "glob": "^7.1.4", + "graceful-fs": "^4.2.0", + "lazystream": "^1.0.0", + "lodash.defaults": "^4.2.0", + "lodash.difference": "^4.5.0", + "lodash.flatten": "^4.4.0", + "lodash.isplainobject": "^4.0.6", + "lodash.union": "^4.6.0", + "normalize-path": "^3.0.0", + "readable-stream": "^2.0.0" + } + }, + "are-we-there-yet": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.7.tgz", + "integrity": "sha512-nxwy40TuMiUGqMyRHgCSWZ9FM4VAoRP4xUYSTv5ImRog+h9yISPbVH7H8fASCIzYn9wlEv4zvFL7uKDMCFQm3g==", + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true + }, + "asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", + "dev": true + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", + "dev": true, + "optional": true + }, + "astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true, + "optional": true + }, + "async": { + "version": "3.2.5", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.5.tgz", + "integrity": "sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==", + "dev": true + }, + "async-exit-hook": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/async-exit-hook/-/async-exit-hook-2.0.1.tgz", + "integrity": "sha512-NW2cX8m1Q7KPA7a5M2ULQeZ2wR5qI5PAbw5L0UOMxdioVk9PMZ0h1TmyZEkPYrCvYjDlFICusOu1dlEKAAeXBw==", + "dev": true + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true + }, + "at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "dev": true + }, + "babel-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", + "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", + "dev": true, + "peer": true, + "requires": { + "@jest/transform": "^29.7.0", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^29.6.3", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + } + }, + "babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "dev": true, + "peer": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "dependencies": { + "istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "dev": true, + "peer": true, + "requires": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + } + }, + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "peer": true + } + } + }, + "babel-plugin-jest-hoist": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", + "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", + "dev": true, + "peer": true, + "requires": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.1.14", + "@types/babel__traverse": "^7.0.6" + } + }, + "babel-preset-current-node-syntax": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", + "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", + "dev": true, + "peer": true, + "requires": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.8.3", + "@babel/plugin-syntax-import-meta": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.8.3", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-top-level-await": "^7.8.3" + } + }, + "babel-preset-jest": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", + "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", + "dev": true, + "peer": true, + "requires": { + "babel-plugin-jest-hoist": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0" + } + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" + }, + "big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "dev": true + }, + "bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "requires": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, + "bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", + "dev": true + }, + "bluebird-lst": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/bluebird-lst/-/bluebird-lst-1.0.9.tgz", + "integrity": "sha512-7B1Rtx82hjnSD4PGLAjVWeYH3tHAcVUmChh85a3lltKQm6FresXh9ErQo6oAv6CqxttczC3/kEg8SY5NluPuUw==", + "dev": true, + "requires": { + "bluebird": "^3.5.5" + } + }, + "boolean": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.2.0.tgz", + "integrity": "sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==", + "dev": true, + "optional": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "browserslist": { + "version": "4.23.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz", + "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==", + "dev": true, + "requires": { + "caniuse-lite": "^1.0.30001587", + "electron-to-chromium": "^1.4.668", + "node-releases": "^2.0.14", + "update-browserslist-db": "^1.0.13" + } + }, + "bs-logger": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", + "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", + "dev": true, + "requires": { + "fast-json-stable-stringify": "2.x" + } + }, + "bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "peer": true, + "requires": { + "node-int64": "^0.4.0" + } + }, + "buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "dev": true + }, + "buffer-equal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-1.0.1.tgz", + "integrity": "sha512-QoV3ptgEaQpvVwbXdSO39iqPQTCxSF7A5U99AxbHYqUdCizL/lH2Z0A2y6nbZucxMEOtNyZfG2s6gsVugGpKkg==", + "dev": true + }, + "buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, + "builder-util": { + "version": "24.13.1", + "resolved": "https://registry.npmjs.org/builder-util/-/builder-util-24.13.1.tgz", + "integrity": "sha512-NhbCSIntruNDTOVI9fdXz0dihaqX2YuE1D6zZMrwiErzH4ELZHE6mdiB40wEgZNprDia+FghRFgKoAqMZRRjSA==", + "dev": true, + "requires": { + "@types/debug": "^4.1.6", + "7zip-bin": "~5.2.0", + "app-builder-bin": "4.0.0", + "bluebird-lst": "^1.0.9", + "builder-util-runtime": "9.2.4", + "chalk": "^4.1.2", + "cross-spawn": "^7.0.3", + "debug": "^4.3.4", + "fs-extra": "^10.1.0", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.1", + "is-ci": "^3.0.0", + "js-yaml": "^4.1.0", + "source-map-support": "^0.5.19", + "stat-mode": "^1.0.0", + "temp-file": "^3.4.0" + }, + "dependencies": { + "fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, + "jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^2.0.0" + } + }, + "universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true + } + } + }, + "builder-util-runtime": { + "version": "9.2.4", + "resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-9.2.4.tgz", + "integrity": "sha512-upp+biKpN/XZMLim7aguUyW8s0FUpDvOtK6sbanMFDAMBzpHDqdhgVYm6zc9HJ6nWo7u2Lxk60i2M6Jd3aiNrA==", + "dev": true, + "requires": { + "debug": "^4.3.4", + "sax": "^1.2.4" + } + }, + "cacheable-lookup": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", + "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==", + "dev": true + }, + "cacheable-request": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.4.tgz", + "integrity": "sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==", + "dev": true, + "requires": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^4.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^6.0.1", + "responselike": "^2.0.0" + } + }, + "call-bind": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "dev": true, + "requires": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" + } + }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "peer": true + }, + "caniuse-lite": { + "version": "1.0.30001618", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001618.tgz", + "integrity": "sha512-p407+D1tIkDvsEAPS22lJxLQQaG8OTBEqo0KhzfABGk0TU4juBNDSfH0hyAp/HRyx+M8L17z/ltyhxh27FTfQg==", + "dev": true + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true, + "peer": true + }, + "chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" + }, + "chrome-trace-event": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz", + "integrity": "sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==", + "dev": true + }, + "chromium-pickle-js": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/chromium-pickle-js/-/chromium-pickle-js-0.2.0.tgz", + "integrity": "sha512-1R5Fho+jBq0DDydt+/vHWj5KJNJCKdARKOCwZUen84I5BreWoLqRLANH1U87eJy1tiASPtMnGqJJq0ZsLoRPOw==", + "dev": true + }, + "ci-info": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.5.0.tgz", + "integrity": "sha512-yH4RezKOGlOhxkmhbeNuC4eYZKAUsEaGtBuBzDDP1eFUKiccDWzBABxBfOx31IDwDIXMTxWuwAxUGModvkbuVw==", + "dev": true + }, + "cjs-module-lexer": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.3.1.tgz", + "integrity": "sha512-a3KdPAANPbNE4ZUv9h6LckSl9zLsYOP4MBmhIPkRaeyybt+r4UghLvq+xw/YwUcC1gqylCkL4rdVs3Lwupjm4Q==", + "dev": true, + "peer": true + }, + "cli-truncate": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", + "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", + "dev": true, + "optional": true, + "requires": { + "slice-ansi": "^3.0.0", + "string-width": "^4.2.0" + } + }, + "cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + } + }, + "clone-deep": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", + "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4", + "kind-of": "^6.0.2", + "shallow-clone": "^3.0.0" + } + }, + "clone-response": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", + "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==", + "dev": true, + "requires": { + "mimic-response": "^1.0.0" + } + }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "dev": true, + "peer": true + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" + }, + "collect-v8-coverage": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", + "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", + "dev": true, + "peer": true + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "colorette": { + "version": "2.0.16", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.16.tgz", + "integrity": "sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g==", + "dev": true + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "compare-version": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/compare-version/-/compare-version-0.1.2.tgz", + "integrity": "sha512-pJDh5/4wrEnXX/VWRZvruAGHkzKdr46z11OlTPN+VrATlWWhSKewNCJ1futCO5C7eJB3nPMFZA1LeYtcFboZ2A==", + "dev": true + }, + "compress-commons": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-4.1.2.tgz", + "integrity": "sha512-D3uMHtGc/fcO1Gt1/L7i1e33VOvD4A9hfQLP+6ewd+BvG/gQ84Yh4oftEhAdjSMgBgwGL+jsppT7JYNpo6MHHg==", + "dev": true, + "peer": true, + "requires": { + "buffer-crc32": "^0.2.13", + "crc32-stream": "^4.0.2", + "normalize-path": "^3.0.0", + "readable-stream": "^3.6.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "peer": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "config-file-ts": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/config-file-ts/-/config-file-ts-0.2.6.tgz", + "integrity": "sha512-6boGVaglwblBgJqGyxm4+xCmEGcWgnWHSWHY5jad58awQhB6gftq0G8HbzU39YqCIYHMLAiL1yjwiZ36m/CL8w==", + "dev": true, + "requires": { + "glob": "^10.3.10", + "typescript": "^5.3.3" + }, + "dependencies": { + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "glob": { + "version": "10.3.16", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.16.tgz", + "integrity": "sha512-JDKXl1DiuuHJ6fVS2FXjownaavciiHNUU4mOvV/B793RLh05vZL1rcPnCSaOgv1hDT6RDlY7AB7ZUvFYAtPgAw==", + "dev": true, + "requires": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.1", + "minipass": "^7.0.4", + "path-scurry": "^1.11.0" + } + }, + "minimatch": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + }, + "minipass": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.1.tgz", + "integrity": "sha512-UZ7eQ+h8ywIRAW1hIEl2AqdwzJucU/Kp59+8kkZeSvafXhZjul247BvIJjEVFVeON6d7lM46XX1HXCduKAS8VA==", + "dev": true + }, + "typescript": { + "version": "5.4.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", + "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", + "dev": true + } + } + }, + "console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=" + }, + "convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "peer": true + }, + "core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + }, + "crc": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/crc/-/crc-3.8.0.tgz", + "integrity": "sha512-iX3mfgcTMIq3ZKLIsVFAbv7+Mc10kxabAGQb8HvjA1o3T1PIYprbakQ65d3I+2HGHt6nSKkM9PYjgoJO2KcFBQ==", + "dev": true, + "optional": true, + "requires": { + "buffer": "^5.1.0" + } + }, + "crc-32": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", + "dev": true, + "peer": true + }, + "crc32-stream": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-4.0.3.tgz", + "integrity": "sha512-NT7w2JVU7DFroFdYkeq8cywxrgjPHWkdX1wjpRQXPX5Asews3tA+Ght6lddQO5Mkumffp3X7GEqku3epj2toIw==", + "dev": true, + "peer": true, + "requires": { + "crc-32": "^1.2.0", + "readable-stream": "^3.4.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "peer": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, + "create-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", + "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", + "dev": true, + "peer": true, + "requires": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "prompts": "^2.0.1" + } + }, + "cross-env": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", + "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.1" + } + }, + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "cssom": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz", + "integrity": "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==", + "dev": true + }, + "cssstyle": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", + "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", + "dev": true, + "requires": { + "cssom": "~0.3.6" + }, + "dependencies": { + "cssom": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", + "dev": true + } + } + }, + "csstype": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.10.tgz", + "integrity": "sha512-2u44ZG2OcNUO9HDp/Jl8C07x6pU/eTR3ncV91SiK3dhG9TWvRVsCoJw14Ckx5DgWkzGA3waZWO3d7pgqpUI/XA==" + }, + "data-urls": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-3.0.2.tgz", + "integrity": "sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==", + "dev": true, + "requires": { + "abab": "^2.0.6", + "whatwg-mimetype": "^3.0.0", + "whatwg-url": "^11.0.0" + } + }, + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "decimal.js": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", + "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==", + "dev": true + }, + "decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "requires": { + "mimic-response": "^3.1.0" + }, + "dependencies": { + "mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==" + } + } + }, + "dedent": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.3.tgz", + "integrity": "sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ==", + "dev": true, + "peer": true, + "requires": {} + }, + "deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==" + }, + "deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "peer": true + }, + "defer-to-connect": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", + "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", + "dev": true + }, + "define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "requires": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + } + }, + "define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "optional": true, + "requires": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true + }, + "delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=" + }, + "detect-libc": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz", + "integrity": "sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==" + }, + "detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true, + "peer": true + }, + "detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", + "dev": true, + "optional": true + }, + "dezalgo": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz", + "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==", + "dev": true, + "requires": { + "asap": "^2.0.0", + "wrappy": "1" + } + }, + "diff-sequences": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", + "dev": true + }, + "dir-compare": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/dir-compare/-/dir-compare-3.3.0.tgz", + "integrity": "sha512-J7/et3WlGUCxjdnD3HAAzQ6nsnc0WL6DD7WcwJb7c39iH1+AWfg+9OqzJNaI6PkBwBvm1mhZNL9iY/nRiZXlPg==", + "dev": true, + "requires": { + "buffer-equal": "^1.0.0", + "minimatch": "^3.0.4" + } + }, + "dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "requires": { + "path-type": "^4.0.0" + } + }, + "dmg-builder": { + "version": "24.13.3", + "resolved": "https://registry.npmjs.org/dmg-builder/-/dmg-builder-24.13.3.tgz", + "integrity": "sha512-rcJUkMfnJpfCboZoOOPf4L29TRtEieHNOeAbYPWPxlaBw/Z1RKrRA86dOI9rwaI4tQSc/RD82zTNHprfUHXsoQ==", + "dev": true, + "requires": { + "app-builder-lib": "24.13.3", + "builder-util": "24.13.1", + "builder-util-runtime": "9.2.4", + "dmg-license": "^1.0.11", + "fs-extra": "^10.1.0", + "iconv-lite": "^0.6.2", + "js-yaml": "^4.1.0" + }, + "dependencies": { + "fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, + "jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^2.0.0" + } + }, + "universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true + } + } + }, + "dmg-license": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/dmg-license/-/dmg-license-1.0.11.tgz", + "integrity": "sha512-ZdzmqwKmECOWJpqefloC5OJy1+WZBBse5+MR88z9g9Zn4VY+WYUkAyojmhzJckH5YbbZGcYIuGAkY5/Ys5OM2Q==", + "dev": true, + "optional": true, + "requires": { + "@types/plist": "^3.0.1", + "@types/verror": "^1.10.3", + "ajv": "^6.10.0", + "crc": "^3.8.0", + "iconv-corefoundation": "^1.1.7", + "plist": "^3.0.4", + "smart-buffer": "^4.0.2", + "verror": "^1.10.0" + } + }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "domexception": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz", + "integrity": "sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==", + "dev": true, + "requires": { + "webidl-conversions": "^7.0.0" + } + }, + "dotenv": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-9.0.2.tgz", + "integrity": "sha512-I9OvvrHp4pIARv4+x9iuewrWycX6CcZtoAu1XrzPxc5UygMJXJZYmBsynku8IkrJwgypE5DGNjDPmPRhDCptUg==", + "dev": true + }, + "dotenv-expand": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz", + "integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==", + "dev": true + }, + "eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, + "ejs": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", + "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", + "dev": true, + "requires": { + "jake": "^10.8.5" + } + }, + "electron": { + "version": "30.0.9", + "resolved": "https://registry.npmjs.org/electron/-/electron-30.0.9.tgz", + "integrity": "sha512-ArxgdGHVu3o5uaP+Tqj8cJDvU03R6vrGrOqiMs7JXLnvQHMqXJIIxmFKQAIdJW8VoT3ac3hD21tA7cPO10RLow==", + "dev": true, + "requires": { + "@electron/get": "^2.0.0", + "@types/node": "^20.9.0", + "extract-zip": "^2.0.1" + } + }, + "electron-builder": { + "version": "24.13.3", + "resolved": "https://registry.npmjs.org/electron-builder/-/electron-builder-24.13.3.tgz", + "integrity": "sha512-yZSgVHft5dNVlo31qmJAe4BVKQfFdwpRw7sFp1iQglDRCDD6r22zfRJuZlhtB5gp9FHUxCMEoWGq10SkCnMAIg==", + "dev": true, + "requires": { + "app-builder-lib": "24.13.3", + "builder-util": "24.13.1", + "builder-util-runtime": "9.2.4", + "chalk": "^4.1.2", + "dmg-builder": "24.13.3", + "fs-extra": "^10.1.0", + "is-ci": "^3.0.0", + "lazy-val": "^1.0.5", + "read-config-file": "6.3.2", + "simple-update-notifier": "2.0.0", + "yargs": "^17.6.2" + }, + "dependencies": { + "fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, + "jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^2.0.0" + } + }, + "universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "dev": true + } + } + }, + "electron-builder-squirrel-windows": { + "version": "24.13.3", + "resolved": "https://registry.npmjs.org/electron-builder-squirrel-windows/-/electron-builder-squirrel-windows-24.13.3.tgz", + "integrity": "sha512-oHkV0iogWfyK+ah9ZIvMDpei1m9ZRpdXcvde1wTpra2U8AFDNNpqJdnin5z+PM1GbQ5BoaKCWas2HSjtR0HwMg==", + "dev": true, + "peer": true, + "requires": { + "app-builder-lib": "24.13.3", + "archiver": "^5.3.1", + "builder-util": "24.13.1", + "fs-extra": "^10.1.0" + }, + "dependencies": { + "fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "peer": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, + "jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "peer": true, + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^2.0.0" + } + }, + "universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "peer": true + } + } + }, + "electron-publish": { + "version": "24.13.1", + "resolved": "https://registry.npmjs.org/electron-publish/-/electron-publish-24.13.1.tgz", + "integrity": "sha512-2ZgdEqJ8e9D17Hwp5LEq5mLQPjqU3lv/IALvgp+4W8VeNhryfGhYEQC/PgDPMrnWUp+l60Ou5SJLsu+k4mhQ8A==", + "dev": true, + "requires": { + "@types/fs-extra": "^9.0.11", + "builder-util": "24.13.1", + "builder-util-runtime": "9.2.4", + "chalk": "^4.1.2", + "fs-extra": "^10.1.0", + "lazy-val": "^1.0.5", + "mime": "^2.5.2" + }, + "dependencies": { + "fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, + "jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^2.0.0" + } + }, + "universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true + } + } + }, + "electron-to-chromium": { + "version": "1.4.767", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.767.tgz", + "integrity": "sha512-nzzHfmQqBss7CE3apQHkHjXW77+8w3ubGCIoEijKCJebPufREaFETgGXWTkh32t259F3Kcq+R8MZdFdOJROgYw==", + "dev": true + }, + "emittery": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", + "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", + "dev": true, + "peer": true + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "dev": true + }, + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "requires": { + "once": "^1.4.0" + } + }, + "enhanced-resolve": { + "version": "5.16.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.16.1.tgz", + "integrity": "sha512-4U5pNsuDl0EhuZpq46M5xPslstkviJuhrdobaRDBk2Jy2KO37FDAJl4lb2KlNabxT0m4MTK2UHNrsAcphE8nyw==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + } + }, + "entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true + }, + "env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "dev": true + }, + "envinfo": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.8.1.tgz", + "integrity": "sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw==", + "dev": true + }, + "err-code": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", + "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", + "dev": true + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "peer": true, + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "dev": true, + "requires": { + "get-intrinsic": "^1.2.4" + } + }, + "es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true + }, + "es-module-lexer": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.5.2.tgz", + "integrity": "sha512-l60ETUTmLqbVbVHv1J4/qj+M8nq7AwMzEcg3kmJDt9dCNrTk+yHcYFf/Kw75pMDwd9mPcIGCG5LcS20SxYRzFA==", + "dev": true + }, + "es6-error": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", + "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", + "dev": true, + "optional": true + }, + "escalade": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", + "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", + "dev": true + }, + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true + }, + "escodegen": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", + "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", + "dev": true, + "requires": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2", + "source-map": "~0.6.1" + }, + "dependencies": { + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + } + } + }, + "eslint": { + "version": "8.26.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.26.0.tgz", + "integrity": "sha512-kzJkpaw1Bfwheq4VXUezFriD1GxszX6dUekM7Z3aC2o4hju+tsR/XyTC3RcoSD7jmy9VkPU3+N6YjVU2e96Oyg==", + "dev": true, + "requires": { + "@eslint/eslintrc": "^1.3.3", + "@humanwhocodes/config-array": "^0.11.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.1.1", + "eslint-utils": "^3.0.0", + "eslint-visitor-keys": "^3.3.0", + "espree": "^9.4.0", + "esquery": "^1.4.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.15.0", + "grapheme-splitter": "^1.0.4", + "ignore": "^5.2.0", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-sdsl": "^4.1.4", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "regexpp": "^3.2.0", + "strip-ansi": "^6.0.1", + "strip-json-comments": "^3.1.0", + "text-table": "^0.2.0" + }, + "dependencies": { + "eslint-scope": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", + "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + } + }, + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + }, + "find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "requires": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + } + }, + "glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "requires": { + "is-glob": "^4.0.3" + } + }, + "locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "requires": { + "p-locate": "^5.0.0" + } + }, + "p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "requires": { + "yocto-queue": "^0.1.0" + } + }, + "p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "requires": { + "p-limit": "^3.0.2" + } + }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true + } + } + }, + "eslint-plugin-tsdoc": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/eslint-plugin-tsdoc/-/eslint-plugin-tsdoc-0.2.17.tgz", + "integrity": "sha512-xRmVi7Zx44lOBuYqG8vzTXuL6IdGOeF9nHX17bjJ8+VE6fsxpdGem0/SBTmAwgYMKYB1WBkqRJVQ+n8GK041pA==", + "dev": true, + "requires": { + "@microsoft/tsdoc": "0.14.2", + "@microsoft/tsdoc-config": "0.16.2" + } + }, + "eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + } + }, + "eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^2.0.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true + } + } + }, + "eslint-visitor-keys": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", + "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "dev": true + }, + "espree": { + "version": "9.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.0.tgz", + "integrity": "sha512-DQmnRpLj7f6TgN/NYb0MTzJXL+vJF9h3pHy4JhCIs3zwcgez8xmGg3sXHcEO97BrmO2OSvCwMdfdlyl+E9KjOw==", + "dev": true, + "requires": { + "acorn": "^8.8.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.3.0" + } + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, + "esquery": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", + "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "dev": true, + "requires": { + "estraverse": "^5.1.0" + }, + "dependencies": { + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + } + } + }, + "esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "requires": { + "estraverse": "^5.2.0" + }, + "dependencies": { + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + } + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true + }, + "events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "dev": true + }, + "execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "peer": true, + "requires": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "dependencies": { + "get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "peer": true + } + } + }, + "exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", + "dev": true, + "peer": true + }, + "expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==" + }, + "expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", + "dev": true, + "requires": { + "@jest/expect-utils": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0" + } + }, + "extract-zip": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", + "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", + "dev": true, + "requires": { + "@types/yauzl": "^2.9.1", + "debug": "^4.1.1", + "get-stream": "^5.1.0", + "yauzl": "^2.10.0" + } + }, + "extsprintf": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.4.1.tgz", + "integrity": "sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA==", + "dev": true, + "optional": true + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "fast-glob": { + "version": "3.2.12", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", + "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + } + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "fastest-levenshtein": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.12.tgz", + "integrity": "sha512-On2N+BpYJ15xIC974QNVuYGMOlEVt4s0EOI3wwMqOmK1fdDY+FN/zltPV8vosq4ad4c/gJ1KHScUn/6AWIgiow==", + "dev": true + }, + "fastq": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", + "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", + "dev": true, + "requires": { + "reusify": "^1.0.4" + } + }, + "fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "dev": true, + "peer": true, + "requires": { + "bser": "2.1.1" + } + }, + "fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", + "dev": true, + "requires": { + "pend": "~1.2.0" + } + }, + "file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "requires": { + "flat-cache": "^3.0.4" + } + }, + "filelist": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", + "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", + "dev": true, + "requires": { + "minimatch": "^5.0.1" + }, + "dependencies": { + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + } + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "requires": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + } + }, + "flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "requires": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + } + }, + "flatted": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", + "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", + "dev": true + }, + "foreground-child": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", + "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "dependencies": { + "signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true + } + } + }, + "form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + }, + "formidable": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/formidable/-/formidable-2.1.2.tgz", + "integrity": "sha512-CM3GuJ57US06mlpQ47YcunuUZ9jpm8Vx+P2CGt2j7HpgkKZO/DJYQ0Bobim8G6PFQmK5lOqOOdUXboU+h73A4g==", + "dev": true, + "requires": { + "dezalgo": "^1.0.4", + "hexoid": "^1.0.0", + "once": "^1.4.0", + "qs": "^6.11.0" + } + }, + "framesync": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/framesync/-/framesync-4.1.0.tgz", + "integrity": "sha512-MmgZ4wCoeVxNbx2xp5hN/zPDCbLSKiDt4BbbslK7j/pM2lg5S0vhTNv1v8BCVb99JPIo6hXBFdwzU7Q4qcAaoQ==", + "requires": { + "hey-listen": "^1.0.5" + } + }, + "free-style": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/free-style/-/free-style-3.1.0.tgz", + "integrity": "sha512-vJujYSIyT30iDoaoeigNAxX4yB1RUrh+N2ZMhIElMr3BvCuGXOw7XNJMEEJkDUeamK2Rnb/IKFGKRKlTWIGRWA==" + }, + "fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" + }, + "fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dev": true, + "requires": { + "minipass": "^3.0.0" + }, + "dependencies": { + "minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + } + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "optional": true, + "peer": true + }, + "function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true + }, + "gauge": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", + "requires": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "^2.0.0" + } + } + } + }, + "gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "peer": true + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, + "get-intrinsic": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "dev": true, + "requires": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + } + }, + "get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "peer": true + }, + "get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4=" + }, + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "requires": { + "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-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "dev": true + }, + "global-agent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-agent/-/global-agent-3.0.0.tgz", + "integrity": "sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==", + "dev": true, + "optional": true, + "requires": { + "boolean": "^3.0.1", + "es6-error": "^4.1.1", + "matcher": "^3.0.0", + "roarr": "^2.15.3", + "semver": "^7.3.2", + "serialize-error": "^7.0.1" + } + }, + "globals": { + "version": "13.17.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.17.0.tgz", + "integrity": "sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw==", + "dev": true, + "requires": { + "type-fest": "^0.20.2" + }, + "dependencies": { + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true + } + } + }, + "globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "dev": true, + "optional": true, + "requires": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + } + }, + "globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + } + }, + "gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dev": true, + "requires": { + "get-intrinsic": "^1.1.3" + } + }, + "got": { + "version": "11.8.6", + "resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz", + "integrity": "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==", + "dev": true, + "requires": { + "@sindresorhus/is": "^4.0.0", + "@szmarczak/http-timer": "^4.0.5", + "@types/cacheable-request": "^6.0.1", + "@types/responselike": "^1.0.0", + "cacheable-lookup": "^5.0.3", + "cacheable-request": "^7.0.2", + "decompress-response": "^6.0.0", + "http2-wrapper": "^1.0.0-beta.5.2", + "lowercase-keys": "^2.0.0", + "p-cancelable": "^2.0.0", + "responselike": "^2.0.0" + } + }, + "graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, + "grapheme-splitter": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", + "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "requires": { + "es-define-property": "^1.0.0" + } + }, + "has-proto": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", + "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", + "dev": true + }, + "has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "dev": true + }, + "has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=" + }, + "hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "requires": { + "function-bind": "^1.1.2" + } + }, + "hexoid": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/hexoid/-/hexoid-1.0.0.tgz", + "integrity": "sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==", + "dev": true + }, + "hey-listen": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/hey-listen/-/hey-listen-1.0.8.tgz", + "integrity": "sha512-COpmrF2NOg4TBWUJ5UVyaCU2A88wEMkUPK4hNqyCkqHbxT92BbvfjoSozkAIIm6XhicGlJHhFdullInrdhwU8Q==" + }, + "hosted-git-info": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", + "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + }, + "html-encoding-sniffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", + "integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==", + "dev": true, + "requires": { + "whatwg-encoding": "^2.0.0" + } + }, + "html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true, + "peer": true + }, + "http-cache-semantics": { + "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==", + "dev": true + }, + "http-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "dev": true, + "requires": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + } + }, + "http2-wrapper": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", + "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", + "dev": true, + "requires": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.0.0" + } + }, + "https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, + "requires": { + "agent-base": "6", + "debug": "4" + } + }, + "human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "peer": true + }, + "iconv-corefoundation": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/iconv-corefoundation/-/iconv-corefoundation-1.1.7.tgz", + "integrity": "sha512-T10qvkw0zz4wnm560lOEg0PovVqUXuOFhhHAkixw8/sycy7TJt7v/RrkEKEQnAw2viPSJu6iAkErxnzR0g8PpQ==", + "dev": true, + "optional": true, + "requires": { + "cli-truncate": "^2.1.0", + "node-addon-api": "^1.6.3" + } + }, + "iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + } + }, + "ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" + }, + "ignore": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "dev": true + }, + "import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + } + }, + "import-local": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", + "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", + "dev": true, + "requires": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" + }, + "interpret": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-2.2.0.tgz", + "integrity": "sha512-Ju0Bz/cEia55xDwUWEa8+olFpCiQoypjnQySseKtmjNrnps3P+xfpUmGr90T7yjlVJmOtybRvPXhKMbHr+fWnw==", + "dev": true + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true, + "peer": true + }, + "is-ci": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.1.tgz", + "integrity": "sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==", + "dev": true, + "requires": { + "ci-info": "^3.2.0" + } + }, + "is-core-module": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", + "dev": true, + "requires": { + "hasown": "^2.0.0" + } + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" + }, + "is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true, + "peer": true + }, + "is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true + }, + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "dev": true + }, + "is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "peer": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "isbinaryfile": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-5.0.2.tgz", + "integrity": "sha512-GvcjojwonMjWbTkfMpnVHVqXW/wKMYDfEpY94/8zy8HFMOqb/VL6oeONq9v87q4ttVlaTLnGXnJD4B5B1OTGIg==", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + }, + "istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "peer": true + }, + "istanbul-lib-instrument": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.2.tgz", + "integrity": "sha512-1WUsZ9R1lA0HtBSohTkm39WTPlNKSJ5iFk7UwqXkBLoHQT+hfqPsfsTDVuZdKGaBwn7din9bS7SsnoAr943hvw==", + "dev": true, + "peer": true, + "requires": { + "@babel/core": "^7.23.9", + "@babel/parser": "^7.23.9", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^7.5.4" + } + }, + "istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "peer": true, + "requires": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + } + }, + "istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "peer": true, + "requires": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + } + }, + "istanbul-reports": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", + "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", + "dev": true, + "peer": true, + "requires": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + } + }, + "jackspeak": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.1.2.tgz", + "integrity": "sha512-kWmLKn2tRtfYMF/BakihVVRzBKOxz4gJMiL2Rj91WnAB5TPZumSH99R/Yf1qE1u4uRimvCSJfm6hnxohXeEXjQ==", + "dev": true, + "requires": { + "@isaacs/cliui": "^8.0.2", + "@pkgjs/parseargs": "^0.11.0" + } + }, + "jake": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.1.tgz", + "integrity": "sha512-61btcOHNnLnsOdtLgA5efqQWjnSi/vow5HbI7HMdKKWqvrKR1bLK3BPlJn9gcSaP2ewuamUSMB5XEy76KUIS2w==", + "dev": true, + "requires": { + "async": "^3.2.3", + "chalk": "^4.0.2", + "filelist": "^1.0.4", + "minimatch": "^3.1.2" + } + }, + "jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", + "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", + "dev": true, + "peer": true, + "requires": { + "@jest/core": "^29.7.0", + "@jest/types": "^29.6.3", + "import-local": "^3.0.2", + "jest-cli": "^29.7.0" + } + }, + "jest-changed-files": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", + "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", + "dev": true, + "peer": true, + "requires": { + "execa": "^5.0.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0" + }, + "dependencies": { + "p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "peer": true, + "requires": { + "yocto-queue": "^0.1.0" + } + } + } + }, + "jest-circus": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", + "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", + "dev": true, + "peer": true, + "requires": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^1.0.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^29.7.0", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0", + "pretty-format": "^29.7.0", + "pure-rand": "^6.0.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "dependencies": { + "p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "peer": true, + "requires": { + "yocto-queue": "^0.1.0" + } + } + } + }, + "jest-cli": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", + "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", + "dev": true, + "peer": true, + "requires": { + "@jest/core": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "create-jest": "^29.7.0", + "exit": "^0.1.2", + "import-local": "^3.0.2", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "yargs": "^17.3.1" + } + }, + "jest-config": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", + "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", + "dev": true, + "peer": true, + "requires": { + "@babel/core": "^7.11.6", + "@jest/test-sequencer": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-jest": "^29.7.0", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-circus": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "dependencies": { + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "peer": true + } + } + }, + "jest-diff": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", + "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + } + }, + "jest-docblock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", + "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", + "dev": true, + "peer": true, + "requires": { + "detect-newline": "^3.0.0" + } + }, + "jest-each": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", + "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", + "dev": true, + "peer": true, + "requires": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "jest-util": "^29.7.0", + "pretty-format": "^29.7.0" + } + }, + "jest-environment-jsdom": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-29.7.0.tgz", + "integrity": "sha512-k9iQbsf9OyOfdzWH8HDmrRT0gSIcX+FLNW7IQq94tFX0gynPwqDTW0Ho6iMVNjGz/nb+l/vW3dWM2bbLLpkbXA==", + "dev": true, + "requires": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/jsdom": "^20.0.0", + "@types/node": "*", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0", + "jsdom": "^20.0.0" + } + }, + "jest-environment-node": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", + "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", + "dev": true, + "peer": true, + "requires": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + } + }, + "jest-get-type": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", + "dev": true + }, + "jest-haste-map": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", + "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", + "dev": true, + "peer": true, + "requires": { + "@jest/types": "^29.6.3", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "fsevents": "^2.3.2", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + }, + "dependencies": { + "jest-worker": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", + "dev": true, + "peer": true, + "requires": { + "@types/node": "*", + "jest-util": "^29.7.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + } + }, + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "peer": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-leak-detector": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", + "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", + "dev": true, + "peer": true, + "requires": { + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + } + }, + "jest-matcher-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", + "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + } + }, + "jest-message-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", + "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.6.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + } + }, + "jest-mock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", + "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", + "dev": true, + "requires": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-util": "^29.7.0" + } + }, + "jest-pnp-resolver": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", + "dev": true, + "peer": true, + "requires": {} + }, + "jest-regex-util": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", + "dev": true, + "peer": true + }, + "jest-resolve": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", + "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", + "dev": true, + "peer": true, + "requires": { + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "resolve": "^1.20.0", + "resolve.exports": "^2.0.0", + "slash": "^3.0.0" + }, + "dependencies": { + "resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "dev": true, + "peer": true, + "requires": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + } + } + } + }, + "jest-resolve-dependencies": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", + "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", + "dev": true, + "peer": true, + "requires": { + "jest-regex-util": "^29.6.3", + "jest-snapshot": "^29.7.0" + } + }, + "jest-runner": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", + "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", + "dev": true, + "peer": true, + "requires": { + "@jest/console": "^29.7.0", + "@jest/environment": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "graceful-fs": "^4.2.9", + "jest-docblock": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-leak-detector": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-resolve": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-util": "^29.7.0", + "jest-watcher": "^29.7.0", + "jest-worker": "^29.7.0", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" + }, + "dependencies": { + "jest-worker": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", + "dev": true, + "peer": true, + "requires": { + "@types/node": "*", + "jest-util": "^29.7.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + } + }, + "p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "peer": true, + "requires": { + "yocto-queue": "^0.1.0" + } + }, + "source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "dev": true, + "peer": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "peer": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-runtime": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", + "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", + "dev": true, + "peer": true, + "requires": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/globals": "^29.7.0", + "@jest/source-map": "^29.6.3", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + } + }, + "jest-snapshot": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", + "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", + "dev": true, + "peer": true, + "requires": { + "@babel/core": "^7.11.6", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-jsx": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/types": "^7.3.3", + "@jest/expect-utils": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "natural-compare": "^1.4.0", + "pretty-format": "^29.7.0", + "semver": "^7.5.3" + } + }, + "jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "requires": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + } + }, + "jest-validate": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", + "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", + "dev": true, + "peer": true, + "requires": { + "@jest/types": "^29.6.3", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "leven": "^3.1.0", + "pretty-format": "^29.7.0" + }, + "dependencies": { + "camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "peer": true + } + } + }, + "jest-watcher": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", + "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", + "dev": true, + "peer": true, + "requires": { + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "jest-util": "^29.7.0", + "string-length": "^4.0.1" + } + }, + "jest-worker": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", + "integrity": "sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==", + "dev": true, + "requires": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "dependencies": { + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jju": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/jju/-/jju-1.4.0.tgz", + "integrity": "sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==", + "dev": true + }, + "js-sdsl": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.1.5.tgz", + "integrity": "sha512-08bOAKweV2NUC1wqTtf3qZlnpOX/R2DU9ikpjOHs0H+ibQv3zpncVQg6um4uYtRtrwIX8M4Nh3ytK4HGlYAq7Q==", + "dev": true + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "requires": { + "argparse": "^2.0.1" + } + }, + "jsdom": { + "version": "20.0.3", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-20.0.3.tgz", + "integrity": "sha512-SYhBvTh89tTfCD/CRdSOm13mOBa42iTaTyfyEWBdKcGdPxPtLFBXuHR8XHb33YNYaP+lLbmSvBTsnoesCNJEsQ==", + "dev": true, + "requires": { + "abab": "^2.0.6", + "acorn": "^8.8.1", + "acorn-globals": "^7.0.0", + "cssom": "^0.5.0", + "cssstyle": "^2.3.0", + "data-urls": "^3.0.2", + "decimal.js": "^10.4.2", + "domexception": "^4.0.0", + "escodegen": "^2.0.0", + "form-data": "^4.0.0", + "html-encoding-sniffer": "^3.0.0", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.1", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.2", + "parse5": "^7.1.1", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^4.1.2", + "w3c-xmlserializer": "^4.0.0", + "webidl-conversions": "^7.0.0", + "whatwg-encoding": "^2.0.0", + "whatwg-mimetype": "^3.0.0", + "whatwg-url": "^11.0.0", + "ws": "^8.11.0", + "xml-name-validator": "^4.0.0" + } + }, + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true, + "peer": true + }, + "json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, + "json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", + "dev": true, + "optional": true + }, + "json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true + }, + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6" + } + }, + "keytar": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/keytar/-/keytar-7.9.0.tgz", + "integrity": "sha512-VPD8mtVtm5JNtA2AErl6Chp06JBfy7diFQ7TQQhdpWOl6MrCRB+eRbvAZUsbGQS9kiMq0coJsy0W0vHpDCkWsQ==", + "requires": { + "node-addon-api": "^4.3.0", + "prebuild-install": "^7.0.1" + }, + "dependencies": { + "node-addon-api": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-4.3.0.tgz", + "integrity": "sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==" + } + } + }, + "keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "requires": { + "json-buffer": "3.0.1" + } + }, + "kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true + }, + "kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true, + "peer": true + }, + "lazy-val": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/lazy-val/-/lazy-val-1.0.5.tgz", + "integrity": "sha512-0/BnGCCfyUMkBpeDgWihanIAF9JmZhHBgUhEqzvf+adhNGLoP6TaiI5oF8oyb3I45P+PcnrqihSf01M0l0G5+Q==", + "dev": true + }, + "lazystream": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz", + "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==", + "dev": true, + "peer": true, + "requires": { + "readable-stream": "^2.0.5" + } + }, + "leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true, + "peer": true + }, + "levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + } + }, + "lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "peer": true + }, + "loader-runner": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", + "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", + "dev": true + }, + "loader-utils": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + } + }, + "locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "requires": { + "p-locate": "^4.1.0" + } + }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "lodash.defaults": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", + "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==", + "dev": true, + "peer": true + }, + "lodash.difference": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz", + "integrity": "sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA==", + "dev": true, + "peer": true + }, + "lodash.flatten": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", + "integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==", + "dev": true, + "peer": true + }, + "lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "dev": true, + "peer": true + }, + "lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", + "dev": true + }, + "lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "lodash.union": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz", + "integrity": "sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw==", + "dev": true, + "peer": true + }, + "lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "dev": true + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "peer": true, + "requires": { + "semver": "^7.5.3" + } + }, + "make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dev": true, + "peer": true, + "requires": { + "tmpl": "1.0.5" + } + }, + "matcher": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz", + "integrity": "sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==", + "dev": true, + "optional": true, + "requires": { + "escape-string-regexp": "^4.0.0" + } + }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true + }, + "micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "requires": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + } + }, + "mime": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "dev": true + }, + "mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" + }, + "mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "requires": { + "mime-db": "1.52.0" + } + }, + "mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "peer": true + }, + "mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", + "dev": true + }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" + }, + "minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "dev": true + }, + "minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "dev": true, + "requires": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "dependencies": { + "minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + } + } + }, + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true + }, + "mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==" + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "napi-build-utils": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", + "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==" + }, + "native-ext-loader": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/native-ext-loader/-/native-ext-loader-2.3.0.tgz", + "integrity": "sha512-awBQEfD25+YLi1voefLbjHiUt0yfm3Z4mBOLqbzFVR8ZOVv0MebeN9fhNiY0fxVEFiyBYtr/hF2VWfuMSCfFlw==", + "dev": true + }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true + }, + "node-abi": { + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.15.0.tgz", + "integrity": "sha512-Ic6z/j6I9RLm4ov7npo1I48UQr2BEyFCqh6p7S1dhEx9jPO0GPGq/e2Rb7x7DroQrmiVMz/Bw1vJm9sPAl2nxA==", + "requires": { + "semver": "^7.3.5" + } + }, + "node-addon-api": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-1.7.2.tgz", + "integrity": "sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg==", + "dev": true, + "optional": true + }, + "node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true, + "peer": true + }, + "node-loader": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/node-loader/-/node-loader-2.0.0.tgz", + "integrity": "sha512-I5VN34NO4/5UYJaUBtkrODPWxbobrE4hgDqPrjB25yPkonFhCmZ146vTH+Zg417E9Iwoh1l/MbRs1apc5J295Q==", + "dev": true, + "requires": { + "loader-utils": "^2.0.0" + } + }, + "node-releases": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", + "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", + "dev": true + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "peer": true + }, + "normalize-url": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", + "dev": true + }, + "npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "peer": true, + "requires": { + "path-key": "^3.0.0" + } + }, + "npmlog": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", + "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" + }, + "nwsapi": { + "version": "2.2.10", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.10.tgz", + "integrity": "sha512-QK0sRs7MKv0tKe1+5uZIQk/C8XGza4DAnztJG8iD+TpJIORARrCxczA738awHrZoHeTjSSoHqao2teO0dC/gFQ==", + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "object-inspect": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", + "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", + "dev": true + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "optional": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "peer": true, + "requires": { + "mimic-fn": "^2.1.0" + } + }, + "optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "requires": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + } + }, + "p-cancelable": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", + "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==", + "dev": true + }, + "p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "requires": { + "p-limit": "^2.2.0" + } + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "requires": { + "callsites": "^3.0.0" + } + }, + "parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "peer": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + } + }, + "parse5": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", + "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", + "dev": true, + "requires": { + "entities": "^4.4.0" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "requires": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "dependencies": { + "lru-cache": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.2.tgz", + "integrity": "sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==", + "dev": true + } + } + }, + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true + }, + "pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", + "dev": true + }, + "picocolors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", + "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", + "dev": true + }, + "picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true + }, + "pirates": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", + "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", + "dev": true, + "peer": true + }, + "pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "requires": { + "find-up": "^4.0.0" + } + }, + "plist": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/plist/-/plist-3.1.0.tgz", + "integrity": "sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ==", + "dev": true, + "requires": { + "@xmldom/xmldom": "^0.8.8", + "base64-js": "^1.5.1", + "xmlbuilder": "^15.1.1" + } + }, + "popmotion": { + "version": "8.7.6", + "resolved": "https://registry.npmjs.org/popmotion/-/popmotion-8.7.6.tgz", + "integrity": "sha512-gzU0mRAik8FIEOP4Nk5yqYptJIvHLoq/IRU+rANmKjDZ7tynAivYQ9cIJAxVaoS9h0zfXvN0cFBAg93ncmHHkA==", + "requires": { + "@popmotion/easing": "^1.0.1", + "@popmotion/popcorn": "^0.4.4", + "framesync": "^4.0.0", + "hey-listen": "^1.0.5", + "style-value-types": "^3.1.7", + "stylefire": "^7.0.1", + "tslib": "^1.10.0" + } + }, + "prebuild-install": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.0.tgz", + "integrity": "sha512-CNcMgI1xBypOyGqjp3wOc8AAo1nMhZS3Cwd3iHIxOdAUbb+YxdNuM4Z5iIrZ8RLvOsf3F3bl7b7xGq6DjQoNYA==", + "requires": { + "detect-libc": "^2.0.0", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^1.0.1", + "node-abi": "^3.3.0", + "npmlog": "^4.0.1", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^4.0.0", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + } + }, + "prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true + }, + "pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "requires": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true + } + } + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true + }, + "promise-retry": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", + "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", + "dev": true, + "requires": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + } + }, + "prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, + "peer": true, + "requires": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + } + }, + "psl": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", + "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", + "dev": true + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true + }, + "pure-rand": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", + "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", + "dev": true, + "peer": true + }, + "qs": { + "version": "6.12.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.12.1.tgz", + "integrity": "sha512-zWmv4RSuB9r2mYQw3zxQuHWeU+42aKi1wWig/j4ele4ygELZ7PEO6MM7rim9oAQH2A5MWfsAVf/jPvTPgCbvUQ==", + "dev": true, + "requires": { + "side-channel": "^1.0.6" + } + }, + "querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", + "dev": true + }, + "queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true + }, + "quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", + "dev": true + }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + } + }, + "react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true + }, + "read-config-file": { + "version": "6.3.2", + "resolved": "https://registry.npmjs.org/read-config-file/-/read-config-file-6.3.2.tgz", + "integrity": "sha512-M80lpCjnE6Wt6zb98DoW8WHR09nzMSpu8XHtPkiTHrJ5Az9CybfeQhTJ8D7saeBHpGhLPIVyA8lcL6ZmdKwY6Q==", + "dev": true, + "requires": { + "config-file-ts": "^0.2.4", + "dotenv": "^9.0.2", + "dotenv-expand": "^5.1.0", + "js-yaml": "^4.1.0", + "json5": "^2.2.0", + "lazy-val": "^1.0.4" + } + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "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" + } + }, + "readdir-glob": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.3.tgz", + "integrity": "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==", + "dev": true, + "peer": true, + "requires": { + "minimatch": "^5.1.0" + }, + "dependencies": { + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "peer": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "peer": true, + "requires": { + "brace-expansion": "^2.0.1" + } + } + } + }, + "rechoir": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.1.tgz", + "integrity": "sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg==", + "dev": true, + "requires": { + "resolve": "^1.9.0" + } + }, + "regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "dev": true + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true + }, + "requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "dev": true + }, + "resolve": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz", + "integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==", + "dev": true, + "requires": { + "is-core-module": "^2.1.0", + "path-parse": "^1.0.6" + } + }, + "resolve-alpn": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", + "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==", + "dev": true + }, + "resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "requires": { + "resolve-from": "^5.0.0" + }, + "dependencies": { + "resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true + } + } + }, + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true + }, + "resolve.exports": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", + "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==", + "dev": true, + "peer": true + }, + "responselike": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz", + "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==", + "dev": true, + "requires": { + "lowercase-keys": "^2.0.0" + } + }, + "retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "dev": true + }, + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "roarr": { + "version": "2.15.4", + "resolved": "https://registry.npmjs.org/roarr/-/roarr-2.15.4.tgz", + "integrity": "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==", + "dev": true, + "optional": true, + "requires": { + "boolean": "^3.0.1", + "detect-node": "^2.0.4", + "globalthis": "^1.0.1", + "json-stringify-safe": "^5.0.1", + "semver-compare": "^1.0.0", + "sprintf-js": "^1.1.2" + } + }, + "run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "requires": { + "queue-microtask": "^1.2.2" + } + }, + "run-script-os": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/run-script-os/-/run-script-os-1.1.6.tgz", + "integrity": "sha512-ql6P2LzhBTTDfzKts+Qo4H94VUKpxKDFz6QxxwaUZN0mwvi7L3lpOI7BqPCq7lgDh3XLl0dpeXwfcVIitlrYrw==", + "dev": true + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "sanitize-filename": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/sanitize-filename/-/sanitize-filename-1.6.3.tgz", + "integrity": "sha512-y/52Mcy7aw3gRm7IrcGDFx/bCk4AhRh2eI9luHOQM86nZsqwiRkkq2GekHXBBD+SmPidc8i2PqtYZl+pWJ8Oeg==", + "dev": true, + "requires": { + "truncate-utf8-bytes": "^1.0.0" + } + }, + "sax": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.3.0.tgz", + "integrity": "sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA==", + "dev": true + }, + "saxes": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "dev": true, + "requires": { + "xmlchars": "^2.2.0" + } + }, + "schema-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-3.3.0.tgz", + "integrity": "sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.8", + "ajv": "^6.12.5", + "ajv-keywords": "^3.5.2" + } + }, + "semver": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", + "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==" + }, + "semver-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", + "integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==", + "dev": true, + "optional": true + }, + "serialize-error": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz", + "integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==", + "dev": true, + "optional": true, + "requires": { + "type-fest": "^0.13.1" + } + }, + "serialize-javascript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "dev": true, + "requires": { + "randombytes": "^2.1.0" + } + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" + }, + "set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, + "requires": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + } + }, + "shallow-clone": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", + "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", + "dev": true, + "requires": { + "kind-of": "^6.0.2" + } + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "side-channel": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", + "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", + "dev": true, + "requires": { + "call-bind": "^1.0.7", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.4", + "object-inspect": "^1.13.1" + } + }, + "signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" + }, + "simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==" + }, + "simple-get": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "requires": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "simple-update-notifier": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", + "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", + "dev": true, + "requires": { + "semver": "^7.5.3" + } + }, + "sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true, + "peer": true + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, + "slice-ansi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", + "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", + "dev": true, + "optional": true, + "requires": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + } + }, + "smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "dev": true, + "optional": true + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "sourcemap-codec": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", + "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", + "dev": true + }, + "sprintf-js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", + "dev": true, + "optional": true + }, + "stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dev": true, + "requires": { + "escape-string-regexp": "^2.0.0" + }, + "dependencies": { + "escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true + } + } + }, + "stat-mode": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/stat-mode/-/stat-mode-1.0.0.tgz", + "integrity": "sha512-jH9EhtKIjuXZ2cWxmXS8ZP80XyC3iasQxMDV8jzhNJpfDb7VbQLVW4Wvsxz9QZvzV+G4YoSfBUVKDOyxLzi/sg==", + "dev": true + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dev": true, + "peer": true, + "requires": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + } + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "string-width-cjs": { + "version": "npm:string-width@4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "strip-ansi-cjs": { + "version": "npm:strip-ansi@6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true, + "peer": true + }, + "strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "peer": true + }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" + }, + "style-value-types": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/style-value-types/-/style-value-types-3.2.0.tgz", + "integrity": "sha512-ih0mGsrYYmVvdDi++/66O6BaQPRPRMQHoZevNNdMMcPlP/cH28Rnfsqf1UEba/Bwfuw9T8BmIMwbGdzsPwQKrQ==", + "requires": { + "hey-listen": "^1.0.8", + "tslib": "^1.10.0" + } + }, + "stylefire": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/stylefire/-/stylefire-7.0.3.tgz", + "integrity": "sha512-Q0l7NSeFz/OkX+o6/7Zg3VZxSAZeQzQpYomWmIpOehFM/rJNMSLVX5fgg6Q48ut2ETNKwdhm97mPNU643EBCoQ==", + "requires": { + "@popmotion/popcorn": "^0.4.4", + "framesync": "^4.0.0", + "hey-listen": "^1.0.8", + "style-value-types": "^3.1.7", + "tslib": "^1.10.0" + } + }, + "sumchecker": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-3.0.1.tgz", + "integrity": "sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg==", + "dev": true, + "requires": { + "debug": "^4.1.0" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "peer": true + }, + "symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true + }, + "tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "dev": true + }, + "tar": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", + "dev": true, + "requires": { + "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" + }, + "dependencies": { + "chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "dev": true + } + } + }, + "tar-fs": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", + "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", + "requires": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "requires": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, + "temp-file": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/temp-file/-/temp-file-3.4.0.tgz", + "integrity": "sha512-C5tjlC/HCtVUOi3KWVokd4vHVViOmGjtLwIh4MuzPo/nMYTV/p1urt3RnMz2IWXDdKEGJH3k5+KPxtqRsUYGtg==", + "dev": true, + "requires": { + "async-exit-hook": "^2.0.1", + "fs-extra": "^10.0.0" + }, + "dependencies": { + "fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, + "jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^2.0.0" + } + }, + "universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true + } + } + }, + "terser": { + "version": "5.31.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.31.0.tgz", + "integrity": "sha512-Q1JFAoUKE5IMfI4Z/lkE/E6+SwgzO+x4tq4v1AyBLRj8VSYvRO6A/rQrPg1yud4g0En9EKI1TvFRF2tQFcoUkg==", + "dev": true, + "requires": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.8.2", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + } + }, + "terser-webpack-plugin": { + "version": "5.3.10", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz", + "integrity": "sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==", + "dev": true, + "requires": { + "@jridgewell/trace-mapping": "^0.3.20", + "jest-worker": "^27.4.5", + "schema-utils": "^3.1.1", + "serialize-javascript": "^6.0.1", + "terser": "^5.26.0" + } + }, + "test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "peer": true, + "requires": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + } + }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "tmp": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", + "integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==", + "dev": true + }, + "tmp-promise": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/tmp-promise/-/tmp-promise-3.0.3.tgz", + "integrity": "sha512-RwM7MoPojPxsOBYnyd2hy0bxtIlVrihNs9pj5SUvY8Zz1sQcQG2tG1hSr8PDxfgEB8RNKDhqbIlroIarSNDNsQ==", + "dev": true, + "requires": { + "tmp": "^0.2.0" + } + }, + "tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "dev": true, + "peer": true + }, + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "dev": true, + "peer": true + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, + "tough-cookie": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz", + "integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==", + "dev": true, + "requires": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.2.0", + "url-parse": "^1.5.3" + }, + "dependencies": { + "universalify": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", + "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", + "dev": true + } + } + }, + "tr46": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", + "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==", + "dev": true, + "requires": { + "punycode": "^2.1.1" + } + }, + "truncate-utf8-bytes": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz", + "integrity": "sha512-95Pu1QXQvruGEhv62XCMO3Mm90GscOCClvrIUwCM0PYOXK3kaF3l3sIHxx71ThJfcbM2O5Au6SO3AWCSEfW4mQ==", + "dev": true, + "requires": { + "utf8-byte-length": "^1.0.1" + } + }, + "ts-jest": { + "version": "29.1.2", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.1.2.tgz", + "integrity": "sha512-br6GJoH/WUX4pu7FbZXuWGKGNDuU7b8Uj77g/Sp7puZV6EXzuByl6JrECvm0MzVzSTkSHWTihsXt+5XYER5b+g==", + "dev": true, + "requires": { + "bs-logger": "0.x", + "fast-json-stable-stringify": "2.x", + "jest-util": "^29.0.0", + "json5": "^2.2.3", + "lodash.memoize": "4.x", + "make-error": "1.x", + "semver": "^7.5.3", + "yargs-parser": "^21.0.1" + } + }, + "ts-loader": { + "version": "9.4.1", + "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.4.1.tgz", + "integrity": "sha512-384TYAqGs70rn9F0VBnh6BPTfhga7yFNdC5gXbQpDrBj9/KsT4iRkGqKXhziofHOlE2j6YEaiTYVGKKvPhGWvw==", + "dev": true, + "requires": { + "chalk": "^4.1.0", + "enhanced-resolve": "^5.0.0", + "micromatch": "^4.0.0", + "semver": "^7.3.4" + } + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1" + } + }, + "type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true + }, + "type-fest": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", + "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", + "dev": true, + "optional": true + }, + "typescript": { + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.4.tgz", + "integrity": "sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==", + "dev": true + }, + "typestyle": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/typestyle/-/typestyle-2.4.0.tgz", + "integrity": "sha512-/d1BL6Qi+YlMLEydnUEB8KL/CAjAN8cyt3/UyGnOyBrWf7bLGcR/6yhmsaUstO2IcYwZfagjE7AIzuI2vUW9mg==", + "requires": { + "csstype": "3.0.10", + "free-style": "3.1.0" + } + }, + "undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true + }, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true + }, + "update-browserslist-db": { + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.16.tgz", + "integrity": "sha512-KVbTxlBYlckhF5wgfyZXTWnMn7MMZjMu9XG8bPlliUOP9ThaF4QnhP8qrjrH7DRzHfSk0oQv1wToW+iA5GajEQ==", + "dev": true, + "requires": { + "escalade": "^3.1.2", + "picocolors": "^1.0.1" + } + }, + "uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "dev": true, + "requires": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "utf8-byte-length": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/utf8-byte-length/-/utf8-byte-length-1.0.5.tgz", + "integrity": "sha512-Xn0w3MtiQ6zoz2vFyUVruaCL53O/DwUvkEeOvj+uulMm0BkUGYWmBYVyElqZaSLhY6ZD0ulfU3aBra2aVT4xfA==", + "dev": true + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" + }, + "v8-to-istanbul": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.2.0.tgz", + "integrity": "sha512-/EH/sDgxU2eGxajKdwLCDmQ4FWq+kpi3uCmBGpw1xJtnAxEjlD8j8PEiGWpCIMIs3ciNAgH0d3TTJiUkYzyZjA==", + "dev": true, + "peer": true, + "requires": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + } + }, + "verror": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.1.tgz", + "integrity": "sha512-veufcmxri4e3XSrT0xwfUR7kguIkaxBeosDg00yDWhk49wdwkSUrvvsm7nc75e1PUyvIeZj6nS8VQRYz2/S4Xg==", + "dev": true, + "optional": true, + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + }, + "dependencies": { + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", + "dev": true, + "optional": true + } + } + }, + "w3c-xmlserializer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz", + "integrity": "sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw==", + "dev": true, + "requires": { + "xml-name-validator": "^4.0.0" + } + }, + "walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "dev": true, + "peer": true, + "requires": { + "makeerror": "1.0.12" + } + }, + "watchpack": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.1.tgz", + "integrity": "sha512-8wrBCMtVhqcXP2Sup1ctSkga6uc2Bx0IIvKyT7yTFier5AXHooSI+QyQQAtTb7+E0IUCCKyTFmXqdqgum2XWGg==", + "dev": true, + "requires": { + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.1.2" + } + }, + "webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "dev": true + }, + "webpack": { + "version": "5.91.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.91.0.tgz", + "integrity": "sha512-rzVwlLeBWHJbmgTC/8TvAcu5vpJNII+MelQpylD4jNERPwpBJOE2lEcko1zJX3QJeLjTTAnQxn/OJ8bjDzVQaw==", + "dev": true, + "requires": { + "@types/eslint-scope": "^3.7.3", + "@types/estree": "^1.0.5", + "@webassemblyjs/ast": "^1.12.1", + "@webassemblyjs/wasm-edit": "^1.12.1", + "@webassemblyjs/wasm-parser": "^1.12.1", + "acorn": "^8.7.1", + "acorn-import-assertions": "^1.9.0", + "browserslist": "^4.21.10", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.16.0", + "es-module-lexer": "^1.2.1", + "eslint-scope": "5.1.1", + "events": "^3.2.0", + "glob-to-regexp": "^0.4.1", + "graceful-fs": "^4.2.11", + "json-parse-even-better-errors": "^2.3.1", + "loader-runner": "^4.2.0", + "mime-types": "^2.1.27", + "neo-async": "^2.6.2", + "schema-utils": "^3.2.0", + "tapable": "^2.1.1", + "terser-webpack-plugin": "^5.3.10", + "watchpack": "^2.4.1", + "webpack-sources": "^3.2.3" + } + }, + "webpack-cli": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.10.0.tgz", + "integrity": "sha512-NLhDfH/h4O6UOy+0LSso42xvYypClINuMNBVVzX4vX98TmTaTUxwRbXdhucbFMd2qLaCTcLq/PdYrvi8onw90w==", + "dev": true, + "requires": { + "@discoveryjs/json-ext": "^0.5.0", + "@webpack-cli/configtest": "^1.2.0", + "@webpack-cli/info": "^1.5.0", + "@webpack-cli/serve": "^1.7.0", + "colorette": "^2.0.14", + "commander": "^7.0.0", + "cross-spawn": "^7.0.3", + "fastest-levenshtein": "^1.0.12", + "import-local": "^3.0.2", + "interpret": "^2.2.0", + "rechoir": "^0.7.0", + "webpack-merge": "^5.7.3" + }, + "dependencies": { + "commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "dev": true + } + } + }, + "webpack-merge": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.8.0.tgz", + "integrity": "sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q==", + "dev": true, + "requires": { + "clone-deep": "^4.0.1", + "wildcard": "^2.0.0" + } + }, + "webpack-sources": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz", + "integrity": "sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==", + "dev": true + }, + "whatwg-encoding": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", + "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==", + "dev": true, + "requires": { + "iconv-lite": "0.6.3" + } + }, + "whatwg-mimetype": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz", + "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==", + "dev": true + }, + "whatwg-url": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz", + "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==", + "dev": true, + "requires": { + "tr46": "^3.0.0", + "webidl-conversions": "^7.0.0" + } + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "requires": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, + "wildcard": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz", + "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==", + "dev": true + }, + "word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true + }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "wrap-ansi-cjs": { + "version": "npm:wrap-ansi@7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "write-file-atomic": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", + "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", + "dev": true, + "peer": true, + "requires": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + } + }, + "ws": { + "version": "8.17.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.0.tgz", + "integrity": "sha512-uJq6108EgZMAl20KagGkzCKfMEjxmKvZHG7Tlq0Z6nOky7YF7aq4mOx6xK8TJ/i1LeK4Qus7INktacctDgY8Ow==", + "dev": true, + "requires": {} + }, + "xml-name-validator": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz", + "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==", + "dev": true + }, + "xmlbuilder": { + "version": "15.1.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz", + "integrity": "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==", + "dev": true + }, + "xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true + }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "requires": { + "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" + } + }, + "yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true + }, + "yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "dev": true, + "requires": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, + "yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true + }, + "zip-stream": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-4.1.1.tgz", + "integrity": "sha512-9qv4rlDiopXg4E69k+vMHjNN63YFMe9sZMrdlvKnCjlCRWeCBswPPMPUfx+ipsAWq1LXHe70RcbaHdJJpS6hyQ==", + "dev": true, + "peer": true, + "requires": { + "archiver-utils": "^3.0.4", + "compress-commons": "^4.1.2", + "readable-stream": "^3.6.0" + }, + "dependencies": { + "archiver-utils": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-3.0.4.tgz", + "integrity": "sha512-KVgf4XQVrTjhyWmx6cte4RxonPLR9onExufI1jhvw/MQ4BB6IsZD5gT8Lq+u/+pRkWna/6JoHpiQioaqFP5Rzw==", + "dev": true, + "peer": true, + "requires": { + "glob": "^7.2.3", + "graceful-fs": "^4.2.0", + "lazystream": "^1.0.0", + "lodash.defaults": "^4.2.0", + "lodash.difference": "^4.5.0", + "lodash.flatten": "^4.4.0", + "lodash.isplainobject": "^4.0.6", + "lodash.union": "^4.6.0", + "normalize-path": "^3.0.0", + "readable-stream": "^3.6.0" + } + }, + "readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "peer": true, + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..55ed236 --- /dev/null +++ b/package.json @@ -0,0 +1,54 @@ +{ + "name": "httpiness", + "version": "1.4.3", + "appId": "org.httpiness", + "description": "Developer-oriented HTTP client for slalom API testing.", + "main": "./main.js", + "scripts": { + "clean": "git clean -dfX", + "build:dev": "webpack --config-name renderer-dev", + "start:dev": "webpack --config-name renderer-dev --config-name main-dev && electron ./dist/electron-workspace", + "start:dev:clean": "npm run clean && npm install && npm run start:dev", + "build:prod": "webpack --config-name main-dist --config-name renderer-dist && cd dist/electron-workspace && electron-builder", + "build:prod:clean": "npm run clean && npm install && npm run build:prod", + "start:prod": "run-script-os", + "start:prod:macos": "open -n ./dist/bin/mac/httpiness.app", + "start:prod:win32": ".\\dist\\bin\\win-unpacked\\httpiness.exe", + "start:prod:clean": "npm run build:prod:clean && npm run start:prod", + "test": "jest", + "lint": "eslint ./src ./test" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/bognikol/httpiness.git" + }, + "author": "Bogdan Nikolic", + "license": "MIT", + "devDependencies": { + "@marshallofsound/webpack-asset-relocator-loader": "^0.5.0", + "@types/jest": "^29.5.12", + "@typescript-eslint/eslint-plugin": "^5.41.0", + "@typescript-eslint/parser": "^5.41.0", + "cross-env": "^7.0.3", + "electron": "^30.0.9", + "electron-builder": "^24.13.3", + "eslint": "^8.26.0", + "eslint-plugin-tsdoc": "^0.2.17", + "formidable": "^2.0.1", + "jest-environment-jsdom": "^29.7.0", + "native-ext-loader": "^2.3.0", + "node-loader": "^2.0.0", + "run-script-os": "^1.1.6", + "ts-jest": "^29.1.2", + "ts-loader": "^9.4.1", + "typescript": "^4.8.4", + "webpack": "^5.74.0", + "webpack-cli": "^4.10.0" + }, + "dependencies": { + "aflon": "^2.0.10", + "keytar": "^7.9.0", + "mime-db": "^1.52.0", + "uuid": "^8.3.2" + } +} diff --git a/src/main/index.ts b/src/main/index.ts new file mode 100644 index 0000000..77c2c90 --- /dev/null +++ b/src/main/index.ts @@ -0,0 +1,166 @@ +import { app, ipcMain, dialog, BrowserWindow } from "electron"; + +function createWindow (): void { + const win = new BrowserWindow({ + width: 1200, + height: 700, + minWidth: 1200, + minHeight: 600, + webPreferences: { + contextIsolation: false, + nodeIntegration: true, + webviewTag: true + }, + title: "", + frame: false, + titleBarStyle: "hiddenInset" + }); + + win.loadFile("./index.html"); +} + +async function oauth2Window(url: string, host: string, queryKey: string): Promise { + return new Promise((resolve) => { + let win = new BrowserWindow({ + width: 500, + height: 800 + }); + + const tryClose = (): void => { + setTimeout(() => { + try { + if (!win) return; + win.close(); + win = null; + } catch (ex) { + console.log(ex); + } + }, 0); + }; + + const handleNavigate = (): void => { + if (!win) return; + let url = new URL(win.webContents.getURL()); + let hashSearchParams = new URLSearchParams(url.hash.substring(1)); + if (win.webContents.getURL().startsWith(host)) { + if (url.searchParams.has(queryKey)) { + tryClose(); + resolve(url.searchParams.get(queryKey)); + } else if (hashSearchParams.has(queryKey)) { + tryClose(); + resolve(hashSearchParams.get(queryKey)); + } + } + }; + + win.webContents.addListener("did-finish-load", handleNavigate); + win.webContents.addListener("did-fail-load", handleNavigate); + win.addListener("closed", () => { + resolve(null); + }); + + win.loadURL(url); + }); +} + +app.whenReady().then(createWindow); + +app.on("window-all-closed", () => { + if (process.platform !== "darwin") { + app.quit(); + } +}); + +app.on("activate", () => { + if (BrowserWindow.getAllWindows().length === 0) { + createWindow(); + } +}); + +ipcMain.handle("showOpenDialog", async (event, options) => { + return await dialog.showOpenDialog(options); +}); + +ipcMain.handle("showSaveDialog", async (event, options) => { + return await dialog.showSaveDialog(options); +}); + +ipcMain.handle("showMessageBox", async (event, options) => { + return await dialog.showMessageBox(options); +}); + +ipcMain.handle("maximize", (event) => { + let browserWindow = BrowserWindow.fromWebContents(event.sender); + if (!browserWindow) return; + browserWindow.maximize(); +}); + +ipcMain.handle("minimize", (event) => { + let browserWindow = BrowserWindow.fromWebContents(event.sender); + if (!browserWindow) return; + browserWindow.minimize(); +}); + +ipcMain.handle("unmaximize", (event) => { + let browserWindow = BrowserWindow.fromWebContents(event.sender); + if (!browserWindow) return; + browserWindow.unmaximize(); +}); + +ipcMain.handle("isMaximized", (event) => { + let browserWindow = BrowserWindow.fromWebContents(event.sender); + if (!browserWindow) return false; + return browserWindow.isMaximized(); +}); + +ipcMain.handle("close", (event) => { + let browserWindow = BrowserWindow.fromWebContents(event.sender); + if (!browserWindow) return; + browserWindow.close(); +}); + +ipcMain.handle("goFullScreen", (event) => { + let browserWindow = BrowserWindow.fromWebContents(event.sender); + if (!browserWindow) return; + browserWindow.setFullScreen(true); +}); + +ipcMain.handle("exitFullScreen", (event) => { + let browserWindow = BrowserWindow.fromWebContents(event.sender); + if (!browserWindow) return; + browserWindow.setFullScreen(false); +}); + +ipcMain.handle("isInFullScreen", (event) => { + let browserWindow = BrowserWindow.fromWebContents(event.sender); + if (!browserWindow) return false; + return browserWindow.isFullScreen(); +}); + +ipcMain.handle("goKiosk", (event) => { + let browserWindow = BrowserWindow.fromWebContents(event.sender); + if (!browserWindow) return; + browserWindow.setKiosk(true); +}); + +ipcMain.handle("exitKiosk", (event) => { + let browserWindow = BrowserWindow.fromWebContents(event.sender); + if (!browserWindow) return; + browserWindow.setKiosk(false); +}); + +ipcMain.handle("isInKiosk", (event) => { + let browserWindow = BrowserWindow.fromWebContents(event.sender); + if (!browserWindow) return false; + return browserWindow.isKiosk(); +}); + +ipcMain.handle("setMacOsTrafficLightsPosition", (event, x, y) => { + let browserWindow = BrowserWindow.fromWebContents(event.sender); + if (!browserWindow) return; + browserWindow.setWindowButtonPosition({ x, y }); +}); + +ipcMain.handle("oauth2", async (event, url, redirectHost, queryKey) => { + return await oauth2Window(url, redirectHost, queryKey); +}); diff --git a/src/renderer/index.ts b/src/renderer/index.ts new file mode 100644 index 0000000..d6cb833 --- /dev/null +++ b/src/renderer/index.ts @@ -0,0 +1,7 @@ +import { App } from "aflon"; + +import RapidsApp from "./ui/RapidsApp"; + +window.addEventListener("load", () => { + App.run(new RapidsApp()); +}); diff --git a/src/renderer/lib/Clipboard.ts b/src/renderer/lib/Clipboard.ts new file mode 100644 index 0000000..9d375ef --- /dev/null +++ b/src/renderer/lib/Clipboard.ts @@ -0,0 +1,18 @@ +import { clipboard } from "electron"; + +interface Clipboard { + setText(text: string): void; + getText(): string; +} + +class ElectronClipboard implements Clipboard { + setText(text: string): void { + clipboard.writeText(text); + } + + getText(): string { + return clipboard.readText(); + } +} + +export const Clipboard: Clipboard = new ElectronClipboard(); diff --git a/src/renderer/lib/Environment.ts b/src/renderer/lib/Environment.ts new file mode 100644 index 0000000..efeb3ed --- /dev/null +++ b/src/renderer/lib/Environment.ts @@ -0,0 +1,42 @@ +import * as os from "os"; +import * as path from "path"; +import * as fs from "fs"; + + +class EnvironmentImpl { + private readonly _configFile: string = path.join(os.homedir(), "httpiness.env"); + private _env: Record = {}; + + constructor() { + try { + let lines = fs.readFileSync(this._configFile, { encoding: "utf-8" }).split("\n"); + + for (let line of lines) { + line = line.trim(); + + if (line.length >= 1 && line.charAt(0) == "#") + continue; + + let parts = line.split("="); + + if (parts.length == 2) { + this._env[parts[0].trim().toUpperCase()] = parts[1].trim().toUpperCase(); + } else if (parts.length == 1) { + this._env[parts[0].trim().toUpperCase()] = ""; + } + } + } catch { + // Do nothing - this is best effort. + } + } + + isDefined(name: string): boolean { + return name in this._env; + } + + getValue(name: string): string { + return this._env[name]; + } +} + +export const Environment = new EnvironmentImpl(); diff --git a/src/renderer/lib/Logger.ts b/src/renderer/lib/Logger.ts new file mode 100644 index 0000000..6e7f0e0 --- /dev/null +++ b/src/renderer/lib/Logger.ts @@ -0,0 +1,13 @@ + +export class ErrorRecord { + error: Error = null; +} + +export function log(message: string): void { + console.log(message); +} + +// eslint-disable-next-line @typescript-eslint/no-unused-vars +export function error(message: string, errorRecord: ErrorRecord = null): void { + console.log(message); +} diff --git a/src/renderer/lib/Platform.ts b/src/renderer/lib/Platform.ts new file mode 100644 index 0000000..c4c3384 --- /dev/null +++ b/src/renderer/lib/Platform.ts @@ -0,0 +1,37 @@ +import * as process from "process"; +import { execSync } from "child_process"; + +export enum Platform { + Unknown, Win32, MacOS, Linux +} + +export function currentPlatform(): Platform { + if (process.platform == "darwin") return Platform.MacOS; + else if (process.platform == "win32") return Platform.Win32; + else if (process.platform == "linux") return Platform.Linux; + return Platform.Unknown; +} + +export function openFileInDefaultApp(file: string): boolean { + try { + let command = ""; + + const platform = currentPlatform(); + + if (platform == Platform.MacOS) + command = `open ${file}`; + else if (platform == Platform.Win32) + command = `start ${file}`; + else if (platform == Platform.Linux) + command = `xdg-open ${file}`; + else + return false; + + execSync(command); + + return true; + + } catch (ex) { + return false; + } +} diff --git a/src/renderer/lib/RapidsWebApi.ts b/src/renderer/lib/RapidsWebApi.ts new file mode 100644 index 0000000..21f2830 --- /dev/null +++ b/src/renderer/lib/RapidsWebApi.ts @@ -0,0 +1,36 @@ +import { Version } from "./Version"; +import { Environment } from "./Environment"; + +class RapidsWebApiImpl { + private _baseUrl: string = ""; + + constructor(baseUrl: string = "https://api.httpiness.com") { + this._baseUrl = baseUrl; + } + + async getLatestVersion(): Promise { + if (Environment.isDefined("DO_NOT_SEND_TELEMETRY")) return Version.current; + + const myHeaders = new Headers(); + myHeaders.append("pragma", "no-cache"); + myHeaders.append("cache-control", "no-cache"); + + const config = { + method: "GET", + headers: myHeaders + }; + + let result = await fetch(`${this._baseUrl}/latest`, config); + if (!result.ok) + return new Version(); + + return Version.fromString(await result.text()); + } + + async reportHeartBeat(): Promise { + let result = await fetch(`${this._baseUrl}/report/hb`); + return result.ok; + } +} + +export const RapidsWebApi = new RapidsWebApiImpl(); diff --git a/src/renderer/lib/SimpleEvent.ts b/src/renderer/lib/SimpleEvent.ts new file mode 100644 index 0000000..743af41 --- /dev/null +++ b/src/renderer/lib/SimpleEvent.ts @@ -0,0 +1,48 @@ +/* eslint-disable @typescript-eslint/ban-types */ +export class SimpleEvent { + sender: Object; + detail: Record; +} + +export type SimpleEventListener = (e: SimpleEvent) => void; + +export interface ISimpleEventable { + on(eventName: string, handler: SimpleEventListener): this; + off(eventName: string, handler: SimpleEventListener): this; + raise(eventName: string, args: Record): void; +} + +export class SimpleEventBroker implements ISimpleEventable { + private _owner: Object; + private _eventDirectory: Record = {}; + + constructor(owner: Object) { + this._owner = owner; + } + + on(eventName: string, handler: SimpleEventListener): this { + if (!this._eventDirectory.hasOwnProperty(eventName)) + this._eventDirectory[eventName] = []; + + if (this._eventDirectory[eventName].find(h => h == handler) != undefined) return this; + + this._eventDirectory[eventName].push(handler); + return this; + } + + off(eventName: string, handler: SimpleEventListener): this { + if (!this._eventDirectory.hasOwnProperty(eventName)) return this; + this._eventDirectory[eventName] = this._eventDirectory[eventName].filter(h => h != handler); + return this; + } + + raise(eventName: string, args: Record): void { + if (!this._eventDirectory.hasOwnProperty(eventName)) return; + + let e = new SimpleEvent(); + e.sender = this._owner; + e.detail = args; + + this._eventDirectory[eventName].forEach(h => h(e)); + } +} diff --git a/src/renderer/lib/SystemDialogs.ts b/src/renderer/lib/SystemDialogs.ts new file mode 100644 index 0000000..0bbf05a --- /dev/null +++ b/src/renderer/lib/SystemDialogs.ts @@ -0,0 +1,44 @@ +import { OpenDialogOptions, SaveDialogOptions, MessageBoxOptions, ipcRenderer, + OpenDialogReturnValue, SaveDialogReturnValue, MessageBoxReturnValue } from "electron"; + +export async function showOpenDialog(options: OpenDialogOptions): Promise { + return await >ipcRenderer.invoke("showOpenDialog", options); +} + +export async function showSaveDialog(options: SaveDialogOptions): Promise { + return await >ipcRenderer.invoke("showSaveDialog", options); +} + +export async function showMessageBox(options: MessageBoxOptions): Promise { + return await >ipcRenderer.invoke("showMessageBox", options); +} + +export async function showConfirmNative(text: string): Promise { + const buttonIdx = await showMessageBox({ + type: "question", + buttons: ["Yes", "No"], + defaultId: 0, + cancelId: 1, + message: text + }); + + return buttonIdx.response === 0; +} + +export async function showChooseNative(text: string, options: Array<{ text: string, id: string }>): Promise { + const buttonIdx = await showMessageBox({ + type: "question", + buttons: options.map(option => option.text), + message: text + }); + + return options[buttonIdx.response].id; +} + +export async function showAlertNative(text: string, button: string = "OK"): Promise { + await showMessageBox({ + type: "info", + buttons: [button], + message: text + }); +} diff --git a/src/renderer/lib/Telemetry.ts b/src/renderer/lib/Telemetry.ts new file mode 100644 index 0000000..11bf840 --- /dev/null +++ b/src/renderer/lib/Telemetry.ts @@ -0,0 +1,90 @@ +import { RapidsWebApi } from "./RapidsWebApi"; +import { Environment } from "./Environment"; + + +export enum TelemetryEvent { + HttpRequestSent = "HttpRequestSent", + FeedbackSent = "FeedbackSent", + IssueReported = "IssueReported", + AskedForFeedback = "AskedForFeedback", + HeartBeatSent = "HeartBeatSent" +} + +class TelemetryStorageKeys { + static counterKey = (event: TelemetryEvent): string => `evt_${event}_ctr;`; + static lastTimestampKey = (event: TelemetryEvent): string => `evt_${event}_ltmst`; +} + +class TelemetryImpl { + reportEvent(event: TelemetryEvent): void { + setTimeout(() => { + this._updateEventStats(event); + + switch (event) { + case TelemetryEvent.HttpRequestSent: this._onHttpRequestSent(); break; + } + }, 0); + } + + getNumberOfEvents(event: TelemetryEvent): number { + let currentNumLCValue = localStorage.getItem(TelemetryStorageKeys.counterKey(event)); + + if (!currentNumLCValue) return 0; + + let numValue = Number.parseInt(currentNumLCValue); + if (Number.isNaN(numValue)) return 0; + + return numValue; + } + + getLastEventTimestamp(event: TelemetryEvent): Date { + let dateString = localStorage.getItem(TelemetryStorageKeys.lastTimestampKey(event)); + if (!dateString) return null; + + let timestamp = Date.parse(dateString); + if (isNaN(timestamp)) return null; + + return new Date(timestamp); + } + + private _reportHeartBeatIfNecessary(): void { + if (Environment.isDefined("DO_NOT_SEND_TELEMETRY")) return; + + let currentTimeStamp = new Date(); + let lastHeartBeatTimeStamp = this.getLastEventTimestamp(TelemetryEvent.HeartBeatSent); + + if (lastHeartBeatTimeStamp && + currentTimeStamp.getTime() - lastHeartBeatTimeStamp.getTime() < 24 * 60 * 60 * 1000) + return; + + if (!RapidsWebApi.reportHeartBeat()) return; + this.reportEvent(TelemetryEvent.HeartBeatSent); + } + + private _onHttpRequestSent(): void { + this._reportHeartBeatIfNecessary(); + } + + private _updateEventStats(event: TelemetryEvent): void { + let counterKey = TelemetryStorageKeys.counterKey(event); + let ltmstKey = TelemetryStorageKeys.lastTimestampKey(event); + + localStorage.setItem(ltmstKey, new Date().toISOString()); + + let currentNumLCValue = localStorage.getItem(counterKey); + if (!currentNumLCValue) { + localStorage.setItem(counterKey, "1"); + return; + } + + let currentParsedNum = Number.parseInt(currentNumLCValue); + if (Number.isNaN(currentParsedNum)) { + localStorage.setItem(counterKey, "1"); + return; + } + + localStorage.setItem(counterKey, String(currentParsedNum + 1)); + } +} + +export const Telemetry = new TelemetryImpl(); diff --git a/src/renderer/lib/TempManager.ts b/src/renderer/lib/TempManager.ts new file mode 100644 index 0000000..5aad3d9 --- /dev/null +++ b/src/renderer/lib/TempManager.ts @@ -0,0 +1,43 @@ +import * as path from "path"; +import * as os from "os"; +import * as fs from "fs"; + +declare const APP_ID: string; + +class HttpinessTempManager { + private readonly _directory = path.join(os.tmpdir(), APP_ID); + private readonly _directoryResponses = path.join(this._directory, "responses"); + + toTempFile(content: string, extension: string): string { + if (!fs.existsSync(this._directoryResponses)) { + fs.mkdirSync(this._directoryResponses, { recursive: true }); + } + + let fileName = path.join( + this._directoryResponses, + this._generateTempFileName() + "." + extension + ); + + fs.writeFileSync(fileName, content, "binary"); + + return fileName; + } + + trashTemp(): boolean { + throw new Error("Not implemented"); + } + + private _generateTempFileName(): string { + const length = 10; + + let result: Array = []; + let characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + let charactersLength = characters.length; + for (let i = 0; i < length; i++ ) { + result.push(characters.charAt(Math.floor(Math.random() * charactersLength))); + } + return result.join(""); + } +} + +export const TempManager = new HttpinessTempManager(); diff --git a/src/renderer/lib/Version.ts b/src/renderer/lib/Version.ts new file mode 100644 index 0000000..be485ba --- /dev/null +++ b/src/renderer/lib/Version.ts @@ -0,0 +1,60 @@ +declare const VERSION: string; + +export class Version { + public static readonly current: Version = Version.fromString(VERSION); + + private _major: number = 0; + private _minor: number = 0; + private _build: number = 0; + + public static fromString(version: string): Version { + try { + let v = new Version(); + + let parts = version.trim().split("."); + + if (parts.length != 3) return v; + + v._major = Number.parseInt(parts[0]); + v._minor = Number.parseInt(parts[1]); + v._build = Number.parseInt(parts[2]); + + return v; + } catch { + return new Version(); + } + } + + getMajor(): number { + return this._major; + } + + getMinor(): number { + return this._minor; + } + + getRevision(): number { + return this._build; + } + + isDefault(): boolean { + return (this._major == 0 && this._minor == 0 && this._build == 0); + } + + toString(includeRevision: boolean = false): string { + if (includeRevision) { + return `${this._major}.${this._minor}.${this._build}`; + } else { + return `${this._major}.${this._minor}`; + } + } + + newerThen(version: Version): boolean { + if ((this._major > version._major) || + (this._major == version._major && this._minor > version._minor)) return true; + + return false; + } +} + +console.log(Version.current.toString(true)); diff --git a/src/renderer/lib/WindowManipulation.ts b/src/renderer/lib/WindowManipulation.ts new file mode 100644 index 0000000..cb713f0 --- /dev/null +++ b/src/renderer/lib/WindowManipulation.ts @@ -0,0 +1,114 @@ +import { ipcRenderer, webFrame } from "electron"; + +import { currentPlatform, Platform } from "./Platform"; + +export async function minimize(): Promise { + await ipcRenderer.invoke("minimize"); +} + +export async function maximize(): Promise { + await ipcRenderer.invoke("maximize"); +} + +export async function unmaximize(): Promise { + await ipcRenderer.invoke("unmaximize"); +} + +export async function isMaximized(): Promise { + return await >ipcRenderer.invoke("isMaximized"); +} + +export async function close(): Promise { + await ipcRenderer.invoke("close"); +} + +export async function goFullScreen(): Promise { + await ipcRenderer.invoke("goFullScreen"); +} + +export async function exitFullScreen(): Promise { + await ipcRenderer.invoke("exitFullScreen"); +} + +export async function isInFullScreen(): Promise { + return await >ipcRenderer.invoke("isInFullScreen"); +} + +export async function goKiosk(): Promise { + await ipcRenderer.invoke("goKiosk"); +} + +export async function exitKiosk(): Promise { + await ipcRenderer.invoke("exitKiosk"); +} + +export async function isInKiosk(): Promise { + return await >ipcRenderer.invoke("isInKiosk"); +} + +export async function setMacOsTrafficLightsPosition(x: number, y: number): Promise { + await ipcRenderer.invoke("setMacOsTrafficLightsPosition", x, y); +} + +export class ZoomManager { + private static readonly _availableZooms: Array = [ 0.7, 0.8, 0.9, 1.0, 1.1, 1.2, 1.3 ]; + private static _currentZoomIndex: number; + private static _hasInit: boolean = false; + + static zoomIn(): void { + ZoomManager.init(); + + ZoomManager._currentZoomIndex++; + if (ZoomManager._currentZoomIndex > ZoomManager._availableZooms.length - 1) + ZoomManager._currentZoomIndex = ZoomManager._availableZooms.length - 1; + + ZoomManager._updateZoomLevel(); + } + + static zoomOut(): void { + ZoomManager.init(); + + ZoomManager._currentZoomIndex--; + if (ZoomManager._currentZoomIndex < 0) + ZoomManager._currentZoomIndex = 0; + + ZoomManager._updateZoomLevel(); + } + + static zoomReset(): void { + ZoomManager.init(); + + ZoomManager._currentZoomIndex = ZoomManager._availableZooms.indexOf(1.0); + ZoomManager._updateZoomLevel(); + } + + public static init(): void { + if (ZoomManager._hasInit) return; + + ZoomManager._hasInit = true; + + let result: number = Number.parseInt(localStorage.getItem("zoom")); + + if (Number.isNaN(result) || result < 0 || result > ZoomManager._availableZooms.length - 1) { + ZoomManager._currentZoomIndex = 3; + } else { + ZoomManager._currentZoomIndex = result; + } + + ZoomManager._updateZoomLevel(); + } + + private static _updateZoomLevel(): void { + let zoom = ZoomManager._availableZooms[ZoomManager._currentZoomIndex]; + webFrame.setZoomFactor(zoom); + localStorage.setItem("zoom", ZoomManager._currentZoomIndex.toString()); + + if (currentPlatform() == Platform.MacOS) { + setTimeout(async () => { + setMacOsTrafficLightsPosition(19 * zoom - 5, 19 * zoom - 7); + }, 0); + } + } +} + +ZoomManager.init(); diff --git a/src/renderer/lib/http/HttpRequest.ts b/src/renderer/lib/http/HttpRequest.ts new file mode 100644 index 0000000..289ce7f --- /dev/null +++ b/src/renderer/lib/http/HttpRequest.ts @@ -0,0 +1,154 @@ +/* eslint-disable @typescript-eslint/ban-types */ + +export enum HttpRequestMethod { + GET = "GET", + POST = "POST", + PUT = "PUT", + DELETE = "DELETE", + HEAD = "HEAD", + CONNECT = "CONNECT", + OPTIONS = "OPTIONS", + TRACE = "TRACE", + PATCH = "PATCH", + NONE = "NONE" +} + +export interface HttpHeaderRecord { + name: string; + value: string; +} + +export interface AutomaticHttpHeaderRecord { + name: string; + value: string; + editable: boolean; +} + +export enum HttpBodyContentType { + Text = "Text", File = "File" +} + +export class HttpFormRecord { + name: string; + value: string; + type: HttpBodyContentType; +} + +export enum HttpBodyType { + Regular = "Regular", Form = "Form" +} + +export abstract class HttpBody { + type: HttpBodyType; + + static fromPlainObject(plainObject: Object): HttpBody { + if (plainObject["type"] == HttpBodyType.Form) + return new HttpFormBody(plainObject); + else if (plainObject["type"] == HttpBodyType.Regular) { + return new HttpTextBody(plainObject); + } + + throw new Error(`Body type ${plainObject["type"]} not supported properly.`); + } + + abstract toPlainObject(): Object; + abstract clone(): HttpBody; +} + +export enum FormEncoding { + Multipart = "multipart/form-data", + UrlEncoded = "application/x-www-form-urlencoded" +} + +export class HttpFormBody extends HttpBody { + encoding: FormEncoding = FormEncoding.Multipart; + records: Array = []; + + constructor(plainObject: Object = null) { + super(); + + this.type = HttpBodyType.Form; + + if (plainObject == null) return; + this.encoding = plainObject["encoding"]; + this.records = plainObject["records"]; + } + + toPlainObject(): Object { + return { + type: this.type, + encoding: this.encoding, + records: this.records + }; + } + + clone(): HttpFormBody { + let clone = new HttpFormBody(); + clone.encoding = this.encoding; + clone.records = this.records.map(record => ({ + name: record.name, + value: record.value, + type: record.type + })); + return clone; + } +} + +export class HttpTextBody extends HttpBody { + value: string; + valueType: HttpBodyContentType; + + constructor(plainObject: Object = null) { + super(); + + this.type = HttpBodyType.Regular; + + if (plainObject == null) return; + this.value = plainObject["value"]; + this.valueType = plainObject["valueType"]; + } + + toPlainObject(): Object { + return { + type: this.type, + value: this.value, + valueType: this.valueType + }; + } + + clone(): HttpTextBody { + let clone = new HttpTextBody(); + clone.value = this.value; + clone.valueType = this.valueType; + return clone; + } +} + +export class HttpRequest { + method: HttpRequestMethod = HttpRequestMethod.GET; + url: string = ""; + headers: Array = []; + body: HttpBody = null; + + constructor(plainObject: Object = null) { + if (plainObject == null) return; + + this.method = plainObject["method"]; + this.url = plainObject["url"]; + this.headers = plainObject["headers"]; + + if (!plainObject["body"]) + this.body = null; + else + this.body = HttpBody.fromPlainObject(plainObject["body"]); + } + + toPlainObject(): Object { + return { + method: this.method, + url: this.url, + headers: this.headers, + body: this.body?.toPlainObject() ?? null + }; + } +} diff --git a/src/renderer/lib/http/HttpResponse.ts b/src/renderer/lib/http/HttpResponse.ts new file mode 100644 index 0000000..d365fa3 --- /dev/null +++ b/src/renderer/lib/http/HttpResponse.ts @@ -0,0 +1,157 @@ +import * as db from "mime-db"; +import * as path from "path"; +import * as fs from "fs"; + +import { TempManager } from "../TempManager"; +import { HttpHeaderRecord } from "./HttpRequest"; +import { MacroRecord } from "./Macro"; + +export class HttpContentType { + private _value: string = "unknown"; + private _type: string = "unknown"; + private _parameters: Array = []; + + static parse(headers: Array): HttpContentType { + const cntType = new HttpContentType(); + + const contentTypeHeaders = headers.filter(record => record.name.toUpperCase().trim() == "CONTENT-TYPE"); + if (contentTypeHeaders.length == 0) + return cntType; + + cntType._value = contentTypeHeaders[0].value; + + const sections = cntType._value.split(";"); + cntType._type = sections[0]; + + for (let i = 1; i <= sections.length - 1; i++) { + const [ name, value ] = sections[i].split("="); + if (!name || !value) continue; + cntType._parameters[name] = value; + } + + return cntType; + } + + getValue(): string { + return this._value; + } + + getType(): string { + return this._type; + } + + getBaseType(): string { + return this._type.split("/")[0]; + } + + getSubtype(): string { + let parts = this._type.split("/"); + if (parts.length == 1) + return "unknown"; + return parts[1]; + } + + getParameters(): Array { + return this._parameters; + } + + getDefaultExtension(): string { + let dbMimeRecord = db[this._type]; + + if (dbMimeRecord && + dbMimeRecord["extensions"] && + dbMimeRecord["extensions"].length && + dbMimeRecord["extensions"].length >= 1) + return String(dbMimeRecord["extensions"][0]); + + return "unknown"; + } + + getExtensions(): Array { + let dbMimeRecord = db[this._type]; + + if (dbMimeRecord && + dbMimeRecord["extensions"] && + dbMimeRecord["extensions"].length && + dbMimeRecord["extensions"].length >= 0) + return >dbMimeRecord["extensions"]; + + return []; + } +} + +export class HttpResponseBody { + private _content: string = null; + private _baseUrl: string = ""; + private _contentType: HttpContentType = null; + private _contentSize: number = 0; + private _tempFile: string = null; + + constructor(data: string, headers: Array, baseUrl: string) { + this._content = data; + this._baseUrl = baseUrl; + this._contentType = HttpContentType.parse(headers); + this._contentSize = this._content.length; + + if (this._contentSize > 1024 * 1024) { + this.getTempFile(); + this._content == null; + } + } + + /*private static _readContentLengthFromHeaders(headers: Array): number { + const contentTypeHeaders = headers.filter(record => record.name.toUpperCase().trim() == "CONTENT-LENGTH"); + if (contentTypeHeaders.length == 0) + return 0; + + try { + return Number.parseInt(contentTypeHeaders[0].value); + } catch (ex) { + return 0; + } + }*/ + + toString(encoding: "binary" | "utf-8" | "utf-16" = "binary"): string { + return new TextDecoder(encoding).decode(Buffer.from(this.getContent(), "binary")); + } + + getContent(): string { + if (this._content != null) + return this._content; + + return fs.readFileSync(this.getTempFile(), "binary"); + } + + getBaseUrl(): string { + return this._baseUrl; + } + + getContentType(): HttpContentType { + return this._contentType; + } + + getContentSize(): number { + return this._contentSize; + } + + getTempFile(): string { + if (this._tempFile == null) { + this._tempFile = TempManager.toTempFile(this._content, this._contentType.getDefaultExtension()); + } + return this._tempFile; + } + + getTempFilePosixStyle(): string { + return this.getTempFile().split(path.sep).join(path.posix.sep); + } + + toFile(path: string): void { + fs.writeFileSync(path, this._content, "binary"); + } +} + +export class HttpResponse { + status: number; + headers: Array; + body: HttpResponseBody; +} diff --git a/src/renderer/lib/http/HttpUrl.ts b/src/renderer/lib/http/HttpUrl.ts new file mode 100644 index 0000000..51c4b9a --- /dev/null +++ b/src/renderer/lib/http/HttpUrl.ts @@ -0,0 +1,66 @@ +export class HttpUrlQuery extends Array { + public static parse(stringQuery: string): Array { + if (stringQuery.length == 0) + return []; + + if (stringQuery.charAt(0) != "?") + return [ stringQuery ]; + + return stringQuery.substring(1).split("&").map((keyValuePair, index) => { + if (index == 0) + return "?" + keyValuePair; + + return "&" + keyValuePair; + }); + } +} + +export class HttpUrl { + public protocol: string = ""; + public hostname: string = ""; + public path: string = ""; + public query: string = ""; + public hash: string = ""; + + public static parse(stringUrl: string): HttpUrl { + + let parsedUrl: HttpUrl = new HttpUrl(); + + let protocolEnd = stringUrl.indexOf("://"); + let leftOver = stringUrl; + + if (protocolEnd != -1) { + parsedUrl.protocol = stringUrl.substring(0, protocolEnd + 3); + leftOver = leftOver.substring(protocolEnd + 3); + } + + let currentToken: keyof HttpUrl = "hostname"; + + for (let i = 0; i <= leftOver.length - 1; i++) { + const currentChar = leftOver.charAt(i); + + if (currentChar == "/" && parsedUrl.path == "" && + parsedUrl.query == "" && parsedUrl.hash == "") { + currentToken = "path"; + parsedUrl.path = parsedUrl.path + "/"; + } else if (currentChar == "?" && parsedUrl.query == "" && parsedUrl.hash == "") { + currentToken = "query"; + parsedUrl.query = parsedUrl.query + "?"; + } else if (currentChar == "#" && parsedUrl.hash == "") { + currentToken = "hash"; + parsedUrl.hash = parsedUrl.hash + "#"; + } else { + parsedUrl[currentToken] = parsedUrl[currentToken] + currentChar; + } + } + + //if (protocolEnd != -1) + // parsedUrl.hostname = "//" + parsedUrl.hostname; + + return parsedUrl; + } + + public toString(): string { + return this.protocol + this.hostname + this.path + this.query + this.hash; + } +} diff --git a/src/renderer/lib/http/Macro.ts b/src/renderer/lib/http/Macro.ts new file mode 100644 index 0000000..ce31e09 --- /dev/null +++ b/src/renderer/lib/http/Macro.ts @@ -0,0 +1,131 @@ +import { ISimpleEventable } from "../SimpleEvent"; + +export interface MacroRecord { + name: string; + value: string; +} + +export interface MacroPreset { + macros: Array; + name: string; +} + +export interface IMacroSource { + getMacroNames(): Array; +} + +export interface IReadOnlyMacroContext extends ISimpleEventable, IMacroSource { + eventMacroValueChanged: string; + + getMacroValue(name: string): Promise; + getMacroPublicValue(name: string): string; + isMacroSensitive(name: string): boolean; + isMacroEmpty(name: string): boolean; +} + +export interface IMacroContext extends IReadOnlyMacroContext { + setMacro(name: string, value: string, sensitive: boolean): Promise; + deleteMacro(name: string): this; + + getMacroPresets(): Array; + setMacroPresets(preset: Array): Promise; + applyMacroPreset(presetName: string): this; +} + +export enum MacroedTextPartType { + Parameter, PlainText, EqualityChar +} + +export class MacroedTextPart { + text: string; + type: MacroedTextPartType; +} + +export class MacroedText implements IMacroSource { + private _parts: Array = []; + + static parse(text: string, parseEqualityCharacter = false): MacroedText { + let parametricText = new MacroedText(); + + if (!text && text != "") { + return parametricText; + } + + if (text == "") { + parametricText._parts.push({ text: "", type: MacroedTextPartType.PlainText }); + return parametricText; + } + + let currentPartStartIndex = 0; + let currentPartType = MacroedTextPartType.PlainText; + + let cm1 = null; // character at offset -1 from current character + let c0 = null; // current character + let cp1 = text[0]; // character at offset +1 from current character + + for (let i = 0; i <= text.length - 1; i++) { + + cm1 = c0; + c0 = cp1; + cp1 = text[i + 1]; // when out of range, empty string is returned + + if (parseEqualityCharacter && currentPartType != MacroedTextPartType.Parameter && c0 == "=") { + parseEqualityCharacter = false; + + if (currentPartStartIndex != i) + parametricText._parts.push({ + text: text.substring(currentPartStartIndex, i), type: currentPartType + }); + + parametricText._parts.push({ + text: "=", type: MacroedTextPartType.EqualityChar + }); + + currentPartStartIndex = i + 1; + currentPartType = MacroedTextPartType.PlainText; + } else if (currentPartType != MacroedTextPartType.Parameter && c0 == "$" && cp1 == "{") { + if (currentPartStartIndex != i) + parametricText._parts.push({ + text: text.substring(currentPartStartIndex, i), type: currentPartType + }); + currentPartStartIndex = i; + currentPartType = MacroedTextPartType.Parameter; + } else if (currentPartType == MacroedTextPartType.Parameter && c0 == "}" && cm1 != "\\") { + + parametricText._parts.push({ + text: text.substring(currentPartStartIndex, i + 1), type: currentPartType + }); + + currentPartStartIndex = i + 1; + currentPartType = MacroedTextPartType.PlainText; + } + } + + if (currentPartStartIndex <= text.length - 1) { + parametricText._parts.push({ + text: text.substring(currentPartStartIndex), type: currentPartType + }); + } + + return parametricText; + } + + getParts(): Array { + return this._parts; + } + + getMacroNames(): Array { + let macroNames: Array = []; + + this._parts + .filter(part => part.type == MacroedTextPartType.Parameter && part.text.length >= 3) + .forEach(part => { + if (part.text[part.text.length - 1] == "}") + macroNames.push(part.text.substring(2, part.text.length - 1)); + else + macroNames.push(part.text.substring(2)); + }); + + return macroNames; + } +} diff --git a/src/renderer/lib/http/executors/CurlHttpExecutor.ts b/src/renderer/lib/http/executors/CurlHttpExecutor.ts new file mode 100644 index 0000000..7a34908 --- /dev/null +++ b/src/renderer/lib/http/executors/CurlHttpExecutor.ts @@ -0,0 +1,283 @@ +import { execFile } from "child_process"; +import * as process from "process"; +import * as path from "path"; +import * as fs from "fs"; + +import * as log from "../../Logger"; +import { currentPlatform, Platform } from "../../Platform"; +import { TempManager } from "../../TempManager"; + +import { HttpRequest, HttpBodyType, HttpFormBody, HttpBodyContentType, HttpTextBody, FormEncoding } from "../HttpRequest"; +import { HttpResponse, HttpResponseBody } from "../HttpResponse"; + +import { IHttpExecutor, HttpExecution, HttpExecutionMetadata } from "./HttpExecutor"; + +declare let DIST: boolean; +declare let VERSION: boolean; + +async function getCurlLocation(): Promise { + let basePath = path.join(process.cwd(), "dist/electron-workspace"); + if (DIST) + basePath = path.join(process.resourcesPath, "app.asar"); + + let osDir = ""; + let exeName = ""; + + const platform = currentPlatform(); + if (platform == Platform.Win32) { + osDir = "win32"; + exeName = "curl.exe"; + } else if (platform == Platform.MacOS) { + osDir = "macOS"; + exeName = "curl"; + } else { + throw new Error(`Platform ${platform} not supported.`); + } + + let location = path.join(basePath, "resources", "bin", osDir, "curl", exeName); + + if (await testCurl(location)) return location; + return "curl"; +} + +async function testCurl(path: string): Promise { + return new Promise((resolve) => { + try { + execFile(path, ["-V"], + (error, stdout) => { + if (error && !stdout) { + resolve(false); + return; + } + resolve(true); + } + ); + } catch (e: any) { + resolve(false); + } + }); +} + +let curlLocation; +(async (): Promise => curlLocation = await getCurlLocation())(); + +export class CurlHttpExecutorConfiguration { + timeoutInSeconds: number = 30; +} + +export class CurlHttpExecutor implements IHttpExecutor { + public static autoHeaderValuePlaceholder: string = ""; + public static responseBufferMaxSize = 20 * 1024 * 1024; + + private static _metadataDelimiter: string = "\r\n---METADATA---RandomIdentifier:6iOlEfOLcO3AuadaODxT\r\n"; + + private static _parseCurlResponse(responseOutput: string): HttpExecution { + let chunks = responseOutput.split(CurlHttpExecutor._metadataDelimiter); + + const responseChunk = chunks[0]; + const metaChunk = chunks[1]; + + let metadata = new HttpExecutionMetadata(); + metadata.executor = "curl"; + + let metadataRawObject = null; + + if (metaChunk) { + try { + metadataRawObject = JSON.parse(metaChunk.replace(/:0*\.?0*,/g, ":0,")); + } catch (ex) { + if (ex instanceof Error) + log.error(`Error parsing curl metadata JSON: ${ex.message}`, { error: ex }); + } + + if (metadataRawObject) { + metadata.executorVersion = metadataRawObject["curl_version"]; + metadata.errorMessage = metadataRawObject["errormsg"]; + metadata.httpVersion = metadataRawObject["http_version"]; + metadata.headerNumber = metadataRawObject["num_headers"]; + metadata.localIp = metadataRawObject["local_ip"]; + metadata.localPort = metadataRawObject["local_port"]; + metadata.remoteIp = metadataRawObject["remote_ip"]; + metadata.remotePort = metadataRawObject["remote_port"]; + metadata.effectiveUrl = metadataRawObject["url_effective"]; + metadata.redirectUrl = metadataRawObject["redirect_url"]; + metadata.numberOfRedirects = metadataRawObject["num_redirects"]; + metadata.executionTimeInSeconds = metadataRawObject["time_total"]; + metadata.downloadSizeInBytes = metadataRawObject["size_download"]; + metadata.uploadSizeInBytes = metadataRawObject["size_upload"]; + metadata.speedDownload = metadataRawObject["speed_download"]; + metadata.speedUpload = metadataRawObject["speed_upload"]; + + if (metadata.errorMessage != null) + return { response: null, metadata }; + } + } else { + metadata.errorMessage = "Metadata is missing"; + } + + let httpResponseParts = responseChunk.split("\r\n\r\n"); + + let headersAndStatus = httpResponseParts[0]; + + const headersAndStatusChunks = headersAndStatus.split("\r\n"); + const status = parseInt(headersAndStatusChunks[0].split(" ")[1]); + + let headers = headersAndStatusChunks.slice(1); + + let response = new HttpResponse(); + + response.status = status; + response.headers = headers.map(row => { + let parts = row.split(":"); + return { + name: parts[0].trim(), + value: parts.slice(1).join(":").trim() + }; + }); + + let parsedUrl = new URL(metadata.effectiveUrl); + let baseUrl = `${parsedUrl.protocol}//${parsedUrl.host}`; + + let body: HttpResponseBody = null; + if (httpResponseParts.length != 1) + body = new HttpResponseBody(httpResponseParts.slice(1).join("\r\n\r\n"), response.headers, baseUrl); + + response.body = body; + + return { response, metadata }; + } + + private static _buildCurlCmdArgs(request: HttpRequest): Array { + let cmdArgs: Array = [ + "-q", "-k", "-g", "-w", "\r\n---METADATA---RandomIdentifier:6iOlEfOLcO3AuadaODxT\r\n%{json}", "-i", "-X", request.method, "--url", request.url + ]; + + const userAgentHeader = request.headers.filter(header => header.name.toUpperCase().trim() == "USER-AGENT"); + + if (userAgentHeader.length == 0) { + request.headers.push({ + name: "User-Agent", + value: `httpiness/${VERSION}` + }); + } + + request.headers.forEach(header => { + cmdArgs.push("-H"); + cmdArgs.push(`${header.name}:${header.value}`); + }); + + if (!request.body) return cmdArgs; + + if (request.body.type == HttpBodyType.Form) { + cmdArgs.push("-H"); + cmdArgs.push("Expect:"); + let formBody = request.body; + if (formBody.encoding == FormEncoding.Multipart) { + formBody.records.forEach(record => { + if (record.type == HttpBodyContentType.Text) { + cmdArgs.push("--form-string"); + cmdArgs.push(`${record.name}=${record.value}`); + } else if (record.type == HttpBodyContentType.File) { + if (!fs.existsSync(record.value)) + throw new Error(`File ${record.value} does not exist.`); + + cmdArgs.push("-F"); + cmdArgs.push(`${record.name}=@${record.value}`); + } else { + throw new Error(`Request body content type ${record.type} is not supported.`); + } + }); + } else if (formBody.encoding == FormEncoding.UrlEncoded) { + formBody.records.forEach(record => { + if (!record.value) return; + if (record.type == HttpBodyContentType.Text) { + cmdArgs.push("--data-urlencode"); + cmdArgs.push(`${record.name}=${record.value}`); + } else if (record.type == HttpBodyContentType.File) { + if (!fs.existsSync(record.value)) + throw new Error(`File ${record.value} does not exist.`); + + cmdArgs.push("--data-urlencode"); + cmdArgs.push(`${record.name}=${fs.readFileSync(record.value)}`); + } else { + throw new Error(`Request body content type ${record.type} is not supported.`); + } + }); + } else { + throw new Error(`Request body form encoding ${formBody.encoding} is not supported.`); + } + + } else if (request.body.type == HttpBodyType.Regular) { + let regularBody = request.body; + if (regularBody.valueType == HttpBodyContentType.Text) { + if (regularBody.value.length > 10 * 1024) { + let file = TempManager.toTempFile(regularBody.value, "txt"); + cmdArgs.push("--data-binary"); + cmdArgs.push(`@${file}`); + cmdArgs.push("-H"); + cmdArgs.push("Expect:"); + } else { + let contentTypeHeader = request.headers.filter(header => header.name.toUpperCase().trim() == "CONTENT-TYPE"); + + if (contentTypeHeader.length == 0) { + cmdArgs.push("-H"); + cmdArgs.push("Content-Type:text/plain"); + } + + cmdArgs.push("--data-raw"); + cmdArgs.push(regularBody.value); + } + } else if (regularBody.valueType == HttpBodyContentType.File) { + if (!fs.existsSync(regularBody.value)) + throw new Error(`File ${regularBody.value} does not exist.`); + cmdArgs.push("--data-binary"); + cmdArgs.push(`@${regularBody.value}`); + cmdArgs.push("-H"); + cmdArgs.push("Expect:"); + } else { + throw new Error(`Request body content type ${regularBody.type} is not supported.`); + } + } else { + throw new Error(`Request body type ${request.body.type} is not supported.`); + } + + return cmdArgs; + } + + execute(request: HttpRequest): Promise { + return new Promise((resolve) => { + try { + execFile(curlLocation, CurlHttpExecutor._buildCurlCmdArgs(request), + { + encoding: "binary", + maxBuffer: CurlHttpExecutor.responseBufferMaxSize + }, + (error, stdout) => { + if (error && !stdout) { + log.error(`CurlHttpExecutor reported following error: ${error}`); + let metadata = new HttpExecutionMetadata(); + metadata.executor = "curl"; + metadata.errorMessage = "There was internal error while sending request."; + + resolve({ metadata, response: null }); + return; + } + resolve(CurlHttpExecutor._parseCurlResponse(stdout)); + }); + } catch (e: any) { + let metadata = new HttpExecutionMetadata(); + metadata.executor = "curl"; + + if (e instanceof Error) { + metadata.errorMessage = (e).message; + log.error(`Exception happened while sending request: ${e.message}`, { error: e }); + } else { + metadata.errorMessage = "Unknown error happened."; + log.error("Unknown exception happened while sending request.", { error: e }); + } + + resolve({ response: null, metadata }); + } + }); + } +} diff --git a/src/renderer/lib/http/executors/DummyHttpExecutor.ts b/src/renderer/lib/http/executors/DummyHttpExecutor.ts new file mode 100644 index 0000000..c9e2a8a --- /dev/null +++ b/src/renderer/lib/http/executors/DummyHttpExecutor.ts @@ -0,0 +1,22 @@ +import { HttpResponseBody } from "../HttpResponse"; +import { IHttpExecutor, HttpExecution } from "./HttpExecutor"; + +function delay(ms): Promise { + return new Promise(resolve => setTimeout(resolve, ms)); +} + +export class DummyHttpExecutor implements IHttpExecutor { + async execute(): Promise { + + await delay(2000); + + const status = 404; + const headers = [{ name: "Content-Type", value: "application/json" }]; + const body = new HttpResponseBody("SomeText", headers, ""); + + return { + response: { status, headers, body }, + metadata: null + }; + } +} diff --git a/src/renderer/lib/http/executors/FetchApiHttpExecutor.ts b/src/renderer/lib/http/executors/FetchApiHttpExecutor.ts new file mode 100644 index 0000000..7c95aee --- /dev/null +++ b/src/renderer/lib/http/executors/FetchApiHttpExecutor.ts @@ -0,0 +1,107 @@ +import { HttpRequest, HttpHeaderRecord, HttpBodyType, HttpFormBody, HttpBodyContentType, HttpTextBody } from "../HttpRequest"; +import { HttpResponseBody } from "../HttpResponse"; +import { IHttpExecutor, HttpExecution, HttpVersion } from "./HttpExecutor"; + +import * as fs from "fs"; + +export class FetchApiHttpExecutor implements IHttpExecutor { + async execute(request: HttpRequest): Promise { + + try { + let headers = new Headers(); + request.headers.forEach(header => headers.append(header.name, header.value)); + + const init: RequestInit = { + method: request.method, + headers, + mode: "cors" + }; + + if (request.body != null) { + if (request.body.type == HttpBodyType.Regular) { + let textBody: HttpTextBody = (request.body); + if (textBody.valueType == HttpBodyContentType.Text) { + init["body"] = (request.body).value; + } else { + init["body"] = fs.readFileSync(textBody.value, "utf-8"); + } + } else { + let formData = new FormData(); + const formBody: HttpFormBody = (request.body); + formBody.records.forEach(record => { + if (record.type == HttpBodyContentType.Text) { + formData.append(record.name, record.value); + } else { + formData.append(record.name, fs.readFileSync(record.value, "utf-8")); + } + }); + init["body"] = formData; + } + } + + const startTimeStamp = Date.now(); + let fetchResponse = await fetch(request.url, init); + const endTimeStamp = Date.now(); + + let responseHeaders: Array = []; + fetchResponse.headers.forEach((value, name) => responseHeaders.push({ name, value })); + + let parsedUrl = new URL(request.url); + let baseUrl = `${parsedUrl.protocol}//${parsedUrl.host}`; + + let body: HttpResponseBody = new HttpResponseBody(await fetchResponse.text(), responseHeaders, baseUrl); + + return { + response: { + status: fetchResponse.status, + headers: responseHeaders, + body + }, + metadata: { + timestamp: new Date(), + errorMessage: null, + executor: "fetch", + executorVersion: null, + httpVersion: HttpVersion.Unknown, + headerNumber: -1, + localIp: "", + localPort: "", + remoteIp: "", + remotePort: "", + effectiveUrl: "", + numberOfRedirects: -1, + redirectUrl: "", + executionTimeInSeconds: (endTimeStamp - startTimeStamp) / 1000.0, + downloadSizeInBytes: -1, + uploadSizeInBytes: -1, + speedDownload: -1, + speedUpload: -1 + } + }; + } catch (ex) { + return { + response: null, + metadata: { + timestamp: new Date(), + errorMessage: ex["message"] ? ex["message"] : "There was an error making Fetch API request.", + executor: "fetch", + executorVersion: null, + httpVersion: HttpVersion.Unknown, + headerNumber: -1, + localIp: "0", + localPort: "0", + remoteIp: "0", + remotePort: "0", + effectiveUrl: "", + numberOfRedirects: -1, + redirectUrl: "", + executionTimeInSeconds: -1, + downloadSizeInBytes: -1, + uploadSizeInBytes: -1, + speedDownload: -1, + speedUpload: -1 + } + }; + } + } +} diff --git a/src/renderer/lib/http/executors/HttpExecutor.ts b/src/renderer/lib/http/executors/HttpExecutor.ts new file mode 100644 index 0000000..e92d826 --- /dev/null +++ b/src/renderer/lib/http/executors/HttpExecutor.ts @@ -0,0 +1,39 @@ +import { HttpRequest } from "../HttpRequest"; +import { HttpResponse } from "../HttpResponse"; + +export enum HttpVersion { + Unknown = "Unknown", + Http1Dot0 = "1.0", + Http1Dot1 = "1.1", + Http2Dot0 = "2.0" +} + +export class HttpExecutionMetadata { + timestamp: Date = new Date(); + errorMessage: string; + executor: "dummy" | "fetch" | "curl"; + executorVersion: string; + httpVersion: HttpVersion; + headerNumber: number; + localIp: string; + localPort: string; + remoteIp: string; + remotePort: string; + effectiveUrl: string; + numberOfRedirects: number; + redirectUrl: string; + executionTimeInSeconds: number; + downloadSizeInBytes: number; + uploadSizeInBytes: number; + speedDownload: number; + speedUpload: number; +} + +export class HttpExecution { + response: HttpResponse; + metadata: HttpExecutionMetadata; +} + +export interface IHttpExecutor { + execute(request: HttpRequest): Promise ; +} diff --git a/src/renderer/lib/http/executors/index.ts b/src/renderer/lib/http/executors/index.ts new file mode 100644 index 0000000..4b7b3f5 --- /dev/null +++ b/src/renderer/lib/http/executors/index.ts @@ -0,0 +1,8 @@ +import { CurlHttpExecutor } from "./CurlHttpExecutor"; +import { IHttpExecutor } from "./HttpExecutor"; + +export { + IHttpExecutor, HttpExecution, HttpExecutionMetadata, HttpVersion +} from "./HttpExecutor"; + +export const HttpExecutor: IHttpExecutor = new CurlHttpExecutor(); diff --git a/src/renderer/lib/http/index.ts b/src/renderer/lib/http/index.ts new file mode 100644 index 0000000..89ba4e3 --- /dev/null +++ b/src/renderer/lib/http/index.ts @@ -0,0 +1,17 @@ + + +export { + HttpExecutor, IHttpExecutor, HttpExecution, HttpExecutionMetadata, HttpVersion +} from "./executors"; + +export * from "./templates"; + +export { HttpUrl } from "./HttpUrl"; + +export { MacroRecord, IMacroContext, IReadOnlyMacroContext, IMacroSource, + MacroedText, MacroedTextPart, MacroedTextPartType, MacroPreset } from "./Macro"; + +export { HttpBody, HttpFormBody, FormEncoding, HttpFormRecord, HttpRequestMethod, + HttpBodyContentType, HttpBodyType, HttpHeaderRecord, HttpRequest, HttpTextBody } from "./HttpRequest"; + +export { HttpResponse, HttpResponseBody, HttpContentType } from "./HttpResponse"; diff --git a/src/renderer/lib/http/templates/ExperimentCollection.ts b/src/renderer/lib/http/templates/ExperimentCollection.ts new file mode 100644 index 0000000..a403d1c --- /dev/null +++ b/src/renderer/lib/http/templates/ExperimentCollection.ts @@ -0,0 +1,57 @@ +import { HttpReqt, HttpCollection } from "."; + +const RequestPlainObjects = [ + { + name: "GitHub Zen", + rawRequest: { + method: "GET", + url: "https://api.github.com/zen", + headers: [] + } + }, + { + name: "Loopback JSON", + rawRequest: { + method: "POST", + url: "https://httpbin.org/anything/${ANY_ID}/sample?sort=${SORT}", + headers: [ + { name: "Authorization", value: "Bearer ${AUTH_TOKEN}" }, + { name: "Content-Type", value: "application/json" } + ], + body: { + type: "Regular", + value: "{\n\"action\": \"query\",\n\"object_type\": \"${OBJ_TYPE}\"\n}", + valueType: "Text" + } + } + }, + { + name: "Loopback URL-encoded", + rawRequest: { + method: "POST", + url: "https://httpbin.org/anything/${ANY_ID}/sample?sort=${SORT}", + headers: [ + { name: "Authorization", value: "Bearer ${AUTH_TOKEN}" }, + { name: "Content-Type", value: "application/x-www-form-urlencoded" } + ], + body: { + type: "Form", + encoding: "application/x-www-form-urlencoded", + records: [ + { name: "action", value: "query", type: "Text" }, + { name: "object_type", value: "${OBJECT_TYPE}", type: "Text" }] + } + } + } +]; + +const Macros = [ + { name: "ANY_ID", value: "ID123" }, + { name: "SORT", value: "desc" }, + { name: "AUTH_TOKEN", value: "Auth123" } +]; + +export function initExpCollection(collection: HttpCollection): void { + RequestPlainObjects.forEach(obj => collection.addReqt(new HttpReqt().fromPlainObject(obj))); + Macros.forEach(macro => collection.setMacro(macro.name, macro.value)); +} diff --git a/src/renderer/lib/http/templates/HttpAuth/AuthDefinition.ts b/src/renderer/lib/http/templates/HttpAuth/AuthDefinition.ts new file mode 100644 index 0000000..9c9ca8b --- /dev/null +++ b/src/renderer/lib/http/templates/HttpAuth/AuthDefinition.ts @@ -0,0 +1,67 @@ +export enum AuthType { + None = "None", + ApiKey = "ApiKey", + Basic = "Basic", + Bearer = "Bearer", + OAuth2 = "OAuth2" +} + +export interface NoneAuthDefinition { + readonly type: AuthType.None; +} + +export interface ApiKeyAuthDefinition { + readonly type: AuthType.ApiKey; + readonly apiKey?: string; +} + +export interface BearerAuthDefinition { + readonly type: AuthType.Bearer; + readonly bearerToken?: string; +} + +export interface BasicAuthDefinition { + readonly type: AuthType.Basic; + readonly username?: string; + readonly password?: string; +} + +export enum OAuth2Type { + AuthorizationCode = "AuthorizationCode", + Implicit = "Implicit", + ClientCredentials = "ClientCredentials" +} + +export enum OAuth2ClientAuthentication { + BasicAuthentication = "BasicAuthentication", + InBody = "InBody" +} + +export enum PkceCodeChallengeMethod +{ + None = "None", + Plain = "Plain", + SHA256 = "SHA256" +} + +export interface OAuth2AuthDefinition { + readonly type: AuthType.OAuth2; + readonly oauth2Type?: OAuth2Type; + + readonly codeChallengeMethod?: PkceCodeChallengeMethod; + readonly clientAuthentication?: OAuth2ClientAuthentication; + + readonly callbackURL?: string; + readonly authURL?: string; + readonly accessTokenURL?: string; + + readonly clientID?: string; + readonly clientSecret?: string; + + readonly scope?: string; + readonly state?: string; +} + +export type AuthDefinition = NoneAuthDefinition | + ApiKeyAuthDefinition | BearerAuthDefinition | + BasicAuthDefinition | OAuth2AuthDefinition; diff --git a/src/renderer/lib/http/templates/HttpAuth/AuthInserter.ts b/src/renderer/lib/http/templates/HttpAuth/AuthInserter.ts new file mode 100644 index 0000000..6a31e2e --- /dev/null +++ b/src/renderer/lib/http/templates/HttpAuth/AuthInserter.ts @@ -0,0 +1,101 @@ +/* eslint-disable camelcase */ +/* eslint-disable @typescript-eslint/naming-convention */ + +import { HttpBodyContentType, HttpRequest, HttpTextBody } from "../../HttpRequest"; +import { AuthorizationValue } from "./Authorizer"; + +export enum AuthLocationType { + Header = "Header", + UrlQuery = "UrlQuery", + UrlEncodedBody = "UrlEncodedBody" +} + +export interface AuthLocation { + type: AuthLocationType; + key: string; + prefix: string; +} + +export class AuthInserter { + public static insert(req: HttpRequest, value: AuthorizationValue, loc: AuthLocation): HttpRequest { + switch (loc.type) { + case AuthLocationType.UrlEncodedBody: return AuthInserter._urlEncodedBody_insert(req, value); + case AuthLocationType.UrlQuery: return AuthInserter._urlQuery_insert(req, value, loc); + default: return AuthInserter._header_insert(req, value, loc); + } + } + + private static _header_insert(req: HttpRequest, value: AuthorizationValue, loc: AuthLocation): HttpRequest { + if (!value) return req; + + let stringValue: string = null; + if (typeof value == "string") { + stringValue = value; + } else if (typeof value == "object") { + stringValue = Object.keys(value).map(key => `${key}=${value[key]}`).join(","); + } + + if (stringValue == null) return req; + + let authHeader = req.headers.find(record => record.name.trim().toLowerCase() == loc.key.toLowerCase()); + if (authHeader) { + authHeader.name = loc.key; + authHeader.value = `${loc.prefix.trim()} ${stringValue}`; + } else { + req.headers.push({ + name: loc.key, + value: `${loc.prefix.trim()} ${stringValue}` + }); + } + + return req; + } + + private static _urlQuery_insert(req: HttpRequest, value: AuthorizationValue, loc: AuthLocation): HttpRequest { + if (!value) return req; + + let stringValue = null; + if (typeof value == "string") { + stringValue = value; + } else if (typeof value == "object") { + throw new Error("AuthorizationValue of type object cannot be inserted to URL"); + } + + if (stringValue == null) return req; + + let url = new URL(req.url); + url.searchParams.set(loc.key, `${loc.prefix}${value}`); + req.url = url.toString(); + return req; + } + + private static _urlEncodedBody_insert(req: HttpRequest, value: AuthorizationValue): HttpRequest { + if (!value) return req; + + let stringValue = null; + if (typeof value == "string") { + stringValue = value; + } else if (typeof value == "object") { + stringValue = Object.keys(value).map(key => `${key}=${value[key]}`).join("&"); + } + + if (stringValue == null) return req; + + let authHeader = req.headers.find(record => record.name.trim().toLowerCase() == "content-type"); + if (authHeader) { + authHeader.value = "application/x-www-form-urlencoded"; + } else { + req.headers.push({ + name: "Content-Type", + value: "application/x-www-form-urlencoded" + }); + } + + let body = new HttpTextBody(); + body.valueType = HttpBodyContentType.Text; + body.value = stringValue; + req.body = body; + + return req; + } +} diff --git a/src/renderer/lib/http/templates/HttpAuth/Authorizer.ts b/src/renderer/lib/http/templates/HttpAuth/Authorizer.ts new file mode 100644 index 0000000..2126669 --- /dev/null +++ b/src/renderer/lib/http/templates/HttpAuth/Authorizer.ts @@ -0,0 +1,58 @@ +/* eslint-disable camelcase */ +/* eslint-disable @typescript-eslint/naming-convention */ + +import { IReadOnlyMacroContext, MacroedText } from "../../Macro"; + +import { ApiKeyAuthDefinition, AuthDefinition, AuthType, BasicAuthDefinition, + BearerAuthDefinition, OAuth2AuthDefinition } from "./AuthDefinition"; +import { OAuth2Authorizer } from "./OAuth2"; + +export type AuthorizationValue = string | Record; + +export interface AuthorizationResult { + value: AuthorizationValue; + error: string; +} + +export class Authorizer { + static async authorize(def: AuthDefinition, cnt: IReadOnlyMacroContext): Promise { + if (!def) return null; + + switch (def.type) { + case AuthType.ApiKey: return await Authorizer._apiKey_getAV(def, cnt); + case AuthType.Bearer: return await Authorizer._bearer_getAV(def, cnt); + case AuthType.Basic: return await Authorizer._basic_getAV(def, cnt); + case AuthType.OAuth2: return await Authorizer._oauth2_getAV(def, cnt); + default: return { value: null, error: null }; + } + } + + private static async _replaceMacros(text: string, macroContext: IReadOnlyMacroContext): Promise { + let macroedText = MacroedText.parse(text); + + for (let macro of macroedText.getMacroNames()) { + const macroValue = await macroContext.getMacroValue(macro); + text = text.replace("${" + macro + "}", macroValue); + } + return text; + } + + private static async _apiKey_getAV(def: ApiKeyAuthDefinition, cnt: IReadOnlyMacroContext): Promise { + return { value: await Authorizer._replaceMacros(def.apiKey, cnt), error: null }; + } + + private static async _bearer_getAV(def: BearerAuthDefinition, cnt: IReadOnlyMacroContext): Promise { + return { value: await Authorizer._replaceMacros(def.bearerToken, cnt), error: null }; + } + + private static async _basic_getAV(def: BasicAuthDefinition, cnt: IReadOnlyMacroContext): Promise { + let username = await Authorizer._replaceMacros(def.username, cnt); + let password = await Authorizer._replaceMacros(def.password, cnt); + + return { value: btoa(`${username}:${password}`), error: null }; + } + + private static async _oauth2_getAV(def: OAuth2AuthDefinition, cnt: IReadOnlyMacroContext): Promise { + return await OAuth2Authorizer.authorize(def, cnt); + } +} diff --git a/src/renderer/lib/http/templates/HttpAuth/HttpAuth.ts b/src/renderer/lib/http/templates/HttpAuth/HttpAuth.ts new file mode 100644 index 0000000..5fc9eef --- /dev/null +++ b/src/renderer/lib/http/templates/HttpAuth/HttpAuth.ts @@ -0,0 +1,147 @@ +/* eslint-disable @typescript-eslint/ban-types */ + +import { HttpRequest } from "../../HttpRequest"; +import { MacroedText } from "../../Macro"; + +import { HttpCollectionItem } from "../HttpCollectionItem"; +import { HttpDir } from "../HttpDir"; +import { HttpReqt } from "../HttpReqt"; + +import { AuthInserter, AuthLocation, AuthLocationType } from "./AuthInserter"; +import { AuthDefinition, AuthType } from "./AuthDefinition"; +import { AuthorizationValue, Authorizer } from "./Authorizer"; + + +export interface IAsyncMiddleware { + modify(item: T): Promise; +} + +export class HttpAuth extends HttpCollectionItem implements IAsyncMiddleware { + protected _authDef: AuthDefinition; + protected _location: AuthLocation; + protected _macroNames: Array = []; + + protected _authValue: AuthorizationValue; + + fromPlainObject(plainObject: Object = null): this { + if (plainObject == null) return this; + + if (plainObject["name"]) + this.setName(plainObject["name"]); + + this._authDef = plainObject["authDef"]; + this._location = plainObject["location"]; + this._enumerateMacroNames(); + + return this; + } + + setParent(parent: HttpDir | HttpReqt): this { + super.setParent(parent); + return this; + } + + getParent(): HttpDir | HttpReqt { + return this._parent; + } + + getMacroNames(): Array { + return this._macroNames; + } + + async authorize(): Promise { + let result = await Authorizer.authorize(this._authDef, this.getContainingCollection()); + if (result.error) + return result.error; + + this._authValue = result.value; + return ""; + } + + async modify(request: HttpRequest): Promise { + if (this._authDef.type != AuthType.OAuth2 || !this._authValue) { + let error = await this.authorize(); + if (error) { + console.log(`There was an error during authorization: ${error}`); + } + } + return AuthInserter.insert(request, this._authValue, this._getEffectiveLocation()); + } + + toPlainObject(): Object { + return { + name: this.getName(), + authDef: this._authDef, + location: this._location + }; + } + + setAuthDefinition(authDef: AuthDefinition): this { + this._authDef = authDef; + this._enumerateMacroNames(); + this._makeDirty(); + return this; + } + + getAuthDefinition(): AuthDefinition { + return this._authDef; + } + + setAuthLocation(location: AuthLocation): this { + this._location = location; + this._enumerateMacroNames(); + this._makeDirty(); + return this; + } + + getAuthLocation(): AuthLocation { + return this._location; + } + + clone(): HttpAuth { + return new HttpAuth().setParent(this.getParent()).fromPlainObject(this.toPlainObject()); + } + + _initSymLinks(): void { + return; + } + + private _getEffectiveLocation(): AuthLocation { + if (this._location) + return this._location; + + let authType = this._authDef.type; + + if (authType == AuthType.ApiKey) { + return { type: AuthLocationType.Header, key: "Authorization", prefix: "" }; + } + + if (authType == AuthType.Basic) { + return { type: AuthLocationType.Header, key: "Authorization", prefix: "Basic " }; + } + + return { type: AuthLocationType.Header, key: "Authorization", prefix: "Bearer " }; + } + + private _enumerateMacroNames(): void { + let macroNames: Array = []; + + const parsePlainObject = (obj: AuthDefinition | AuthLocation):void => { + if (!obj) return; + + for (let key in obj) { + let macroedText = MacroedText.parse(obj[key]); + let names = macroedText.getMacroNames(); + names.forEach(name => { + if (macroNames.indexOf(name) == -1) + macroNames.push(name); + }); + } + }; + + parsePlainObject(this._authDef); + parsePlainObject(this._location); + + this._macroNames = macroNames; + } +} diff --git a/src/renderer/lib/http/templates/HttpAuth/OAuth2.ts b/src/renderer/lib/http/templates/HttpAuth/OAuth2.ts new file mode 100644 index 0000000..80a9739 --- /dev/null +++ b/src/renderer/lib/http/templates/HttpAuth/OAuth2.ts @@ -0,0 +1,293 @@ +import { ipcRenderer } from "electron"; + +import { HttpExecutor } from "../../executors"; +import { HttpBodyContentType, HttpRequest, HttpRequestMethod, HttpTextBody } from "../../HttpRequest"; +import { IReadOnlyMacroContext, MacroedText } from "../../Macro"; + +import { OAuth2AuthDefinition, OAuth2ClientAuthentication as OAuth2ClientAuthentication, OAuth2Type, PkceCodeChallengeMethod } from "./AuthDefinition"; +import { AuthorizationResult } from "./Authorizer"; + +export class OAuth2Authorizer { + private _definition: OAuth2AuthDefinition; + private _macroResolvedDefinition: OAuth2AuthDefinition; + private _macroContext: IReadOnlyMacroContext; + private _codeVerifier: string = null; + + constructor(definition: OAuth2AuthDefinition, macroContext: IReadOnlyMacroContext) { + this._definition = definition; + this._macroContext = macroContext; + } + + public static async authorize(definition: OAuth2AuthDefinition, macroContext: IReadOnlyMacroContext): Promise { + return new OAuth2Authorizer(definition, macroContext)._authorize(); + } + + private static async _showOAuth2Window(url: string, redirectHost: string, queryKey: string): Promise { + return await >(ipcRenderer.invoke("oauth2", url, redirectHost, queryKey)); + } + + private static _generateCodeVerifier(): string { + let array = new Uint32Array(56 / 2); + window.crypto.getRandomValues(array); + return Array.from(array, dec => ("0" + dec.toString(16)).substr(-2)).join(""); + } + + private static _isValidUrl(url: string): boolean { + try { + let parsedUrl = new URL(url); + return parsedUrl.protocol === "http:" || parsedUrl.protocol === "https:"; + } catch { + return false; + } + } + + private async _generateSha256CodeChallenge(): Promise { + let data = new TextEncoder().encode(this._codeVerifier); + let bytes = new Uint8Array(await window.crypto.subtle.digest("SHA-256", data)); + + let str = ""; + for (let i = 0; i < bytes.byteLength; i++) { + str += String.fromCharCode(bytes[i]); + } + + return btoa(str) + .replace(/\+/g, "-") + .replace(/\//g, "_") + .replace(/=+$/, ""); + } + + private async _authorize(): Promise { + + try { + this._macroResolvedDefinition = await this._replaceMacrosInAuthDefinition(); + let validationResult = this._validateAuthDefinition(); + + if (validationResult != null) + return { value: null, error: validationResult }; + + let result: AuthorizationResult = null; + + if (this._macroResolvedDefinition.oauth2Type == OAuth2Type.AuthorizationCode) { + result = await this._doAuthorizationCodeFlow(); + } else if (this._macroResolvedDefinition.oauth2Type == OAuth2Type.Implicit) { + result = await this._doImplicitFlow(); + } else if (this._macroResolvedDefinition.oauth2Type == OAuth2Type.ClientCredentials) { + result = await this._doClientCredentialsFlow(); + } + + this._macroResolvedDefinition = null; + return result; + } catch (ex) { + return { value: null, error: String(ex) }; + } + } + + private async _replaceMacrosInString(text: string): Promise { + if (!text) return text; + + let macroedText = MacroedText.parse(text); + + for (let macro of macroedText.getMacroNames()) { + const macroValue = await this._macroContext.getMacroValue(macro); + text = text.replace("${" + macro + "}", macroValue); + } + + return text; + } + + private async _replaceMacrosInAuthDefinition(): Promise { + let def = this._definition; + + let [ callbackURL, authURL, accessTokenURL, clientID, clientSecret, scope, state ] = + await Promise.all([ + def.callbackURL, def.authURL, def.accessTokenURL, def.clientID, + def.clientSecret, def.scope, def.state] + .map(input => this._replaceMacrosInString(input))); + + return { + type: def.type, + oauth2Type: def.oauth2Type, + codeChallengeMethod: def.codeChallengeMethod, + clientAuthentication: def.clientAuthentication, + callbackURL, authURL, accessTokenURL, clientID, clientSecret, scope, state + }; + } + + private _validateAuthDefinition(): string { + const errorMessage = (paramName: string): string => + `${paramName} cannot be empty when OAuth2 type is set to ${this._macroResolvedDefinition.oauth2Type}`; + + const notValidUrlMessage = (paramName: string): string => + `${paramName} must be valid URL.`; + + if (!this._macroResolvedDefinition.clientID) + return "Client ID cannot be empty"; + + if (this._macroResolvedDefinition.oauth2Type == OAuth2Type.AuthorizationCode) { + if (!this._macroResolvedDefinition.callbackURL) + return errorMessage("Callback URL"); + if (!OAuth2Authorizer._isValidUrl(this._macroResolvedDefinition.callbackURL)) + return notValidUrlMessage("Callback URL"); + if (!this._macroResolvedDefinition.authURL) + return errorMessage("Auth URL"); + if (!OAuth2Authorizer._isValidUrl(this._macroResolvedDefinition.authURL)) + return notValidUrlMessage("Auth URL"); + if (!this._macroResolvedDefinition.accessTokenURL) + return errorMessage("Access token URL"); + if (!OAuth2Authorizer._isValidUrl(this._macroResolvedDefinition.accessTokenURL)) + return notValidUrlMessage("Access token URL"); + if (!this._macroResolvedDefinition.clientSecret) + return errorMessage("Client secret"); + return null; + } + + if (this._macroResolvedDefinition.oauth2Type == OAuth2Type.Implicit) { + if (!this._macroResolvedDefinition.callbackURL) + return errorMessage("Callback URL"); + if (!this._macroResolvedDefinition.authURL) + return errorMessage("Auth URL"); + return null; + } + + if (this._macroResolvedDefinition.oauth2Type == OAuth2Type.ClientCredentials) { + if (!this._macroResolvedDefinition.accessTokenURL) + return errorMessage("Access token"); + if (!this._macroResolvedDefinition.clientSecret) + return errorMessage("Client Secret"); + return null; + } + + return `OAuth2 authorization type ${this._macroResolvedDefinition.oauth2Type} is not supported`; + } + + private async _doAuthorizationCodeFlow(): Promise { + let url = await this._buildAuthorizationUrl("code"); + let code = await OAuth2Authorizer._showOAuth2Window(url, this._macroResolvedDefinition.callbackURL, "code"); + + if (code == null) { + return { value: null, error: "Authorization code browser error" }; + } + + return await this._doTokenRequest(code); + } + + private async _doImplicitFlow(): Promise { + + let url = await this._buildAuthorizationUrl("token"); + let token = await OAuth2Authorizer._showOAuth2Window(url, this._macroResolvedDefinition.callbackURL, "access_token"); + + if (token == null) { + return { value: null, error: "Authorization code browser error" }; + } + + return { value: token, error: "" }; + } + + private async _doClientCredentialsFlow(): Promise { + return await this._doTokenRequest(); + + } + + private async _buildAuthorizationUrl(type: "token" | "code"): Promise { + + let url = new URL(this._macroResolvedDefinition.authURL); + url.searchParams.append("response_type", type); + url.searchParams.append("client_id", this._macroResolvedDefinition.clientID); + url.searchParams.append("redirect_uri", this._macroResolvedDefinition.callbackURL); + url.searchParams.append("scope", this._macroResolvedDefinition.scope); + + if (this._macroResolvedDefinition.oauth2Type == OAuth2Type.AuthorizationCode && + this._macroResolvedDefinition.codeChallengeMethod != PkceCodeChallengeMethod.None) { + + this._codeVerifier = OAuth2Authorizer._generateCodeVerifier(); + + if (this._macroResolvedDefinition.codeChallengeMethod == PkceCodeChallengeMethod.SHA256) { + url.searchParams.append("code_challenge_method", "S256"); + url.searchParams.append("code_challenge", await this._generateSha256CodeChallenge()); + } else { + url.searchParams.append("code_challenge_method", "plain"); + url.searchParams.append("code_challenge", this._codeVerifier); + } + } + + if (this._macroResolvedDefinition.state) + url.searchParams.append("state", this._macroResolvedDefinition.state); + + return url.toString(); + } + + private async _doTokenRequest(authorizationCode: string = null): Promise { + let request = new HttpRequest(); + request.method = HttpRequestMethod.POST; + request.url = this._macroResolvedDefinition.accessTokenURL; + + let searchParams = new URLSearchParams(); + searchParams.append("client_id", this._macroResolvedDefinition.clientID); + + if (this._macroResolvedDefinition.clientAuthentication == OAuth2ClientAuthentication.BasicAuthentication) { + let token = btoa(`${this._macroResolvedDefinition.clientID}:${this._macroResolvedDefinition.clientSecret}`); + request.headers.push({ name: "Authorization", value: `Basic ${token}` }); + } else { + searchParams.append("client_secret", this._macroResolvedDefinition.clientSecret); + } + + if (authorizationCode == null) { + searchParams.append("grant_type", "client_credentials"); + } else { + searchParams.append("grant_type", "authorization_code"); + searchParams.append("code", authorizationCode); + searchParams.append("redirect_uri", this._macroResolvedDefinition.callbackURL); + } + + if (this._codeVerifier) { + searchParams.append("code_verifier", this._codeVerifier); + } + + let body = new HttpTextBody(); + body.valueType = HttpBodyContentType.Text; + body.value = searchParams.toString(); + + request.body = body; + + let execution = await HttpExecutor.execute(request); + + try { + if (!execution.response || execution.response.status >= 300) + throw new Error(); + + try { + let jsonBody = JSON.parse(execution.response.body.getContent()); + return { + value: jsonBody["access_token"], + error: "" + }; + } catch { + try { + let query = new URLSearchParams(execution.response.body.getContent()); + let accessToken = query.get("access_token"); + + if (!accessToken) + throw new Error(); + + return { + value: accessToken, + error: "" + }; + } catch { + throw new Error(); + } + } + } catch (ex) { + let error = ""; + + if (!execution.response) + error = execution.metadata.errorMessage; + else + error = `Status code: ${execution.response.status}; Payload: ${execution.response.body.getContent()}`; + + return { + value: null, error + }; + } + } +} diff --git a/src/renderer/lib/http/templates/HttpAuth/index.ts b/src/renderer/lib/http/templates/HttpAuth/index.ts new file mode 100644 index 0000000..68ddec3 --- /dev/null +++ b/src/renderer/lib/http/templates/HttpAuth/index.ts @@ -0,0 +1,5 @@ +export { AuthLocation, AuthLocationType } from "./AuthInserter"; +export { AuthType, AuthDefinition, NoneAuthDefinition, ApiKeyAuthDefinition, BearerAuthDefinition, + BasicAuthDefinition, OAuth2AuthDefinition, OAuth2Type, OAuth2ClientAuthentication, + PkceCodeChallengeMethod } from "./AuthDefinition"; +export { HttpAuth } from "./HttpAuth"; diff --git a/src/renderer/lib/http/templates/HttpCollection.ts b/src/renderer/lib/http/templates/HttpCollection.ts new file mode 100644 index 0000000..40b819b --- /dev/null +++ b/src/renderer/lib/http/templates/HttpCollection.ts @@ -0,0 +1,384 @@ +import * as fs from "fs"; +import * as path from "path"; +import * as keytar from "keytar"; +import * as uuid from "uuid"; + +import { IMacroContext, MacroPreset } from "../Macro"; +import { FormEncoding } from "../HttpRequest"; + +import { HttpReqt } from "./HttpReqt"; +import { HttpDir } from "./HttpDir"; +import { HttpAuth } from "./HttpAuth"; +import { initExpCollection } from "./ExperimentCollection"; + +declare const APP_ID: string; + +export enum CollectionOpeningError { Success, UnsupportedVersion, UnknownVersion, UnknownError } + +export interface CollectionOpeningResult { + error: CollectionOpeningError; + collection: HttpCollection; +} + +class CollectionVersionUpdater { + static from0Dot9To0Dot10(plainObject: any): any { + + let _updateDir = (dir: any): any => { + dir["authChildren"] = []; + if (dir["reqtChildren"]) { + for (let i = 0; i <= dir["reqtChildren"].length - 1; i++) { + dir["reqtChildren"][i]["auth"] = null; + } + } + if (dir["dirChildren"]) { + for (let i = 0; i <= dir["dirChildren"].length - 1; i++) { + dir["dirChildren"][i] = _updateDir(dir["dirChildren"][i]); + } + } + + // eslint-disable-next-line @typescript-eslint/no-unsafe-return + return dir; + }; + + plainObject["collectionVersion"] = "httpiness/JSON/0.10"; + // eslint-disable-next-line @typescript-eslint/no-unsafe-return + return _updateDir(plainObject); + } + + static from0Dot10To0Dot11(plainObject: any): any { + let _updateDir = (dir: any): any => { + if (dir["reqtChildren"]) { + if (!Array.isArray(dir["reqtChildren"])) return; + + for (let i = 0; i <= dir["reqtChildren"].length - 1; i++) { + let request = dir["reqtChildren"][i].rawRequest; + + if (!request) continue; + if (!request.body) continue; + + if (request.body["type"] == "Form") { + request.body["encoding"] = FormEncoding.Multipart; + } else if (request.body["type"] == "Regular") { + try { + if (request.body["valueType"] != "Text") continue; + if (request.headers && Array.isArray(request.headers)) { + let header = request.headers.find(header => + header["name"].toUpperCase() == "CONTENT-TYPE" && + header["value"].toUpperCase() == "APPLICATION/X-WWW-FORM-URLENCODED" + ); + + if (!header) continue; + if (!request.body["value"]) continue; + + let parsedUrlEncoded = request.body["value"].split("&"); + request.body = { + type: "Form", + encoding: FormEncoding.UrlEncoded, + records: [] + }; + + for (let elem of parsedUrlEncoded) { + let [ name, value ] = elem.split("="); + + if (!value) value = ""; + + if (name) { + request.body.records.push({ + type: "Text", name, value + }); + } + } + } + } catch { + continue; + } + } + } + } + + if (dir["dirChildren"]) { + for (let i = 0; i <= dir["dirChildren"].length - 1; i++) { + dir["dirChildren"][i] = _updateDir(dir["dirChildren"][i]); + } + } + + // eslint-disable-next-line @typescript-eslint/no-unsafe-return, consistent-return + return dir; + }; + + plainObject["collectionVersion"] = "httpiness/JSON/0.11"; + + return _updateDir(plainObject); + } +} + +/* eslint-disable @typescript-eslint/ban-types */ +export class HttpCollection extends HttpDir implements IMacroContext { + public static readonly localStorageExpCollectionPath = "local-storage-exp"; + private static readonly _defaultSensitiveValue = "******sensitive******"; + + public eventMacroValueChanged = "macroValueChanged"; + + private _pathToFile: string = ""; + private _lastSavedTimestamp: Date = null; + private _dirty: boolean = false; + + private _macros: Record = {}; + private _macroPresets: Array = []; + private _uuid: string = ""; + + private constructor() { + super(); + + this._uuid = uuid.v4(); + this._macroPresets = [ { name: "Development", macros: [] }, { name: "Production", macros: [] } ]; + } + + public static fromFile(pathToFile: string): CollectionOpeningResult { + try { + let collection = new HttpCollection(); + + collection._pathToFile = pathToFile; + collection._name = HttpCollection._getCollectionName(pathToFile); + collection._lastSavedTimestamp = HttpCollection._getLastModifiedFileTime(pathToFile); + + let content = HttpCollection._readContent(pathToFile); + + if (content == null) { + collection.save(); + if (collection.isLocalExperimental()) + initExpCollection(collection); + return { error: CollectionOpeningError.Success, collection}; + } + + let parsedObject = JSON.parse(content); + + let version = parsedObject["collectionVersion"]; + + if (typeof version != "string") + return { error: CollectionOpeningError.UnknownVersion, collection: null}; + + if (version == "httpiness/JSON/0.9") { + parsedObject = CollectionVersionUpdater.from0Dot9To0Dot10(parsedObject); + parsedObject = CollectionVersionUpdater.from0Dot10To0Dot11(parsedObject); + } else if (version == "httpiness/JSON/0.10") { + parsedObject = CollectionVersionUpdater.from0Dot10To0Dot11(parsedObject); + } else if (version != "httpiness/JSON/0.11") { + if (version.startsWith("httpiness/JSON/")) + return { error: CollectionOpeningError.UnsupportedVersion, collection: null}; + else + return { error: CollectionOpeningError.UnknownVersion, collection: null}; + } + + if (parsedObject["uuid"]) + collection._uuid = parsedObject["uuid"]; + + collection._authChildren = [...parsedObject["authChildren"]].map(obj => new HttpAuth().setParent(collection).fromPlainObject(obj)); + collection._dirChildren = [...parsedObject["dirChildren"]].map(obj => new HttpDir().setParent(collection).fromPlainObject(obj)); + collection._reqtChildren = [...parsedObject["reqtChildren"]].map(obj => new HttpReqt().setParent(collection).fromPlainObject(obj)); + collection._macros = parsedObject["parameters"]; + + if (parsedObject["parameterPresets"]) + collection._macroPresets = parsedObject["parameterPresets"]; + else + collection._macroPresets = [ { name: "Development", macros: [] }, { name: "Production", macros: [] } ]; + + collection._initSymLinks(); + + return { error: CollectionOpeningError.Success, collection}; + } catch (ex) { + console.log(ex); + return { error: CollectionOpeningError.UnknownError, collection: null}; + } + } + + static isValidPath(path: string): boolean { + return path == HttpCollection.localStorageExpCollectionPath || fs.existsSync(path); + } + + private static _readContent(pathToFile: string): string { + if (pathToFile == HttpCollection.localStorageExpCollectionPath) { + return localStorage.getItem(HttpCollection.localStorageExpCollectionPath); + } + + if (!fs.existsSync(pathToFile)) return null; + return fs.readFileSync(pathToFile, "utf-8"); + } + + private static _getCollectionName(pathToFile: string): string { + if (pathToFile == HttpCollection.localStorageExpCollectionPath) { + return "Experiment"; + } + + return path.parse(pathToFile).name; + } + + private static _getLastModifiedFileTime(pathToFile: string): Date { + if (pathToFile == HttpCollection.localStorageExpCollectionPath) { + return null; + } + + try { + let stats = fs.statSync(pathToFile); + if (stats) return stats.mtime; + return null; + } catch { + return null; + } + } + + getUuid(): string { + return this._uuid; + } + + setName(name: string): this { + throw new Error(`Collection name cannot be changed at all, including to ${name}.`); + } + + getFilePath(): string { + return this._pathToFile; + } + + isLocalExperimental(): boolean { + return this._pathToFile == HttpCollection.localStorageExpCollectionPath; + } + + toPlainObject(): Object { + let macrosRecs = {}; + this.getMacroNames().forEach(name => macrosRecs[name] = this._macros[name] ?? "" ); + return { + collectionVersion: "httpiness/JSON/0.11", + uuid: this._uuid, + authChildren: this._authChildren.map(a => a.toPlainObject()), + dirChildren: this._dirChildren.map(d => d.toPlainObject()), + reqtChildren: this._reqtChildren.map(r => r.toPlainObject()), + parameters: macrosRecs, + parameterPresets: this._macroPresets + }; + } + + _makeDirty(): void { + this._dirty = true; + } + + isDirty(): boolean { + return this._dirty; + } + + save(): void { + let plainObject = this.toPlainObject(); + + if (this.isLocalExperimental()) { + localStorage.setItem(this._pathToFile, JSON.stringify(plainObject)); + } else { + fs.writeFileSync(this._pathToFile, JSON.stringify(plainObject, null, 2), "utf-8"); + } + + this._lastSavedTimestamp = HttpCollection._getLastModifiedFileTime(this._pathToFile); + this._dirty = false; + } + + isModifiedExternally(): boolean { + if (this.isLocalExperimental()) false; + let lastModifiedTime = HttpCollection._getLastModifiedFileTime(this._pathToFile); + return this._lastSavedTimestamp != null && lastModifiedTime != null && + this._lastSavedTimestamp.getTime() != lastModifiedTime.getTime(); + } + + async setMacro(name: string, value: string = null, sensitive: boolean = false): Promise { + let raiseValueChangedEvent = true; + + if (value == null) { + raiseValueChangedEvent = false; + value = await this.getMacroValue(name); + } else if (sensitive) { + raiseValueChangedEvent = false; + } else { + if (value == await this.getMacroValue(name)) { + raiseValueChangedEvent = false; + } + } + + if (sensitive) { + this._macros[name] = HttpCollection._defaultSensitiveValue; + if (value != null && value.length != 0) + keytar.setPassword(APP_ID, this._keychainAccountName(name), value); + } else { + keytar.deletePassword(APP_ID, this._keychainAccountName(name)); + this._macros[name] = value; + } + + if (raiseValueChangedEvent) { + this.raise(this.eventMacroValueChanged, { + macroName: name, macroValue: this._macros[name] + }); + } + + this._makeDirty(); + } + + deleteMacro(name: string): this { + keytar.deletePassword(APP_ID, this._keychainAccountName(name)); + delete this._macros[name]; + this._makeDirty(); + return this; + } + + async getMacroValue(name: string): Promise { + let value = this.getMacroPublicValue(name); + + if (value == HttpCollection._defaultSensitiveValue) { + value = await keytar.getPassword(APP_ID, this._keychainAccountName(name)); + if (value == null) + value = ""; + } + + return value; + } + + getMacroPublicValue(name: string): string { + if (!(name in this._macros)) return ""; + return this._macros[name]; + } + + isMacroSensitive(name: string): boolean { + return this._macros[name] == HttpCollection._defaultSensitiveValue; + } + + isMacroEmpty(name: string): boolean { + return this.getMacroPublicValue(name).length == 0; + } + + getMacroPresets(): Array { + return this._macroPresets.map(preset => + ({ name: preset.name, macros: preset.macros.map(macro => ({ ...macro })) }) + ); + } + + setMacroPresets(presets: Array): Promise { + this._macroPresets = presets.map(preset => + ({ name: preset.name, macros: preset.macros.map(macro => ({ ...macro })) }) + ); + + this._makeDirty(); + + return Promise.resolve(); + } + + applyMacroPreset(presetName: string): this { + let existingPreset = this._macroPresets.find(p => p.name == presetName); + + if (!existingPreset) return this; + + existingPreset.macros.forEach(macro => { + if (!macro.value) return; + this.setMacro(macro.name, macro.value, this.isMacroSensitive(macro.name)); + }); + + return this; + } + + private _keychainAccountName(macroName: string): string { + return macroName + "_" + this._uuid; + } +} diff --git a/src/renderer/lib/http/templates/HttpCollectionItem.ts b/src/renderer/lib/http/templates/HttpCollectionItem.ts new file mode 100644 index 0000000..7c9099a --- /dev/null +++ b/src/renderer/lib/http/templates/HttpCollectionItem.ts @@ -0,0 +1,88 @@ +import { ISimpleEventable, SimpleEventBroker, SimpleEventListener } from "../../SimpleEvent"; +import { IMacroSource } from "../Macro"; + +import { HttpCollection } from "./HttpCollection"; + +export abstract class HttpCollectionItem implements ISimpleEventable, IMacroSource { + public eventNameChanged = "nameChanged"; + public eventAboutToBeDeleted = "aboutToBeDeleted"; + + protected _name: string = ""; + protected _parent: HttpCollectionItem = null; + + private _eventBroker: SimpleEventBroker = new SimpleEventBroker(this); + + setName(name: string): this { + if (this._name == name) return this; + + // TODO: We need to check if sibling with same name already exists + + this._name = name; + this._makeDirty(); + this.raise(this.eventNameChanged); + return this; + } + + getName(): string { + return this._name; + } + + getFullPath(): string { + let pathElements: Array = []; + + let parent = this; + + if (parent._parent == null) return ""; + + while (parent._parent != null) { + pathElements.push(parent.getName()); + parent = parent._parent; + } + + return "/" + pathElements.reverse().join("/"); + } + + setParent(parent: HttpCollectionItem): this { + if (this._parent == parent) return this; + + this._parent = parent; + this._makeDirty(); + return this; + } + + getParent(): HttpCollectionItem { + return this._parent; + } + + getContainingCollection(): HttpCollection { + let parent = this; + + while (parent._parent != null) { + parent = parent._parent; + } + + return parent; + } + + on(eventName: string, handler: SimpleEventListener): this { + this._eventBroker.on(eventName, handler); + return this; + } + + off(eventName: string, handler: SimpleEventListener): this { + this._eventBroker.off(eventName, handler); + return this; + } + + raise(eventName: string, args: Record = {}): void { + this._eventBroker.raise(eventName, args); + } + + protected _makeDirty(): void { + if (this._parent != null) + this._parent._makeDirty(); + } + + abstract getMacroNames(): Array; + abstract _initSymLinks(): void; +} diff --git a/src/renderer/lib/http/templates/HttpDir.ts b/src/renderer/lib/http/templates/HttpDir.ts new file mode 100644 index 0000000..aacf619 --- /dev/null +++ b/src/renderer/lib/http/templates/HttpDir.ts @@ -0,0 +1,185 @@ +/* eslint-disable @typescript-eslint/ban-types */ +import { HttpReqt } from "./HttpReqt"; +import { HttpAuth } from "./HttpAuth"; +import { HttpCollectionItem } from "./HttpCollectionItem"; + +export class HttpDir extends HttpCollectionItem { + protected _authChildren: Array = []; + protected _dirChildren: Array = []; + protected _reqtChildren: Array = []; + + fromPlainObject(plainObject: Object): this { + this._name = plainObject["name"]; + this._authChildren = [...plainObject["authChildren"]].map(obj => new HttpAuth().setParent(this).fromPlainObject(obj)); + this._dirChildren = [...plainObject["dirChildren"]].map(obj => new HttpDir().setParent(this).fromPlainObject(obj)); + this._reqtChildren = [...plainObject["reqtChildren"]].map(obj => new HttpReqt().setParent(this).fromPlainObject(obj)); + + return this; + } + + toPlainObject(): Object { + return { + name: this.getName(), + authChildren: this._authChildren.map(a => a.toPlainObject()), + reqtChildren: this._reqtChildren.map(r => r.toPlainObject()), + dirChildren: this._dirChildren.map(d => d.toPlainObject()) + }; + } + + setParent(parent: HttpDir): this { + super.setParent(parent); + return this; + } + + getParent(): HttpDir { + return this._parent; + } + + isAscendantOf(item: HttpCollectionItem): boolean { + let currentItem: HttpCollectionItem = item; + + while (currentItem) { + currentItem = currentItem.getParent(); + if (currentItem == this) + return true; + } + return false; + } + + findFromRelativePath(path: string): HttpCollectionItem { + return this._findItem(path.split("/")); + } + + findFromAbsolutePath(path: string): HttpCollectionItem { + if (!path.startsWith(this.getFullPath())) return null; + let relativePath = path.replace(`${this.getFullPath()}/`, ""); + return this.findFromRelativePath(relativePath); + } + + addDir(dir: HttpDir): boolean { + if (this.containsChild(dir.getName())) return false; + + this._dirChildren.push(dir); + dir.setParent(this); + this._makeDirty(); + return true; + } + + removeDir(dir: HttpDir): void { + dir.raise(dir.eventAboutToBeDeleted); + this._dirChildren = this._dirChildren.filter(dirChild => dirChild != dir); + this._makeDirty(); + } + + getDirs(): Array { + return this._dirChildren; + } + + addReqt(reqt: HttpReqt, insertAfter: HttpReqt = null): boolean { + if (this.containsChild(reqt.getName())) return false; + + if (insertAfter == null) { + this._reqtChildren.push(reqt); + } else { + let index = this._reqtChildren.indexOf(insertAfter); + if (index == -1) return false; + + this._reqtChildren.splice(index + 1, 0, reqt); + } + reqt.setParent(this); + this._makeDirty(); + return true; + } + + removeReqt(reqt: HttpReqt): void { + reqt.raise(reqt.eventAboutToBeDeleted); + this._reqtChildren = this._reqtChildren.filter(reqtChild => reqtChild != reqt); + this._makeDirty(); + } + + getReqts(): Array { + return this._reqtChildren; + } + + addAuth(auth: HttpAuth): boolean { + if (this.containsChild(auth.getName())) return false; + + this._authChildren.push(auth); + auth.setParent(this); + this._makeDirty(); + return true; + } + + removeAuth(auth: HttpAuth): void { + auth.raise(auth.eventAboutToBeDeleted); + this._authChildren = this._authChildren.filter(authChild => authChild != auth); + this._makeDirty(); + } + + getAuths(): Array { + return this._authChildren; + } + + clone(): HttpDir { + let clone = new HttpDir(); + + clone.setName(this._name); + this._authChildren.forEach(child => clone.addAuth(child.clone())); + this._reqtChildren.forEach(child => clone.addReqt(child.clone())); + this._dirChildren.forEach(child => clone.addDir(child.clone())); + + return clone; + } + + getMacroNames(): Array { + let names: Array = []; + + this._authChildren.forEach(a => a.getMacroNames().forEach(name => names.push(name))); + this._reqtChildren.forEach(r => r.getMacroNames().forEach(name => names.push(name))); + this._dirChildren.forEach(d => d.getMacroNames().forEach(name => names.push(name))); + + let tempSet = new Set(names); + return [...tempSet]; + } + + containsChild(name: string): boolean { + for (let auth of this._authChildren) + if (auth.getName() == name) return true; + + for (let dir of this._dirChildren) + if (dir.getName() == name) return true; + + for (let reqt of this._reqtChildren) + if (reqt.getName() == name) return true; + + return false; + } + + _initSymLinks(): void { + this._authChildren.forEach(auth => auth._initSymLinks()); + this._reqtChildren.forEach(reqt => reqt._initSymLinks()); + this._dirChildren.forEach(dir => dir._initSymLinks()); + } + + private _findItem(names: Array): HttpCollectionItem { + if (names.length == 0) return this; + + let childName = names[0]; + let leftOver = names.slice(1); + + let index; + index = this._dirChildren.findIndex(dir => dir.getName() == childName); + if (index != -1) + return this._dirChildren[index]._findItem(leftOver); + + index = this._reqtChildren.findIndex(reqt => reqt.getName() == childName); + if (index != -1) + return this._reqtChildren[index]; + + index = this._authChildren.findIndex(auth => auth.getName() == childName); + if (index != -1) + return this._authChildren[index]; + + return null; + } +} diff --git a/src/renderer/lib/http/templates/HttpReqt.ts b/src/renderer/lib/http/templates/HttpReqt.ts new file mode 100644 index 0000000..84fd1ee --- /dev/null +++ b/src/renderer/lib/http/templates/HttpReqt.ts @@ -0,0 +1,438 @@ +/* eslint-disable @typescript-eslint/ban-types */ +// Reqt is short from "REQuest Template" +import * as path from "path"; + + +import { HttpDir, HttpUrl } from ".."; +import { MacroedText } from "../Macro"; +import { HttpBody, HttpBodyContentType, HttpBodyType, HttpFormBody, HttpHeaderRecord, HttpRequest, HttpRequestMethod, HttpTextBody } from "../HttpRequest"; +import { HttpCollectionItem } from "./HttpCollectionItem"; +import { HttpAuth } from "./HttpAuth"; + +export class HttpReqt extends HttpCollectionItem { + public eventMethodChanged = "methodChanged"; + public eventUrlChanged = "urlChanged"; + public eventHeadersChanged = "headersChanged"; + public eventBodyChanged = "bodyChanged"; + public eventAuthChanged = "authChanged"; + + private _rawRequest: HttpRequest = new HttpRequest(); + + private _defaultBody: HttpBody = null; + + private _auth: HttpAuth = null; + private _authPath: string = null; + + private _urlMacros: Array = []; + private _headerMacros: Array = []; + private _bodyMacros: Array = []; + + fromPlainObject(plainObject: Object): this { + this._name = plainObject["name"]; + this._rawRequest = new HttpRequest(plainObject["rawRequest"]); + + if (plainObject["defaultBody"]) + this._defaultBody = HttpBody.fromPlainObject(plainObject["defaultBody"]); + else + this._defaultBody = this._rawRequest.body ? this._rawRequest.body.clone() : null; + + let auth = plainObject["auth"]; + + if (auth) { + if (typeof auth == "string") { + this._authPath = auth; + } else if (typeof auth == "object") { + this._auth = new HttpAuth().fromPlainObject(auth).setName(null).setParent(this); + } + } + + this._enumerateUrlMacros(); + this._enumerateHeaderMacros(); + this._enumerateBodyMacros(); + + return this; + } + + toPlainObject(): Object { + let plainObject = { + name: this.getName(), + rawRequest: this._rawRequest.toPlainObject(), + defaultBody: this._defaultBody?.toPlainObject() + }; + + if (this._auth) { + if (this._auth.getParent() instanceof HttpDir) + plainObject["auth"] = this._auth.getFullPath(); + else + plainObject["auth"] = this._auth.toPlainObject(); + } + + return plainObject; + } + + setParent(parent: HttpDir): this { + super.setParent(parent); + return this; + } + + getParent(): HttpDir { + return this._parent; + } + + setMethod(method: HttpRequestMethod): this { + if (this._rawRequest.method == method) return this; + + this._rawRequest.method = method; + this.raise(this.eventMethodChanged); + + if (this._rawRequest.method != HttpRequestMethod.POST && + this._rawRequest.method != HttpRequestMethod.PUT) + this._rawRequest.body = null; + + this._makeDirty(); + return this; + } + + setUrl(url: string): this { + if (this._rawRequest.url == url) return this; + + this._rawRequest.url = url; + this._enumerateUrlMacros(); + this.raise(this.eventUrlChanged); + this._makeDirty(); + return this; + } + + setHeaders(headers: Array): this { + this._rawRequest.headers = headers; + this._enumerateHeaderMacros(); + this.raise(this.eventHeadersChanged); + this._makeDirty(); + return this; + } + + setBody(body: HttpBody): this { + this._rawRequest.body = body; + this._enumerateBodyMacros(); + this.raise(this.eventBodyChanged); + this._makeDirty(); + return this; + } + + saveCurrentBodyAsDefault(): this { + let rawBody = this.getRawHttpRequest().body; + + if (rawBody) + rawBody = rawBody.clone(); + + this._defaultBody = rawBody; + return this; + } + + revertBodyToDefault(): this { + let body = this._defaultBody; + + if (body) body = body.clone(); + + this.setBody(body); + return this; + } + + setAuth(auth: HttpAuth): this { + if (this._auth != null) { + this._auth + .off(this._auth.eventAboutToBeDeleted, this._onAuthAboutToBeDeleted) + .off(this._auth.eventNameChanged, this._onAuthNameChanged); + } + + if (this._auth == auth) return this; + + this._auth = auth; + + if (this._auth != null) { + this._auth + .on(this._auth.eventAboutToBeDeleted, this._onAuthAboutToBeDeleted) + .on(this._auth.eventNameChanged, this._onAuthNameChanged); + } + + if (this._auth && this._auth.getParent() instanceof HttpReqt) + this._auth.setParent(this); + + this.raise(this.eventAuthChanged); + this._makeDirty(); + return this; + } + + getAuth(): HttpAuth { + return this._auth; + } + + getRawHttpRequest(): HttpRequest { + return this._rawRequest; + } + + async getHttpRequest(skipQueryItemsWithEmptyValue: boolean = true): Promise { + const collection = this.getContainingCollection(); + + let request = new HttpRequest(); + request.method = this._rawRequest.method; + + request.url = this._rawRequest.url; + for (let macro of this._urlMacros) { + request.url = request.url.replaceAll("${" + macro + "}", await collection.getMacroValue(macro)); + } + + if (skipQueryItemsWithEmptyValue) + request.url = this._sanitizeUrlForEmptyQueryValues(request.url); + + request.headers = this._rawRequest.headers.map(header => ({ name: header.name, value: header.value })); + + for (let macro of this._headerMacros) { + const macroValue = await collection.getMacroValue(macro); + for (let header of request.headers) { + header.name = header.name.replaceAll("${" + macro + "}", macroValue); + header.value = header.value.replaceAll("${" + macro + "}", macroValue); + } + } + + if (this._rawRequest.body != null) { + request.body = this._rawRequest.body.clone(); + if (request.body.type == HttpBodyType.Regular) { + let body = (request.body); + let bodyValue = body.value; + for (let macro of this._bodyMacros) { + bodyValue = bodyValue.replaceAll("${" + macro + "}", await collection.getMacroValue(macro)); + } + + if (body.valueType == HttpBodyContentType.File) { + if (!path.isAbsolute(bodyValue)) { + bodyValue = path.join( + path.dirname(this.getContainingCollection().getFilePath()), bodyValue + ); + } + } + + ((request.body)).value = bodyValue; + } else if (request.body.type == HttpBodyType.Form) { + let formBody = (request.body); + formBody.records = [ ...formBody.records ]; + + for (let macro of this._bodyMacros) { + const macroValue = await collection.getMacroValue(macro); + for (let record of formBody.records) { + record.name = record.name.replaceAll("${" + macro + "}", macroValue); + record.value = record.value.replaceAll("${" + macro + "}", macroValue); + } + } + + for (let record of formBody.records) { + if (record.type == HttpBodyContentType.File) { + if (!path.isAbsolute(record.value)) { + record.value = path.join( + path.dirname(this.getContainingCollection().getFilePath()), record.value + ); + } + } + } + + formBody.records = formBody.records.filter(record => record.value); + } else { + throw new Error(`Unsupported HttpBodyType ${request.body.type}.`); + } + } + + let effectiveAuth = this._findEffectiveAuth(); + + if (effectiveAuth) + request = await effectiveAuth.modify(request); + + return request; + } + + clone(): HttpReqt { + let clone = new HttpReqt(); + + clone.setName(this._name); + clone.setMethod(this._rawRequest.method); + clone.setUrl(this._rawRequest.url); + clone.setHeaders(this._rawRequest.headers.map(header => ({ name: header.name, value: header.value }))); + clone.setBody(this._rawRequest.body != null ? this._rawRequest.body.clone() : null); + + if (this._auth && this._auth.getParent() == this) { + clone.setAuth(this._auth.clone()); + } else { + clone.setAuth(this._auth); + } + + return clone; + } + + getUrlMacroNames(): Array { + return this._urlMacros; + } + + getHeaderMacroNames(): Array { + return this._headerMacros; + } + + getBodyMacroNames(): Array { + return this._bodyMacros; + } + + getAuthMacroNames(): Array { + let auth = this._findEffectiveAuth(); + if (!auth) return []; + return auth.getMacroNames(); + } + + getMacroNames(): Array { + let macros: Array = []; + + this._urlMacros.forEach(macro => { + if (macros.indexOf(macro) == -1) + macros.push(macro); + }); + + this._headerMacros.forEach(macro => { + if (macros.indexOf(macro) == -1) + macros.push(macro); + }); + + this._bodyMacros.forEach(macro => { + if (macros.indexOf(macro) == -1) + macros.push(macro); + }); + + this.getAuthMacroNames().forEach(macro => { + if (macros.indexOf(macro) == -1) + macros.push(macro); + }); + + return macros; + } + + _initSymLinks(): void { + if (!this._authPath) return; + let value = this.getContainingCollection().findFromAbsolutePath(this._authPath); + if (!(value instanceof HttpAuth)) return; + + if (this._auth != null) { + this._auth.off(this._auth.eventNameChanged, this._onAuthNameChanged); + } + + this._auth = value; + + if (this._auth != null) { + this._auth.on(this._auth.eventNameChanged, this._onAuthNameChanged); + } + } + + private _findEffectiveAuth(): HttpAuth { + if (this._auth) + return this._auth; + + let parent = this.getParent(); + + while (parent != null) { + let authIndex = parent.getAuths().findIndex(auth => auth.getName() == "Default Auth"); + if (authIndex != -1) + return parent.getAuths()[authIndex]; + + parent = parent.getParent(); + } + + return null; + } + + private _onAuthNameChanged = (): void => { + this.raise(this.eventAuthChanged); + }; + + private _onAuthAboutToBeDeleted = (): void => { + this.setAuth(null); + }; + + private _enumerateUrlMacros(): void { + this._urlMacros = MacroedText.parse(this._rawRequest.url).getMacroNames(); + } + + private _enumerateHeaderMacros(): void { + this._headerMacros = []; + + this._rawRequest.headers.forEach(headerRecord => { + MacroedText.parse(headerRecord.name) + .getMacroNames() + .forEach(macro => { + if (this._headerMacros.indexOf(macro) == -1) + this._headerMacros.push(macro); + }); + + MacroedText.parse(headerRecord.value) + .getMacroNames() + .forEach(macro => { + if (this._headerMacros.indexOf(macro) == -1) + this._headerMacros.push(macro); + }); + }); + } + + private _enumerateBodyMacros(): void { + this._bodyMacros = []; + + const body = this._rawRequest.body; + if (!body) return; + + if (body.type == HttpBodyType.Regular) { + MacroedText.parse((body).value) + .getMacroNames() + .forEach(macro => { + if (this._bodyMacros.indexOf(macro) == -1) + this._bodyMacros.push(macro); + }); + } else if (body.type == HttpBodyType.Form) { + let formBody = body; + + formBody.records.forEach(record => { + MacroedText.parse(record.name) + .getMacroNames() + .forEach(macro => { + if (this._bodyMacros.indexOf(macro) == -1) + this._bodyMacros.push(macro); + }); + + MacroedText.parse(record.value) + .getMacroNames() + .forEach(macro => { + if (this._bodyMacros.indexOf(macro) == -1) + this._bodyMacros.push(macro); + }); + }); + } else { + throw new Error(`Unsupported HttpBodyType ${body.type}.`); + } + } + + private _sanitizeUrlForEmptyQueryValues(url: string): string { + let pUrl = HttpUrl.parse(url); + let query = pUrl.query; + + if (query.length <= 1) return url; + let queryParts = query.slice(1).split("&"); + + let validParts = []; + for (let part of queryParts) { + if (part.indexOf("=") == part.length - 1) { + continue; + } + + validParts.push(part); + } + + if (validParts.length > 0) + pUrl.query = "?" + validParts.join("&"); + else + pUrl.query = ""; + + return pUrl.toString(); + } +} diff --git a/src/renderer/lib/http/templates/index.ts b/src/renderer/lib/http/templates/index.ts new file mode 100644 index 0000000..e66019e --- /dev/null +++ b/src/renderer/lib/http/templates/index.ts @@ -0,0 +1,5 @@ +export { HttpReqt } from "./HttpReqt"; +export { HttpDir } from "./HttpDir"; +export { HttpCollection, CollectionOpeningError } from "./HttpCollection"; +export { HttpCollectionItem } from "./HttpCollectionItem"; +export * from "./HttpAuth"; diff --git a/src/renderer/lib/interop/collections/IThirdPartyCollectionConverter.ts b/src/renderer/lib/interop/collections/IThirdPartyCollectionConverter.ts new file mode 100644 index 0000000..be8cc0b --- /dev/null +++ b/src/renderer/lib/interop/collections/IThirdPartyCollectionConverter.ts @@ -0,0 +1,96 @@ +export enum ConversionLogRecordType { Schema, Dir, Request, Warning } + +export class ConversionLogRecord { + type: ConversionLogRecordType; + message: string; +} + +export class ConversionLog extends Array { + schema(schema: string): void { + this.push({ + type: ConversionLogRecordType.Schema, + message: schema + }); + } + + dir(dirName: string): void { + this.push({ + type: ConversionLogRecordType.Dir, + message: dirName + }); + } + + request(requestName: string): void { + this.push({ + type: ConversionLogRecordType.Request, + message: requestName + }); + } + + warn(message: string): void { + this.push({ + type: ConversionLogRecordType.Warning, + message + }); + } + + warnTypeIs(variableName: string, type: string, input: unknown): void { + this.warn(`Type of ${variableName} is invalid; ${type} expected. Actual value ${input}.`); + } + + warnTypeIsNot(variableName: string, type: string, input: unknown): void { + this.warn(`Type of ${variableName} is invalid; must not be ${type}. Actual value ${input}.`); + } + + toString(): string { + let result = ""; + + this.forEach(record => { + switch (record.type) { + case ConversionLogRecordType.Schema: + result += "SCH " + record.message + "\n"; + break; + case ConversionLogRecordType.Dir: + result += "DIR " + record.message + "\n"; + break; + case ConversionLogRecordType.Request: + result += "REQ " + record.message + "\n"; + break; + case ConversionLogRecordType.Warning: + result += " WARNING --> " + record.message + "\n"; + break; + default: + throw new Error(`ConversionLogRecordType ${record.type} is not implemented.`); + } + }); + + return result; + } +} + +export enum ConversionError { Success, NoFile, JsonParsing, Unknown } + +export enum NotSupported { + CollectionSchema = "unknown collection schema", + Digest = "digest authentication", + OAuth1 = "OAuth1", + OAuth2Password = "OAuth2 with password grant type", + OAuth2UnknownGrantType = "OAuth2 unknown grant type", + Hawk = "hawk authentication", + NTLM = "NTLM authentication", + AWSv4 = "AWSv4 authentication", + EdgeGrid = "AWSv4 authentication", + AuthOther = "unknown authentication protocol", + MultiFileFormRecord = "multi-file form record", + FileContent = "file content stored in collection" +} + +export class ConversionResult { + error: ConversionError; + notSupported: Set; +} + +export interface IThirdPartyCollectionConverter { + convert(): ConversionResult; + save(pathToHttpinessCollection: string, pathToConversionLog: string): boolean; +} diff --git a/src/renderer/lib/interop/collections/Postman2dot1CollectionConverter.ts b/src/renderer/lib/interop/collections/Postman2dot1CollectionConverter.ts new file mode 100644 index 0000000..bc043c4 --- /dev/null +++ b/src/renderer/lib/interop/collections/Postman2dot1CollectionConverter.ts @@ -0,0 +1,859 @@ +/* eslint-disable camelcase */ +/* eslint-disable @typescript-eslint/naming-convention */ +import * as fs from "fs"; + +import { HttpBodyContentType, HttpBodyType, HttpHeaderRecord, HttpRequestMethod, AuthLocationType, AuthType, FormEncoding } from "../../http"; +import { OAuth2ClientAuthentication, OAuth2Type, PkceCodeChallengeMethod } from "../../http"; +import { ConversionError, ConversionLog, ConversionResult, IThirdPartyCollectionConverter, NotSupported } from "./IThirdPartyCollectionConverter"; + +const ALLOWED_METHODS: Array = [ + HttpRequestMethod.CONNECT, + HttpRequestMethod.DELETE, + HttpRequestMethod.GET, + HttpRequestMethod.HEAD, + HttpRequestMethod.NONE, + HttpRequestMethod.OPTIONS, + HttpRequestMethod.PATCH, + HttpRequestMethod.POST, + HttpRequestMethod.PUT, + HttpRequestMethod.TRACE +]; + +type PostmanAuthRecord = unknown; +type PostmanCollection2dot1Record = unknown; +type PostmanFolderRecord = unknown; +type PostmanItemRecord = unknown; + +enum AssertionType { + String = "string", Undefined = "undefined", Object = "object", Array = "array" +} + +const STR = AssertionType.String; +const UND = AssertionType.Undefined; +const OBJ = AssertionType.Object; +const ARR = AssertionType.Array; + +export class Postman2dot1CollectionConverter implements IThirdPartyCollectionConverter { + private _pathToCollection: string; + private _notSupported: Set = new Set(); + private _log: ConversionLog = new ConversionLog(); + private _httpinessCollectionObject: unknown = null; + + constructor(pathToThirdPartyCollection: string) { + this._pathToCollection = pathToThirdPartyCollection; + } + + convert(): ConversionResult { + let result = fs.existsSync(this._pathToCollection); + + if (!result) { + return { + error: ConversionError.NoFile, + notSupported: this._notSupported + }; + } + + try { + let fileContent = fs.readFileSync(this._pathToCollection); + let plainObject = JSON.parse(fileContent.toString()); + this._httpinessCollectionObject = + this._convert_PostmanCollection2dot1_to_HttpinessCollection0dot10PO(plainObject); + } catch (ex) { + console.log(ex); + + let error = ConversionError.Unknown; + + if (ex instanceof SyntaxError) { + error = ConversionError.JsonParsing; + } + + return { + error: error, + notSupported: this._notSupported + }; + } + + return { + error: ConversionError.Success, + notSupported: this._notSupported + }; + } + + save(pathToHttpinessCollection: string = null, pathToConversionLog: string = null): boolean { + try { + if (pathToHttpinessCollection) + fs.writeFileSync(pathToHttpinessCollection, JSON.stringify(this._httpinessCollectionObject)); + if (pathToConversionLog) + fs.writeFileSync(pathToConversionLog, this._log.toString()); + return true; + } catch { + return false; + } + } + + //#region HELPERS + + private _replace_Parameters(input: string): string { + if (!input) return input; + return input.replace(new RegExp("{{", "g"), "${").replace(new RegExp("}}", "g"), "}"); + } + + private _extractFrom_ArrayOfKeyValues(input: Array, key: string): string { + let rcd = input.find(elem => elem["key"] == key); + if (typeof rcd != "object") return null; + + let val = rcd["value"]; + if (typeof val != "string") return null; + return val; + } + + private _checkType(input: unknown, type: AssertionType): boolean { + let result = false; + + if (type == AssertionType.Array) + result = Array.isArray(input); + else if (type == AssertionType.Object) + result = !Array.isArray(input) && typeof input == type; + else + result = typeof input == type; + + return result; + } + + private _check_is(input: unknown, type: AssertionType, variableName: string = null): boolean { + let result = this._checkType(input, type); + if (result && variableName) + this._log.warnTypeIsNot(variableName, type, input); + return result; + } + + private _check_isNot(input: unknown, type: AssertionType, variableName: string = null): boolean { + let result = !this._checkType(input, type); + if (result && variableName) + this._log.warnTypeIs(variableName, type, input); + return result; + } + + //#endregion + + //#region AUTH + + private _convert_Auth_to_HttpAuthPO(input: PostmanAuthRecord): Record { + if (this._check_is(input, UND)) return null; + if (this._check_isNot(input, OBJ, "Authentication object")) + return null; + + let type = input["type"]; + if (this._check_isNot(type, STR, "Authentication type")) + return null; + + switch (type) { + case "noauth": return this._noauth_to_HttpAuthPO(); + case "apikey": return this._apikey_to_HttpAuthPO(input); + case "basic": return this._basic_to_HttpAuthPO(input); + case "bearer": return this._bearer_to_HttpAuthPO(input); + case "digest": return this._digest_to_HttpAuthPO(); + case "hawk": return this._hawk_to_HttpAuthPO(); + case "edgegrid": return this._edgegrid_to_HttpAuthPO(); + case "oauth1": return this._oauth1_to_HttpAuthPO(); + case "oauth2": return this._oauth2_to_HttpAuthPO(input); + case "ntlm": return this._ntlm_to_HttpAuthPO(); + case "awsv4": return this._awsv4_to_HttpAuthPO(); + } + + this._log.warn(`No support auth type ${type}`); + this._notSupported.add(NotSupported.AuthOther); + return null; + } + + private _noauth_to_HttpAuthPO(): Record { + return { + name: "Default Auth", + authDef: { + type: "NoAuth" + } + }; + } + + private _apikey_to_HttpAuthPO(input: PostmanAuthRecord): Record { + let data = input["apikey"]; + + let helper: Record = { + name: "Default Auth", + authDef: { type: "ApiKey" }, + location: { + type: AuthLocationType.Header, + key: "Authorization", + prefix: "" + } + }; + + if (this._check_isNot(data, ARR, "ApiKey auth data")) + return helper; + + let inParam = this._extractFrom_ArrayOfKeyValues(data, "in"); + + if (inParam == "header") { + helper.location = { + type: AuthLocationType.Header + }; + } else if (inParam == "query") { + helper.location = { + type: AuthLocationType.UrlQuery + }; + } else { + this._log.warn(`"In" parameter in ApiKey authentication data is invalid; expected either "query" or "header"; using "header". Actual value: ${inParam}`); + } + + let keyRecord = this._extractFrom_ArrayOfKeyValues(data, "key"); + let valueParam = this._extractFrom_ArrayOfKeyValues(data, "value"); + + if (!keyRecord) keyRecord = "Authorization"; + helper.location["key"] = this._replace_Parameters(keyRecord); + + if (this._check_isNot(valueParam, STR, "Api key")) { + helper.authDef["apiKey"] = "${API_KEY}"; + } else { + helper.authDef["apiKey"] = this._replace_Parameters(valueParam); + } + + return helper; + } + + private _basic_to_HttpAuthPO(input: PostmanAuthRecord): Record { + let data = input["basic"]; + + if (this._check_isNot(data, ARR, "Basic auth data")) + return null; + + let username = this._extractFrom_ArrayOfKeyValues(data, "username"); + if (username) + username = this._replace_Parameters(username); + else + username = "${USERNAME}"; + let password = this._extractFrom_ArrayOfKeyValues(data, "password"); + if (password) + password = this._replace_Parameters(password); + else + password = "${PASSWORD}"; + + return { + name: "Default Auth", + authDef: { + type: "Basic", + username, + password + }, + location: null + }; + } + + private _bearer_to_HttpAuthPO(input: PostmanAuthRecord): Record { + let data = input["bearer"]; + + if (this._check_isNot(data, ARR, "Bearer auth data")) + return null; + if (this._check_isNot(data[0], OBJ, "Bearer auth data first element")) + return null; + + let value = data[0]["value"]; + + if (this._check_isNot(value, STR, "Property named 'value' in Bearer auth data first element")) + return null; + + if (value) + value = this._replace_Parameters(value); + else + value = "${BEARER_TOKEN}"; + + return { + name: "Default Auth", + authDef: { + type: "Bearer", + bearerToken: value + }, + location: null + }; + } + + private _digest_to_HttpAuthPO(): Record { + this._log.warn("Auth method 'Digest' is not supported by httpiness."); + this._notSupported.add(NotSupported.Digest); + return null; + } + + private _hawk_to_HttpAuthPO(): Record { + this._log.warn("Auth method 'Hawk' is not supported by httpiness."); + this._notSupported.add(NotSupported.Hawk); + return null; + } + + private _edgegrid_to_HttpAuthPO(): Record { + this._log.warn("Auth method 'EdgeGrid' is not supported by httpiness."); + this._notSupported.add(NotSupported.EdgeGrid); + return null; + } + + private _oauth1_to_HttpAuthPO(): Record { + this._log.warn("Auth method 'OAuth1' is not supported by httpiness."); + this._notSupported.add(NotSupported.OAuth1); + return null; + } + + private _oauth2_to_HttpAuthPO(input: PostmanAuthRecord): Record { + let data = input["oauth2"]; + + if (this._check_isNot(data, ARR, "OAuth2 auth data")) { + return { + name: "Default Auth", + authDef: { + type: AuthType.OAuth2 + }, + location: null + }; + } + + const grantType_to_OAuth2Type = (grantType: string): OAuth2Type => { + if (!grantType || grantType == "authorization_code") return OAuth2Type.AuthorizationCode; + if (grantType == "implicit") return OAuth2Type.Implicit; + if (grantType == "client_credentials") return OAuth2Type.ClientCredentials; + if (grantType == "authorization_code_with_pkce") return OAuth2Type.AuthorizationCode; + + if (grantType == "password") { + this._notSupported.add(NotSupported.OAuth2Password); + return null; + } + + this._notSupported.add(NotSupported.OAuth2UnknownGrantType); + return null; + }; + + const challengeAlgorithm_to_PkceCodeChallengeMethod = (grantType: string, challengeAlgorithm: string): PkceCodeChallengeMethod => { + if (grantType != "authorization_code_with_pkce") return PkceCodeChallengeMethod.None; + + if (challengeAlgorithm == "plain") return PkceCodeChallengeMethod.Plain; + return PkceCodeChallengeMethod.SHA256; + }; + + let challengeAlgorithm = this._replace_Parameters(this._extractFrom_ArrayOfKeyValues(data, "challengeAlgorithm")); + let grantType = this._replace_Parameters(this._extractFrom_ArrayOfKeyValues(data, "grant_type")); + let clientAuthentication = this._replace_Parameters(this._extractFrom_ArrayOfKeyValues(data, "client_authentication")); + let headerPrefix = this._replace_Parameters(this._extractFrom_ArrayOfKeyValues(data, "headerPrefix")); + + + let oauth2Type = grantType_to_OAuth2Type(grantType); + let codeChallengeMethod = challengeAlgorithm_to_PkceCodeChallengeMethod(grantType, challengeAlgorithm); + if (!oauth2Type) return null; + + return { + name: "Default Auth", + authDef: { + type: AuthType.OAuth2, + oauth2Type, + codeChallengeMethod, + callbackURL: this._replace_Parameters(this._extractFrom_ArrayOfKeyValues(data, "redirect_uri")), + authURL: this._replace_Parameters(this._extractFrom_ArrayOfKeyValues(data, "authUrl")), + accessTokenURL: this._replace_Parameters(this._extractFrom_ArrayOfKeyValues(data, "accessTokenUrl")), + clientID: this._replace_Parameters(this._extractFrom_ArrayOfKeyValues(data, "clientId")), + clientSecret: this._replace_Parameters(this._extractFrom_ArrayOfKeyValues(data, "clientSecret")), + scope: this._replace_Parameters(this._extractFrom_ArrayOfKeyValues(data, "scope")), + state: this._replace_Parameters(this._extractFrom_ArrayOfKeyValues(data, "state")), + clientAuthentication: clientAuthentication == "body" ? OAuth2ClientAuthentication.InBody : OAuth2ClientAuthentication.BasicAuthentication + }, + location: { + type: AuthLocationType.Header, + key: "Authorization", + prefix: headerPrefix == null ? "Bearer" : headerPrefix + } + }; + } + + private _ntlm_to_HttpAuthPO(): Record { + this._log.warn("Auth method 'NTLM' is not supported by httpiness."); + this._notSupported.add(NotSupported.NTLM); + return null; + } + + private _awsv4_to_HttpAuthPO(): Record { + this._log.warn("Auth method 'AWSv4' is not supported by httpiness."); + this._notSupported.add(NotSupported.AWSv4); + return null; + } + + //#endregion AUTH + + //#region DIRECTORY AND ROOT + + private _convert_PostmanCollection2dot1_to_HttpinessCollection0dot10PO(input: PostmanCollection2dot1Record): unknown { + // convert metadata + let htpCollStub = this._convert_PostmanCollectionMetadata_to_HttpinessCollection0dot10Stub(input); + if (htpCollStub == null) return htpCollStub; + + //convert items + let folderResult = this._convert_Folder_to_HttpReqtDirPO(input, ""); + if (folderResult == null) return htpCollStub; + + htpCollStub = { + ...htpCollStub, + ...folderResult + }; + + delete htpCollStub.name; + + return htpCollStub; + } + + private _convert_PostmanCollectionMetadata_to_HttpinessCollection0dot10Stub(input: PostmanCollection2dot1Record): Record { + if (this._check_isNot(input, OBJ, "Postman collection root")) + return null; + + const info = input["info"]; + if (this._check_isNot(info, OBJ, "Collection info record")) + return null; + + const schema = info["schema"]; + if (!this._check_isNot(schema, STR, "Collection schema")) { + this._log.schema(String(schema)); + } + + const variables = input["variable"]; + let parameters = {}; + + if (this._check_is(variables, ARR)) { + for (let i of variables) { + let key = i["key"]; + let value = i["value"]; + + if (!this._check_isNot(key, STR, "Variable key") && + !this._check_isNot(value, STR, "Variable value")) { + parameters[key] = value; + } + } + } + + return { + collectionVersion: "httpiness/JSON/0.11", + authChildren: [], + reqtChildren: [], + dirChildren: [], + parameters + }; + } + + private _convert_Folder_to_HttpReqtDirPO(input: PostmanFolderRecord, parentName: string): Record { + let dir: any = null; + + let dirName = input["name"]; + if (this._check_isNot(dirName, STR)) { + dirName = input["info"]["name"]; + if (this._check_isNot(dirName, STR)) { + this._log.warn("Folder does not have valid name record. Using default value."); + dirName = "Untitled directory"; + } + } + + this._log.dir(parentName + "/" + dirName); + let defaultAuth = this._convert_Auth_to_HttpAuthPO(input["auth"]); + + const items = input["item"]; + if (this._check_is(items, UND)) { + // do nothing + } else if (this._check_isNot(items, ARR, "Parameter item of directory")) { + return >dir; + } else { + dir = { + name: dirName, + reqtChildren: [], + dirChildren: [], + authChildren: defaultAuth ? [ defaultAuth ] : [] + }; + + for (let i of items) { + if (this._check_isNot(i["request"], UND)) { + let result = this._convert_Item_to_HttpReqtPO(i, `${parentName}/${dirName}`); + if (result != null) + dir.reqtChildren.push(result); + } else if (this._check_isNot(i["item"], UND)) { + const result = this._convert_Folder_to_HttpReqtDirPO(i, `${parentName}/${dirName}`);//, effectiveAuth); + if (result != null) + dir.dirChildren.push(result); + } else { + this._log.warn(`Directory ${dir.name} has item which in neither Request or Folder. Ignoring. Item value: ${i}.`); + } + } + } + + return >dir; + } + + //#endregion + + //#region REQUEST + + private _convert_Item_to_HttpReqtPO(item: PostmanItemRecord, parentName: string): unknown { + let name = item["name"]; + + if (this._check_isNot(name, STR, "Item name")) { + name = "Untitled request"; + } + + this._log.request(parentName + "/" + name); + + let postmanRequest = item["request"]; + + if (this._check_is(postmanRequest, STR)) { + return { + name, + rawRequest: { + method: HttpRequestMethod.GET, + url: postmanRequest + } + }; + } + + if (this._check_isNot(postmanRequest, OBJ, "Item request property")) { + return null; + } + + let auth = null; + if (postmanRequest["auth"]) + auth = this._convert_Auth_to_HttpAuthPO(postmanRequest["auth"]); + + let method = this._convert_method(postmanRequest["method"]); + let url = this._convert_url(postmanRequest["url"]); + let [body, contentType] = this._convert_body(postmanRequest["body"]); + let headers = this._convert_header(postmanRequest["header"], contentType); + + return { name, rawRequest: { method, url, headers, body }, auth }; + } + + private _convert_method(method: unknown): HttpRequestMethod { + if (this._check_isNot(method, STR, "Request method")) + return HttpRequestMethod.GET; + + if (!ALLOWED_METHODS.includes(String(method))) { + this._log.warn(`Postman method ${method} cannot be mapped to httpiness method. Using GET instead.`); + return HttpRequestMethod.GET; + } + + return method; + } + + private _convert_url(input: unknown): string { + if (!input) return ""; + + if (this._check_is(input, STR)) + return this._replace_Parameters(String(input)); + + if (this._check_isNot(input, OBJ, "Request url")) + return null; + + let postmanProtocol = input["protocol"]; + let postmanHost = input["host"]; + let postmanPort = input["port"]; + let postmanPath = input["path"]; + let postmanQuery = input["query"]; + let postmanHash = input["hash"]; + + let url = ""; + + if (this._check_is(postmanProtocol, UND)) { + // do nothing + } else if (this._check_is(postmanProtocol, STR)) { + url += postmanProtocol + "://"; + } else { + this._log.warnTypeIsNot("Request URL protocol", "undefined/string", postmanProtocol); + } + + if (this._check_is(postmanHost, STR)) { + url += postmanHost; + } else if (this._check_is(postmanHost, ARR)) { + url += postmanHost.join("."); + } else { + this._log.warnTypeIsNot("Request URL host", "string/array", postmanHost); + } + + if (this._check_is(postmanPort, UND)) { + // do nothing + } else if (this._check_is(postmanPort, STR)) { + url += ":" + postmanPort; + } else { + this._log.warnTypeIsNot("Request URL port", "undefined/string", postmanPort); + } + + if (this._check_is(postmanPath, UND)) { + // do nothing + } else if (this._check_is(postmanPath, STR)) { + url += "/" + postmanPath; + } else if (this._check_is(postmanPath, ARR)) { + for (let i = 0; i <= postmanPath.length - 1; i++) { + if (postmanPath[i][0] == ":") { + postmanPath[i] = "${" + (postmanPath[i].slice(1)) + "}"; + } + } + url += "/" + postmanPath.join("/"); + } else { + this._log.warnTypeIsNot("Request URL path", "undefined/string/array", postmanPath); + } + + url += "?"; + if (this._check_is(postmanQuery, UND)) { + // do nothing + } else if (this._check_is(postmanQuery, ARR)) { + for (const singleParam of postmanQuery) { + if (singleParam.key == null) + continue; + if (singleParam.value != null) + if (singleParam.disabled) + url += singleParam.key + "=&"; + else + url += singleParam.key + "=" + singleParam.value + "&"; + else + url += singleParam.key + "&"; + } + } else { + this._log.warnTypeIsNot("Request URL query", "undefined/array", postmanQuery); + } + url = url.slice(0, -1); + + if (this._check_is(postmanHash, UND)) { + // do nothing + } else if (this._check_is(postmanHash, STR)) { + url += postmanHash; + } else { + this._log.warnTypeIsNot("Request URL query", "undefined/string", postmanQuery); + } + + return this._replace_Parameters(url); + } + + private _convert_header(input: unknown, defaultContentType: string): Array { + let headers: Array = []; + + if (defaultContentType) { + if (this._check_is(input, ARR)) { + if ((>input).findIndex(elem => elem.key == "Content-Type") == -1) { + headers.push({ name: "Content-Type", value: defaultContentType }); + } + } else { + headers.push({ name: "Content-Type", value: defaultContentType }); + } + } + + if (this._check_is(input, UND)) { + // do nothing + } else if (this._check_is(input, ARR)) { + for (let header of >input) { + if (header["disabled"]) continue; + const key = header["key"]; + const value = header["value"]; + + if (!this._check_isNot(key, STR, "A header key") && !this._check_isNot(value, STR, "A header value")) { + headers.push({ name: this._replace_Parameters(key), value: this._replace_Parameters(value) }); + } + } + } else { + this._log.warnTypeIsNot("Request header", "undefined/array", input); + } + + return headers; + } + + private _convert_body(input: unknown): [unknown, string] { + if (this._check_is(input, UND)) return [null, null]; + if (this._check_isNot(input, OBJ, "Request body")) return [null, null]; + + const mode = input["mode"]; + + if (this._check_isNot(mode, STR)) { + return [null, null]; + } + + switch (mode) { + case "raw": return this._raw_to_BodyHelper(input); + case "formdata": return this._formdata_to_BodyHelper(input); + case "urlencoded": return this._urlencoded_to_BodyHelper(input); + case "file": return this._file_to_BodyHelper(input); + case "graphql": return this._graphql_to_BodyHelper(input); + } + + this._log.warnTypeIsNot("Request mode", "raw/formdata/urlencoded/file/graphql", mode); + return [null, null]; + } + + private _raw_to_BodyHelper(input: unknown): [unknown, string] { + let content = input["raw"]; + + if (this._check_isNot(content, STR, "Request body of type raw")) { + content = ""; + } + + let contentType = "text/plain"; + let options = input["options"]; + + if (this._check_is(options, UND)) { + // do nothing + } else if (!this._check_isNot(options, OBJ, "Request options") && + this._check_isNot(options["raw"], UND) && + !this._check_isNot(options["raw"], OBJ, "Request options for raw type")) { + switch (options["raw"]["language"]) { + case "json": contentType = "application/json"; break; + case "javaScript": contentType = "application/javascript"; break; + case "xml": contentType = "application/xml"; break; + case "html": contentType = "application/html"; break; + default: break; + } + } + + return [{ + type: HttpBodyType.Regular, + value: this._replace_Parameters(content), + valueType: HttpBodyContentType.Text + }, contentType]; + } + + private _formdata_to_BodyHelper(input: unknown): [unknown, string] { + let body = { + type: HttpBodyType.Form, + encoding: FormEncoding.Multipart, + records: [] + }; + + let formParts = input["formdata"]; + + if (this._check_isNot(formParts, ARR, "Request body of type formdata")) { + return [body, null]; + } + + for (let part of formParts) { + let type = part["type"]; + let key = part["key"]; + + if (this._check_isNot(type, STR, "Formdata part type")) { + continue; + } + + if (this._check_isNot(key, STR, "Formdata part key")) { + key = ""; + } + + if (type == "text") { + let value = part["value"]; + if (this._check_isNot(value, STR, "Formdata part value")) { + value = ""; + } + body.records.push({ + name: this._replace_Parameters(key), + value: this._replace_Parameters(value), + type: HttpBodyContentType.Text + }); + } else if (type == "file") { + let value = part["src"]; + + if (this._check_is(value, ARR)) { + this._log.warn("Request object contains array as form part value for type 'file'; using only first member."); + this._notSupported.add(NotSupported.MultiFileFormRecord); + value = value[0]; + } + + if (this._check_isNot(value, STR, "File src data")) { + value = ""; + } + + body.records.push({ + name: this._replace_Parameters(key), + value: this._replace_Parameters(value), + type: HttpBodyContentType.File + }); + } + } + + return [body, null]; + } + + private _urlencoded_to_BodyHelper(input: unknown): [unknown, string] { + let body = { + type: HttpBodyType.Form, + encoding: FormEncoding.UrlEncoded, + records: [] + }; + + let urlEncodedParts = input["urlencoded"]; + + if (this._check_isNot(urlEncodedParts, ARR, "Request body of type urlencoded")) { + return [body, null]; + } + + for (let part of urlEncodedParts) { + let key = part["key"]; + if (this._check_isNot(key, STR, "Urlencoded part key")) { + key = ""; + } + + let value = part["value"]; + if (this._check_isNot(value, STR, "Urlencoded part value")) { + value = ""; + } + + body.records.push({ + name: this._replace_Parameters(key), + value: this._replace_Parameters(value), + type: HttpBodyContentType.Text + }); + } + + return [body, null]; + } + + private _file_to_BodyHelper(input: unknown): [unknown, string] { + let fileRecord = input["file"]; + + let body = { + type: HttpBodyType.Regular, + valueType: HttpBodyContentType.File, + value: "" + }; + + if (this._check_isNot(fileRecord, OBJ, "Request body of type file")) { + return [body, null]; + } + + let src = fileRecord["src"]; + let content = fileRecord["content"]; + + if (this._check_is(src, STR)) { + body.value = this._replace_Parameters(src); + } else if (this._check_isNot(content, UND, "Parameter 'content' of request body of type file")) { + this._notSupported.add(NotSupported.FileContent); + } else { + this._log.warn(`Request object contains invalid file body value; no valid src or content properties. Body value: ${fileRecord}`); + } + + return [body, null]; + } + + private _graphql_to_BodyHelper(input: unknown): [unknown, string] { + let graphqlContent = input["graphql"]; + + if (this._check_isNot(graphqlContent, OBJ, "Request body of type graphql")) { + return [{ + type: HttpBodyType.Regular, + value:"{\"query\": \"\"}", + valueType: HttpBodyContentType.Text + }, "application/json"]; + } + + let query = graphqlContent["query"]; + if (this._check_isNot(query, STR)) { + query = ""; + } + + return [{ + type: HttpBodyType.Regular, + value: `{"query": ${this._replace_Parameters(query)}}`, + valueType: HttpBodyContentType.Text + }, "application/json"]; + } +} + +//#endregion diff --git a/src/renderer/lib/interop/requests/CurlCommandConverter.ts b/src/renderer/lib/interop/requests/CurlCommandConverter.ts new file mode 100644 index 0000000..a682f8a --- /dev/null +++ b/src/renderer/lib/interop/requests/CurlCommandConverter.ts @@ -0,0 +1,239 @@ +import { FormEncoding, HttpBodyContentType, HttpBodyType, HttpFormBody, + HttpRequest, HttpRequestMethod, HttpTextBody } from "../../http"; + + +export class CurlCommandConverter { + private static _myRegexp = /([^\s'"]([^\s'"]*(['"])([^\3]*?)\3)+[^\s'"]*)|[^\s'"]+|(['"])([^\5]*?)\5/gi; + + static convert(curlCommand: string): HttpRequest { + try { + let request = new HttpRequest(); + + let argv = CurlCommandConverter._parseArgsStringToArgv(curlCommand); + + if (argv[0] != "curl") return null; + + let contentType: string = null; + let explicitGet: boolean = false; + let methodExplicitlySpecified: boolean = false; + + let index: number = 0; + let command: string = ""; + + // In the first scan extract headers, method and url: + + while (index < argv.length - 1) { + + index++; + command = argv[index].trim(); + + if (CurlCommandConverter._isValidUrl(command)) { + request.url = command; + continue; + } + + if (command == "--url") { + index++; + let param = argv[index]; + + request.url = param; + continue; + } + + if (command == "-X" || command == "--request") { + methodExplicitlySpecified = true; + + index++; + let param = argv[index]; + + request.method = param; + continue; + } + + if (command == "-G" || command == "--get") { + methodExplicitlySpecified = true; + explicitGet = true; + + request.method = HttpRequestMethod.GET; + continue; + } + + if (command == "-I" || command == "--head") { + methodExplicitlySpecified = true; + + request.method = HttpRequestMethod.HEAD; + continue; + } + + if (command == "-H" || command == "--header") { + index++; + let param = argv[index]; + + let parts = param.split(":"); + let name = parts[0].trim(); + let value = parts.slice(1).join(":").trim(); + + if (name.toLowerCase().trim() == "content-type") { + contentType = value; + } + + request.headers.push({ name, value }); + continue; + } + } + + // In the second scan extract body: + + index = 0; + command = ""; + + while (index < argv.length - 1) { + + index++; + command = argv[index]; + + if (["-F", "--form", "--form-string"].includes(command)) { + + if (!request.body || request.body.type != HttpBodyType.Form || + (request.body).encoding != FormEncoding.Multipart) { + request.body = new HttpFormBody(); + (request.body).encoding = FormEncoding.Multipart; + } + + if (!methodExplicitlySpecified) + request.method = HttpRequestMethod.POST; + + index++; + let param = argv[index]; + + let parts = param.split("="); + let name = parts[0].trim(); + let value = parts.slice(1).join("=").trim(); + + let type = HttpBodyContentType.Text; + if (value.startsWith("@") && command != "--form-string") { + type = HttpBodyContentType.File; + value = value.substring(1); + } + + ((request.body)).records.push({ name, value, type }); + continue; + } + + if (["-d", "--data", "--data-ascii", "--data-raw", + "--data-binary", "--data-urlencode", "--json", "--url-query"].includes(command)) { + + if (explicitGet || command == "--url-query") { + + index++; + let param = argv[index]; + + let parts = param.split("="); + let name = parts[0].trim(); + let value = parts.slice(1).join("=").trim(); + + let newUrl = new URL(request.url); + newUrl.searchParams.append(name, value); + request.url = newUrl.toString(); + + } else if (command != "--data-binary" && command != "--json" && + (!contentType || contentType.includes("application/x-www-form-urlencoded"))) { + + if (!methodExplicitlySpecified) + request.method = HttpRequestMethod.POST; + + if (!request.body || request.body.type != HttpBodyType.Form || + (request.body).encoding != FormEncoding.UrlEncoded) { + request.body = new HttpFormBody(); + (request.body).encoding = FormEncoding.UrlEncoded; + } + + index++; + let param = argv[index]; + + let parts = param.split("="); + let name = parts[0].trim(); + let value = parts.slice(1).join("=").trim(); + + let type = HttpBodyContentType.Text; + + ((request.body)).records.push({ name, value, type }); + } else { + if (!methodExplicitlySpecified) + request.method = HttpRequestMethod.POST; + + if (!request.body || request.body.type != HttpBodyType.Regular) { + request.body = new HttpTextBody(); + } + + (request.body).valueType = HttpBodyContentType.Text; + + if (command == "--json") { + request.headers.push({ name: "Content-Type", value: "application/json" }); + request.headers.push({ name: "Accept", value: "application/json" }); + } + + index++; + let param = argv[index]; + + let value = param; + let type = HttpBodyContentType.Text; + + if (value.startsWith("@") && command != "--data-raw") { + type = HttpBodyContentType.File; + value = value.substring(1); + } + + ((request.body)).value = value; + ((request.body)).valueType = type; + } + } + } + + return request; + } catch (ex) { + return null; + } + } + + private static _parseArgsStringToArgv(value: string): string[] { + // ([^\s'"]([^\s'"]*(['"])([^\3]*?)\3)+[^\s'"]*) Matches nested quotes until the first space outside of quotes + + // [^\s'"]+ or Match if not a space ' or " + + // (['"])([^\5]*?)\5 or Match "quoted text" without quotes + // `\3` and `\5` are a backreference to the quote style (' or ") captured + let argv: Array = []; + let match: RegExpExecArray | null; + do { + // Each call to exec returns the next regex match as an array + match = CurlCommandConverter._myRegexp.exec(value); + + if (match == null) break; + + // Index 1 in the array is the captured group if it exists + // Index 0 is the matched text, which we use if no captured group exists + + let captures = [match[1], match[6], match[0]]; + + for (let i = 0; i <= captures.length - 1; i++) { + if (typeof captures[i] === "string") { + argv.push(captures[i]); + break; + } + } + // eslint-disable-next-line no-constant-condition + } while (true); + + return argv; + } + + private static _isValidUrl(url: string): boolean { + try { + let parsedUrl = new URL(url); + return parsedUrl.protocol === "http:" || parsedUrl.protocol === "https:"; + } catch { + return false; + } + } +} diff --git a/src/renderer/ui/AppStatePersister.ts b/src/renderer/ui/AppStatePersister.ts new file mode 100644 index 0000000..d6f4b61 --- /dev/null +++ b/src/renderer/ui/AppStatePersister.ts @@ -0,0 +1,74 @@ +export interface OpenWItemRecord { + path: string; + collectionUuid: string; + pinned: boolean; +} + +class HttpinessStatePersister { + private static _openCollectionsLocalStorageKey = "collectionsCache"; + private static _openWItemsLocalStorageKey = "workspace-items"; + private static _parametersWidth = "parameters-width"; + private static _requestBrowserWidth = "req-browser-width"; + + + setOpenedCollections(collections: Array): void { + localStorage.setItem(HttpinessStatePersister._openCollectionsLocalStorageKey, + JSON.stringify(collections) + ); + } + + getOpenedCollections(): Array { + try { + let items = JSON.parse(localStorage.getItem(HttpinessStatePersister._openCollectionsLocalStorageKey)); + + if (!items || !Array.isArray(items)) return []; + return >items; + } catch { + return []; + } + } + + setOpenedWItems(witems: Array): void { + localStorage.setItem(HttpinessStatePersister._openWItemsLocalStorageKey, JSON.stringify(witems)); + } + + getOpenedWItems(): Array { + try { + let items = JSON.parse(localStorage.getItem(HttpinessStatePersister._openWItemsLocalStorageKey)); + + if (!items || !Array.isArray(items)) return []; + return >(items); + } catch { + return []; + } + } + + setParametersWidth(width: number): void { + localStorage.setItem(HttpinessStatePersister._parametersWidth, JSON.stringify(width)); + + } + + getParametersWidth(): number { + try { + let items = JSON.parse(localStorage.getItem(HttpinessStatePersister._parametersWidth)); + return Number.parseFloat(items); + } catch { + return 350; + } + } + + setRequestBrowserWidth(width: number): void { + localStorage.setItem(HttpinessStatePersister._requestBrowserWidth, JSON.stringify(width)); + } + + getRequestBrowserWidth(): number { + try { + let items = JSON.parse(localStorage.getItem(HttpinessStatePersister._requestBrowserWidth)); + return Number.parseFloat(items); + } catch { + return 300; + } + } +} + +export let AppStatePersister = new HttpinessStatePersister(); diff --git a/src/renderer/ui/BasicControls.ts b/src/renderer/ui/BasicControls.ts new file mode 100644 index 0000000..52f1882 --- /dev/null +++ b/src/renderer/ui/BasicControls.ts @@ -0,0 +1,188 @@ +import * as aflon from "aflon"; + +import { SimpleEvent } from "../lib/SimpleEvent"; +import { ContextMenu, ContextMenuItemType, ContextMenuShowTrigger } from "./ContextMenu"; + +import { BoxShadowValues, Colors, FontStyles } from "./StyleConstants"; + +export class Button extends aflon.Button { } + +Button.style = { + _: { + ...FontStyles.sansSerifBold, + color: Colors.workspaceDescriptor, + background: Colors.workspaceLine, + appearance: "none", + border: "none", + outline: "none", + fontSize: "12px", + minWidth: "80px", + height: "25px", + borderRadius: "4px", + textAlign: "center", + paddingLeft: "20px", + paddingRight: "20px", + letterSpacing: "0.5px", + boxShadow: BoxShadowValues.button, + borderBottom: `0.5px solid ${Colors.workspacePlaceholder}`, + cursor: "pointer", + whiteSpace: "nowrap", + "&:focus": { + border: `1px solid ${Colors.workspacePlaceholder}` + }, + "&:hover": { + background: Colors.workspacePlaceholder + } + } +}; + +export class SelectBox extends aflon.Div implements aflon.AbstractSelectBox { + eventSelected: string = "selected"; + eventChange: string = "change"; + eventInput: string = "input"; + + private _contextMenu: ContextMenu; + + private _options: Array = []; + private _selectedValue: string = null; + + constructor() { + super(); + + this.addAttr("tabindex", "0"); + + (this._contextMenu = new ContextMenu(this, [], ContextMenuShowTrigger.OnClickEvent)) + .on(this._contextMenu.eventAboutToBeShown, () => this._onContextMenuAboutToBeShown()) + .on(this._contextMenu.eventSelected, e => this._onContextMenuSelected(e)); + } + + insertOption(option: aflon.ISelectOption): this { + let entry = this._options.find(opt => option.value == opt.value); + + if (entry) { + entry.text = option.text; + return this; + } + + this._options.push({ ...option }); + + if (!this._selectedValue && this._options.length > 0) + this.setSelectedOption(this._options[0].value); + return this; + } + + removeOption(optionValue: string): this { + let removeIndex = this._options.findIndex(elem => elem.value == optionValue); + if (removeIndex == -1) return this; + + this._options = this._options.splice(removeIndex, 1); + return this; + } + + insertOptions(options: aflon.ISelectOption[]): this { + options.forEach(opt => { + let entry = this._options.find(opt2 => opt2.value == opt.value); + + if (entry) { + entry.text = opt.text; + return; + } + + this._options.push({ ...opt }); + }); + + if (!this._selectedValue && this._options.length > 0) + this.setSelectedOption(this._options[0].value); + + return this; + } + + setSelectedOption(optionValue: string): this { + if (this._selectedValue == optionValue) return this; + + let entry = this._options.find(opt2 => opt2.value == optionValue); + if (!entry) return this; + + this._selectedValue = entry.value; + this.setText(entry.text); + + return this; + } + + getSelectedOption(): aflon.ISelectOption { + let selected = this._options.find(opt2 => opt2.value == this._selectedValue); + return selected; + } + + getAllOptions(): Array { + return [ ...this._options ]; + } + + setDisabled(disabled: boolean): this { + if (disabled) { + this.addAttr("disabled"); + this.removeAttr("tabindex"); + } else { + this.removeAttr("disabled"); + this.addAttr("tabindex"); + } + + return this; + } + + getDisabled(): boolean { + return this.hasAttr("disabled"); + } + + focus(): void { + this.getHtmlElement().focus(); + } + + blur(): void { + this.getHtmlElement().blur(); + } + + private _onContextMenuAboutToBeShown(): void { + this._contextMenu.setDefinition( + this._options.map(option => ({ + type: ContextMenuItemType.Button, + text: option.text, + id: option.value }) + ) + ); + } + + private _onContextMenuSelected(e: SimpleEvent): void { + let entry = this._options.find(opt2 => opt2.value == e.detail["id"]); + if (!entry) return; + + let selectedValue = e.detail["id"]; + + if (selectedValue == this._selectedValue) return; + + this.setSelectedOption(e.detail["id"]); + + this.raise(this.eventSelected); + this.raise(this.eventInput); + this.raise(this.eventChange); + } +} + +SelectBox.style = { + _: { + ...FontStyles.sansSerifBold, + color: Colors.workspaceDefault, + appearance: "none", + border: "none", + background: "none", + cursor: "pointer", + textDecoration: "underline", + fontSize: "12px", + display: "inline-block", + whiteSpace: "nowrap", + "&:focus": { + border: "none", + outline: "none" + } + } +}; diff --git a/src/renderer/ui/ContextMenu/ContextMenu.ts b/src/renderer/ui/ContextMenu/ContextMenu.ts new file mode 100644 index 0000000..1ad7fef --- /dev/null +++ b/src/renderer/ui/ContextMenu/ContextMenu.ts @@ -0,0 +1,246 @@ +import { Div, Element } from "aflon"; + +import { ISimpleEventable, SimpleEventBroker, SimpleEventListener } from "../../lib/SimpleEvent"; + +import { BoxShadowValues, Colors, ZIndexLayers } from "../StyleConstants"; + +import { ContextMenuItem, ContextMenuItemDefinition } from "./ContextMenuItem"; + +export enum ContextMenuShowTrigger { + OnContextMenuEvent, OnClickEvent +} + +export class ContextMenu implements ISimpleEventable { + public eventSelected = "selected"; + public eventAboutToBeShown = "aboutToBeShown"; + + private _owner: Element; + private _items: Array; + private _eventBroker: SimpleEventBroker = new SimpleEventBroker(this); + private _maxWidth: number = 0; + + constructor(owner: Element, items: Array, + trigger: ContextMenuShowTrigger = ContextMenuShowTrigger.OnContextMenuEvent, maxWidth: number = 0) { + + this._owner = owner; + this._items = items; + this._maxWidth = maxWidth; + + if (trigger == ContextMenuShowTrigger.OnContextMenuEvent) + this._owner.on(this._owner.eventContextMenu, e => this._onOwnerContextMenu(e)); + else if (trigger == ContextMenuShowTrigger.OnClickEvent) + this._owner.on(this._owner.eventClick, e => this._onOwnerContextMenu(e)); + else + throw new Error(`ContextMenuShowTrigger ${trigger} is not supported.`); + } + + private static _findItemWithId(items: Array, id: string): ContextMenuItemDefinition { + + for (let item of items) { + if (item.id == id) return item; + if (item.submenu) { + let result = this._findItemWithId(item.submenu, id); + if (result) return result; + } + } + + return null; + } + + getOwner(): Element { + return this._owner; + } + + setItemDisabled(itemId: string, disabled: boolean): this { + const item = ContextMenu._findItemWithId(this._items, itemId); + if (!item) return this; + item.disabled = disabled; + + return this; + } + + getItemDisabled(itemId: string): boolean { + const item = ContextMenu._findItemWithId(this._items, itemId); + if (!item) return false; + return item.disabled; + } + + setChecked(itemId: string, checked: boolean): this { + const item = ContextMenu._findItemWithId(this._items, itemId); + if (!item) return this; + item.checked = checked; + + return this; + } + + getChecked(itemId: string): boolean { + const item = ContextMenu._findItemWithId(this._items, itemId); + if (!item) return false; + return item.checked; + } + + setText(itemId: string, text: string): this { + const item = ContextMenu._findItemWithId(this._items, itemId); + if (!item) return this; + item.text = text; + + return this; + } + + getText(itemId: string): string { + const item = ContextMenu._findItemWithId(this._items, itemId); + if (!item) return ""; + return item.text; + } + + setDefinition(items: Array): this { + this._items = items; + return this; + } + + getDefinition(): Array { + return this._items; + } + + on(eventName: string, handler: SimpleEventListener): this { + this._eventBroker.on(eventName, handler); + return this; + } + + off(eventName: string, handler: SimpleEventListener): this { + this._eventBroker.off(eventName, handler); + return this; + } + + raise(eventName: string, args: Record = {}): void { + this._eventBroker.raise(eventName, args); + } + + private async _onOwnerContextMenu(e: Event): Promise { + e.preventDefault(); + + const mouseEvent = e; + this.raise(this.eventAboutToBeShown); + + let result = await ContextMenuElement.show(this._items, [ mouseEvent.clientX, mouseEvent.clientY ], this._maxWidth); + + if (result) + this.raise(this.eventSelected, { id: result }); + } +} + +export class ContextMenuRun extends Div { + private _onContextMenuItemButtonClickHandler: (e:Event) => void; + + show(items: Array, x: number, y: number, onContextMenuItemButtonClickHandler: (e:Event) => void, maxWidth: number = 0): void { + this._onContextMenuItemButtonClickHandler = onContextMenuItemButtonClickHandler; + + this.append(items.map(item => { + let cmElem = ContextMenuItem.create(item); + cmElem.on(cmElem.eventItemSelected, this._onContextMenuItemButtonClickHandler); + return cmElem; + })); + + if (maxWidth != 0) { + this.setInlineCss({ maxWidth: `${maxWidth}px` }); + } + + this.setInlineCss({ + top: "0px", + left: "0px", + display: "flex", + opacity: 0 + }); + + // We need to invoke querying of size async as engine + // needs some time to render invisible element. + setTimeout(() => { + let width = this.getHtmlElement().offsetWidth; + let height = this.getHtmlElement().offsetHeight; + + let windowHeight = window.innerHeight; + let windowWidth = window.innerWidth; + + let Y = Math.max(y - 6, 6); + if (Y + height > windowHeight - 12) + Y = Math.max(y - height - 6, 6); + if (Y + height > windowHeight - 12) { + Y = 6; + height = windowHeight - 12; + } + + if (Y < 0) { + Y = 0; + height = windowHeight; + } + + let X = x + 2; + if (x + width > windowWidth) + X = x - width - 2; + + this.setInlineCss({ + top: `${Y}px`, + left: `${X}px`, + height: `${height}px`, + opacity: 1 + }); + }, 0); + } +} + +ContextMenuRun.style = { + _: { + background: Colors.tooltipBackgroundDefault, + borderRadius: "6px", + boxShadow: BoxShadowValues.contextMenu, + display: "flex", + flexFlow: "column nowrap", + alignItems: "stretch", + position: "fixed", + paddingTop: "6px", + paddingBottom: "6px", + maxWidth: "250px", + zIndex: ZIndexLayers.context, + overflowX: "scroll", + "&::-webkit-scrollbar": { + display: "none" + } + } +}; + +class ContextMenuElement extends Div { + private static _instance: ContextMenuElement = null; + + private constructor() { + super(); + } + + static initialize(): void { + if (ContextMenuElement._instance != null) return; + ContextMenuElement._instance = new ContextMenuElement(); + document.body.appendChild(ContextMenuElement._instance.getHtmlElement()); + } + + static async show(items: Array, mouseLocation: [number, number], maxWidth: number = 0): Promise { + return new Promise((resolve) => { + let selectedItemId = ""; + + const onDocumentClick = (): void => { + resolve(selectedItemId); + document.removeEventListener("mousedown", onDocumentClick); + ContextMenuElement._instance.empty(); + }; + + const onContextMenuItemButtonClick = (e: Event): void => { + selectedItemId = e["detail"]["id"]; + }; + let run = new ContextMenuRun(); + ContextMenuElement._instance.append([ run ]); + run.show(items, mouseLocation[0], mouseLocation[1], onContextMenuItemButtonClick, maxWidth); + + document.addEventListener("mousedown", onDocumentClick); + }); + } +} + +ContextMenuElement.initialize(); diff --git a/src/renderer/ui/ContextMenu/ContextMenuItem.ts b/src/renderer/ui/ContextMenu/ContextMenuItem.ts new file mode 100644 index 0000000..2ba8bcb --- /dev/null +++ b/src/renderer/ui/ContextMenu/ContextMenuItem.ts @@ -0,0 +1,382 @@ +import { Div } from "aflon"; +import { Icon } from "../Icon"; +import { Colors, FontStyles } from "../StyleConstants"; +import { ContextMenuRun } from "./ContextMenu"; + +export enum ContextMenuItemType { + Button, Title, Text, Divider, CheckBox +} + +export interface ContextMenuItemDefinition { + type: ContextMenuItemType; + text?: string; + id: string; + disabled?: boolean; + checked?: boolean; + iconName?: string; + iconSizeAdjustment?: number; + submenu?: Array +} + +export class ContextMenuItem extends Div { + public eventItemSelected = "itemSelected"; + + private _id: string; + + protected constructor(definition: ContextMenuItemDefinition) { + super(); + + this._id = definition.id; + } + + static create(definition: ContextMenuItemDefinition): ContextMenuItem { + if (definition.type == ContextMenuItemType.Button) { + return new ContextMenuButton(definition); + } else if (definition.type == ContextMenuItemType.Text) { + return new ContextMenuText(definition); + } else if (definition.type == ContextMenuItemType.Title) { + return new ContextMenuTitle(definition); + } else if (definition.type == ContextMenuItemType.Divider) { + return new ContextMenuDivider(definition); + } else if (definition.type == ContextMenuItemType.CheckBox) { + return new ContextMenuCheckBox(definition); + } else { + throw new Error(`ContextMenuItemType ${definition.type} is not supported.`); + } + } + + getId(): string { + return this._id; + } +} + +class ContextMenuButton extends ContextMenuItem { + private static _defaultIconSize: number = 12; + + private _iconContainer: Div; + private _text: Div; + private _subMenuArrow: Div; + + private _submenu: Array = null; + + constructor(definition: ContextMenuItemDefinition) { + super(definition); + + this.append([ + (this._iconContainer = new Div()), + (this._text = new Div()) + .setText(definition.text), + (this._subMenuArrow = new Div()) + .append([ new Icon("more") ]) + .setInlineCss({ display: "none" }) + ]); + + this._prepareDisabled(definition); + this._prepareIcon(definition); + this._prepareSubmenu(definition); + } + + private _prepareDisabled(definition: ContextMenuItemDefinition): void { + if (definition.disabled) { + this.addAttr("disabled") + .setInlineCss({ + pointerEvents: "none" + }); + + this.setInlineCss({ color: Colors.tooltipDisabled }); + } else { + this.removeAttr("disabled") + .setInlineCss({ + pointerEvents: "auto" + }); + + this.setInlineCss({ color: Colors.tooltipText }); + } + } + + private _prepareIcon(definition: ContextMenuItemDefinition): void { + if ("iconName" in definition) { + let sizeAdjustment = 0; + if ("iconSizeAdjustment" in definition) + sizeAdjustment = definition.iconSizeAdjustment; + + let size = ContextMenuButton._defaultIconSize + sizeAdjustment; + + this._iconContainer.append([ + new Icon(definition.iconName).setInlineCss({ + fontSize: size.toString() + "px" + }) + ]); + } + } + + private _prepareSubmenu(definition: ContextMenuItemDefinition): void { + if ("submenu" in definition) { + this._submenu = definition.submenu; + + if (this._submenu != null) { + this.on(this.eventMouseEnter, () => this._onMouseEnter()); + this._subMenuArrow.setInlineCss({ display: "block" }); + } else { + this._subMenuArrow.setInlineCss({ display: "none" }); + } + } else { + this.on(this.eventMouseDown, () => this._onMouseDown()); + } + } + + private _onMouseDown(): void { + this.raise(this.eventItemSelected, { + id: this.getId() + }); + } + + private _onMouseEnter(): void { + let rect = this.getHtmlElement().getBoundingClientRect(); + + let left = rect.right + window.scrollX - 2; + let top = rect.top + window.scrollY; + + let submenuRun = new ContextMenuRun(); + + const onLeave = (): void => { + this.off(this.eventMouseLeave, onLeave); + this.removeChild(submenuRun); + }; + + this.on(this.eventMouseLeave, onLeave); + this.append([ submenuRun ]); + submenuRun.show(this._submenu, left, top, e => { + this.raise(this.eventItemSelected, { + id: e["detail"]["id"] + }); + }); + } +} + +ContextMenuButton.style = { + _: { + display: "flex", + flexFlow: "row nowrap", + alignItems: "center", + flex: "0 0 22px", + cursor: "pointer", + color: Colors.tooltipText, + "&:hover": { + background: Colors.tooltipBackgroundHover + } + }, + _iconContainer: { + flex: "0 0 auto", + width: "16px", + height: "12px", + lineHeight: "13px", + textAlign: "center", + marginLeft: "10px", + marginRight: "7px" + }, + _text: { + ...FontStyles.sansSerifNormal, + fontSize: "11px", + lineHeight: "22px", + flex: "1 1 100px", + marginRight: "13px", + overflow: "hidden", + textOverflow: "ellipsis", + whiteSpace: "nowrap" + }, + _subMenuArrow: { + flex: "0 0 auto", + width: "8px", + height: "13px", + fontSize: "13px", + lineHeight: "13px", + textAlign: "center", + marginLeft: "8px", + marginRight: "8px" + } +}; + +class ContextMenuCheckBox extends ContextMenuItem { + private _iconContainer: Div; + private _icon: Icon; + private _text: Div; + + constructor(definition: ContextMenuItemDefinition) { + super(definition); + + this + .append([ + (this._iconContainer = new Div()) + .append([ + (this._icon = new Icon("checkmark")) + .setVisibility(definition.checked) + ]), + (this._text = new Div()) + .setText(definition.text) + ]) + .on(this.eventMouseDown, () => this._onMouseDown()); + + this._prepareDisabled(definition); + } + + private _prepareDisabled(definition: ContextMenuItemDefinition): void { + if (definition.disabled) { + this.addAttr("disabled") + .setInlineCss({ + pointerEvents: "none" + }); + + this.setInlineCss({ color: Colors.tooltipDisabled }); + } else { + this.removeAttr("disabled") + .setInlineCss({ + pointerEvents: "auto" + }); + + this.setInlineCss({ color: Colors.tooltipText }); + } + } + + private _onMouseDown(): void { + this.raise(this.eventItemSelected, { + id: this.getId(), + checked: this._icon.getVisibility() + }); + } +} + +ContextMenuCheckBox.style = { + _: { + display: "flex", + flexFlow: "row nowrap", + alignItems: "center", + flex: "0 0 22px", + cursor: "pointer", + color: Colors.tooltipText, + "&:hover": { + background: Colors.tooltipBackgroundHover + } + }, + _iconContainer: { + flex: "0 0 auto", + width: "14px", + height: "14px", + border: `1px solid ${Colors.tooltipText}`, + lineHeight: "14px", + textAlign: "center", + marginLeft: "10px", + marginRight: "7px", + fontSize: "9px" + }, + _text: { + ...FontStyles.sansSerifNormal, + fontSize: "11px", + lineHeight: "22px", + flex: "1 1 100px", + marginRight: "13px", + overflow: "hidden", + textOverflow: "ellipsis", + whiteSpace: "nowrap" + } +}; + +class ContextMenuText extends ContextMenuItem { + private _text: Div; + + constructor(definition: ContextMenuItemDefinition) { + super(definition); + + this.append([ + (this._text = new Div()) + .setText(definition.text) + ]); + } + + setText(text: string): this { + this._text.setText(text); + return this; + } + + getText(): string { + return this._text.getText(); + } +} + +ContextMenuText.style = { + _: { + marginLeft: "10px", + marginRight: "10px", + marginTop: "3px", + marginBottom: "3px", + lineHeight: "16px", + ...FontStyles.sansSerifNormal, + color: Colors.tooltipText, + fontSize: "10.5px" + } +}; + +class ContextMenuTitle extends ContextMenuItem { + + private _text: Div; + + constructor(definition: ContextMenuItemDefinition) { + super(definition); + + this.append([ + (this._text = new Div()) + .setText(definition.text) + ]); + } + + setText(text: string): this { + this._text.setText(text); + return this; + } + + getText(): string { + return this._text.getText(); + } +} + +ContextMenuTitle.style = { + _: { + display: "flex", + flexFlow: "row nowrap", + alignItems: "center", + flex: "0 0 22px", + marginLeft: "10px", + marginRight: "10px" + }, + _text: { + ...FontStyles.sansSerifBold, + color: Colors.tooltipText, + fontSize: "11px", + lineHeight: "12px", + flex: "1 1 100px" + } +}; + +class ContextMenuDivider extends ContextMenuItem { + private _line: Div; + + constructor(definition: ContextMenuItemDefinition) { + super(definition); + + this.append([ + (this._line = new Div()) + ]); + } +} + +ContextMenuDivider.style = { + _: { + paddingTop: "4px", + paddingBottom: "4px" + }, + _line: { + width: "100%", + height: "1px", + background: Colors.tooltipDisabled + } +}; diff --git a/src/renderer/ui/ContextMenu/Tooltip.ts b/src/renderer/ui/ContextMenu/Tooltip.ts new file mode 100644 index 0000000..8b444f6 --- /dev/null +++ b/src/renderer/ui/ContextMenu/Tooltip.ts @@ -0,0 +1,150 @@ +import { Div, Element } from "aflon"; +import { ContextMenuItemType } from "."; + +import { BoxShadowValues, Colors, ZIndexLayers } from "../StyleConstants"; + +import { ContextMenuItem } from "./ContextMenuItem"; + +export class Tooltip extends Div { + eventAboutToBeShown: string = "aboutToBeShown"; + + private _owner: Element; + + private _stale: boolean = true; + private _title: string = ""; + private _text: string = "Tooltip"; + + private _width: number = 0; + private _height: number = 0; + + constructor(owner: Element) { + super(); + this.setInlineCss({ display: "none" }); + + this._owner = owner; + + this._owner.append([ this ]); + this._owner.on(this._owner.eventMouseEnter, e => this._onOwnerEnter(e)); + this._owner.on(this._owner.eventMouseLeave, () => this._onOwnerLeave()); + + return this; + } + + getOwner(): Element { + return this._owner; + } + + setTitle(title: string): this { + if (this._title == title) return this; + this._title = title; + this._stale = true; + return this; + } + + getTitle(): string { + return this._title; + } + + setText(text: string): this { + if (this._text == text) return this; + this._text = text; + this._stale = true; + return this; + } + + getText(): string { + return this._text; + } + + private _update(): void { + this.empty(); + + if (this._title) + this.append([ + ContextMenuItem.create({ + type: ContextMenuItemType.Title, + id: "title", + text: this._title}) + ]); + + if (this._text) + this.append([ + ContextMenuItem.create({ + type: ContextMenuItemType.Text, + id: "text", + text: this._text}) + ]); + + this.setInlineCss({ + top: "0px", + left: "0px" + }); + + if (this.getInlineCss()["display"] == "flex") { + this._width = this.getHtmlElement().offsetWidth; + this._height = this.getHtmlElement().offsetHeight; + } else { + this.setInlineCss({ + display: "flex", + opacity: 0 + }); + + this._width = this.getHtmlElement().offsetWidth; + this._height = this.getHtmlElement().offsetHeight; + + this.setInlineCss({ + display: "none", + opacity: 1 + }); + } + + this._stale = false; + } + + private _onOwnerEnter(e: Event): void { + this.raise(this.eventAboutToBeShown); + + if (this._stale) + this._update(); + + const mouseEvent = e; + + let Y = mouseEvent.clientY + 5; + if (mouseEvent.clientY + this._height > window.innerHeight) + Y = mouseEvent.clientY - this._height - 5; + + let X = mouseEvent.clientX + 3; + if (mouseEvent.clientX + this._width > window.innerWidth) + X = mouseEvent.clientX - this._width - 3; + + this.setInlineCss({ + display: "flex", + top: `${Y}px`, + left: `${X}px` + }); + } + + private _onOwnerLeave(): void { + this.setInlineCss({ display: "none" }); + } +} + +Tooltip.style = { + _: { + background: Colors.tooltipBackgroundDefault, + borderRadius: "4px", + boxShadow: BoxShadowValues.contextMenu, + display: "none", + flexFlow: "column nowrap", + alignItems: "stretch", + position: "fixed", + paddingTop: "4px", + paddingBottom: "4px", + maxWidth: "200px", + pointerEvents: "none", + textAlign: "left", + wordWrap: "break-word", + zIndex: ZIndexLayers.tooltip, + whiteSpace: "normal" + } +}; diff --git a/src/renderer/ui/ContextMenu/index.ts b/src/renderer/ui/ContextMenu/index.ts new file mode 100644 index 0000000..7e06d6f --- /dev/null +++ b/src/renderer/ui/ContextMenu/index.ts @@ -0,0 +1,3 @@ +export { ContextMenuItemType, ContextMenuItemDefinition } from "./ContextMenuItem"; +export { ContextMenu, ContextMenuShowTrigger } from "./ContextMenu"; +export { Tooltip } from "./Tooltip"; diff --git a/src/renderer/ui/ControlBar/ControlBar.ts b/src/renderer/ui/ControlBar/ControlBar.ts new file mode 100644 index 0000000..aa1fc3a --- /dev/null +++ b/src/renderer/ui/ControlBar/ControlBar.ts @@ -0,0 +1,277 @@ +import { Div } from "aflon"; + +import { ZoomManager } from "../../lib/WindowManipulation"; +import { maximize } from "../../lib/WindowManipulation"; +import { Platform, currentPlatform, openFileInDefaultApp } from "../../lib/Platform"; +import { Version } from "../../lib/Version"; +import { RapidsWebApi } from "../../lib/RapidsWebApi"; +import { SimpleEvent } from "../../lib/SimpleEvent"; +import { Telemetry, TelemetryEvent } from "../../lib/Telemetry"; + +import { Icon } from "../Icon"; +import { Colors, ColorTheme, FontStyles, ImageUrls } from "../StyleConstants"; +import { ContextMenu, ContextMenuItemType, ContextMenuShowTrigger, Tooltip } from "../ContextMenu"; +import { PreferenceStore, ResponseControlLocation } from "../PreferenceStore"; + +import { Win32ControlBox } from "./Win32ControlBox"; +import { InAppHelp } from "./InAppHelp"; + +export class ControlBar extends Div { + private _controlBoxMacOsBuffer: Div; + private _wordmark: Div; + private _title: Div; + private _version: Div; + private _emptyBuffer: Div; + private _updateButton: Div; + private _helpButton: Div; + private _help: InAppHelp; + private _settingsButton: Div; + private _controlBarOptions: Div; + private _controlBoxWin32: Div; + + private _settingsContextMenu: ContextMenu; + + constructor() { + super(); + + if (currentPlatform() == Platform.MacOS) { + this.append([ + (this._controlBoxMacOsBuffer = new Div()) + .on(this.eventDblClick, () => maximize()) + .addClass("control-bar") + ]); + } + + this.append([ + (this._wordmark = new Div()) + .on(this.eventDblClick, () => maximize()) + .addClass("control-bar"), + (this._version = new Div()) + .setText(Version.current.toString()), + (this._emptyBuffer = new Div()) + .on(this.eventDblClick, () => maximize()) + .addClass("control-bar"), + (this._controlBarOptions = new Div()) + .append([ + (this._updateButton = new Div()) + .append([ new Icon("update") ]) + .setVisibility(false) + .on(this._updateButton.eventClick, () => this._onUpdateButtonClick()), + (this._helpButton = new Div()) + .append([ new Icon("docs")]) + .on(this._helpButton.eventClick, () => this._onHelpButtonClick()), + (this._settingsButton = new Div()) + .append([ new Icon("settings") ]) + ]), + (this._help = new InAppHelp()) + .setVisibility(false) + ]); + + if (currentPlatform() == Platform.Win32) { + this.append([ + (this._controlBoxWin32 = new Win32ControlBox()) + ]); + this._emptyBuffer.addCssClass({ marginRight: "100px" }); + } + + new Tooltip(this._updateButton).setText("Update available"); + new Tooltip(this._helpButton).setText("Help"); + new Tooltip(this._settingsButton).setText("Settings"); + + (this._settingsContextMenu = new ContextMenu(this._settingsButton, [ + { type: ContextMenuItemType.CheckBox, id: "no-req-labels", text: "Hide request labels" }, + { type: ContextMenuItemType.CheckBox, id: "no-res-labels", text: "Hide response labels"}, + { type: ContextMenuItemType.CheckBox, id: "single-line-url", text: "Prefer single-line URL" }, + { type: ContextMenuItemType.CheckBox, id: "close-console-on-mouse-leave", text: "Close console on mouse leave" }, + { type: ContextMenuItemType.CheckBox, id: "auto-hide-params", text: "Auto-hide parameters" }, + { type: ContextMenuItemType.Divider, id: "dev1" }, + { type: ContextMenuItemType.Button, id: "res-loc", text: "Response location", submenu: [ + { type: ContextMenuItemType.CheckBox, id: "res-loc-console", text: "Console" }, + { type: ContextMenuItemType.CheckBox, id: "res-loc-workspace", text: "Workspace" }, + { type: ContextMenuItemType.CheckBox, id: "res-loc-auto", text: "Automatic, depending on space" } + ]}, + { type: ContextMenuItemType.Divider, id: "dev2" }, + { type: ContextMenuItemType.Button, id: "zoom-in", text: "Zoom in" }, + { type: ContextMenuItemType.Button, id: "zoom-out", text: "Zoom out" }, + { type: ContextMenuItemType.Button, id: "zoom-reset", text: "Reset zoom" }, + { type: ContextMenuItemType.Divider, id: "dev3" }, + { type: ContextMenuItemType.Button, id: "color-theme", text: "Switch to dark theme" }, + { type: ContextMenuItemType.Divider, id: "dev4" }, + { type: ContextMenuItemType.Button, id: "report-issue", text: "Report an issue" }, + { type: ContextMenuItemType.Button, id: "give-feedback", text: "Give feedback", iconName: "say" } + ], ContextMenuShowTrigger.OnClickEvent)) + .on(this._settingsContextMenu.eventSelected, e => this._onSettingsMenuSelected(e)) + .on(this._settingsContextMenu.eventAboutToBeShown, () => this._onSettingsMenuAboutToBeShown()); + + setTimeout(async () => { + let version = await RapidsWebApi.getLatestVersion(); + if (version.newerThen(Version.current)) + this._updateButton.setVisibility(true); + }, 1); + } + + showHelp(): this { + this._help.setVisibility(!this._help.getVisibility()); + return this; + } + + private _onUpdateButtonClick(): void { + openFileInDefaultApp("https://www.httpiness.com#download"); + } + + private _onSettingsMenuSelected(e: SimpleEvent): void { + let selectedId = e["detail"]["id"]; + if (!selectedId) return; + + if (selectedId == "zoom-in") + ZoomManager.zoomIn(); + else if (selectedId == "zoom-out") + ZoomManager.zoomOut(); + else if (selectedId == "zoom-reset") + ZoomManager.zoomReset(); + else if (selectedId == "no-req-labels") { + PreferenceStore.setHideRequestDescriptor(!PreferenceStore.getHideRequestDescriptor()); + } else if (selectedId == "no-res-labels") { + PreferenceStore.setHideResponseDescriptor(!PreferenceStore.getHideResponseDescriptor()); + } else if (selectedId == "single-line-url") { + PreferenceStore.setPreferSingleLineUrl(!PreferenceStore.getPreferSingleLineUrl()); + } else if (selectedId == "close-console-on-mouse-leave") { + PreferenceStore.setCloseConsoleOnMouseLeave(!PreferenceStore.getCloseConsoleOnMouseLeave()); + } else if (selectedId == "res-loc-console") { + PreferenceStore.setResponseLocation(ResponseControlLocation.Console); + } else if (selectedId == "res-loc-workspace") { + PreferenceStore.setResponseLocation(ResponseControlLocation.Workspace); + } else if (selectedId == "res-loc-auto") { + PreferenceStore.setResponseLocation(ResponseControlLocation.Automatic); + } else if (selectedId == "auto-hide-params") { + PreferenceStore.setAutoHideParameters(!PreferenceStore.getAutoHideParameters()); + } else if (selectedId == "color-theme") { + let newTheme = ColorTheme.Light; + if (PreferenceStore.getColorTheme() == ColorTheme.Light) + newTheme = ColorTheme.Dark; + + PreferenceStore.setColorTheme(newTheme); + } else if (selectedId == "report-issue") { + openFileInDefaultApp("https://forms.gle/nC7XvMZLXXPHyRik9"); + } else if (selectedId == "give-feedback") { + Telemetry.reportEvent(TelemetryEvent.FeedbackSent); + openFileInDefaultApp("https://forms.gle/CMN15fRycYADAAbT6"); + } + } + + private _onSettingsMenuAboutToBeShown(): void { + this._settingsContextMenu.setChecked("no-req-labels", PreferenceStore.getHideRequestDescriptor()); + this._settingsContextMenu.setChecked("no-res-labels", PreferenceStore.getHideResponseDescriptor()); + this._settingsContextMenu.setChecked("single-line-url", PreferenceStore.getPreferSingleLineUrl()); + this._settingsContextMenu.setChecked("close-console-on-mouse-leave", PreferenceStore.getCloseConsoleOnMouseLeave()); + this._settingsContextMenu.setChecked("res-loc-console", PreferenceStore.getResponseLocation() == ResponseControlLocation.Console); + this._settingsContextMenu.setChecked("res-loc-workspace", PreferenceStore.getResponseLocation() == ResponseControlLocation.Workspace); + this._settingsContextMenu.setChecked("res-loc-auto", PreferenceStore.getResponseLocation() == ResponseControlLocation.Automatic); + this._settingsContextMenu.setChecked("auto-hide-params", PreferenceStore.getAutoHideParameters()); + + if (PreferenceStore.getColorTheme() == ColorTheme.Dark) { + this._settingsContextMenu.setText("color-theme", "Switch to light theme"); + } else { + this._settingsContextMenu.setText("color-theme", "Switch to dark theme"); + } + } + + private _onHelpButtonClick(): void { + this._help.setVisibility(!this._help.getVisibility()); + } +} + +ControlBar.style = { + _: { + background: Colors.controlBarBackground, + display: "flex", + flexFlow: "row nowrap", + alignItems: "center", + borderBottom: `solid 1px ${Colors.workspaceLine}` + }, + _controlBoxMacOsBuffer: { + flex: "1 1 1px", + height: "100%", + paddingLeft: "80px" + }, + _wordmark: { + height: "100%", + width: "100px", + marginLeft: "10px", + marginTop: "4px", + backgroundImage: `url(${ImageUrls.wordmark})`, + backgroundPosition: "center", + backgroundSize: "contain", + backgroundRepeat: "no-repeat" + }, + _title: { + ...FontStyles.sansSerifBold, + fontSize: "16px", + color: "#717171" + }, + _version: { + ...FontStyles.sansSerifBold, + fontSize: "9px", + marginLeft: "5px", + marginTop: "6px", + color: "#717171" + }, + _emptyBuffer: { + flex: "1 1 1px", + height: "100%" + }, + _controlBarOptions: { + display: "flex", + flexFlow: "row nowrap", + color: Colors.browserDefault, + fontSize: "13px", + height: "100%", + paddingRight: "10px" + }, + _updateButton: { + display: "flex", + justifyContent: "center", + alignItems: "center", + height: "100%", + width: "30px", + fontSize: "13px", + textAlign: "center", + color: Colors.statusWarn, + "&:hover": { + color: Colors.consoleDominant + } + }, + _helpButton: { + position: "relative", + display: "flex", + justifyContent: "center", + alignItems: "center", + height: "100%", + width: "29px", + fontSize: "16px", + textAlign: "center", + "&:hover": { + color: Colors.consoleDominant + } + }, + _settingsButton: { + display: "flex", + justifyContent: "center", + alignItems: "center", + height: "100%", + fontSize: "15px", + width: "29px", + textAlign: "center", + "&:hover": { + color: Colors.consoleDominant + } + }, + _controlBoxWin32: { + height: "100%" + }, + _help: { + position: "absolute", + top: "30px", + right: "30px" + } +}; diff --git a/src/renderer/ui/ControlBar/InAppHelp.ts b/src/renderer/ui/ControlBar/InAppHelp.ts new file mode 100644 index 0000000..c614b02 --- /dev/null +++ b/src/renderer/ui/ControlBar/InAppHelp.ts @@ -0,0 +1,259 @@ +import { Div, TextBox } from "aflon"; + +import { Icon } from "../Icon"; +import { Colors, BoxShadowValues, FontStyles, ZIndexLayers } from "../StyleConstants"; + +enum HelpElementType { + Question = "Question", SectionTitle = "SectionTitle" +} + +interface HelpQuestion { + type: HelpElementType.Question; + question: string; + answer: string; +} + +interface HelpSectionTitle { + type: HelpElementType.SectionTitle; + title: string; +} + +type HelpElement = HelpQuestion | HelpSectionTitle; + +class HelpElementControl extends Div { + + private _question: Div; + private _answer: Div; + private _sectionTitle: Div; + + constructor(element: HelpElement) { + super(); + + if (element.type == HelpElementType.Question) { + this.append([ + (this._question = new Div()) + .setText(element.question) + .on(this._question.eventClick, () => this._onClick()), + (this._answer = new Div()) + .setText(element.answer) + .setVisibility(false) + ]); + } else if (element.type == HelpElementType.SectionTitle) { + this.append([ + (this._sectionTitle = new Div()) + .setText(element.title) + ]); + } + } + + setExpanded(expanded: boolean): this { + if (this._answer) + this._answer.setVisibility(expanded); + return this; + } + + getExpanded(): boolean { + if (this._answer) + return this._answer.getVisibility(); + return false; + } + + setSearchPhrase(phrase: string): void { + if (!phrase) { + this.setVisibility(true); + return; + } + + if (this._sectionTitle) { + this.setVisibility(false); + return; + } + + if (this._question.getText().toUpperCase().indexOf(phrase.toUpperCase()) != -1 || + this._answer.getText().toUpperCase().indexOf(phrase.toUpperCase()) != -1) { + this.setVisibility(true); + } else { + this.setVisibility(false); + } + } + + private _onClick(): void { + this.setExpanded(!this.getExpanded()); + } +} + +HelpElementControl.style = { + _: { + ...FontStyles.sansSerifNormal, + fontSize: "12px", + color: Colors.workspaceDefault, + "&:hover": { + color: Colors.workspaceDescriptor + } + }, + _question: { + cursor: "pointer", + fontSize: "14px", + margin: "5px 20px 5px 20px" + }, + _answer: { + userSelect: "text", + margin: "5px 20px 5px 40px", + lineHeight: "160%" + }, + _sectionTitle: { + marginTop: "5px", + marginBottom: "5px", + cursor: "pointer", + ...FontStyles.sansSerifBold, + textTransform: "uppercase", + fontSize: "11px", + letterSpacing: "1px", + color: Colors.workspaceDescriptor + } +}; + +export class InAppHelp extends Div { + + private _header: Div; + private _title: Div; + private _closeBtn: Icon; + private _searchBox: TextBox; + private _entries: Div; + + constructor() { + super(); + + this.append([ + (this._header = new Div()) + .append([ + (this._title = new Div()) + .setText("Help"), + (this._closeBtn = new Icon("close")) + .on(this._closeBtn.eventClick, () => this._onCloseBtnClick()) + ]), + (this._searchBox = new TextBox()) + .setPlaceholder("Search...") + .on(this._searchBox.eventInput, () => this._onSearchBoxInput()), + (this._entries = new Div()) + ]); + + setTimeout(async () => { + let elements = await InAppHelp.getHelpContent(); + this._entries.append(elements.map(element => new HelpElementControl(element))); + }, 0); + } + + static async getHelpContent(): Promise> { + let content = await (await fetch("./resources/faq.txt")).text(); + + let sections = content.split(">>section-"); + + let result: Array = []; + + for (let section of sections) { + if (!section) continue; + if (section.trim().length == 0) continue; + + let questions = section.split(">>question-"); + + for (let i = 0; i <= questions.length - 1; i++) { + let faq = questions[i]; + + if (!faq) continue; + if (faq.trim().length == 0) continue; + + if (i == 0) { + result.push({ type: HelpElementType.SectionTitle, title: faq }); + continue; + } + + let [ question, answer ] = faq.split(">>answer-"); + + if (!question || !answer) continue; + + question = question.trim(); + answer = answer.trim(); + + result.push({ type: HelpElementType.Question, question, answer }); + } + } + + return result; + } + + setVisibility(visible: boolean): this { + super.setVisibility(visible); + if (visible) + this._searchBox.focus(); + return this; + } + + private _onSearchBoxInput(): void { + this._entries.children().forEach(child => { + let helpElement = (child); + helpElement.setSearchPhrase(this._searchBox.getText()); + helpElement.setExpanded(false); + }); + } + + private async _onCloseBtnClick(): Promise { + this.setVisibility(false); + } +} + +InAppHelp.style = { + _: { + width: "400px", + height: "80vh", + background: Colors.consoleBackground, + borderTop: `solid 1px ${Colors.consoleBorder}`, + boxShadow: BoxShadowValues.consoleExtended, + color: Colors.browserDefault, + display: "flex", + flexFlow: "column nowrap", + alignItems: "stretch", + borderRadius: "20px", + zIndex: ZIndexLayers.modal + }, + _header: { + display: "flex", + flexFlow: "row nowrap", + alignItems: "center", + margin: "10px 20px 10px 20px" + }, + _title: { + color: Colors.workspaceDescriptor, + fontSize: "12px", + flex: "1 1 content" + }, + _closeBtn: { + fontSize: "12px", + cursor: "pointer" + }, + _searchBox: { + ...FontStyles.sansSerifNormal, + appearance: "none", + border: "none", + height: "25px", + padding: "0 10px", + fontSize: "12px", + borderRadius: "6px", + marginLeft: "10px", + marginRight: "10px", + color: Colors.workspaceDefault, + "&:focus": { + outline: "none" + } + }, + _entries: { + flex: "1 1 1px", + display: "flex", + flexFlow: "column nowrap", + overflowY: "scroll", + padding: "10px 20px", + "&::-webkit-scrollbar": { + display: "none" + } + } +}; diff --git a/src/renderer/ui/ControlBar/MacOsControlBox.ts b/src/renderer/ui/ControlBar/MacOsControlBox.ts new file mode 100644 index 0000000..2a7d52f --- /dev/null +++ b/src/renderer/ui/ControlBar/MacOsControlBox.ts @@ -0,0 +1,85 @@ +import { Div } from "aflon"; + +import { minimize, goFullScreen, exitFullScreen, isInFullScreen, close } from "../../lib/WindowManipulation"; + +export class MacOsControlBox extends Div { + private _closeButton: Div; + private _minimizeButton: Div; + private _maximizeButton: Div; + + constructor() { + super(); + + this.append([ + (this._closeButton = new Div()) + .on(this._closeButton.eventClick, () => close()), + (this._minimizeButton = new Div()) + .on(this._minimizeButton.eventClick, () => minimize()), + (this._maximizeButton = new Div()) + .on(this._maximizeButton.eventClick, () => this._onMaximizeButtonClick()) + ]); + } + + hide(): void { + this.animations("show").stop(); + this.animations("hide").start(); + } + + show(): void { + this.animations("hide").stop(); + this.animations("show").start(); + } + + private async _onMaximizeButtonClick(): Promise { + if (await isInFullScreen()) exitFullScreen(); + else goFullScreen(); + } +} + +MacOsControlBox.style = { + _: { + display: "flex", + flexFlow: "row nowrap", + width: "0", + justifyContent: "space-evenly" + }, + _closeButton: { + background: "#FC605C", + width: "11px", + height: "11px", + borderRadius: "6px", + opacity: 0 + }, + _minimizeButton: { + background: "#FCBB40", + width: "11px", + height: "11px", + borderRadius: "6px", + opacity: 0 + }, + _maximizeButton: { + background: "#34C648", + width: "11px", + height: "11px", + borderRadius: "6px" + } +}; + +MacOsControlBox.animations = { + show: { + animations: [ + { track: "width", to: "80px", ease: "circOut", duration: 300 }, + { target: "_closeButton", track: "opacity", to: 1.0, duration: 200, delay: 0 }, + { target: "_minimizeButton", track: "opacity", to: 1.0, duration: 200, delay: 0 }, + { target: "_maximizeButton", track: "opacity", to: 1.0, duration: 200, delay: 0 } + ] + }, + hide: { + animations: [ + { track: "width", to: "0px", ease: "circOut", duration: 300, delay: 100 }, + { target: "_closeButton", track: "opacity", to: 0, duration: 200 }, + { target: "_minimizeButton", track: "opacity", to: 0, duration: 200 }, + { target: "_maximizeButton", track: "opacity", to: 0, duration: 200 } + ] + } +}; diff --git a/src/renderer/ui/ControlBar/Win32ControlBox.ts b/src/renderer/ui/ControlBar/Win32ControlBox.ts new file mode 100644 index 0000000..b8f2268 --- /dev/null +++ b/src/renderer/ui/ControlBar/Win32ControlBox.ts @@ -0,0 +1,84 @@ +import { Div } from "aflon"; + +import { minimize, close, isMaximized, maximize, unmaximize } from "../../lib/WindowManipulation"; + +import { Colors } from "../StyleConstants"; +import { Icon } from "../Icon"; +import { Tooltip } from "../ContextMenu"; + +export class Win32ControlBox extends Div { + private _minimizeButton: Div; + private _maximizeButton: Div; + private _closeButton: Div; + + constructor() { + super(); + + this.append([ + (this._minimizeButton = new Div()) + .append([ new Icon("minimize") ]) + .on(this._minimizeButton.eventClick, () => minimize()), + (this._maximizeButton = new Div()) + .append([ new Icon("maximize") ]) + .on(this._maximizeButton.eventClick, () => this._onMaximizeButtonClick()), + (this._closeButton = new Div()) + .append([ new Icon("close") ]) + .on(this._closeButton.eventClick, () => close()) + ]); + + new Tooltip(this._minimizeButton).setText("Minimize"); + new Tooltip(this._maximizeButton).setText("Maximize"); + new Tooltip(this._closeButton).setText("Close"); + } + + private async _onMaximizeButtonClick(): Promise { + if (await isMaximized()) unmaximize(); + else maximize(); + } +} + +Win32ControlBox.style = { + _: { + display: "flex", + flexFlow: "row nowrap", + justifyContent: "center", + width: "110px", + color: Colors.browserDefault, + fontSize: "12px", + borderLeft: `solid 1px ${Colors.workspaceLine}` + }, + _minimizeButton: { + display: "flex", + justifyContent: "center", + alignItems: "center", + height: "100%", + width: "29px", + textAlign: "center", + "&:hover": { + color: Colors.consoleDominant + } + }, + _maximizeButton: { + display: "flex", + justifyContent: "center", + alignItems: "center", + height: "100%", + width: "32px", + paddingLeft: "2px", + textAlign: "center", + "&:hover": { + color: Colors.consoleDominant + } + }, + _closeButton: { + display: "flex", + justifyContent: "center", + alignItems: "center", + height: "100%", + width: "29px", + textAlign: "center", + "&:hover": { + color: Colors.consoleDominant + } + } +}; diff --git a/src/renderer/ui/ControlBar/index.ts b/src/renderer/ui/ControlBar/index.ts new file mode 100644 index 0000000..b91410c --- /dev/null +++ b/src/renderer/ui/ControlBar/index.ts @@ -0,0 +1 @@ +export { ControlBar } from "./ControlBar"; diff --git a/src/renderer/ui/ExecutionHistoryControl/HttpResponseControl/HttpExecutionDetailsControl.ts b/src/renderer/ui/ExecutionHistoryControl/HttpResponseControl/HttpExecutionDetailsControl.ts new file mode 100644 index 0000000..22f9f4c --- /dev/null +++ b/src/renderer/ui/ExecutionHistoryControl/HttpResponseControl/HttpExecutionDetailsControl.ts @@ -0,0 +1,72 @@ +import { Div } from "aflon"; +import { HttpExecutionMetadata } from "../../../lib/http"; + +import { Colors, FontStyles } from "../../StyleConstants"; + +export class HttpExecutionDetailsControl extends Div { + + private _executionDetailsText: Div; + + private _executionMetadata: HttpExecutionMetadata; + + constructor() { + super(); + + this.append([ + (this._executionDetailsText = new Div()) + ]); + } + + setExecutionMetadata(metadata: HttpExecutionMetadata): this { + this._executionMetadata = metadata; + this._executionDetailsText.empty(); + + if (!this._executionMetadata) return this; + + const meta = this._executionMetadata; + + if (meta.errorMessage) { + this._executionDetailsText.append([ + new Div().setText("There was an error."), + new Div().setText(meta.errorMessage) + ]); + return this; + } + + this._executionDetailsText.append([ + new Div().setText(`@ ${meta.timestamp.toLocaleString()}`) + ]); + + if (meta.localIp && meta.localPort && meta.remoteIp && meta.remotePort) { + this._executionDetailsText.append([ + new Div().setText(`${meta.localIp}:${meta.localPort} <--> ${meta.remoteIp}:${meta.remotePort}`) + ]); + } + + if (!isNaN(meta.executionTimeInSeconds) && + !isNaN(meta.downloadSizeInBytes) && + !isNaN(meta.downloadSizeInBytes) && + !isNaN(meta.uploadSizeInBytes)) { + this._executionDetailsText.append([ + new Div().setText(`Duration ${Math.round(meta.executionTimeInSeconds * 1000)} ms, download ${Math.ceil(meta.downloadSizeInBytes / 1024) / 10} KB, upload ${Math.ceil(meta.uploadSizeInBytes / 1024) / 10} KB`) + ]); + } + + this._executionDetailsText.append([ + new Div().setText(`Protocol http/${meta.httpVersion}`) + ]); + + return this; + } + + getExecutionMetadata(): HttpExecutionMetadata { + return this._executionMetadata; + } +} + +HttpExecutionDetailsControl.style = { + _: { + ...FontStyles.monoSpace, + color: Colors.workspaceDefault + } +}; diff --git a/src/renderer/ui/ExecutionHistoryControl/HttpResponseControl/HttpResponseBodyControl.ts b/src/renderer/ui/ExecutionHistoryControl/HttpResponseControl/HttpResponseBodyControl.ts new file mode 100644 index 0000000..cafdf72 --- /dev/null +++ b/src/renderer/ui/ExecutionHistoryControl/HttpResponseControl/HttpResponseBodyControl.ts @@ -0,0 +1,331 @@ +import { Div, Span, Br } from "aflon"; + +import { HttpResponseBody, IMacroContext } from "../../../lib/http"; +import { showSaveDialog } from "../../../lib/SystemDialogs"; +import { openFileInDefaultApp } from "../../../lib/Platform"; + +import { Colors, FontStyles } from "../../StyleConstants"; +import { SelectBox } from "../../BasicControls"; +import { ExpandableRow } from "../../ExpandableTable"; + +import { HttpResponseBodyTextPreviewer, ResponseBodyTextFormatting } from "./HttpResponseBodyTextPreviewer"; +import { WebView } from "./WebView"; + +enum PreviewFormat { + None = "none", + TextPlain = "plain", + TextJSON = "json", + TextXML = "xml", + Image = "image", + InBrowser = "inbrowser" +} + +class HttpResponseBodyHeaderControl extends Div { + public eventOpenRequested = "openRequested"; + public eventSaveRequested = "saveRequested"; + public eventPreviewFormatChanged = "previewFormatChanged"; + + private _messageContainer: Span; + private _controlBox: Span; + private _message: Span; + private _previewSelectBox: SelectBox; + private _saveBtn: Span; + private _openBtn: Span; + + constructor() { + super(); + + this.append([ + (this._messageContainer = new Span()) + .append([ + (this._message = new Span()) + .setText("Some text"), + new Br() + ]), + (this._controlBox = new Span()) + .append([ + (this._saveBtn = new Span()) + .setText("Save to file") + .on(this._saveBtn.eventClick, () => this.raise(this.eventSaveRequested)), + new Span().setText(" | "), + (this._openBtn = new Span()) + .setText("Open in default app") + .on(this._openBtn.eventClick, () => this.raise(this.eventOpenRequested)), + new Span().setText(" | "), + new Span().setText("Preview as "), + (this._previewSelectBox = new SelectBox()) + .insertOptions([ + { text: "None", value: PreviewFormat.None }, + { text: "Text - Plain", value: PreviewFormat.TextPlain }, + { text: "Text - JSON", value: PreviewFormat.TextJSON }, + { text: "Browser preview", value: PreviewFormat.InBrowser }, + { text: "Image", value: PreviewFormat.Image } + ]) + .on(this._previewSelectBox.eventChange, () => this.raise(this.eventPreviewFormatChanged)) + ]) + ]); + } + + setPreviewSelectBoxDisabled(disabled: boolean): this { + this._previewSelectBox.setDisabled(disabled); + return this; + } + + getPreviewSelectBoxDisabled(): boolean { + return this._previewSelectBox.getDisabled(); + } + + setMessage(message: string): this { + if (!message) { + this._messageContainer.setInlineCss({ display: "none" }); + this._message.setText(""); + } else { + this._messageContainer.setInlineCss({ display: "inline" }); + this._message.setText(message); + } + + return this; + } + + getMessage(): string { + return this._message.getText(); + } + + setPreviewFormat(previewFormat: PreviewFormat): this { + this._previewSelectBox.setSelectedOption(previewFormat); + return this; + } + + getPreviewFormat(): PreviewFormat { + return (this._previewSelectBox.getSelectedOption().value); + } + + setControlBoxVisibility(visible: boolean): this { + if (visible == this.getControlBoxVisibility()) return this; + + if (visible) { + this._controlBox.setInlineCss({ display: "inline" }); + } else { + this._controlBox.setInlineCss({ display: "none" }); + } + + return this; + } + + getControlBoxVisibility(): boolean { + return this._controlBox.getInlineCss()["display"] != "none"; + } +} + +HttpResponseBodyHeaderControl.style = { + _: { + ...FontStyles.sansSerifNormal, + color: Colors.workspaceDefault, + fontSize: "12px", + lineHeight: "20px" + }, + _controlBox: { + whiteSpace: "nowrap" + }, + _saveBtn: { + ...FontStyles.sansSerifBold, + cursor: "pointer", + textDecoration: "underline" + }, + _openBtn: { + ...FontStyles.sansSerifBold, + cursor: "pointer", + textDecoration: "underline" + }, + _messageContainer: { + display: "none", + whiteSpace: "nowrap" + }, + _searchBox: { + ...FontStyles.monoSpace, + color: Colors.workspaceDefault, + background: "none", + lineHeight: "20px", + height: "22px", + border: `1px solid ${Colors.consoleDefault}`, + marginLeft: "5px", + marginRight: "5px", + "&:focus": { + outline: "none" + } + } +}; + +export class HttpResponseBodyControl extends ExpandableRow { + public eventSearchResultsUpdated = "searchResultsUpdated"; + + private _textPreviewer: HttpResponseBodyTextPreviewer; + private _imagePreviewer: Div; + private _browserPreviewer: WebView; + private _controlBar: HttpResponseBodyHeaderControl; + + private _body: HttpResponseBody = null; + private _macroContext: IMacroContext = null; + + constructor() { + super(); + + this.setTitle("Body"); + this.appendContent([ + (this._controlBar = new HttpResponseBodyHeaderControl()) + .on(this._controlBar.eventPreviewFormatChanged, () => this._onPreviewFormatChanged()) + .on(this._controlBar.eventSaveRequested, () => this._onSaveRequested()) + .on(this._controlBar.eventOpenRequested, () => this._onOpenInDefaultAppRequested()) + .addCssClass({ + marginBottom: "20px" + }), + (this._textPreviewer = new HttpResponseBodyTextPreviewer()) + .addCssClass({ + minWidth: 0, + overflowX: "scroll" + }) + .on(this._textPreviewer.eventSearchResultsUpdated, e => this.raise(this.eventSearchResultsUpdated, e["detail"])), + (this._imagePreviewer = new Div()) + .addCssClass({ + minWidth: 0, + width: "80%", + minHeight: "500px", + maxHeight: "800px", + backgroundSize: "contain", + backgroundRepeat: "no-repeat" + }), + (this._browserPreviewer = new WebView()) + .addCssClass({ + minWidth: 0, + flex: "1 1 1px", + marginRight: "20px" + }) + ]); + + this.addCssClass({ marginBottom: "20px" }); + } + + setBody(body: HttpResponseBody, macroContext: IMacroContext = null): this { + if (this._body == body) return this; + + this._body = body; + this._macroContext = macroContext; + this._controlBar.setPreviewSelectBoxDisabled(false); + + if (this._body.getContentSize() == 0) { + this._controlBar.setMessage("Response body is empty."); + this._controlBar.setControlBoxVisibility(false); + this._controlBar.setPreviewFormat(PreviewFormat.None); + } else { + this._controlBar.setControlBoxVisibility(true); + + const type = body.getContentType().getBaseType(); + const subtype = body.getContentType().getSubtype(); + + if (subtype.includes("json") || + subtype == "xml" || + subtype == "html" || + type == "text") { + if (this._body.getContentSize() > 700 * 1024) { + this._controlBar.setMessage("Textual body is too large to be previewed in httpiness."); + this._controlBar.setPreviewFormat(PreviewFormat.None); + this._controlBar.setPreviewSelectBoxDisabled(true); + } else { + if (subtype.includes("json")) { + this._controlBar.setMessage(""); + this._controlBar.setPreviewFormat(PreviewFormat.TextJSON); + } else if (subtype == "html") { + this._controlBar.setMessage(""); + this._controlBar.setPreviewFormat(PreviewFormat.InBrowser); + } else if (type == "text" || subtype.includes("xml")) { + this._controlBar.setMessage(""); + this._controlBar.setPreviewFormat(PreviewFormat.TextPlain); + } + } + } else if (type == "image") { + this._controlBar.setMessage(""); + this._controlBar.setPreviewFormat(PreviewFormat.Image); + } else { + this._controlBar.setMessage(`httpiness does not support preview of content type ${this._body.getContentType().getType()}.`); + this._controlBar.setPreviewFormat(PreviewFormat.None); + } + } + + this._onPreviewFormatChanged(); + + return this; + } + + setSearchPhrase(phrase: string): this { + this._textPreviewer.setSearchPhrase(phrase); + return this; + } + + getSearchPhrase(): string { + return this._textPreviewer.getSearchPhrase(); + } + + searchFocusNext(): this { + this._textPreviewer.searchFocusNext(); + return this; + } + + searchFocusPrevious(): this { + this._textPreviewer.searchFocusPrevious(); + return this; + } + + private _onPreviewFormatChanged(): void { + const previewType = this._controlBar.getPreviewFormat(); + + if (previewType == PreviewFormat.Image) { + this._textPreviewer.setInlineCss({ display: "none" }); + this._browserPreviewer.setInlineCss({ display: "none" }); + this._imagePreviewer.setInlineCss({ + display: "block", + backgroundImage: `url('${this._body.getTempFilePosixStyle()}')` + }); + } else if (previewType == PreviewFormat.TextJSON) { + this._browserPreviewer.setInlineCss({ display: "none" }); + this._imagePreviewer.setInlineCss({ display: "none" }); + this._textPreviewer.setInlineCss({ display: "inline-block" }); + this._textPreviewer.setText(this._body.toString("utf-8"), ResponseBodyTextFormatting.JSON, this._macroContext); + } else if (previewType == PreviewFormat.InBrowser) { + this._browserPreviewer.setInlineCss({ display: "inline-block" }); + this._textPreviewer.setInlineCss({ display: "none" }); + this._imagePreviewer.setInlineCss({ display: "none" }); + this._browserPreviewer.setContent(this._body); + } else if (previewType == PreviewFormat.TextPlain) { + this._browserPreviewer.setInlineCss({ display: "none" }); + this._textPreviewer.setInlineCss({ display: "inline-block" }); + this._imagePreviewer.setInlineCss({ display: "none" }); + this._textPreviewer.setText(this._body.toString("utf-8"), ResponseBodyTextFormatting.Plain, this._macroContext); + } else { + this._browserPreviewer.setInlineCss({ display: "none" }); + this._textPreviewer.setInlineCss({ display: "none" }); + this._imagePreviewer.setInlineCss({ display: "none" }); + } + } + + private async _onSaveRequested(): Promise { + const extensions = this._body.getContentType().getExtensions(); + + let filters = []; + if (extensions.length > 0) + filters.push({ name: this._body.getContentType().getDefaultExtension() + " file", extensions }); + + const result = await showSaveDialog({ + title: "Save file as...", + filters, + properties: [ "createDirectory", "showOverwriteConfirmation" ] + }); + + if (result.canceled) return; + + this._body.toFile(result.filePath); + } + + private _onOpenInDefaultAppRequested(): void { + openFileInDefaultApp(this._body.getTempFile()); + } +} diff --git a/src/renderer/ui/ExecutionHistoryControl/HttpResponseControl/HttpResponseBodyTextPreviewer.ts b/src/renderer/ui/ExecutionHistoryControl/HttpResponseControl/HttpResponseBodyTextPreviewer.ts new file mode 100644 index 0000000..359ce06 --- /dev/null +++ b/src/renderer/ui/ExecutionHistoryControl/HttpResponseControl/HttpResponseBodyTextPreviewer.ts @@ -0,0 +1,227 @@ +import { CSS, Div, Span } from "aflon"; + +import { Clipboard } from "../../../lib/Clipboard"; +import { SimpleEvent } from "../../../lib/SimpleEvent"; +import { IMacroContext } from "../../../lib/http"; + +import { Colors, FontStyles } from "../../StyleConstants"; +import { ContextMenuItemType, ContextMenu, ContextMenuItemDefinition } from "../../ContextMenu"; + +export enum ResponseBodyTextFormatting { + Unknown, Plain, JSON, XML +} + +export class HttpResponseBodyTextPreviewer extends Div { + private static _searchClass: string = CSS.class({ + background: Colors.workspaceSearch, + color: Colors.backgroundDefault, + borderRadius: "2px" + }); + + private static _focusClass: string = CSS.class({ + border: `solid 2px ${Colors.workspaceSearchFocus}` + }); + + private static _searchIdPrefix = "htbsrch-"; + + public eventSearchResultsUpdated = "searchResultUpdated"; + + private _text: string = ""; + private _formatting: ResponseBodyTextFormatting = ResponseBodyTextFormatting.Plain; + private _macroContext: IMacroContext; + private _searchPhrase: string; + + private _contextMenu: ContextMenu; + + private _numberOfFoundPhrases: number = 0; + private _currentSearchResultInFocus: number = -1; + + private _currentSearchFocus: HTMLElement = null; + + constructor() { + super(); + + this._contextMenu = new ContextMenu(this, [ + { type: ContextMenuItemType.Button, text: "Copy", id: "copy" }, + { type: ContextMenuItemType.Button, text: "Set as parameter value", id: "param", disabled: true } + ]); + + this._contextMenu + .on(this._contextMenu.eventAboutToBeShown, () => this._onContextMenuAboutToBeShown()) + .on(this._contextMenu.eventSelected, e => this._onContextMenuSelected(e)); + } + + setText(text: string, formatting: ResponseBodyTextFormatting = ResponseBodyTextFormatting.Unknown, macroContext: IMacroContext = null): this { + this._text = text; + this._macroContext = macroContext; + + if (formatting != ResponseBodyTextFormatting.Unknown) + this._formatting = formatting; + + this._update(); + this._setSearchFocus(0); + + return this; + } + + getText(): string { + return this._text; + } + + setFormatting(formatting: ResponseBodyTextFormatting): this { + if (this._formatting == formatting) return this; + + this._formatting = formatting; + this._update(); + + return this; + } + + getFormatting(): ResponseBodyTextFormatting { + return this._formatting; + } + + setSearchPhrase(searchPhrase: string): this { + if (this._searchPhrase == searchPhrase) return this; + + this._searchPhrase = searchPhrase; + this._update(); + this._setSearchFocus(0); + + return this; + } + + getSearchPhrase(): string { + return this._searchPhrase; + } + + searchFocusNext(): this { + this._setSearchFocus(this._currentSearchResultInFocus + 1); + return this; + } + + searchFocusPrevious(): this { + this._setSearchFocus(this._currentSearchResultInFocus - 1); + return this; + } + + private _setSearchFocus(itemNo: number): void { + if (this._numberOfFoundPhrases == 0) return; + + if (itemNo > this._numberOfFoundPhrases - 1) + itemNo = itemNo % this._numberOfFoundPhrases; + + if (itemNo < 0) + itemNo = this._numberOfFoundPhrases + itemNo; + + this._currentSearchResultInFocus = itemNo; + + if (this._currentSearchFocus != null) + this._currentSearchFocus.classList.remove(HttpResponseBodyTextPreviewer._focusClass); + + this._currentSearchFocus = document.getElementById(`${HttpResponseBodyTextPreviewer._searchIdPrefix}${this._currentSearchResultInFocus}`); + + if (this._currentSearchFocus != null) { + this._currentSearchFocus.scrollIntoView({ block: "center", behavior: "smooth" }); + this._currentSearchFocus.classList.add(HttpResponseBodyTextPreviewer._focusClass); + } + } + + private _update(): void { + let numberOfFound = 0; + + let actualText = this._text; + + if (this._formatting == ResponseBodyTextFormatting.JSON) { + actualText = this._formatAsJSON(this._text); + } else if (this._formatting == ResponseBodyTextFormatting.XML) { + actualText = this._formatAsXML(this._text); + } + + if (this._currentSearchFocus != null) + this._currentSearchFocus.classList.remove(HttpResponseBodyTextPreviewer._focusClass); + + if (!this._searchPhrase) { + super.setText(actualText); + numberOfFound = 0; + } else { + let parts = actualText.split(this._searchPhrase); + + numberOfFound = parts.length - 1; + if (numberOfFound < 0) numberOfFound = 0; + + super.empty(); + parts.forEach((part, i) => { + if (i == parts.length - 1) + super.append([ new Span().setText(part) ]); + else + super.append([ + new Span().setText(part), + new Span() + .setText(this._searchPhrase) + .addClass(HttpResponseBodyTextPreviewer._searchClass) + .setId(`${HttpResponseBodyTextPreviewer._searchIdPrefix}${i}`) + ]); + }); + } + + if (this._numberOfFoundPhrases != numberOfFound) { + this._numberOfFoundPhrases = numberOfFound; + this.raise(this.eventSearchResultsUpdated, { numberOfFoundPhrases: this._numberOfFoundPhrases }); + } + } + + private _formatAsJSON(text: string): string { + try { + return JSON.stringify(JSON.parse(text), null, 2); + } catch (ex) { + return text; + } + } + + private _formatAsXML(text: string): string { + return text; + } + + private _onContextMenuSelected(e: SimpleEvent): void { + const id = e["detail"]["id"]; + if (!id) return; + + let text = ""; + if (window.getSelection) { + text = window.getSelection().toString(); + } + + if (id == "copy") { + Clipboard.setText(text); + } else if (id != "params") { + this._macroContext.setMacro(id, text, this._macroContext.isMacroSensitive(id)); + } + } + + private _onContextMenuAboutToBeShown(): void { + let parameters: Array = []; + + if (this._macroContext != null) { + parameters = this._macroContext.getMacroNames().map(name => ({ + type: ContextMenuItemType.Button, + text: name, + id: name + })); + } + + this._contextMenu.setDefinition([ + { type: ContextMenuItemType.Button, text: "Copy", id: "copy" }, + { type: ContextMenuItemType.Button, text: "Set as parameter value", id: "params", submenu: parameters }]); + } +} + +HttpResponseBodyTextPreviewer.style = { + _: { + ...FontStyles.monoSpace, + color: Colors.workspaceDefault, + whiteSpace: "pre-wrap", + overflowWrap: "break-word", + userSelect: "text" + } +}; diff --git a/src/renderer/ui/ExecutionHistoryControl/HttpResponseControl/HttpResponseControl.ts b/src/renderer/ui/ExecutionHistoryControl/HttpResponseControl/HttpResponseControl.ts new file mode 100644 index 0000000..43c5515 --- /dev/null +++ b/src/renderer/ui/ExecutionHistoryControl/HttpResponseControl/HttpResponseControl.ts @@ -0,0 +1,258 @@ +import { Div } from "aflon"; + +import { HttpExecution, HttpReqt } from "../../../lib/http"; +import { currentPlatform, Platform } from "../../../lib/Platform"; + +import { HttpHeadersControl } from "../../HttpRequestControl/HttpHeadersControl"; +import { PreferenceStore } from "../../PreferenceStore"; +import { Colors, ZIndexLayers } from "../../StyleConstants"; + +import { SearchToolbox } from "./SearchToolbox"; +import { HttpResponseBodyControl } from "./HttpResponseBodyControl"; +import { HttpStatusAndMetadataControl } from "./HttpStatusAndMetadataControl"; + +export class HttpResponseControl extends Div { + eventHistoryRequested: string = "historyRequested"; + + private _progressBar: Div; + private _statusAndExecutionControl: HttpStatusAndMetadataControl; + private _searchBoxVisibilityWrapper: Div; + private _searchToolbox: SearchToolbox; + private _headersAndBody: Div; + private _headersControl: HttpHeadersControl; + private _bodyControl: HttpResponseBodyControl; + + private _httpExecution: HttpExecution; + + constructor() { + super(); + + this.append([ + (this._progressBar = new Div()), + (this._statusAndExecutionControl = new HttpStatusAndMetadataControl()) + .on(this._statusAndExecutionControl.eventHistoryRequested, () => this.raise(this.eventHistoryRequested)) + .on(this._statusAndExecutionControl.eventSearchActivationRequested, () => this.activateSearch()), + (this._headersAndBody = new Div()) + .append([ + (this._headersControl = new HttpHeadersControl()) + .setDescriptorShown(!PreferenceStore.getHideResponseDescriptor()) + .setVisibility(false) + .setReadOnly(true) + .setExpanded(false), + (this._bodyControl = new HttpResponseBodyControl()) + .setDescriptorShown(!PreferenceStore.getHideResponseDescriptor()) + .setVisibility(false) + .on(this._bodyControl.eventSearchResultsUpdated, e => this._onSearchResultsUpdated(e)) + ]), + (this._searchBoxVisibilityWrapper = new Div()) + .setVisibility(false) + .append([ + (this._searchToolbox = new SearchToolbox()) + .setVisibility(false) + .on(this._searchToolbox.eventSearchParamsChanged, e => this.setSearchPhrase(e["detail"]["text"])) + .on(this._searchToolbox.eventFocusNextResult, () => this.searchFocusNext()) + .on(this._searchToolbox.eventFocusPreviousResult, () => this.searchFocusPrevious()) + ]) + ]); + } + + clear(): this { + this.setExecution(null, null); + this.animations("stop").stop(); + this.animations("start").stop(); + this.animations("start").toBegining(); + return this; + } + + setExecution(reqt: HttpReqt, httpExecution: HttpExecution): this { + this._httpExecution = httpExecution; + + this._statusAndExecutionControl.setExecution(reqt, httpExecution); + + this.animations("end").stop(); + this.animations("start").stop(); + + if (!this._httpExecution) { + this.animations("start").start(); + this._searchBoxVisibilityWrapper.setVisibility(false); + this._headersControl.setVisibility(false); + this._bodyControl.setVisibility(false); + return this; + } + + this.animations("end").start(); + + const { response } = this._httpExecution; + + if (response) { + this._searchBoxVisibilityWrapper.setVisibility(true); + this._headersControl.setVisibility(true); + this._bodyControl.setVisibility(true); + this._headersControl.setHeaders(response.headers); + this._bodyControl.setBody(response.body, reqt.getContainingCollection()); + } else { + this._searchBoxVisibilityWrapper.setVisibility(false); + this._headersControl.setVisibility(false); + this._bodyControl.setVisibility(false); + } + + return this; + } + + setSearchPhrase(phrase: string): this { + this._bodyControl.setSearchPhrase(phrase); + return this; + } + + getSearchPhrase(): string { + return this._bodyControl.getSearchPhrase(); + } + + searchFocusNext(): this { + this._bodyControl.searchFocusNext(); + return this; + } + + searchFocusPrevious(): this { + this._bodyControl.searchFocusPrevious(); + return this; + } + + activateSearch(): this { + if (this._httpExecution == null || this._httpExecution.response == null) return this; + this._searchToolbox.activate(); + return this; + } + + setCompactHeader(compact: boolean): this { + this._statusAndExecutionControl.setExpanded(!compact); + + if (compact) { + this._searchToolbox.setInlineCss({ + top: "39px", + background: Colors.consoleBackground + }); + } else { + this._searchToolbox.setInlineCss({ + top: "75px", + background: Colors.backgroundDefault + }); + } + + return this; + } + + getCompactHeader(): boolean { + return !this._statusAndExecutionControl.getExpanded(); + } + + protected _onEnteringDom(): void { + document.addEventListener("keydown", this._onKeyDown); + PreferenceStore + .on(PreferenceStore.eventHideResponseDescriptorChanged, this._onHideResponseDescriptorChanged); + } + + protected _onLeavingDom(): void { + document.removeEventListener("keydown", this._onKeyDown); + PreferenceStore + .off(PreferenceStore.eventHideResponseDescriptorChanged, this._onHideResponseDescriptorChanged); + } + + private _onSearchResultsUpdated(e: Event): void { + this._searchToolbox.setResultNumber(e["detail"]["numberOfFoundPhrases"]); + } + + private _onHideResponseDescriptorChanged = (): void => { + let descriptorsShown = !PreferenceStore.getHideResponseDescriptor(); + + this._headersControl.setDescriptorShown(descriptorsShown); + this._bodyControl.setDescriptorShown(descriptorsShown); + }; + + private _onKeyDown = (e: Event): void => { + if (this.getHtmlElement().offsetParent == null) return; + + let keyEvent = e; + + const modifierKeyPressed = + (currentPlatform() == Platform.MacOS && keyEvent.metaKey) || + (currentPlatform() != Platform.MacOS && keyEvent.ctrlKey); + + if (keyEvent.key.toUpperCase() == "F" && modifierKeyPressed) { + if (this._httpExecution != null && this._httpExecution.response != null) { + this._searchToolbox.activate(); + } + } + }; +} + +HttpResponseControl.style = { + _: { + display: "flex", + flexFlow: "column nowrap", + position: "relative", + overflowX: "hidden" + }, + _progressBar: { + width: "0%", + minHeight: "2px", + maxHeight: "2px", + marginBottom: "-2px", + background: Colors.workspaceAccent, + zIndex: ZIndexLayers.console + 1 + }, + _status: { + flex: "0 0 35px" + }, + _statusAndExecutionControl: { + flex: "0 0 content" + }, + _searchToolbox: { + position: "absolute", + top: "0", + right: "20px" + }, + _headersAndBody: { + position: "relative", + display: "flex", + flexFlow: "column nowrap", + flex: "1 1 1px", + maxWidth: "100%", + overflowY: "auto", + overflowX: "hidden", + paddingTop: "20px", + "&::-webkit-scrollbar": { + borderLeft: `solid 1px ${Colors.workspaceLine}`, + width: "10px" + }, + "&::-webkit-scrollbar-thumb": { + background: Colors.scrollThumb, + opacity: 1.0, + border: `solid 1px ${Colors.workspaceLine}`, + borderRight: "none" + } + }, + _headersControl: { + marginBottom: "20px", + maxWidth: "100%" + }, + _bodyControl: { + maxWidth: "100%", + flex: "1 1 content" + } +}; + +HttpResponseControl.animations = { + start: { + animations: [ + { target: "_progressBar", track: "width", from: "0%", to: "70%", duration: 10000, ease: "circOut" }, + { target: "_progressBar", track: "opacity", to: 1, duration: 1 } + ] + }, + end: { + animations: [ + { target: "_progressBar", track: "width", to: "100%", duration: 200, ease: "circOut" }, + { target: "_progressBar", track: "opacity", to: 0, delay: 150, duration: 200} + ] + } +}; diff --git a/src/renderer/ui/ExecutionHistoryControl/HttpResponseControl/HttpStatusAndMetadataControl.ts b/src/renderer/ui/ExecutionHistoryControl/HttpResponseControl/HttpStatusAndMetadataControl.ts new file mode 100644 index 0000000..5871547 --- /dev/null +++ b/src/renderer/ui/ExecutionHistoryControl/HttpResponseControl/HttpStatusAndMetadataControl.ts @@ -0,0 +1,279 @@ +import { Div } from "aflon"; + +import { HttpExecution, HttpReqt } from "../../../lib/http"; + +import { Colors, FontStyles, getShortMethodDesignation } from "../../StyleConstants"; +import { Icon } from "../../Icon"; +import { Tooltip } from "../../ContextMenu"; + +import { RequestExecutionStatus } from "../RequestExecutionStatus"; +import { ResponsePanelOptionsButton } from "../ResponsePanelOptionsButton"; + +export class HttpStatusAndMetadataControl extends Div { + eventSearchActivationRequested: string = "searchActivationRequested"; + eventHistoryRequested: string = "historyRequested"; + + private _row1: Div; + private _methodDescriptor: Div; + private _requestName: Div; + private _arrow: Div; + private _icon: Icon; + private _statusUp: RequestExecutionStatus; + private _buffer: Div; + private _headerSearchButton: Div; + private _headerOptionsButton: ResponsePanelOptionsButton; + + + private _row2: Div; + private _statusDown: RequestExecutionStatus; + private _metadata: Div; + private _metadataRow2: Div; + private _metadataRow1: Div; + + private _expanded: boolean = true; + private _reqt: HttpReqt = null; + private _execution: HttpExecution = null; + + constructor() { + super(); + + this.append([ + (this._row1 = new Div()) + .setVisibility(false) + .append([ + (this._methodDescriptor = new Div()) + .on(this._methodDescriptor.eventClick, () => this.raise(this.eventHistoryRequested)), + (this._requestName = new Div()) + .on(this._methodDescriptor.eventClick, () => this.raise(this.eventHistoryRequested)), + (this._arrow = new Div()) + .on(this._methodDescriptor.eventClick, () => this.raise(this.eventHistoryRequested)) + .append([ this._icon = new Icon("result") ]), + (this._statusUp = new RequestExecutionStatus()) + .on(this._methodDescriptor.eventClick, () => this.raise(this.eventHistoryRequested)), + (this._buffer = new Div()), + (this._headerOptionsButton = new ResponsePanelOptionsButton()) + .setVisibility(false), + (this._headerSearchButton = new Div()) + .append([ new Icon("search") ]) + .setVisibility(false) + .on(this._headerSearchButton.eventClick, () => this.raise(this.eventSearchActivationRequested)) + ]), + (this._row2 = new Div()) + .setVisibility(false) + .append([ + (this._statusDown = new RequestExecutionStatus()), + (this._metadata = new Div()) + .append([ + (this._metadataRow1 = new Div()), + (this._metadataRow2 = new Div()) + ]) + ]) + ]); + + new Tooltip(this._headerSearchButton).setText("Search response"); + + this.setExecution(null, null); + } + + setExpanded(expanded: boolean): this { + this._expanded = expanded; + this.setExecution(this._reqt, this._execution); + return this; + } + + getExpanded(): boolean { + return this._row1.getVisibility(); + } + + setExecution(reqt: HttpReqt, httpExecution: HttpExecution): this { + this._reqt = reqt; + this._execution = httpExecution; + + if (reqt == null) { + this._row1.setVisibility(false); + this._row2.setVisibility(false); + + return this; + } + + let name = reqt.getName(); + let method = reqt.getRawHttpRequest().method; + + if (!httpExecution) { + this._row1.setVisibility(this._expanded); + this._headerSearchButton.setVisibility(false); + this._headerOptionsButton.setVisibility(false); + this._row2.setVisibility(false); + this._statusDown.setVisibility(!this._expanded); + this._statusUp.setStatus(0); + this._statusDown.setStatus(0); + this._methodDescriptor.setText(getShortMethodDesignation(method)); + this._requestName.setText(name); + return this; + } + + this._row1.setVisibility(this._expanded); + this._row2.setVisibility(true); + this._statusDown.setVisibility(!this._expanded); + this._methodDescriptor.setText(getShortMethodDesignation(method)); + this._requestName.setText(name); + this._headerSearchButton.setVisibility(true); + this._headerOptionsButton.setVisibility(true); + + let { response, metadata } = httpExecution; + + if (metadata.errorMessage) { + this._metadataRow1.setText("There was an error."); + this._metadataRow2.setText(metadata.errorMessage); + this._statusUp.setStatus(-1); + this._statusDown.setStatus(-1); + return this; + } + + if (response) { + this._statusUp.setStatus(httpExecution.response.status); + this._statusDown.setStatus(httpExecution.response.status); + } + + this._metadataRow1.empty(); + + if (metadata.localIp && metadata.localPort && metadata.remoteIp && metadata.remotePort) { + this._metadataRow1.append([ + new Div().setText(`${metadata.localIp}:${metadata.localPort}`), + new Icon("sync").setInlineCss({ padding: "0 5px" }), + new Div().setText(`${metadata.remoteIp}:${metadata.remotePort}`) + ]); + } + + if (!isNaN(metadata.executionTimeInSeconds) && + !isNaN(metadata.downloadSizeInBytes) && + !isNaN(metadata.uploadSizeInBytes)) { + this._metadataRow1.append([ + new Icon("timer").setInlineCss({ padding: "0 5px 0 10px", fontSize: "10px" }), + new Div().setText(`${Math.round(metadata.executionTimeInSeconds * 1000)} MS`), + new Icon("upload").setInlineCss({ padding: "0 5px 0 10px" }), + new Div().setText(`${Math.ceil(metadata.uploadSizeInBytes / 1024) / 10} KB`), + new Icon("download").setInlineCss({ padding: "0 5px 0 10px" }), + new Div().setText(`${Math.ceil(metadata.downloadSizeInBytes / 1024) / 10} KB`) + ]); + } + + this._metadataRow2.setText(`HTTP/${metadata.httpVersion} @ ${metadata.timestamp.toLocaleString()}`); + + return this; + } +} + +HttpStatusAndMetadataControl.style = { + _: { + display: "flex", + flexFlow: "column nowrap" + }, + _row1: { + display: "flex", + flexFlow: "row nowrap", + alignItems: "center", + color: Colors.browserDefault, + padding: "0 0 0 10px", + flex: "0 0 36px", + borderBottom: `1px solid ${Colors.workspaceLine}`, + overflowX: "scroll", + overflowY: "hidden", + "&::-webkit-scrollbar": { + display: "none" + } + }, + _methodDescriptor: { + ...FontStyles.sansSerifExtraBold, + fontSize: "12px", + width: "35px", + textAlign: "left", + cursor: "pointer" + }, + _requestName: { + ...FontStyles.sansSerifNormal, + fontSize: "13px", + maxWidth: "200px", + minWidth: "0px", + overflow: "hidden", + whiteSpace: "nowrap", + textOverflow: "ellipsis", + cursor: "pointer" + }, + _arrow: { + padding: "4px", + borderRadius: "4px", + fontSize: "13px", + marginLeft: "10px", + marginRight: "10px", + cursor: "pointer" + }, + _statusUp: { + marginRight: "10px", + cursor: "pointer" + }, + _headerSearchButton: { + display: "flex", + fontSize: "15px", + alignContent: "center", + alignItems: "center", + height: "100%", + width: "30px", + paddingLeft: "2px", + paddingTop: "2px", + textAlign: "center", + color: Colors.workspaceDefault, + cursor: "pointer", + "&:hover": { + color: Colors.consoleDominant + } + }, + _headerOptionsButton: { + height: "100%", + width: "30px" + }, + _buffer: { + flex: "1 1 1px" + }, + _row2: { + flex: "0 0 40px", + display: "flex", + flexFlow: "row nowrap", + alignItems: "stretch", + borderBottom: `1px solid ${Colors.workspaceLine}`, + overflowX: "scroll", + overflowY: "hidden", + "&::-webkit-scrollbar": { + display: "none" + } + }, + _statusDown: { + paddingLeft: "30px", + paddingRight: "30px", + borderRight: `1px solid ${Colors.workspaceLine}` + }, + _metadata: { + display: "flex", + flexFlow: "column nowrap", + justifyContent: "center", + paddingLeft: "10px", + height: "100%", + ...FontStyles.sansSerifNormal, + color: Colors.workspaceDefault, + textTransform: "uppercase", + letterSpacing: "0.5px", + fontSize: "10px" + }, + _metadataRow1: { + display: "flex", + flexFlow: "row nowrap", + alignItems: "center", + whiteSpace: "nowrap" + }, + _metadataRow2: { + display: "flex", + flexFlow: "row nowrap", + alignItems: "center", + whiteSpace: "nowrap" + } +}; diff --git a/src/renderer/ui/ExecutionHistoryControl/HttpResponseControl/IFrame.ts b/src/renderer/ui/ExecutionHistoryControl/HttpResponseControl/IFrame.ts new file mode 100644 index 0000000..8738f63 --- /dev/null +++ b/src/renderer/ui/ExecutionHistoryControl/HttpResponseControl/IFrame.ts @@ -0,0 +1,37 @@ +import { Element } from "aflon"; + +export class IFrame extends Element { + constructor() { + super(); + + this.on(this.eventLoad, (): void => { + this.getHtmlElement().style.height = ((this.getHtmlElement())).contentWindow.document.body.scrollHeight + 20 + "px"; + }); + + this.addAttr("scrolling", "no"); + } + + setSrc(src: string): this { + this.removeAttr("src"); + this.addAttr("src", src); + return this; + } + + getSrc(): string { + return this.getAttr("src"); + } + + setSrcDoc(content: string): this { + this.removeAttr("srcdoc"); + this.addAttr("srcdoc", content); + return this; + } + + getSrcDoc(): void { + this.getAttr("srcdoc"); + } + + protected _createElement(): HTMLElement { + return document.createElement("iframe"); + } +} diff --git a/src/renderer/ui/ExecutionHistoryControl/HttpResponseControl/SearchToolbox.ts b/src/renderer/ui/ExecutionHistoryControl/HttpResponseControl/SearchToolbox.ts new file mode 100644 index 0000000..914725a --- /dev/null +++ b/src/renderer/ui/ExecutionHistoryControl/HttpResponseControl/SearchToolbox.ts @@ -0,0 +1,177 @@ +import { Div, TextBox } from "aflon"; +import { Icon } from "../../Icon"; +import { Colors, FontStyles } from "../../StyleConstants"; + +export class SearchToolbox extends Div { + public eventSearchParamsChanged = "searchParamsChanged"; + public eventFocusNextResult = "focusNextResult"; + public eventFocusPreviousResult = "focusPreviousResult"; + + private _searchTextBox: TextBox; + private _currentResult: Div; + private _nextBtn: Icon; + private _previousBtn: Icon; + private _closeBtn: Icon; + + private _searchBoxTimeout: NodeJS.Timeout = null; + + constructor() { + super(); + + this.append([ + (this._searchTextBox = new TextBox()) + .setPlaceholder("Enter search phrase") + .on(this._searchTextBox.eventInput, () => this._onSearchTxtBxInput()) + .on(this._searchTextBox.eventKeyDown, e => this._onSearchBoxKeyDown(e)), + (this._currentResult = new Div()) + .setText("No hits"), + (this._nextBtn = new Icon("more")) + .on(this._nextBtn.eventClick, () => this._onNextBtnClick()), + (this._previousBtn = new Icon("more")) + .on(this._previousBtn.eventClick, () => this._onPreviousBtnClick()), + (this._closeBtn = new Icon("close")) + .on(this._closeBtn.eventClick, () => this._onClick()) + ]); + } + + setResultNumber(resultNumber: number): this { + if (resultNumber == 0) + this._currentResult.setText("No hits"); + else + this._currentResult.setText(`${resultNumber} hits`); + + return this; + } + + activate(): this { + this.setVisibility(true); + this._searchTextBox.focus(); + this.raise(this.eventSearchParamsChanged, { + text: this._searchTextBox.getText() + }); + return this; + } + + deactivate(): this { + if (this._searchBoxTimeout != null) + clearTimeout(this._searchBoxTimeout); + + this.setVisibility(false); + this._searchTextBox.setText(""); + this.raise(this.eventSearchParamsChanged, { + text: this._searchTextBox.getText() + }); + + return this; + } + + private _onNextBtnClick(): void { + this.raise(this.eventFocusNextResult); + } + + private _onPreviousBtnClick(): void { + this.raise(this.eventFocusPreviousResult); + } + + private _onClick(): void { + this.deactivate(); + } + + private _onSearchBoxKeyDown(e: Event): void { + let keyEvent = e; + + if (keyEvent.key == "Escape") + this.deactivate(); + + if (keyEvent.key == "Enter") + this.raise(this.eventFocusNextResult); + } + + private _onSearchTxtBxInput(): void { + if (this._searchBoxTimeout != null) + clearTimeout(this._searchBoxTimeout); + + this._searchBoxTimeout = setTimeout(() => { + this.raise(this.eventSearchParamsChanged, { + text: this._searchTextBox.getText() + }); + this._searchBoxTimeout = null; + }, 500); + } +} + +SearchToolbox.style = { + _: { + ...FontStyles.sansSerifNormal, + display: "flex", + flexFlow: "row nowrap", + alignItems: "center", + height: "35px", + width: "350px", + background: Colors.backgroundDefault, + borderColor: Colors.workspaceLine, + borderTop: "none", + color: Colors.workspaceDefault, + borderWidth: "1px", + borderStyle: "solid", + borderRadius: "0 0 8px 8px", + fontSize: "12px", + paddingLeft: "10px", + paddingRight: "5px" + }, + _searchTextBox: { + ...FontStyles.monoSpace, + color: Colors.workspaceDefault, + height: "22px", + minWidth: "1px", + flex: "1 1 1px", + border: "none", + outline: "none", + background: "none", + "&::placeholder": { + color: Colors.workspacePlaceholder + } + }, + _currentResult: { + marginLeft: "5px", + marginRight: "5px", + flex: "0 0 content" + }, + _nextBtn: { + fontSize: "15px", + flex: "0 0 30px", + height: "30px", + transform: "rotate(90deg)", + textAlign: "center", + lineHeight: "30px", + cursor: "pointer", + color: Colors.workspaceDefault, + "&:hover": { + color: Colors.consoleDominant + } + }, + _previousBtn: { + fontSize: "15px", + flex: "0 0 30px", + height: "30px", + transform: "rotate(-90deg)", + textAlign: "center", + lineHeight: "30px", + cursor: "pointer", + color: Colors.workspaceDefault, + "&:hover": { + color: Colors.consoleDominant + } + }, + _closeBtn: { + flex: "0 0 30px", + height: "30px", + lineHeight: "30px", + textAlign: "center", + cursor: "pointer", + color: Colors.workspaceDefault, + "&:hover": { + color: Colors.consoleDominant + } + } +}; diff --git a/src/renderer/ui/ExecutionHistoryControl/HttpResponseControl/WebView.ts b/src/renderer/ui/ExecutionHistoryControl/HttpResponseControl/WebView.ts new file mode 100644 index 0000000..ed043ec --- /dev/null +++ b/src/renderer/ui/ExecutionHistoryControl/HttpResponseControl/WebView.ts @@ -0,0 +1,42 @@ +import { Element } from "aflon"; +import { HttpResponseBody } from "../../../lib/http"; +import { Colors } from "../../StyleConstants"; + +export class WebView extends Element { + private _webview: Electron.WebviewTag = null; + + setContent(body: HttpResponseBody): this { + if (!this._webview) return this; + + this._webview.loadURL(`data:${body.getContentType().getValue()},${encodeURIComponent(body.toString("utf-8"))}`, { + baseURLForDataURL: body.getBaseUrl() + }); + + return this; + } + + protected _createElement(): HTMLElement { + const webview = document.createElement("webview"); + webview.style.height = "100%"; + webview.style.width = "100%"; + webview.src = "about:blank"; + + const load = (): void => { + this._webview = webview; + webview.removeEventListener("dom-ready", load); + }; + + webview.addEventListener("dom-ready", load); + + const container = document.createElement("div"); + container.appendChild(webview); + return container; + } +} + +WebView.style = { + _: { + background: "white", + border: `1px solid ${Colors.consoleBorder}` + } +}; diff --git a/src/renderer/ui/ExecutionHistoryControl/HttpResponseControl/index.ts b/src/renderer/ui/ExecutionHistoryControl/HttpResponseControl/index.ts new file mode 100644 index 0000000..4f2dc37 --- /dev/null +++ b/src/renderer/ui/ExecutionHistoryControl/HttpResponseControl/index.ts @@ -0,0 +1 @@ +export { HttpResponseControl } from "./HttpResponseControl"; diff --git a/src/renderer/ui/ExecutionHistoryControl/RequestExecutionHistoryControl/RequestExecutionHistoryControl.ts b/src/renderer/ui/ExecutionHistoryControl/RequestExecutionHistoryControl/RequestExecutionHistoryControl.ts new file mode 100644 index 0000000..36113b6 --- /dev/null +++ b/src/renderer/ui/ExecutionHistoryControl/RequestExecutionHistoryControl/RequestExecutionHistoryControl.ts @@ -0,0 +1,84 @@ +import { Div, typeAflonTarget } from "aflon"; +import { HttpReqt } from "../../../lib/http"; +import { RequestExecutionItemControl } from "./RequestExecutionItemControl"; + + +export class RequestExecutionHistoryControl extends Div { + eventExecutionSelected: string = "executionSelected"; + + private _selectedREControl: RequestExecutionItemControl = null; + + push(reqt: HttpReqt): this { + let requestExeControl = new RequestExecutionItemControl(reqt); + requestExeControl + .on(requestExeControl.eventSelected, e => this._onRequestSelected(e)) + .on(requestExeControl.eventResponseReady, e => this._onResponseReady(e)) + .on(requestExeControl.eventReexecutionRequested, e => this._onReexecutionRequested(e)); + + this.prepend([ requestExeControl ]); + + requestExeControl.setSelected(true); + + return this; + } + + clear(): void { + this.children().forEach(child => { + if (!(child instanceof RequestExecutionItemControl)) return; + + let requestExeControl = (child); + requestExeControl + .off(requestExeControl.eventSelected, e => this._onRequestSelected(e)) + .off(requestExeControl.eventResponseReady, e => this._onResponseReady(e)) + .off(requestExeControl.eventReexecutionRequested, e => this._onReexecutionRequested(e)); + + }); + + this.raise(this.eventExecutionSelected, { + reqt: null, + execution: null + }); + + this.empty(); + } + + private _onRequestSelected(e): void { + if (this._selectedREControl != null) + this._selectedREControl.setSelected(false); + + this._selectedREControl = typeAflonTarget(e, RequestExecutionItemControl); + + this.raise(this.eventExecutionSelected, { + reqt: this._selectedREControl.getReqt(), + execution: this._selectedREControl.getExecution() + }); + } + + private _onResponseReady(e): void { + let reControl = typeAflonTarget(e, RequestExecutionItemControl); + + let lastControl = this.children().find(control => control instanceof RequestExecutionItemControl); + + if (reControl != lastControl) return; + + if (this._selectedREControl != reControl) + reControl.setSelected(true); + else + this.raise(this.eventExecutionSelected, { + reqt: this._selectedREControl.getReqt(), + execution: this._selectedREControl.getExecution() + }); + } + + private _onReexecutionRequested(e): void { + setTimeout(() => this.push(e["detail"]["reqt"]), 0); + } +} + +RequestExecutionHistoryControl.style = { + _: { + display: "flex", + flexFlow: "column nowrap", + overflowX: "scroll" + } +}; diff --git a/src/renderer/ui/ExecutionHistoryControl/RequestExecutionHistoryControl/RequestExecutionItemControl.ts b/src/renderer/ui/ExecutionHistoryControl/RequestExecutionHistoryControl/RequestExecutionItemControl.ts new file mode 100644 index 0000000..68f0698 --- /dev/null +++ b/src/renderer/ui/ExecutionHistoryControl/RequestExecutionHistoryControl/RequestExecutionItemControl.ts @@ -0,0 +1,170 @@ +import { Div } from "aflon"; + +import { HttpExecutor, HttpExecution, HttpReqt } from "../../../lib/http"; +import { Telemetry, TelemetryEvent } from "../../../lib/Telemetry"; + +import { Colors, FontStyles, getShortMethodDesignation } from "../../StyleConstants"; +import { Icon } from "../../Icon"; +import { Tooltip } from "../../ContextMenu"; + +import { RequestExecutionStatus } from "../RequestExecutionStatus"; + +export class RequestExecutionItemControl extends Div { + public eventSelected = "selected"; + public eventResponseReady = "responseReady"; + public eventReexecutionRequested = "reexecutionRequested"; + + private _methodDescriptor: Div; + private _requestName: Div; + private _arrow: Div; + private _icon: Icon; + private _result: RequestExecutionStatus; + + private _selected: boolean = false; + private _reqt: HttpReqt; + private _execution: HttpExecution = null; + + constructor(reqt: HttpReqt) { + super(); + + this._reqt = reqt; + + this.append([ + (this._methodDescriptor = new Div()) + .setText(getShortMethodDesignation(this._reqt.getRawHttpRequest().method)), + (this._requestName = new Div()) + .setText(this._reqt.getName()), + (this._arrow = new Div()) + .append([ this._icon = new Icon("result") ]), + (this._result = new RequestExecutionStatus()) + .setTextShown(false) + ]) + .on(this.eventClick, () => this._onClick()); + + setTimeout(() => this._initiateRequest(), 0); + } + + getReqt(): HttpReqt { + return this._reqt; + } + + getExecution(): HttpExecution { + return this._execution; + } + + setSelected(selected: boolean): this { + if (this._selected == selected) + return this; + + this._selected = selected; + + if (this._selected) { + this.setInlineCss({ background: Colors.browserBackSelected }); + this.raise(this.eventSelected); + } else { + this.setInlineCss({ background: "none" }); + } + + return this; + } + + getSelected(): boolean { + return this._selected; + } + + private _onClick(): void { + this.setSelected(true); + } + + private async _initiateRequest(): Promise { + let request = await this._reqt.getHttpRequest(); + + this._execution = await HttpExecutor.execute(request); + + if (this._execution.response) { + this._result.setStatus(this._execution.response.status); + } else { + this._result.setStatus(-1); + } + + this.on(this.eventMouseEnter, () => this._onMouseEnter()) + .on(this.eventMouseLeave, () => this._onMouseLeave()); + + this._arrow + .on(this._arrow.eventMouseEnter, () => this._onArrowMouseEnter()) + .on(this._arrow.eventMouseLeave, () => this._onArrowMouseLeave()) + .on(this._arrow.eventClick, () => this._onArrowClick()); + + new Tooltip(this._arrow).setText("Send request again"); + + Telemetry.reportEvent(TelemetryEvent.HttpRequestSent); + + this.raise(this.eventResponseReady); + } + + private _onMouseEnter(): void { + this._icon.setName("restart"); + } + + private _onMouseLeave(): void { + this._icon.setName("result"); + } + + private _onArrowMouseEnter(): void { + this._arrow.setInlineCss({ + backgroundColor: Colors.browserBackHover + }); + } + + private _onArrowMouseLeave(): void { + this._arrow.setInlineCss({ + background: "none" + }); + } + + private _onArrowClick(): void { + this.raise(this.eventReexecutionRequested, { + reqt: this._reqt + }); + } +} + +RequestExecutionItemControl.style = { + _: { + display: "flex", + flexFlow: "row nowrap", + alignItems: "center", + color: Colors.browserDefault, + height: "30px", + cursor: "pointer", + "&:hover": { + background: Colors.browserBackHover + } + }, + _methodDescriptor: { + ...FontStyles.sansSerifExtraBold, + fontSize: "11px", + width: "40px", + textAlign: "center" + }, + _requestName: { + ...FontStyles.sansSerifNormal, + fontSize: "12px", + flex: "1 1 1px", + minWidth: "0px", + overflow: "hidden", + whiteSpace: "nowrap", + textOverflow: "ellipsis" + }, + _arrow: { + padding: "4px", + borderRadius: "4px", + fontSize: "14px" + }, + _result: { + marginLeft: "5px", + marginRight: "10px", + height: "18px", + width: "30px" + } +}; diff --git a/src/renderer/ui/ExecutionHistoryControl/RequestExecutionHistoryControl/index.ts b/src/renderer/ui/ExecutionHistoryControl/RequestExecutionHistoryControl/index.ts new file mode 100644 index 0000000..1c71b76 --- /dev/null +++ b/src/renderer/ui/ExecutionHistoryControl/RequestExecutionHistoryControl/index.ts @@ -0,0 +1 @@ +export { RequestExecutionHistoryControl } from "./RequestExecutionHistoryControl"; diff --git a/src/renderer/ui/ExecutionHistoryControl/RequestExecutionStatus.ts b/src/renderer/ui/ExecutionHistoryControl/RequestExecutionStatus.ts new file mode 100644 index 0000000..5439dd3 --- /dev/null +++ b/src/renderer/ui/ExecutionHistoryControl/RequestExecutionStatus.ts @@ -0,0 +1,273 @@ +import { Div } from "aflon"; + +import { Colors, FontStyles, getStatusColor } from "../StyleConstants"; + +class Dot extends Div {} + +Dot.style = { + _:{ + width: "6px", + height: "6px", + borderRadius: "3px", + opacity: "none", + background: Colors.browserDefault + } +}; + +class WaitingLoader extends Div { + constructor() { + super(); + + this.append([ new Dot(), new Dot(), new Dot() ]); + } +} + +WaitingLoader.style = { + _: { + display: "flex", + flexFlow: "row nowrap", + alignItems: "center", + justifyContent: "space-evenly" + } +}; + +const STATUS_CODE_DESCRIPTION_DICT: Record = { + 100: "Continue", + 101: "Switching Protocols", + 102: "Processing", + 200: "OK", + 201: "Created", + 202: "Accepted", + 203: "Non-authoritative Information", + 204: "No Content", + 205: "Reset Content", + 206: "Partial Content", + 207: "Multi-Status", + 208: "Already Reported", + 226: "IM Used", + 300: "Multiple Choices", + 301: "Moved Permanently", + 302: "Found", + 303: "See Other", + 304: "Not Modified", + 305: "Use Proxy", + 307: "Temporary Redirect", + 308: "Permanent Redirect", + 400: "Bad Request", + 401: "Unauthorized", + 402: "Payment Required", + 403: "Forbidden", + 404: "Not Found", + 405: "Method Not Allowed", + 406: "Not Acceptable", + 407: "Proxy Authentication Required", + 408: "Request Timeout", + 409: "Conflict", + 410: "Gone", + 411: "Length Required", + 412: "Precondition Failed", + 413: "Payload Too Large", + 414: "Request-URI Too Long", + 415: "Unsupported Media Type", + 416: "Requested Range Not Satisfiable", + 417: "Expectation Failed", + 418: "I'm a teapot", + 421: "Misdirected Request", + 422: "Unprocessable Entity", + 423: "Locked", + 424: "Failed Dependency", + 426: "Upgrade Required", + 428: "Precondition Required", + 429: "Too Many Requests", + 431: "Request Header Fields Too Large", + 444: "Connection Closed Without Response", + 451: "Unavailable For Legal Reasons", + 499: "Client Closed Request", + 500: "Internal Server Error", + 501: "Not Implemented", + 502: "Bad Gateway", + 503: "Service Unavailable", + 504: "Gateway Timeout", + 505: "HTTP Version Not Supported", + 506: "Variant Also Negotiates", + 507: "Insufficient Storage", + 508: "Loop Detected", + 510: "Not Extended", + 511: "Network Authentication Required", + 599: "Network Connect Timeout Error" +}; + +function getStatusDescription(status: number): string { + if (status in STATUS_CODE_DESCRIPTION_DICT) + return STATUS_CODE_DESCRIPTION_DICT[status]; + + return ""; +} + +export class RequestExecutionStatus extends Div { + private _waiting: Div; + private _error: Div; + private _result: Div; + private _statusText: Div; + + private _showText: boolean = true; + private _animate: boolean = true; + + private _currentStatus: number = 0; + + constructor() { + super(); + + this.append([ + (this._waiting = new WaitingLoader()), + (this._error = new Div()), + (this._result = new Div()), + (this._statusText = new Div()) + ]); + } + + setTextShown(shown: boolean): this { + this._showText = shown; + return this; + } + + getTextShown(): boolean { + return this._showText; + } + + setStatus(status: number): this { + if (this._currentStatus == status) return this; + + this.animations("showError").stop(); + this.animations("showResult").stop(); + this.animations("showWaiting").stop(); + + if (status == 0) { + this.animations("showWaiting").toEnd(); + } else if (status == -1) { + if (this._currentStatus == 0) + this.animations("showError").start(); + else + this.animations("showError").toEnd(); + } else { + this._result + .setText(status.toString()) + .setInlineCss({ + background: getStatusColor(status) + }); + + this._statusText.setText(getStatusDescription(status)); + + if (this._showText) { + if (this._currentStatus == 0) + this.animations("showResultWithStatusText").start(); + else + this.animations("showResultWithStatusText").toEnd(); + } else { + if (this._currentStatus == 0) + this.animations("showResult").start(); + else + this.animations("showResult").toEnd(); + } + } + + this._currentStatus = status; + + return this; + } + + getStatus(): number { + return this._currentStatus; + } +} + +RequestExecutionStatus.style = { + _: { + display: "flex", + flexFlow: "row nowrap", + alignItems: "center", + justifyContent: "center" + }, + _waiting: { + flex: "0 0 30px" + }, + _error: { + width: "20px", + height: "18px", + display: "none", + backgroundImage: "url(./resources/images/StatusError.png)", + backgroundPosition: "center", + backgroundSize: "contain", + backgroundRepeat: "no-repeat" + }, + _result: { + ...FontStyles.monoSpace, + fontSize: "13px", + lineHeight: "18px", + width: "30px", + height: "18px", + borderRadius: "6px", + color: "rgba(255, 255, 255, 0)", + textAlign: "center" + }, + _statusText: { + ...FontStyles.monoSpace, + color: Colors.workspaceDefault, + marginLeft: "10px", + lineHeight: "14px", + whiteSpace: "nowrap", + display: "none", + opacity: "0" + } +}; + +RequestExecutionStatus.animations = { + showResult: { + animations: [ + { track: "width", from: "0px", to: "30px", target: "_result" }, + { track: "marginLeft", from: "15px", to: "0px", target: "_result" }, + { track: "height", from: "0px", to: "18px", target: "_result" }, + { track: "color", to: "rgba(255, 255, 255, 1)", target: "_result", delay: 300 }, + { track: "display", to: "none", target: "_statusText" }, + { track: "opacity", to: 0, target: "_statusText" }, + { track: "display", to: "block", target: "_result" }, + { track: "display", to: "none", target: "_waiting" }, + { track: "display", to: "none", target: "_error" } + ], + ease: "circOut" + }, + showResultWithStatusText: { + animations: [ + { track: "width", from: "0px", to: "30px", target: "_result" }, + { track: "marginLeft", from: "15px", to: "0px", target: "_result" }, + { track: "height", from: "0px", to: "18px", target: "_result" }, + { track: "color", from: "rgba(255, 255, 255, 0)", to: "rgba(255, 255, 255, 1)", target: "_result", delay: 300 }, + { track: "display", to: "block", target: "_statusText" }, + { track: "opacity", from: 0, to: 1, target: "_statusText", delay: 300 }, + { track: "display", to: "block", target: "_result" }, + { track: "display", to: "none", target: "_waiting" }, + { track: "display", to: "none", target: "_error" } + ], + ease: "circOut" + }, + showError: { + animations: [ + { track: "color", to: "rgba(255, 255, 255, 0)", target: "_result", duration: 1 }, + { track: "display", to: "none", target: "_statusText" }, + { track: "opacity", to: 0, target: "_statusText", duration: 0 }, + { track: "display", to: "none", target: "_result" }, + { track: "display", to: "none", target: "_waiting" }, + { track: "display", to: "block", target: "_error" } + ] + }, + showWaiting: { + animations: [ + { track: "color", to: "rgba(255, 255, 255, 0)", target: "_result", duration: 1 }, + { track: "display", to: "none", target: "_statusText" }, + { track: "opacity", to: 0, target: "_statusText", duration: 0 }, + { track: "display", to: "none", target: "_result" }, + { track: "display", to: "flex", target: "_waiting" }, + { track: "display", to: "none", target: "_error" } + ] + } +}; diff --git a/src/renderer/ui/ExecutionHistoryControl/ResponsePanelOptionsButton.ts b/src/renderer/ui/ExecutionHistoryControl/ResponsePanelOptionsButton.ts new file mode 100644 index 0000000..57e020d --- /dev/null +++ b/src/renderer/ui/ExecutionHistoryControl/ResponsePanelOptionsButton.ts @@ -0,0 +1,70 @@ +import { Div } from "aflon"; + +import { SimpleEvent } from "../../lib/SimpleEvent"; + +import { ContextMenu, ContextMenuItemType, ContextMenuShowTrigger } from "../ContextMenu"; +import { Icon } from "../Icon"; +import { PreferenceStore, ResponseControlLocation } from "../PreferenceStore"; +import { Colors } from "../StyleConstants"; + +export class ResponsePanelOptionsButton extends Div { + + private _optionsContextMenu: ContextMenu; + + constructor() { + super(); + + this.append([ + new Icon("options") + ]); + + (this._optionsContextMenu = + new ContextMenu(this, [ + { type: ContextMenuItemType.Title, id: "res-loc-title", text: "Response panel location" }, + { type: ContextMenuItemType.CheckBox, id: "res-loc-console", text: "Console" }, + { type: ContextMenuItemType.CheckBox, id: "res-loc-workspace", text: "Workspace" }, + { type: ContextMenuItemType.CheckBox, id: "res-loc-auto", text: "Automatic, depending on space" } + ], ContextMenuShowTrigger.OnClickEvent)) + .on(this._optionsContextMenu.eventAboutToBeShown, () => this._onOptionsContextMenuAboutToBeShown()) + .on(this._optionsContextMenu.eventSelected, e => this._onOptionsContextMenuSelected(e)); + } + + private _onOptionsContextMenuAboutToBeShown(): void { + this._optionsContextMenu.setChecked("res-loc-console", + PreferenceStore.getResponseLocation() == ResponseControlLocation.Console); + this._optionsContextMenu.setChecked("res-loc-workspace", + PreferenceStore.getResponseLocation() == ResponseControlLocation.Workspace); + this._optionsContextMenu.setChecked("res-loc-auto", + PreferenceStore.getResponseLocation() == ResponseControlLocation.Automatic); + } + + private _onOptionsContextMenuSelected(e: SimpleEvent): void { + let selectedId = e["detail"]["id"]; + if (!selectedId) return; + + if (selectedId == "res-loc-console") { + PreferenceStore.setResponseLocation(ResponseControlLocation.Console); + } else if (selectedId == "res-loc-workspace") { + PreferenceStore.setResponseLocation(ResponseControlLocation.Workspace); + } else if (selectedId == "res-loc-auto") { + PreferenceStore.setResponseLocation(ResponseControlLocation.Automatic); + } + } +} + +ResponsePanelOptionsButton.style = { + _ : { + display: "flex", + fontSize: "15px", + alignContent: "center", + alignItems: "center", + paddingLeft: "2px", + paddingTop: "2px", + textAlign: "center", + color: Colors.workspaceDefault, + cursor: "pointer", + "&:hover": { + color: Colors.consoleDominant + } + } +}; diff --git a/src/renderer/ui/ExecutionHistoryControl/index.ts b/src/renderer/ui/ExecutionHistoryControl/index.ts new file mode 100644 index 0000000..cd022b6 --- /dev/null +++ b/src/renderer/ui/ExecutionHistoryControl/index.ts @@ -0,0 +1,3 @@ +export { RequestExecutionHistoryControl } from "./RequestExecutionHistoryControl"; +export { HttpResponseControl } from "./HttpResponseControl"; +export { ResponsePanelOptionsButton } from "./ResponsePanelOptionsButton"; diff --git a/src/renderer/ui/ExpandableTable/ExpandableDescriptor.ts b/src/renderer/ui/ExpandableTable/ExpandableDescriptor.ts new file mode 100644 index 0000000..1fe0609 --- /dev/null +++ b/src/renderer/ui/ExpandableTable/ExpandableDescriptor.ts @@ -0,0 +1,117 @@ +import { Div } from "aflon"; + +import { Colors, FontStyles } from "../StyleConstants"; + +export class ExpandableDescriptor extends Div { + private _descriptorTriangle: Div; + private _descriptorText: Div; + + private _expanded: boolean = true; + private _descriptorTextShown: boolean = true; + + constructor() { + super(); + + this.append([ + (this._descriptorTriangle = new Div()) + .setText("โ–ถ") + .setInlineCss({ transform: "rotate(90deg)" }), + (this._descriptorText = new Div()) + ]); + } + + setText(text: string): this { + this._descriptorText.setText(text); + return this; + } + + getText(): string { + return this._descriptorText.getText(); + } + + setDescriptorTextShown(shown: boolean): this { + if (shown == this._descriptorTextShown) return this; + + this._descriptorTextShown = shown; + + if (this._descriptorTextShown) { + this.setInlineCss({ + width: "80px", + marginRight: "20px" + }); + this._descriptorText.setInlineCss({ + display: "inline-block" + }); + } else { + this.setInlineCss({ + width: "20px", + marginRight: "10px" + }); + this._descriptorText.setInlineCss({ + display: "none" + }); + } + + return this; + } + + getDescriptorTextShown(): boolean { + return this._descriptorTextShown; + } + + setExpanded(expanded: boolean): this { + if (expanded == this._expanded) return this; + + this._expanded = expanded; + + if (this._expanded) { + this._descriptorTriangle.setInlineCss({ + transform: "rotate(90deg)" + }); + } else { + this._descriptorTriangle.setInlineCss({ + transform: "none" + }); + } + + return this; + } + + getExpanded(): boolean { + return this._expanded; + } + + showExpanderTriangle(): void { + this._descriptorTriangle.setInlineCss({ + opacity: "1" + }); + } + + hideExpanderTriangle(): void { + this._descriptorTriangle.setInlineCss({ + opacity: "0" + }); + } +} + +ExpandableDescriptor.style = { + _: { + ...FontStyles.sansSerifBold, + color: Colors.workspaceDescriptor, + fontSize: "12px", + cursor: "pointer", + textAlign: "right", + lineHeight: "20px", + marginRight: "20px", + width: "80px" + }, + _descriptorTriangle: { + display: "inline-block", + fontSize: "10px", + opacity: "0" + }, + _descriptorText: { + marginLeft: "5px", + display: "inline-block" + } +}; diff --git a/src/renderer/ui/ExpandableTable/ExpandableRow.ts b/src/renderer/ui/ExpandableTable/ExpandableRow.ts new file mode 100644 index 0000000..c1290ad --- /dev/null +++ b/src/renderer/ui/ExpandableTable/ExpandableRow.ts @@ -0,0 +1,94 @@ +import { Element, Div } from "aflon"; + +import { FontStyles, Colors } from "../StyleConstants"; +import { ExpandableDescriptor } from "./ExpandableDescriptor"; + +export class ExpandableRow extends Div { + protected _descriptor: ExpandableDescriptor; + protected _container: Div; + protected _containerPlaceholder: Div; + + constructor() { + super(); + + this.append([ + (this._descriptor = new ExpandableDescriptor()) + .setText("Title") + .on(this._descriptor.eventClick, () => this._onDescriptorClick()), + (this._container = new Div()), + (this._containerPlaceholder = new Div()) + .setText("...") + .on(this._descriptor.eventClick, () => this._onDescriptorClick()) + ]); + + this.on(this.eventMouseEnter, () => this._descriptor.showExpanderTriangle()); + this.on(this.eventMouseLeave, () => this._descriptor.hideExpanderTriangle()); + } + + setTitle(title: string): this { + this._descriptor.setText(title); + return this; + } + + getTitle(): string { + return this._descriptor.getText(); + } + + setDescriptorShown(shown: boolean): this { + this._descriptor.setDescriptorTextShown(shown); + return this; + } + + getDescriptorShown(): boolean { + return this._descriptor.getDescriptorTextShown(); + } + + setExpanded(expanded: boolean): this { + this._descriptor.setExpanded(expanded); + this._executeExpand(expanded); + + return this; + } + + getExpanded(): boolean { + return this._descriptor.getExpanded(); + } + + appendContent(items: Array): this { + this._container.append(items); + return this; + } + + protected _executeExpand(expanded: boolean): void { + if (!expanded) { + this._container.setInlineCss({ display: "none" }); + this._containerPlaceholder.setInlineCss({ display: "block" }); + } else { + this._container.setInlineCss({ display: "flex" }); + this._containerPlaceholder.setInlineCss({ display: "none" }); + } + } + + private _onDescriptorClick(): void { + this.setExpanded(!this.getExpanded()); + } +} + +ExpandableRow.style = { + _: { + display: "flex", + flexFlow: "row nowrap" + }, + _container: { + flex: "1 0 200px", + display: "flex", + flexFlow: "column nowrap", + minWidth: "0" + }, + _containerPlaceholder: { + ...FontStyles.monoSpace, + color: Colors.workspacePlaceholder, + display: "none", + cursor: "pointer" + } +}; diff --git a/src/renderer/ui/ExpandableTable/index.ts b/src/renderer/ui/ExpandableTable/index.ts new file mode 100644 index 0000000..2b4e245 --- /dev/null +++ b/src/renderer/ui/ExpandableTable/index.ts @@ -0,0 +1 @@ +export { ExpandableRow } from "./ExpandableRow"; diff --git a/src/renderer/ui/HttpRequestControl/HttpBodyControl/BodyTextEditor.ts b/src/renderer/ui/HttpRequestControl/HttpBodyControl/BodyTextEditor.ts new file mode 100644 index 0000000..506026c --- /dev/null +++ b/src/renderer/ui/HttpRequestControl/HttpBodyControl/BodyTextEditor.ts @@ -0,0 +1,212 @@ +/* eslint-disable no-fallthrough */ +import { IMacroSource, IReadOnlyMacroContext, MacroedText, MacroedTextPartType } from "../../../lib/http"; +import { SimpleEvent } from "../../../lib/SimpleEvent"; +import { TokenTextEditor, Line, Token, RegularToken, ParameterToken, AccentToken } from "../../TokenTextEditor"; +import { CaretPosition } from "../../IKeyboardNavigable"; + +export enum TextFormatting { + Plain, URLQuery +} + +export class BodyTextEditor extends TokenTextEditor implements IMacroSource { + private _formatting: TextFormatting = TextFormatting.Plain; + private _wrapLines: boolean = false; + + private _macroContext: IReadOnlyMacroContext = null; + private _macros: Array = []; + + constructor(wrapLines: boolean = false) { + super(); + this.setNewLineInsertionAllowed(true) + .setTabKeyInsertionString(" "); + + this._wrapLines = wrapLines; + } + + setFormatting(formatting: TextFormatting): this { + if (this._formatting == formatting) return this; + + this._formatting = formatting; + this._redraw(); + + return this; + } + + getFormatting(): TextFormatting { + return this._formatting; + } + + setMacroContext(macroContext: IReadOnlyMacroContext): this { + if (this._macroContext != null) + this._macroContext.off(this._macroContext.eventMacroValueChanged, this._onMacroChanged); + this._macroContext = macroContext; + this._macroContext.on(this._macroContext.eventMacroValueChanged, this._onMacroChanged); + this._redraw(); + return this; + } + + getMacroContext(): IReadOnlyMacroContext { + return this._macroContext; + } + + getMacroNames(): Array { + return this._macros; + } + + protected _onLeavingDom(): void { + if (this._macroContext == null) return; + this._macroContext.off(this._macroContext.eventMacroValueChanged, this._onMacroChanged); + } + + protected _toStringCaretPosition(lines: Array, position: CaretPosition): number { + if (this._formatting != TextFormatting.URLQuery) + return super._toStringCaretPosition(lines, position); + + if (lines.length == 0) + return 0; + + const focusLine = lines[position.row]; + + if (position.column >= focusLine.length && focusLine.index != lines.length - 1) + return lines[position.row + 1].offset; + + return lines[position.row].offset + position.column; + } + + protected _toTokenCaretPosition(lines: Array, position: number): CaretPosition { + if (this._formatting != TextFormatting.URLQuery) + return super._toTokenCaretPosition(lines, position); + + if (position <= 0 || lines.length == 0) return { + row: 0, column: 0 + }; + + let focusLine: Line = null; + + for (let i = 0; i <= lines.length - 1; i++) { + const currentLine = lines[i]; + if (currentLine.offset <= position && position <= currentLine.length + currentLine.offset) { + focusLine = currentLine; + break; + } + } + + if (focusLine == null) + return { + row: lines.length - 1, + column: lines[lines.length - 1].length + }; + + return { + row: focusLine.index, + column: position - focusLine.offset + }; + } + + protected _tokenize(newText: string): Array { + switch (this._formatting) { + case TextFormatting.Plain: return this._tokenizePlain(newText); + case TextFormatting.URLQuery: return this._tokenizeURLEncoding(newText); + default: + throw new Error(`Text formatting ${this._formatting} is not supported.`); + } + } + + private _tokenizePlain(newText: string): Array { + const lineStrings = newText.split("\n"); + + let macros: Array = []; + let lines: Array = []; + + for (let i = 0; i <= lineStrings.length - 1; i++) { + let macroedText = MacroedText.parse(lineStrings[i]); + + macroedText.getMacroNames().forEach(name => { + if (macros.indexOf(name) == -1) + macros.push(name); + }); + + lines.push(new Line().setTokens( + macroedText.getParts().map(part => { + if (part.type == MacroedTextPartType.Parameter) return new ParameterToken(part.text); + else if (part.type == MacroedTextPartType.PlainText) return new RegularToken(part.text); + else throw new Error(`MacroedTextPartType ${part.type} is not supported.`); + }) + )); + } + + this._macros = macros; + + return lines; + } + + private _tokenizeURLEncoding(newText: string): Array { + let lines: Array = []; + + let macros: Array = []; + + let keyValues = newText.split("&"); + + for (let i = 0; i <= keyValues.length - 1; i++) { + let macroedText = MacroedText.parse(keyValues[i], true); + + macroedText.getMacroNames().forEach(name => { + if (macros.indexOf(name) == -1) + macros.push(name); + }); + + this._macros = macroedText.getMacroNames(); + + + let eqPassed = false; + let isEqInTheEnd = false; + + let tokens: Array = []; + + if (i != 0) + tokens.push(new AccentToken("&")); + + for (let part of macroedText.getParts()) { + if (part.type == MacroedTextPartType.Parameter) { + let macroName = ""; + + if (part.text.length >= 3) + macroName = part.text.substring(2, part.text.length - 1); + + if (eqPassed && isEqInTheEnd && this._macroContext != null) + isEqInTheEnd = this._macroContext.isMacroEmpty(macroName); + + tokens.push(new ParameterToken(part.text)); + } else if (part.type == MacroedTextPartType.PlainText) { + if (eqPassed) isEqInTheEnd = false; + tokens.push(new RegularToken(part.text)); + } else if (part.type == MacroedTextPartType.EqualityChar) { + eqPassed = true; + isEqInTheEnd = true; + tokens.push(new AccentToken(part.text)); + } else + throw new Error(`MacroedTextPartType ${part.type} is not supported.`); + } + + lines.push( + new Line() + .setTokens(tokens) + .setInlineCss({ + opacity: isEqInTheEnd ? "0.5" : "1.0" + })); + } + + this._macros = macros; + + return lines; + } + + private _onMacroChanged = (e: SimpleEvent): void => { + if (this._formatting != TextFormatting.URLQuery) return; + + let macroName = (e["detail"]["macroName"]); + if (this._macros.includes(macroName)) { + this._redraw(); + } + }; +} diff --git a/src/renderer/ui/HttpRequestControl/HttpBodyControl/HttpBodyControl.ts b/src/renderer/ui/HttpRequestControl/HttpBodyControl/HttpBodyControl.ts new file mode 100644 index 0000000..c0c2d95 --- /dev/null +++ b/src/renderer/ui/HttpRequestControl/HttpBodyControl/HttpBodyControl.ts @@ -0,0 +1,223 @@ +import { Div } from "aflon"; + +import { FormEncoding } from "../../../lib/http"; + +import { ExpandableRow } from "../../ExpandableTable"; +import { CaretPosition, IKeyboardNavigable } from "../../IKeyboardNavigable"; + +import { HttpBody, HttpBodyContentType, HttpBodyType, HttpFormBody, HttpTextBody, IMacroSource, IReadOnlyMacroContext } from "../../../lib/http"; + +import { BodyFormat, HttpBodyFormatSelector } from "./HttpBodyFormatSelector"; +import { HttpFileBodyControl } from "./HttpFileBodyControl"; +import { HttpFormBodyControl } from "./HttpFormBodyControl"; +import { BodyTextEditor, TextFormatting } from "./BodyTextEditor"; + +export class HttpBodyControl extends ExpandableRow implements IMacroSource, IKeyboardNavigable { + public eventBodyChanged = "bodyChanged"; + public eventDefaultContentTypeChangeRequested = "defaultContentTypeChangeRequested"; + public eventFocusLeaveRequested = "focusLeaveRequested"; + + private _typeSelector: HttpBodyFormatSelector; + private _tokenTextEditor: BodyTextEditor; + private _fileBodyControl: HttpFileBodyControl; + private _formBodyControl: HttpFormBodyControl; + + constructor() { + super(); + + this.setTitle("Body"); + this.appendContent([ + new Div() + .append([ + (this._typeSelector = new HttpBodyFormatSelector()) + .on(this._typeSelector.eventBodyTypeChanged, () => this._onBodyTypeChanged()) + .on(this._typeSelector.eventDefaultContentTypeRequested, + () => this._onDefaultContentTypeHeaderSetRequested()), + (this._fileBodyControl = new HttpFileBodyControl()) + .addCssClass({ marginBottom: "10px", marginTop: "10px" }) + .on(this._fileBodyControl.eventBodyChanged, () => this._onFileBodyChanged()) + .on(this._fileBodyControl.eventFocusLeaveRequested, e => this.raise(this.eventFocusLeaveRequested, e["detail"])), + (this._formBodyControl = new HttpFormBodyControl()) + .addCssClass({ marginBottom: "10px", marginTop: "10px" }) + .on(this._formBodyControl.eventFormBodyChanged, () => this._onChange()) + .on(this._formBodyControl.eventFocusLeaveRequested, e => this.raise(this.eventFocusLeaveRequested, e["detail"])), + (this._tokenTextEditor = new BodyTextEditor()) + .setText("") + .setPlaceholder("Enter body content") + .addCssClass({ + marginBottom: "10px", + marginTop: "10px", + overflowX: "scroll", + "&::-webkit-scrollbar": { + display: "none" + } + }) + .on(this._tokenTextEditor.eventChange, () => this._onChange()) + .on(this._tokenTextEditor.eventFocusLeaveRequested, e => this.raise(this.eventFocusLeaveRequested, e["detail"])) + ]) + .addCssClass({ + display: "flex", + flexFlow: "column nowrap" + }) + ]); + } + + getMacroNames(): Array { + switch (this._typeSelector.getBodyFormat()) { + case BodyFormat.Text: + return this._tokenTextEditor.getMacroNames(); + case BodyFormat.File: + return this._fileBodyControl.getMacroNames(); + case BodyFormat.Form: + return this._formBodyControl.getMacroNames(); + default: + return []; + } + } + + setHttpBody(body: HttpBody): this { + if (body == null) { + this._typeSelector.setBodyFormat(BodyFormat.None); + this._tokenTextEditor.setText(""); + this._fileBodyControl.setPath(""); + this._formBodyControl.setBody(null); + } else if (body.type == HttpBodyType.Regular) { + let textBody = body; + if (textBody.valueType == HttpBodyContentType.Text) { + this._tokenTextEditor.setText(textBody.value); + this._typeSelector.setBodyFormat(BodyFormat.Text); + } else if (textBody.valueType == HttpBodyContentType.File) { + this._typeSelector.setBodyFormat(BodyFormat.File); + this._fileBodyControl.setPath(textBody.value); + } else { + throw new Error(`BodyContentType ${textBody.valueType} is not supported.`); + } + } else if (body.type == HttpBodyType.Form) { + let formBody = body; + this._typeSelector.setBodyFormat(BodyFormat.Form); + this._typeSelector.setFormEncoding(formBody.encoding); + this._formBodyControl.setBody(formBody); + } + + this._updateControlDependingOnBodyType(); + return this; + } + + getHttpBody(): HttpBody { + switch (this._typeSelector.getBodyFormat()) { + case BodyFormat.Text: { + let body = new HttpTextBody(); + body.valueType = HttpBodyContentType.Text; + body.value = this._tokenTextEditor.getText(); + return body; + } + case BodyFormat.File: { + let body = new HttpTextBody(); + body.valueType = HttpBodyContentType.File; + body.value = this._fileBodyControl.getPath(); + return body; + } + case BodyFormat.Form: { + let body = this._formBodyControl.getBody(); + body.encoding = this._typeSelector.getFormEncoding(); + return body; + } + default: + return null; + } + } + + getDefaultContentType(): string { + switch (this._typeSelector.getBodyFormat()) { + case BodyFormat.Text: return "text/plain"; + case BodyFormat.File: return null; + case BodyFormat.Form: { + switch (this._typeSelector.getFormEncoding()) { + case FormEncoding.UrlEncoded: return "application/x-www-form-urlencoded"; + default: return "multipart/form-data"; + } + } + default: return null; + } + } + + setMacroContext(macroContext: IReadOnlyMacroContext): this { + this._tokenTextEditor.setMacroContext(macroContext); + this._formBodyControl.setMacroContext(macroContext); + return this; + } + + getMacroContext(): IReadOnlyMacroContext { + return this._tokenTextEditor.getMacroContext(); + } + + setCaretPosition(position: CaretPosition): this { + if (this._tokenTextEditor.getVisibility()) { + this._tokenTextEditor.setCaretPosition(position); + } else if (this._fileBodyControl.getVisibility()) { + this._fileBodyControl.setCaretPosition(position); + } else if (this._formBodyControl.getVisibility()) { + this._formBodyControl.setCaretPosition(position); + } + + return this; + } + + getCaretPosition(): CaretPosition { + if (this._tokenTextEditor.getVisibility()) { + return this._tokenTextEditor.getCaretPosition(); + } + + if (this._fileBodyControl.getVisibility()) { + return this._fileBodyControl.getCaretPosition(); + } + + if (this._formBodyControl.getVisibility()) { + return this._formBodyControl.getCaretPosition(); + } + + return null; + } + + private _onChange(): void { + this._typeSelector.setRevertDisabled(false); + this.raise(this.eventBodyChanged); + } + + private _updateControlDependingOnBodyType(): void { + this._fileBodyControl.setVisibility(false); + this._formBodyControl.setVisibility(false); + this._tokenTextEditor.setVisibility(false); + + switch (this._typeSelector.getBodyFormat()) { + case BodyFormat.Text: { + this._tokenTextEditor.setFormatting(TextFormatting.Plain); + this._tokenTextEditor.setVisibility(true); + break; + } + case BodyFormat.File: { + this._fileBodyControl.setVisibility(true); + break; + } + case BodyFormat.Form: { + this._formBodyControl.setVisibility(true); + break; + } + } + } + + private _onBodyTypeChanged(): void { + this._updateControlDependingOnBodyType(); + this._typeSelector.setRevertDisabled(false); + this.raise(this.eventBodyChanged); + } + + private _onFileBodyChanged(): void { + this._typeSelector.setRevertDisabled(false); + this.raise(this.eventBodyChanged); + } + + private _onDefaultContentTypeHeaderSetRequested(): void { + this.raise(this.eventDefaultContentTypeChangeRequested); + } +} diff --git a/src/renderer/ui/HttpRequestControl/HttpBodyControl/HttpBodyFormatSelector.ts b/src/renderer/ui/HttpRequestControl/HttpBodyControl/HttpBodyFormatSelector.ts new file mode 100644 index 0000000..74fedd5 --- /dev/null +++ b/src/renderer/ui/HttpRequestControl/HttpBodyControl/HttpBodyFormatSelector.ts @@ -0,0 +1,185 @@ +import { Div } from "aflon"; + +import { FormEncoding } from "../../../lib/http"; + +import { SelectBox } from "../../BasicControls"; +import { IconButton } from "../../IconButton"; +import { Colors, FontStyles } from "../../StyleConstants"; + +export enum BodyFormat { + None = "None", + Text = "Text", + File = "File", + Form = "Form" +} + +export class HttpBodyFormatSelector extends Div { + public eventBodyTypeChanged = "bodyChanged"; + public eventDefaultContentTypeRequested = "defaultContentTypeRequested"; + public eventRevertBodyToDefaultRequested = "revertBodyToDefaultRequested"; + public eventSaveCurrentBodyAsDefaultRequested = "saveCurrentBodyAsDefaultRequested"; + + private _bodyType: Div; + private _bodyTypeSelectBox: SelectBox; + private _formEncoding: Div; + private _formEncodingSelectBox: SelectBox; + private _revertIcon: IconButton; + private _saveIcon: IconButton; + + private _bodyFormat: BodyFormat; + + constructor() { + super(); + + this.append([ + (this._bodyType = new Div()) + .append([ + new Div().setText("Body type: "), + (this._bodyTypeSelectBox = new SelectBox()) + .insertOptions([ + { value: BodyFormat.None, text: "None" }, + { value: BodyFormat.Text, text: "Text" }, + { value: BodyFormat.File, text: "File" }, + { value: BodyFormat.Form, text: "Form" } + ]) + .on(this._bodyTypeSelectBox.eventSelected, () => this._onSelected()) + ]), + (this._formEncoding = new Div()) + .append([ + new Div().setText("Encode as: "), + (this._formEncodingSelectBox = new SelectBox()) + .insertOptions([ + { value: FormEncoding.Multipart, text: "multipart/form-data" }, + { value: FormEncoding.UrlEncoded, text: "application/x-www-form-urlencoded" } + ]) + .on(this._formEncodingSelectBox.eventSelected, () => this._onSelected()) + ]) + .setVisibility(false), + (this._revertIcon = new IconButton("undo")) + .setTooltip("Revert body to default value") + .on(this._revertIcon.eventClick, () => this._onRevertIconClick()), + (this._saveIcon = new IconButton("save")) + .setTooltip("Save current body as the default for this request") + .on(this._revertIcon.eventClick, () => this._onSaveIconClick()) + ]); + } + + setBodyFormat(format: BodyFormat): this { + this.setRevertDisabled(false); + if (this._bodyFormat == format) return this; + + this._bodyFormat = format; + this._update(); + + return this; + } + + getBodyFormat(): BodyFormat { + return this._bodyTypeSelectBox.getSelectedOption().value; + } + + setFormEncoding(encoding: FormEncoding): this { + this.setRevertDisabled(false); + this._formEncodingSelectBox.setSelectedOption(encoding); + return this; + } + + getFormEncoding(): FormEncoding { + return this._formEncodingSelectBox.getSelectedOption().value; + } + + setRevertDisabled(disabled: boolean): this { + this._saveIcon.setDisabled(disabled); + this._revertIcon.setDisabled(disabled); + return this; + } + + getRevertDisabled(): boolean { + return this._saveIcon.getDisabled(); + } + + private _onSelected(): void { + let newBodyFormat = this.getBodyFormat(); + + this._bodyFormat = newBodyFormat; + + this._update(); + this.raise(this.eventBodyTypeChanged); + + return; + } + + private _update(): void { + switch (this._bodyFormat) { + case BodyFormat.None: { + this._bodyTypeSelectBox.setSelectedOption("None"); + this._formEncoding.setVisibility(false); + break; + } + case BodyFormat.Text: { + this._bodyTypeSelectBox.setSelectedOption("Text"); + this._formEncoding.setVisibility(false); + break; + } + case BodyFormat.File: { + this._bodyTypeSelectBox.setSelectedOption("File"); + this._formEncoding.setVisibility(false); + break; + } + case BodyFormat.Form: { + this._bodyTypeSelectBox.setSelectedOption("Form"); + this._formEncoding.setVisibility(true); + break; + } + default: throw new Error(`Body format ${this._bodyFormat} is not supported.`); + } + } + + private _onRevertIconClick(): void { + this.setRevertDisabled(true); + this.raise(this.eventRevertBodyToDefaultRequested, null, true); + } + + private _onSaveIconClick(): void { + this.setRevertDisabled(true); + this.raise(this.eventSaveCurrentBodyAsDefaultRequested, null, true); + } +} + +HttpBodyFormatSelector.style = { + _: { + ...FontStyles.sansSerifNormal, + color: Colors.workspaceDefault, + fontSize: "12px", + height: "21px", + lineHeight: "21px", + display: "flex", + flexFlow: "row nowrap", + alignItems: "center", + whiteSpace: "nowrap" + }, + _bodyType: { + display: "flex", + flexFlow: "row nowrap" + }, + _bodyTypeSelectBox: { + marginLeft: "4px" + }, + _formEncoding: { + display: "flex", + flexFlow: "row nowrap", + marginLeft: "7px" + }, + _formEncodingSelectBox: { + marginLeft: "4px" + }, + _revertIcon: { + marginLeft: "10px", + height: "21px", + lineHeight: "21px" + }, + _saveIcon: { + height: "21px", + lineHeight: "21px" + } +}; diff --git a/src/renderer/ui/HttpRequestControl/HttpBodyControl/HttpFileBodyControl.ts b/src/renderer/ui/HttpRequestControl/HttpBodyControl/HttpFileBodyControl.ts new file mode 100644 index 0000000..29d7d0e --- /dev/null +++ b/src/renderer/ui/HttpRequestControl/HttpBodyControl/HttpFileBodyControl.ts @@ -0,0 +1,84 @@ +import { Div } from "aflon"; +import { IMacroSource } from "../../../lib/http"; + +import { showOpenDialog } from "../../../lib/SystemDialogs"; + +import { Button } from "../../BasicControls"; +import { CaretPosition, IKeyboardNavigable } from "../../IKeyboardNavigable"; +import { MacroedTextEditor } from "../../TokenTextEditor/MacroedTextEditor"; + + +export class HttpFileBodyControl extends Div implements IKeyboardNavigable, IMacroSource { + public eventBodyChanged = "bodyChanged"; + public eventFocusLeaveRequested = "focusLeaveRequested"; + + private _browseButton: Button; + private _pathTextBox: MacroedTextEditor; + + constructor() { + super(); + + this.append([ + (this._browseButton = new Button()) + .setText("Browse") + .on(this._browseButton.eventClick, () => this._onClick()), + (this._pathTextBox = new MacroedTextEditor()) + .setPlaceholder("Type path to file") + .setNewLineInsertionAllowed(false) + .on(this._pathTextBox.eventChange, () => this.raise(this.eventBodyChanged)) + .on(this._pathTextBox.eventFocusLeaveRequested, e => this.raise(this.eventFocusLeaveRequested, e["detail"])) + ]); + } + + setPath(path: string): this { + this._pathTextBox.setText(path); + return this; + } + + getPath(): string { + return this._pathTextBox.getText().trim(); + } + + setCaretPosition(position: CaretPosition): this { + this._pathTextBox.setCaretPosition(position); + return this; + } + + getCaretPosition(): CaretPosition { + return this._pathTextBox.getCaretPosition(); + } + + getMacroNames(): Array { + return this._pathTextBox.getMacroNames(); + } + + private async _onClick(): Promise { + const result = await showOpenDialog({ + title: "Select file", + properties: [ "openFile" ] + }); + + if (result.canceled) return; + + this._pathTextBox.setText(result.filePaths[0]); + this.raise(this.eventBodyChanged); + } +} + +HttpFileBodyControl.style = { + _: { + display: "flex", + flexFlow: "row nowrap", + alignItems: "center" + }, + _browseButton: { + flex: "0 0 content" + }, + _pathTextBox: { + marginLeft: "10px", + overflowX: "scroll", + "&::-webkit-scrollbar": { + display: "none" + } + } +}; diff --git a/src/renderer/ui/HttpRequestControl/HttpBodyControl/HttpFormBodyControl.ts b/src/renderer/ui/HttpRequestControl/HttpBodyControl/HttpFormBodyControl.ts new file mode 100644 index 0000000..313ad7b --- /dev/null +++ b/src/renderer/ui/HttpRequestControl/HttpBodyControl/HttpFormBodyControl.ts @@ -0,0 +1,211 @@ +import { Div, typeAflonTarget } from "aflon"; + +import { HttpFormBody, IMacroSource, IReadOnlyMacroContext } from "../../../lib/http"; +import { CaretPosition, FocusLeaveDirection, IKeyboardNavigable } from "../../IKeyboardNavigable"; + +import { HttpFormRecordControl } from "./HttpFormRecordControl"; + +export class HttpFormBodyControl extends Div implements IMacroSource, IKeyboardNavigable { + public eventFormBodyChanged = "formBodyChanged"; + public eventFocusLeaveRequested = "focusLeaveRequested"; + + private _readOnly: boolean = false; + + constructor() { + super(); + + this._addEmptyFormRecordControl(); + } + + getMacroNames(): Array { + let macros: Array = []; + + this.children().forEach(child => { + const headerRecord = (child); + headerRecord.getMacroNames().forEach(macro => { + if (macros.indexOf(macro) == -1) + macros.push(macro); + }); + }); + + return macros; + } + + setBody(form: HttpFormBody): this { + this.empty(); + + if (form != null) { + form.records.forEach(record => { + let formRcrdCtrl = new HttpFormRecordControl(); + formRcrdCtrl + .setFormRecord(record) + .setReadOnly(this._readOnly) + .on(formRcrdCtrl.eventFormRecordChanged, () => this._onFormRecordChanged()) + .on(formRcrdCtrl.eventFocusOut, e => this._onFocusOut(e)) + .on(formRcrdCtrl.eventFocusLeaveRequested, e => this._onFocusLeaveRequested(e)); + this.append([formRcrdCtrl]); + }); + } + + if (!this._readOnly) + this._addEmptyFormRecordControl(); + return this; + } + + getBody(): HttpFormBody { + let formBody = new HttpFormBody(); + + this.children().forEach(child => { + const formRecord = (child).getFormRecord(); + if (formRecord.name.length != 0 || + formRecord.value.length != 0) + formBody.records.push(formRecord); + }); + + return formBody; + } + + setReadOnly(readOnly: boolean): this { + if (this._readOnly == readOnly) return this; + + this._readOnly = readOnly; + + this.children().forEach(child => { + if (child instanceof HttpFormRecordControl) + (child).setReadOnly(readOnly); + }); + + if (this._readOnly) + this._removeEmptyFormRecordControl(); + else + this._removeEmptyFormRecordControl(); + + return this; + } + + getReadOnly(): boolean { + return this._readOnly; + } + + setCaretPosition(position: CaretPosition): this { + let children = this.children(); + if (children.length == 0) return this; + + if (position.row == -1 || position.row > children.length - 1) + position.row = children.length - 1; + + let child = children[position.row]; + if (!(child instanceof HttpFormRecordControl)) return this; + + child.setCaretPosition({ row: 0, column: position.column }); + return this; + } + + getCaretPosition(): CaretPosition { + let headers = this.children(); + + for (let i = 0; i <= headers.length - 1; i++) { + let header = headers[i]; + if (!(header instanceof HttpFormRecordControl)) continue; + let caretPosition = header.getCaretPosition(); + if (caretPosition == null) continue; + + return { row: i, column: caretPosition.column }; + } + + return null; + } + + setMacroContext(macroContext: IReadOnlyMacroContext): this { + this.children().forEach(child => { + if (!(child instanceof HttpFormRecordControl)) return; + child.setMacroContext(macroContext); + }); + + return this; + } + + getMacroContext(): IReadOnlyMacroContext { + for (let child of this.children()) { + if (!(child instanceof HttpFormRecordControl)) continue; + return child.getMacroContext(); + } + + return null; + } + + private _onFormRecordChanged(): void { + let children = this.children(); + if (children.length == 0) return; + let lastChild = children[children.length - 1]; + let formRecord = lastChild.getFormRecord(); + if (formRecord.name != "" || formRecord.value != "") + this._addEmptyFormRecordControl(); + + this.raise(this.eventFormBodyChanged); + } + + private _onFocusOut(e): void { + let sender = (
e.target.aflonElement).parent(); + if (!sender) return; + if (sender == this.children()[this.children().length - 1]) return; + + let formRecord = sender.getFormRecord(); + + if (formRecord.name == "" && formRecord.value == "") + this.removeChild(sender); + } + + private _addEmptyFormRecordControl(): void { + let formRecordControl = new HttpFormRecordControl(); + formRecordControl + .on(formRecordControl.eventFormRecordChanged, () => this._onFormRecordChanged()) + .on(formRecordControl.eventFocusOut, e => this._onFocusOut(e)) + .on(formRecordControl.eventFocusLeaveRequested, e => this._onFocusLeaveRequested(e)); + + this.append([ + formRecordControl + ]); + } + + private _removeEmptyFormRecordControl(): void { + let children = this.children(); + if (children.length == 0) return; + let lastChild = children[children.length - 1]; + let formRecord = lastChild.getFormRecord(); + if (formRecord.name == "" && formRecord.value == "") + this.removeChild(lastChild); + } + + private _onFocusLeaveRequested(e: Event): void { + let sender = typeAflonTarget(e, HttpFormRecordControl); + + if (!sender) return; + + let children = this.children(); + let numOfHeaders = children.length; + let index = children.indexOf(sender); + let direction = e["detail"]["direction"]; + + if (direction == FocusLeaveDirection.Up) { + if (index == 0) this.raise(this.eventFocusLeaveRequested, { direction }); + else ( children[index - 1]).setCaretPositionAtStart(); + } else if (direction == FocusLeaveDirection.Down) { + if (index == numOfHeaders - 1 || numOfHeaders == 0) this.raise(this.eventFocusLeaveRequested, { direction }); + else ( children[index + 1]).setCaretPositionAtStart(); + } else if (direction == FocusLeaveDirection.Left || direction == FocusLeaveDirection.Backspace) { + if (index == 0) this.raise(this.eventFocusLeaveRequested, { direction }); + else ( children[index - 1]).setCaretPositionAtEnd(); + } else if (direction == FocusLeaveDirection.Right) { + if (index == numOfHeaders - 1 || numOfHeaders == 0) this.raise(this.eventFocusLeaveRequested, { direction }); + else ( children[index + 1]).setCaretPositionAtStart(); + } + } +} + +HttpFormBodyControl.style = { + _: { + display: "flex", + flexFlow: "column nowrap" + } +}; diff --git a/src/renderer/ui/HttpRequestControl/HttpBodyControl/HttpFormRecordControl.ts b/src/renderer/ui/HttpRequestControl/HttpBodyControl/HttpFormRecordControl.ts new file mode 100644 index 0000000..433656b --- /dev/null +++ b/src/renderer/ui/HttpRequestControl/HttpBodyControl/HttpFormRecordControl.ts @@ -0,0 +1,373 @@ +import { Div, Span } from "aflon"; + +import { HttpBodyContentType, HttpFormRecord, IMacroSource, IReadOnlyMacroContext } from "../../../lib/http"; +import { showOpenDialog } from "../../../lib/SystemDialogs"; + +import { Colors, FontStyles } from "../../StyleConstants"; +import { MacroedTextEditor } from "../../TokenTextEditor"; +import { CaretPosition, FocusLeaveDirection, IKeyboardNavigable } from "../../IKeyboardNavigable"; +import { Tooltip } from "../../ContextMenu"; + +class HttpFormRecordTypeControl extends Div { + eventChange: string = "changed"; + + private _text: Span; + private _tooltip: Tooltip; + + private _disabled: boolean = false; + + constructor() { + super(); + + this.append([ + (this._text = new Span()) + .setText("T") + ]).on(this.eventClick, () => this._onClick()); + + (this._tooltip = new Tooltip(this)) + .setText("Toggle form record type between Text and File."); + } + + setHttpFormRecordType(type: HttpBodyContentType): this { + if (type == HttpBodyContentType.File) + this._text.setText("F"); + else if (type == HttpBodyContentType.Text) + this._text.setText("T"); + else + throw new Error(`HttpBodyContentType ${type} is not supported`); + return this; + } + + getHttpFormRecordType(): HttpBodyContentType { + if (this._text.getText() == "F") return HttpBodyContentType.File; + return HttpBodyContentType.Text; + } + + setDisabled(disabled: boolean): this { + this._disabled = disabled; + return this; + } + + getDisabled(): boolean { + return this._disabled; + } + + private _onClick(): void { + if (this._disabled) return; + + let selectedValue = this.getHttpFormRecordType(); + + if (selectedValue == HttpBodyContentType.Text) + this.setHttpFormRecordType(HttpBodyContentType.File); + else + this.setHttpFormRecordType(HttpBodyContentType.Text); + + this.raise(this.eventChange); + } +} + +HttpFormRecordTypeControl.style = { + _: { + background: Colors.workspaceLine, + color: Colors.workspaceDefault, + fontSize: "11px", + fontWeight: "bold", + cursor: "pointer", + width: "16px", + height: "16px", + borderRadius: "4px", + lineHeight: "16px", + textAlign: "center", + textDecoration: "none", + "&:hover": { + color: Colors.workspaceDescriptor + } + } +}; + +export class HttpFormRecordControl extends Div implements IMacroSource, IKeyboardNavigable { + + public eventFormRecordChanged = "formRecordChanged"; + public eventFocusLeaveRequested = "focusLeaveRequested"; + + private _formRecordType: HttpFormRecordTypeControl; + private _formNameTextBox: MacroedTextEditor; + private _delimiter: Span; + private _formValueTextBox: MacroedTextEditor; + private _browseBtn: Div; + + private _macroContext: IReadOnlyMacroContext = null; + + constructor() { + super(); + + this.append([ + (this._formRecordType = new HttpFormRecordTypeControl()) + .addAttr("tabindex", "-1") + .on(this._formRecordType.eventChange, () => this._onChange()), + (this._formNameTextBox = new MacroedTextEditor()) + .setPlaceholder("...") + .on(this._formNameTextBox.eventChange, () => this._onChange()) + .on(this._formNameTextBox.eventFocusLeaveRequested, e => this._onNameFocusLeaveRequested(e)) + .on(this._formNameTextBox.eventKeyDown, e => this._onFormNameKeyDown(e)), + (this._delimiter = new Span()) + .setText("="), + (this._formValueTextBox = new MacroedTextEditor()) + .setPlaceholder("...") + .setNewLineInsertionAllowed(true) + .on(this._formValueTextBox.eventChange, () => this._onChange()) + .on(this._formValueTextBox.eventInput, () => this._onFormValueInput()) + .on(this._formValueTextBox.eventFocusLeaveRequested, e => this._onValueFocusLeaveRequested(e)), + (this._browseBtn = new Div()) + .setVisibility(false) + .setText("Browse") + .addAttr("tabindex", "-1") + .on(this._browseBtn.eventClick, () => this._onBrowseClick()) + ]); + + this.on(this.eventMouseEnter, () => this._onMouseEnter()) + .on(this.eventMouseLeave, () => this._onMouseLeave()); + } + + getMacroNames(): Array { + let macros = [ ...this._formNameTextBox.getMacroNames() ]; + + this._formValueTextBox.getMacroNames().forEach(macro => { + if (macros.indexOf(macro) == -1) + macros.push(macro); + }); + + return macros; + } + + setFormRecord(record: HttpFormRecord): this { + this._formNameTextBox.setText(record.name); + this._formValueTextBox.setText(record.value); + this._formRecordType.setHttpFormRecordType(record.type); + + this._updateOpacity(); + return this; + } + + getFormRecord(): HttpFormRecord { + return { + name: this._formNameTextBox.getText(), + value: this._formValueTextBox.getText(), + type: this._formRecordType.getHttpFormRecordType() + }; + } + + setReadOnly(readonly: boolean): this { + this._formNameTextBox.setReadOnly(readonly); + this._formValueTextBox.setReadOnly(readonly); + this._formRecordType.setDisabled(readonly); + return this; + } + + getReadOnly(): boolean { + return this._formNameTextBox.getReadOnly() || this._formValueTextBox.getReadOnly(); + } + + setCaretPositionAtEnd(): void { + this._formValueTextBox.setCaretPosition({ row: -1, column: -1 }); + } + + setCaretPositionAtStart(): void { + this._formNameTextBox.setCaretPosition({ row: 0, column: 0 }); + } + + setCaretPosition(position: CaretPosition): this { + if (position.column == -1) { + this._formValueTextBox.setCaretPosition(position); + return this; + } + + let nameLength = this._formNameTextBox.getText().length; + + if (position.column > nameLength) { + position.column -= nameLength; + this._formNameTextBox.setCaretPosition(position); + return this; + } + + this._formNameTextBox.setCaretPosition(position); + return this; + } + + getCaretPosition(): CaretPosition { + let valuePosition = this._formValueTextBox.getCaretPosition(); + + if (valuePosition != null) { + valuePosition.column += this._formNameTextBox.getText().length; + return valuePosition; + } + + return this._formNameTextBox.getCaretPosition(); + } + + setMacroContext(macroContext: IReadOnlyMacroContext): this { + if (this._macroContext != null) + this._macroContext.off(this._macroContext.eventMacroValueChanged, this._onMacroChanged); + this._macroContext = macroContext; + this._macroContext.on(this._macroContext.eventMacroValueChanged, this._onMacroChanged); + this._updateOpacity(); + return this; + } + + getMacroContext(): IReadOnlyMacroContext { + return this._macroContext; + } + + protected _onLeavingDom(): void { + if (this._macroContext == null) return; + this._macroContext.off(this._macroContext.eventMacroValueChanged, this._onMacroChanged); + } + + private _onChange(): void { + this._updateOpacity(); + + if (this._formRecordType.getHttpFormRecordType() == HttpBodyContentType.Text) + this._browseBtn.setVisibility(false); + else + this._browseBtn.setVisibility(true); + + this.raise(this.eventFormRecordChanged); + } + + private _onFormValueInput(): void { + this._updateOpacity(); + } + + private _onNameFocusLeaveRequested(e: Event): void { + let direction = e["detail"]["direction"]; + if (direction == FocusLeaveDirection.Right) { + this._formValueTextBox.setCaretPosition({ row: 0, column: 0 }); + } else { + this.raise(this.eventFocusLeaveRequested, { direction }); + } + } + + private _onValueFocusLeaveRequested(e: Event): void { + let direction = e["detail"]["direction"]; + if (direction == FocusLeaveDirection.Left || direction == FocusLeaveDirection.Backspace) { + this._formNameTextBox.setCaretPosition({ row: -1, column: -1 }); + } else { + this.raise(this.eventFocusLeaveRequested, { direction }); + } + } + + private async _onBrowseClick(): Promise { + const result = await showOpenDialog({ + title: "Select file", + properties: [ "openFile" ] + }); + + if (result.canceled) return; + + this._formValueTextBox.setText(result.filePaths[0]); + this.raise(this.eventFormRecordChanged); + } + + private _onMouseEnter(): void { + if (this._formRecordType.getHttpFormRecordType() != HttpBodyContentType.File) return; + this._browseBtn.setVisibility(true); + } + + private _onMouseLeave(): void { + this._browseBtn.setVisibility(false); + } + + private _onFormNameKeyDown(e: Event): void { + let key = ( e).key; + + let name = this._formNameTextBox.getText(); + + if (key == "=" && name[name.length - 1] == "=") { + this._formNameTextBox.setText(name.substring(0, name.length - 1)); + this._formValueTextBox.focus(); + } else if (name.includes("=")) { + let [ newName, value ] = name.split("="); + + if (!newName) newName = ""; + if (!value) value = ""; + + this._formNameTextBox.setText(newName); + this._formValueTextBox.setText(value); + this._formValueTextBox.setCaretPosition({ row: -1, column: -1 }); + + this.raise(this.eventFormRecordChanged); + } + } + + private async _updateOpacity(): Promise { + let effectiveName = this._formNameTextBox.getText(); + let effectiveValue = this._formValueTextBox.getText(); + + if (this._macroContext) { + for (let name of this._formValueTextBox.getMacroNames()) { + effectiveName = effectiveName.replace("${" + name + "}", await this._macroContext.getMacroValue(name)); + effectiveValue = effectiveValue.replace("${" + name + "}", await this._macroContext.getMacroValue(name)); + } + } + + if (!effectiveValue && effectiveName) { + this._formNameTextBox.setInlineCss({ opacity: 0.5 }); + this._delimiter.setInlineCss({ opacity: 0.5 }); + this._formValueTextBox.setInlineCss({ opacity: 0.5 }); + } else { + this._formNameTextBox.setInlineCss({ opacity: 1.0 }); + this._delimiter.setInlineCss({ opacity: 1.0 }); + this._formValueTextBox.setInlineCss({ opacity: 1.0 }); + } + } + + private _onMacroChanged = (): void => { + this._updateOpacity(); + }; +} + +HttpFormRecordControl.style = { + _ : { + display: "flex", + flexFlow: "row nowrap" + }, + _formRecordType: { + marginRight: "7px", + position: "relative", + alignSelf: "flex-start", + marginTop: "1px", + flex: "0 0 auto" + }, + _delimiter: { + ...FontStyles.monoSpace, + color: Colors.workspaceAccent, + margin: "0 5px", + height: "20px" + }, + _formNameTextBox: { + height: "20px", + overflowX: "scroll", + "&::-webkit-scrollbar": { + display: "none" + } + }, + _formValueTextBox: { + lineHeight: "20px", + overflowX: "scroll", + "&::-webkit-scrollbar": { + display: "none" + } + }, + _browseBtn: { + ...FontStyles.sansSerifBold, + color: Colors.workspaceDefault, + fontSize: "12px", + lineHeight: "20px", + height: "20px", + cursor: "pointer", + textDecoration: "underline", + display: "inline-block", + marginLeft: "10px", + position: "relative" + } +}; diff --git a/src/renderer/ui/HttpRequestControl/HttpBodyControl/index.ts b/src/renderer/ui/HttpRequestControl/HttpBodyControl/index.ts new file mode 100644 index 0000000..931dc3c --- /dev/null +++ b/src/renderer/ui/HttpRequestControl/HttpBodyControl/index.ts @@ -0,0 +1 @@ +export { HttpBodyControl } from "./HttpBodyControl"; diff --git a/src/renderer/ui/HttpRequestControl/HttpHeaderRecordControl.ts b/src/renderer/ui/HttpRequestControl/HttpHeaderRecordControl.ts new file mode 100644 index 0000000..3300918 --- /dev/null +++ b/src/renderer/ui/HttpRequestControl/HttpHeaderRecordControl.ts @@ -0,0 +1,172 @@ +import { Div, Span } from "aflon"; + +import { HttpHeaderRecord, IMacroSource } from "../../lib/http"; + +import { Colors, FontStyles } from "../StyleConstants"; +import { MacroedTextEditor } from "../TokenTextEditor"; +import { CaretPosition, FocusLeaveDirection, IKeyboardNavigable } from "../IKeyboardNavigable"; + +export class HttpHeaderRecordControl extends Div implements IMacroSource, IKeyboardNavigable { + + public eventHeaderRecordChanged = "headerRecordChanged"; + public eventFocusLeaveRequested = "focusLeaveRequested"; + + private _headerNameTextBox: MacroedTextEditor; + private _delimiter: Span; + private _headerValueTextBox: MacroedTextEditor; + + constructor() { + super(); + + this.append([ + (this._headerNameTextBox = new MacroedTextEditor()) + .on(this._headerNameTextBox.eventFocusLeaveRequested, e => this._onNameFocusLeaveRequested(e)) + .on(this._headerNameTextBox.eventKeyDown, e => this._onHeaderNameKeyDown(e)) + .on(this._headerNameTextBox.eventChange, () => this._onChange()), + (this._delimiter = new Span()) + .setText(":"), + (this._headerValueTextBox = new MacroedTextEditor()) + .setNewLineInsertionAllowed(true) + .on(this._headerValueTextBox.eventFocusLeaveRequested, e => this._onValueFocusLeaveRequested(e)) + .on(this._headerValueTextBox.eventChange, () => this._onChange()) + ]); + } + + getMacroNames(): Array { + let macros = [ ...this._headerNameTextBox.getMacroNames() ]; + + this._headerValueTextBox.getMacroNames().forEach(macro => { + if (macros.indexOf(macro) == -1) + macros.push(macro); + }); + + return macros; + } + + setHeaderRecord(header: HttpHeaderRecord): this { + this._headerNameTextBox.setText(header.name); + this._headerValueTextBox.setText(header.value); + return this; + } + + getHeaderRecord(): HttpHeaderRecord { + return { + name: this._headerNameTextBox.getText(), + value: this._headerValueTextBox.getText() + }; + } + + setReadOnly(readonly: boolean): this { + this._headerNameTextBox.setReadOnly(readonly); + this._headerValueTextBox.setReadOnly(readonly); + return this; + } + + getReadOnly(): boolean { + return this._headerNameTextBox.getReadOnly() || this._headerValueTextBox.getReadOnly(); + } + + setCaretPosition(position: CaretPosition): this { + if (position.column == -1) { + this._headerValueTextBox.setCaretPosition(position); + return this; + } + + let nameLength = this._headerNameTextBox.getText().length; + + if (position.column > nameLength) { + position.column -= nameLength; + this._headerNameTextBox.setCaretPosition(position); + return this; + } + + this._headerNameTextBox.setCaretPosition(position); + return this; + } + + getCaretPosition(): CaretPosition { + let valuePosition = this._headerValueTextBox.getCaretPosition(); + + if (valuePosition != null) { + valuePosition.column += this._headerNameTextBox.getText().length; + return valuePosition; + } + + return this._headerNameTextBox.getCaretPosition(); + } + + private _onChange(): void { + this.raise(this.eventHeaderRecordChanged); + } + + private _onNameFocusLeaveRequested(e: Event): void { + let direction = e["detail"]["direction"]; + if (direction == FocusLeaveDirection.Right) { + this._headerValueTextBox.setCaretPosition({ row: 0, column: 0 }); + } else { + this.raise(this.eventFocusLeaveRequested, { direction }); + } + } + + private _onValueFocusLeaveRequested(e: Event): void { + let direction = e["detail"]["direction"]; + + if (direction == FocusLeaveDirection.Left || direction == FocusLeaveDirection.Backspace) { + this._headerNameTextBox.setCaretPosition({ row: -1, column: -1 }); + } else { + this.raise(this.eventFocusLeaveRequested, { direction }); + } + } + + private _onHeaderNameKeyDown(e: Event): void { + let key = ( e).key; + + let name = this._headerNameTextBox.getText(); + + if (key == ":" && name[name.length - 1] == ":") { + this._headerNameTextBox.setText(name.substring(0, name.length - 1)); + this._headerValueTextBox.focus(); + } else if (name.includes(":")) { + let [ newName, value ] = name.split(":"); + + if (!newName) newName = ""; + if (!value) value = ""; + + this._headerNameTextBox.setText(newName); + this._headerValueTextBox.setText(value); + this._headerValueTextBox.setCaretPosition({ row: -1, column: -1 }); + + this.raise(this.eventHeaderRecordChanged); + } + } +} + +HttpHeaderRecordControl.style = { + _ : { + display: "flex", + flexFlow: "row nowrap" + }, + _delimiter: { + ...FontStyles.monoSpace, + color: Colors.workspaceAccent, + margin: "0 5px", + height: "20px" + }, + _headerNameTextBox: { + height: "20px", + overflow: "scroll", + flex: "0 0 auto", + "&::-webkit-scrollbar": { + display: "none" + } + }, + _headerValueTextBox: { + lineHeight: "20px", + whiteSpace: "nowrap", + overflow: "scroll", + flex: "1 1 1px", + "&::-webkit-scrollbar": { + display: "none" + } + } +}; diff --git a/src/renderer/ui/HttpRequestControl/HttpHeadersControl.ts b/src/renderer/ui/HttpRequestControl/HttpHeadersControl.ts new file mode 100644 index 0000000..62841ba --- /dev/null +++ b/src/renderer/ui/HttpRequestControl/HttpHeadersControl.ts @@ -0,0 +1,191 @@ +import { Div, typeAflonTarget } from "aflon"; + +import { HttpHeaderRecord, IMacroSource } from "../../lib/http"; + +import { ExpandableRow } from "../ExpandableTable"; +import { CaretPosition, FocusLeaveDirection, IKeyboardNavigable } from "../IKeyboardNavigable"; + +import { HttpHeaderRecordControl } from "./HttpHeaderRecordControl"; + +export class HttpHeadersControl extends ExpandableRow implements IMacroSource, IKeyboardNavigable { + public eventHeadersChanged = "headersChanged"; + public eventFocusLeaveRequested = "focusLeaveRequested"; + + private _headerControl: Div; + + private _readOnly: boolean = false; + + constructor() { + super(); + + this.setTitle("Headers"); + this.appendContent([ + (this._headerControl = new Div()) + ]); + + this._addEmptyHeaderRecordControl(); + } + + getMacroNames(): Array { + let macros: Array = []; + + this._headerControl.children().forEach(child => { + const headerRecord = (child); + headerRecord.getMacroNames().forEach(macro => { + if (macros.indexOf(macro) == -1) + macros.push(macro); + }); + }); + + return macros; + } + + setHeaders(headers: Array): this { + this._headerControl.empty(); + + headers.forEach(header => { + let headerRcrdCtrl = new HttpHeaderRecordControl(); + headerRcrdCtrl + .setHeaderRecord(header) + .setReadOnly(this._readOnly) + .on(headerRcrdCtrl.eventHeaderRecordChanged, () => this._onHeaderRecordChanged()) + .on(headerRcrdCtrl.eventFocusLeaveRequested, e => this._onFocusLeaveRequested(e)) + .on(headerRcrdCtrl.eventFocusOut, e => this._onFocusOut(e)); + this._headerControl.append([headerRcrdCtrl]); + }); + + if (!this._readOnly) + this._addEmptyHeaderRecordControl(); + return this; + } + + getHeaders(): Array { + let headers: Array = []; + this._headerControl.children().forEach(child => { + const headerRecord = (child).getHeaderRecord(); + if (headerRecord.name.length != 0 || + headerRecord.value.length != 0) + headers.push(headerRecord); + }); + return headers; + } + + setReadOnly(readOnly: boolean): this { + if (this._readOnly == readOnly) return this; + + this._readOnly = readOnly; + + this._headerControl.children().forEach(child => { + if (child instanceof HttpHeaderRecordControl) + (child).setReadOnly(readOnly); + }); + + if (this._readOnly) + this._removeEmptyHeaderRecordControl(); + else + this._removeEmptyHeaderRecordControl(); + + return this; + } + + getReadOnly(): boolean { + return this._readOnly; + } + + setCaretPosition(position: CaretPosition): this { + let children = this._headerControl.children(); + if (children.length == 0) return this; + + if (position.row == -1 || position.row > children.length - 1) + position.row = children.length - 1; + + let child = children[position.row]; + if (!(child instanceof HttpHeaderRecordControl)) return this; + + child.setCaretPosition({ row: 0, column: position.column }); + return this; + } + + getCaretPosition(): CaretPosition { + let headers = this._headerControl.children(); + + for (let i = 0; i <= headers.length - 1; i++) { + let header = headers[i]; + if (!(header instanceof HttpHeaderRecordControl)) continue; + let caretPosition = header.getCaretPosition(); + if (caretPosition == null) continue; + + return { row: i, column: caretPosition.column }; + } + + return null; + } + + private _onHeaderRecordChanged(): void { + let children = this._headerControl.children(); + if (children.length == 0) return; + let lastChild = children[children.length - 1]; + let headerRecord = lastChild.getHeaderRecord(); + if (headerRecord.name != "" || headerRecord.value != "") + this._addEmptyHeaderRecordControl(); + + this.raise(this.eventHeadersChanged); + } + + private _onFocusOut(e): void { + let sender = (
e.target.aflonElement).parent(); + if (!sender) return; + if (sender == this._headerControl.children()[this._headerControl.children().length - 1]) return; + + let headerRecord = sender.getHeaderRecord(); + + if (headerRecord.name == "" && headerRecord.value == "") + this._headerControl.removeChild(sender); + } + + private _addEmptyHeaderRecordControl(): void { + let headerRecordControl = new HttpHeaderRecordControl(); + headerRecordControl + .on(headerRecordControl.eventHeaderRecordChanged, () => this._onHeaderRecordChanged()) + .on(headerRecordControl.eventFocusLeaveRequested, e => this._onFocusLeaveRequested(e)) + .on(headerRecordControl.eventFocusOut, e => this._onFocusOut(e)); + + this._headerControl.append([ + headerRecordControl + ]); + } + + private _removeEmptyHeaderRecordControl(): void { + let children = this._headerControl.children(); + if (children.length == 0) return; + let lastChild = children[children.length - 1]; + let headerRecord = lastChild.getHeaderRecord(); + if (headerRecord.name == "" && headerRecord.value == "") + this._headerControl.removeChild(lastChild); + } + + private _onFocusLeaveRequested(e): void { + let sender = typeAflonTarget(e, HttpHeaderRecordControl); + + if (!sender) return; + + let children = this._headerControl.children(); + let numOfHeaders = children.length; + let index = children.indexOf(sender); + let direction = e["detail"]["direction"]; + + if (direction == FocusLeaveDirection.Up) { + if (index == 0) this.raise(this.eventFocusLeaveRequested, { direction }); + else ( children[index - 1]).setCaretPosition({ row: 0, column: 0 }); + } else if (direction == FocusLeaveDirection.Down) { + if (index == numOfHeaders - 1 || numOfHeaders == 0) this.raise(this.eventFocusLeaveRequested, { direction }); + else ( children[index + 1]).setCaretPosition({ row: 0, column: 0 }); + } else if (direction == FocusLeaveDirection.Left || direction == FocusLeaveDirection.Backspace) { + if (index == 0) this.raise(this.eventFocusLeaveRequested, { direction }); + else ( children[index - 1]).setCaretPosition({ row: -1, column: -1 }); + } else if (direction == FocusLeaveDirection.Right) { + if (index == numOfHeaders - 1 || numOfHeaders == 0) this.raise(this.eventFocusLeaveRequested, { direction }); + else ( children[index + 1]).setCaretPosition({ row: 0, column: 0 }); + } + } +} diff --git a/src/renderer/ui/HttpRequestControl/HttpRequestControl.ts b/src/renderer/ui/HttpRequestControl/HttpRequestControl.ts new file mode 100644 index 0000000..265f497 --- /dev/null +++ b/src/renderer/ui/HttpRequestControl/HttpRequestControl.ts @@ -0,0 +1,241 @@ +import { Div } from "aflon"; + +import { HttpRequest, HttpHeaderRecord, HttpBody, HttpRequestMethod, IReadOnlyMacroContext } from "../../lib/http"; + +import { FocusLeaveDirection } from "../IKeyboardNavigable"; + +import { UrlControl, UrlControlMode } from "./UrlControl"; +import { HttpHeadersControl } from "./HttpHeadersControl"; +import { HttpBodyControl } from "./HttpBodyControl"; + + +export class HttpRequestControl extends Div { + public eventMethodChanged = "methodChanged"; + public eventUrlChanged = "urlChanged"; + public eventHeadersChanged = "headersChanged"; + public eventBodyChanged = "bodyChanged"; + public eventSendRequested = "sendRequested"; + public eventRevertBodyToDefaultRequested = "revertBodyToDefaultRequested"; + public eventSaveCurrentBodyAsDefaultRequested = "saveCurrentBodyAsDefaultRequested"; + + private _descriptorShown: boolean; + + private _urlControl: UrlControl; + private _httpHeadersControl: HttpHeadersControl; + private _httpBodyControl: HttpBodyControl; + + constructor() { + super(); + + this.append([ + (this._urlControl = new UrlControl()) + .on(this._urlControl.eventMethodChanged, () => this._onMethodChanged()) + .on(this._urlControl.eventUrlChanged, () => this.raise(this.eventUrlChanged)) + .on(this._urlControl.eventSendRequested, () => this.raise(this.eventSendRequested)) + .on(this._urlControl.eventFocusLeaveRequested, e => this._onUrlFocusLeaveRequested(e)) + .on(this._urlControl.eventCurlCommandPasted, e => this._onCurlCommandPasted(e)), + (this._httpHeadersControl = new HttpHeadersControl()) + .on(this._httpHeadersControl.eventHeadersChanged, () => this.raise(this.eventHeadersChanged)) + .on(this._httpHeadersControl.eventFocusLeaveRequested, e => this._onHeaderFocusLeaveRequested(e)), + (this._httpBodyControl = new HttpBodyControl()) + .on(this._httpBodyControl.eventBodyChanged, () => this._onBodyChanged()) + .on(this._httpBodyControl.eventFocusLeaveRequested, e => this._onBodyFocusLeaveRequested(e)) + .on(this._httpBodyControl.eventDefaultContentTypeChangeRequested, () => this._onDefaultContentTypeChangeRequested()) + ]); + } + + setDescriptorShown(shown: boolean): this { + if (this._descriptorShown == shown) return this; + + this._descriptorShown = shown; + + this._urlControl.setDescriptorShown(this._descriptorShown); + this._httpHeadersControl.setDescriptorShown(this._descriptorShown); + this._httpBodyControl.setDescriptorShown(this._descriptorShown); + + return this; + } + + getDescriptorShown(): boolean { + return this._descriptorShown; + } + + setUrlEditMode(mode: UrlControlMode): this { + this._urlControl.setUrlControlMode(mode); + return this; + } + + getUrlEditMode(): UrlControlMode { + return this._urlControl.getUrlControlMode(); + } + + setHttpRequest(request: HttpRequest, macroContext: IReadOnlyMacroContext): this { + this.expandAll(); + + this._urlControl + .setHttpMethod(request.method) + .setMacroContext(macroContext) + .setUrl(request.url); + this._httpHeadersControl.setHeaders(request.headers); + + if (request.method != HttpRequestMethod.POST && + request.method != HttpRequestMethod.PUT && + request.method != HttpRequestMethod.PATCH && + request.method != HttpRequestMethod.DELETE && + request.method != HttpRequestMethod.OPTIONS) { + this._httpBodyControl.setInlineCss({ display: "none" }); + } else { + this._httpBodyControl.setInlineCss({ display: "flex" }); + } + + this._httpBodyControl + .setHttpBody(request.body) + .setMacroContext(macroContext); + + return this; + } + + getHttpRequest(): HttpRequest { + let request = new HttpRequest(); + request.method = this._urlControl.getHttpMethod(); + request.url = this._urlControl.getUrl(); + request.headers = this._httpHeadersControl.getHeaders(); + request.body = this.getBody(); + return request; + } + + getHttpMethod(): HttpRequestMethod { + return this._urlControl.getHttpMethod(); + } + + getUrl(): string { + return this._urlControl.getUrl(); + } + + getHeaders(): Array { + return this._httpHeadersControl.getHeaders(); + } + + getBody(): HttpBody { + if (this.getHttpMethod() == HttpRequestMethod.POST || + this.getHttpMethod() == HttpRequestMethod.PUT || + this.getHttpMethod() == HttpRequestMethod.PATCH || + this.getHttpMethod() == HttpRequestMethod.DELETE || + this.getHttpMethod() == HttpRequestMethod.OPTIONS) + return this._httpBodyControl.getHttpBody(); + + return null; + } + + expandAll(): void { + // do not expand this._urlControl; condensed url control is single-line + this._httpHeadersControl.setExpanded(true); + this._httpBodyControl.setExpanded(true); + } + + private _onUrlFocusLeaveRequested(e: Event): void { + let direction: FocusLeaveDirection = e["detail"]["direction"]; + + switch (direction) { + case FocusLeaveDirection.Down: + case FocusLeaveDirection.Right: + this._httpHeadersControl.setCaretPosition({ row: 0, column: 0 }); + break; + } + } + + private _onCurlCommandPasted(e): void { + let request = e["detail"].request; + + if (!request) return; + + this.setHttpRequest(request, this._urlControl.getMacroContext()); + + this.raise(this.eventMethodChanged); + this.raise(this.eventUrlChanged); + this.raise(this.eventHeadersChanged); + this.raise(this.eventBodyChanged); + } + + private _onHeaderFocusLeaveRequested(e: Event): void { + let direction: FocusLeaveDirection = e["detail"]["direction"]; + + switch (direction) { + case FocusLeaveDirection.Up: + this._urlControl.setCaretPosition({ row: -1, column: 0 }); + break; + case FocusLeaveDirection.Left: + this._urlControl.setCaretPosition({ row: -1, column: -1 }); + break; + case FocusLeaveDirection.Down: + case FocusLeaveDirection.Right: + this._httpBodyControl.setCaretPosition({ row: 0, column: 0 }); + break; + } + } + + private _onBodyFocusLeaveRequested(e: Event): void { + let direction: FocusLeaveDirection = e["detail"]["direction"]; + + switch (direction) { + case FocusLeaveDirection.Up: + this._httpHeadersControl.setCaretPosition({ row: -1, column: 0 }); + break; + case FocusLeaveDirection.Left: + this._httpHeadersControl.setCaretPosition({ row: -1, column: -1 }); + break; + } + } + + private _onMethodChanged(): void { + const method = this._urlControl.getHttpMethod(); + + if (method == HttpRequestMethod.NONE) return; + + if (method == HttpRequestMethod.POST || + method == HttpRequestMethod.PUT || + method == HttpRequestMethod.PATCH || + method == HttpRequestMethod.DELETE || + method == HttpRequestMethod.OPTIONS) { + this._httpBodyControl.setInlineCss({ display: "flex" }); + } else { + this._httpBodyControl.setInlineCss({ display: "none" }); + } + + this.raise(this.eventMethodChanged); + } + + private _onBodyChanged(): void { + this.raise(this.eventBodyChanged); + } + + private _onDefaultContentTypeChangeRequested(): void { + let headers = this._httpHeadersControl.getHeaders(); + + let contentTypeHeader = headers.find(header => header.name.trim().toLowerCase() == "content-type"); + + if (!contentTypeHeader) { + headers = [ { name: "Content-Type", value: this._httpBodyControl.getDefaultContentType() }, ...headers ]; + } else { + contentTypeHeader.value = this._httpBodyControl.getDefaultContentType(); + } + + this._httpHeadersControl.setHeaders(headers); + } +} + +HttpRequestControl.style = { + _: { + display: "flex", + flexFlow: "column nowrap" + }, + _urlControl: { + marginBottom: "20px" + }, + _httpHeadersControl: { + marginBottom: "20px" + }, + _httpBodyControl: { + marginBottom: "20px" + } +}; diff --git a/src/renderer/ui/HttpRequestControl/UrlControl.ts b/src/renderer/ui/HttpRequestControl/UrlControl.ts new file mode 100644 index 0000000..24d1c5b --- /dev/null +++ b/src/renderer/ui/HttpRequestControl/UrlControl.ts @@ -0,0 +1,376 @@ +import { Div } from "aflon"; + +import { HttpRequestMethod, IMacroSource, IReadOnlyMacroContext } from "../../lib/http"; + +import { CaretPosition, FocusLeaveDirection, IKeyboardNavigable } from "../IKeyboardNavigable"; +import { Colors, FontStyles } from "../StyleConstants"; +import { TokenTextEditor, Line, RegularToken } from "../TokenTextEditor"; +import { ExpandableRow } from "../ExpandableTable"; +import { ContextMenu, ContextMenuItemType, ContextMenuShowTrigger } from "../ContextMenu"; +import { Icon } from "../Icon"; + +import { UrlTextEditor, UrlTextEditorMode } from "./UrlTextEditor"; + +class HttpMethodTextEditor extends TokenTextEditor { + private _currentlySelectedMethod: HttpRequestMethod = HttpRequestMethod.NONE; + + constructor() { + super(); + + this.setSpaceInsertionAllowed(false) + .setTextFilter(input => input.toUpperCase().substr(0, 7).trim()); + } + + setHttpMethod(method: HttpRequestMethod): this { + this.setText(method); + return this; + } + + getHttpMethod(): HttpRequestMethod { + return this._currentlySelectedMethod; + } + + protected _tokenize(newText: string): Array { + let newTextCap = newText; + let methodColor = Colors.workspaceDefault; + + if (newTextCap == HttpRequestMethod.GET) { + methodColor = Colors.methodGet; + this._currentlySelectedMethod = HttpRequestMethod.GET; + } else if (newTextCap == HttpRequestMethod.POST) { + methodColor = Colors.methodPost; + this._currentlySelectedMethod = HttpRequestMethod.POST; + } else if (newTextCap == HttpRequestMethod.PUT) { + methodColor = Colors.methodPut; + this._currentlySelectedMethod = HttpRequestMethod.PUT; + } else if (newTextCap == HttpRequestMethod.DELETE) { + methodColor = Colors.methodDelete; + this._currentlySelectedMethod = HttpRequestMethod.DELETE; + } else { + methodColor = Colors.workspaceDefault; + if (newTextCap in HttpRequestMethod) { + this._currentlySelectedMethod = HttpRequestMethod[newTextCap]; + } else { + this._currentlySelectedMethod = HttpRequestMethod.NONE; + } + } + + return [ new Line().setTokens([ new RegularToken(newTextCap).setInlineCss({ color: methodColor }) ]) ]; + } +} + +HttpMethodTextEditor.style = { + _: { + ...FontStyles.monoSpace, + color: Colors.workspaceDefault, + fontSize: "15px", + height: "20px", + display: "inline-block", + minWidth: 0, + maxWidth: "60px", + "&:focus": { + border: "none", + outline: "none" + } + } +}; + +export enum UrlControlMode { + SingleLine, MultiLine, MultiLineTable +} + +export class UrlControl extends ExpandableRow implements IMacroSource, IKeyboardNavigable { + public eventMethodChanged = "methodChanged"; + public eventUrlChanged = "urlChanged"; + public eventSendRequested = "sendRequested"; + public eventFocusLeaveRequested = "focusLeaveRequested"; + public eventCurlCommandPasted = "curlCommandPasted"; + + private _urlControlMode: UrlControlMode = UrlControlMode.MultiLineTable; + + private _urlEditorContainer: Div; + private _descriptorTable: Div; + private _editorContainer: Div; + private _methodTextEditor: HttpMethodTextEditor; + private _methodDropDownButton: Icon; + private _methodDropDownMenu: ContextMenu; + private _urlTextEditor: UrlTextEditor; + + private readonly _descTblMethod: Div = new Div().setText("Method"); + private readonly _descTblProtocol: Div = new Div().setText("Protocol"); + private readonly _descTblHost: Div = new Div().setText("Host"); + private readonly _descTblPath: Div = new Div().setText("Path"); + private readonly _descTblQuery: Div = new Div().setText("Query"); + private readonly _descTblHash: Div = new Div().setText("Hash"); + + constructor() { + super(); + + this.setTitle("URL"); + this.appendContent([ + (this._urlEditorContainer = new Div()) + .addCssClass({ + display: "flex", + flexFlow: "row nowrap" + }) + .append([ + (this._descriptorTable = new Div()) + .addCssClass({ + ...FontStyles.sansSerifBold, + color: Colors.workspaceDescriptor, + width: "45px", + fontSize: "11px", + textAlign: "right", + lineHeight: "20px", + marginRight: "10px" + }), + (this._editorContainer = new Div()) + .addCssClass({ + flex: "1 0 200px", + display: "flex", + flexFlow: "column nowrap", + minWidth: 0 + }) + .append([ + new Div().append([ + (this._methodTextEditor = new HttpMethodTextEditor()) + .setHttpMethod(HttpRequestMethod.GET) + .on(this._methodTextEditor.eventFocusLeaveRequested, e => this._onMethodFocusLostRequested(e)) + .on(this._methodTextEditor.eventInput, () => this._onMethodChange()), + (this._methodDropDownButton = new Icon("more")) + .setVisibility(false) + .addCssClass({ + cursor: "pointer", + fontSize: "12px", + width: "15px", + color: Colors.workspaceDefault, + transform: "rotate(90deg)", + textAlign: "center", + position: "relative" + }) + ]) + .addCssClass({ + marginRight: "10px", + display: "flex", + flexFlow: "row nowrap" + }), + (this._urlTextEditor = new UrlTextEditor()) + .setText("") + .setEditorMode(UrlTextEditorMode.MultiLine) + .setPlaceholder("Enter URL") + .on(this._urlTextEditor.eventInput, () => this._onUrlInput()) + .on(this._urlTextEditor.eventChange, () => this._onUrlChange()) + .on(this._urlTextEditor.eventSendRequested, () => this._onSendRequested()) + .on(this._urlTextEditor.eventFocusLeaveRequested, e => this._onUrlFocusLeaveRequested(e)) + .on(this._urlTextEditor.eventCurlCommandPasted, e => this.raise(this.eventCurlCommandPasted, e["detail"])) + .addCssClass({ + flex: "1 1 1px", + overflowX: "scroll", + "&::-webkit-scrollbar": { + display: "none" + } + }) + ]) + ]) + ]); + + this.on(this.eventMouseEnter, () => this._onMouseEnter()) + .on(this.eventMouseLeave, () => this._onMouseLeave()); + + (this._methodDropDownMenu = new ContextMenu(this._methodDropDownButton, [ + { text: "GET", id: "GET", type: ContextMenuItemType.Button }, + { text: "POST", id: "POST", type: ContextMenuItemType.Button }, + { text: "PUT", id: "PUT", type: ContextMenuItemType.Button }, + { text: "DELETE", id: "DELETE", type: ContextMenuItemType.Button }, + { text: "CONNECT", id: "CONNECT", type: ContextMenuItemType.Button }, + { text: "HEAD", id: "HEAD", type: ContextMenuItemType.Button }, + { text: "OPTIONS", id: "OPTIONS", type: ContextMenuItemType.Button }, + { text: "PATCH", id: "PATCH", type: ContextMenuItemType.Button }, + { text: "TRACE", id: "TRACE", type: ContextMenuItemType.Button } + ], ContextMenuShowTrigger.OnClickEvent)) + .on(this._methodDropDownMenu.eventSelected, (e) => this._onMethodDropDownMethodMenuSelected(e)); + } + + setUrl(url: string): this { + this._urlTextEditor.setText(url); + return this; + } + + getUrl(): string { + return this._urlTextEditor.getText(); + } + + setHttpMethod(method: HttpRequestMethod): this { + this._methodTextEditor.setHttpMethod(method); + return this; + } + + getHttpMethod(): HttpRequestMethod { + return this._methodTextEditor.getHttpMethod(); + } + + setMacroContext(macroContext: IReadOnlyMacroContext): this { + this._urlTextEditor.setMacroContext(macroContext); + return this; + } + + getMacroContext(): IReadOnlyMacroContext { + return this._urlTextEditor.getMacroContext(); + } + + getMacroNames(): Array { + return this._urlTextEditor.getMacroNames(); + } + + setUrlControlMode(mode: UrlControlMode): this { + if (this._urlControlMode == mode) return this; + + this._urlControlMode = mode; + + if (this._urlControlMode == UrlControlMode.SingleLine) { + this._urlTextEditor.setEditorMode(UrlTextEditorMode.SingleLine); + this._descriptor.setExpanded(false); + this._methodDropDownButton.setVisibility(true); + this._descriptorTable.setInlineCss({ display: "none" }); + this._editorContainer.setInlineCss({ flexDirection: "row" }); + } else if (this._urlControlMode == UrlControlMode.MultiLine) { + this._urlTextEditor.setEditorMode(UrlTextEditorMode.MultiLine); + this._descriptor.setExpanded(true); + this._methodDropDownButton.setVisibility(false); + this._descriptorTable.setInlineCss({ display: "none" }); + this._editorContainer.setInlineCss({ flexDirection: "column" }); + } else { + this._urlTextEditor.setEditorMode(UrlTextEditorMode.MultiLine); + this._descriptor.setExpanded(true); + this._methodDropDownButton.setVisibility(false); + this._descriptorTable.setInlineCss({ display: "block" }); + this._editorContainer.setInlineCss({ flexDirection: "column" }); + } + + return this; + } + + getUrlControlMode(): UrlControlMode { + return this._urlControlMode; + } + + setCaretPosition(position: CaretPosition): this { + if (position.row == 0) { + this._methodTextEditor.setCaretPosition(position); + return this; + } + + if (position.row != -1) + position.row = position.row - 1; + + this._urlTextEditor.setCaretPosition(position); + return this; + } + + getCaretPosition(): CaretPosition { + let urlCaretPosition = this._urlTextEditor.getCaretPosition(); + + if (urlCaretPosition != null) { + urlCaretPosition.row = urlCaretPosition.row + 1; + return urlCaretPosition; + } + + return this._methodTextEditor.getCaretPosition(); + } + + protected _executeExpand(expanded: boolean): void { + if (!expanded) { + this.setUrlControlMode(UrlControlMode.SingleLine); + } else { + this.setUrlControlMode(UrlControlMode.MultiLineTable); + } + this._methodDropDownButton.setVisibility(true); + } + + private _onUrlInput(): void { + const newUrl = this._urlTextEditor.getUrl(); + const newUrlNumberOfQueryLines = this._urlTextEditor.getNumberOfQueryLines(); + + this._descriptorTable.empty(); + this._descriptorTable.append([ this._descTblMethod ]); + + if (newUrl.protocol) + this._descriptorTable.append([ this._descTblProtocol ]); + + this._descriptorTable.append([ this._descTblHost ]); + + if (newUrl.path) + this._descriptorTable.append([ this._descTblPath ]); + + if (newUrl.query) { + this._descriptorTable.append([ this._descTblQuery ]); + + for (let i = 0; i <= newUrlNumberOfQueryLines - 2; i++) + this._descriptorTable.append([ new Div().setInlineCss({ height: "20px" }) ]); + } + + if (newUrl.hash) + this._descriptorTable.append([ this._descTblHash ]); + } + + private _onMethodChange(): void { + this.raise(this.eventMethodChanged); + } + + private _onUrlChange(): void { + this.raise(this.eventUrlChanged); + } + + private _onSendRequested(): void { + this.raise(this.eventUrlChanged); + this.raise(this.eventSendRequested); + } + + private _onMethodDropDownMethodMenuSelected(e): void { + this._methodTextEditor.setText(e["detail"]["id"]); + } + + private _onMouseEnter(): void { + if (!this.getExpanded()) return; + this._methodDropDownButton.setVisibility(true); + } + + private _onMouseLeave(): void { + if (!this.getExpanded()) return; + this._methodDropDownButton.setVisibility(false); + } + + private _onUrlFocusLeaveRequested(e: Event): void { + let direction = e["detail"]["direction"]; + + switch (direction) { + case FocusLeaveDirection.Up: + this._methodTextEditor.setCaretPosition({ row: 0, column: 0 }); + break; + case FocusLeaveDirection.Backspace: + case FocusLeaveDirection.Left: + this._methodTextEditor.setCaretPosition({ row: -1, column: -1 }); + break; + case FocusLeaveDirection.Down: + case FocusLeaveDirection.Right: + this.raise(this.eventFocusLeaveRequested, { direction }); + break; + } + } + + private _onMethodFocusLostRequested(e: Event): void { + let direction = e["detail"]["direction"]; + + switch (direction) { + case FocusLeaveDirection.Up: + case FocusLeaveDirection.Backspace: + case FocusLeaveDirection.Left: + this.raise(this.eventFocusLeaveRequested, { direction }); + break; + case FocusLeaveDirection.Down: + case FocusLeaveDirection.Right: + this._urlTextEditor.setCaretPosition({ row: 0, column: 0 }); + break; + } + } +} diff --git a/src/renderer/ui/HttpRequestControl/UrlTextEditor.ts b/src/renderer/ui/HttpRequestControl/UrlTextEditor.ts new file mode 100644 index 0000000..64df60e --- /dev/null +++ b/src/renderer/ui/HttpRequestControl/UrlTextEditor.ts @@ -0,0 +1,243 @@ +import { HttpUrl, IReadOnlyMacroContext, MacroedText, MacroedTextPartType } from "../../lib/http"; +import { SimpleEvent } from "../../lib/SimpleEvent"; + +import { TokenTextEditor, Line, AccentToken, RegularToken, ParameterToken, Token } from "../TokenTextEditor"; +import { CaretPosition } from "../IKeyboardNavigable"; +import { CurlCommandConverter } from "../../lib/interop/requests/CurlCommandConverter"; + +export enum UrlTextEditorMode { SingleLine, MultiLine } + +export class UrlTextEditor extends TokenTextEditor { + public eventSendRequested: string = "sendRequested"; + public eventCurlCommandPasted: string = "curlCommandPasted"; + + private _numberOfQueryLines: number = 0; + private _mode: UrlTextEditorMode = UrlTextEditorMode.MultiLine; + private _currentUrl: HttpUrl; + + private _macros: Array = []; + private _macroContext: IReadOnlyMacroContext = null; + + constructor() { + super(); + this.setSpaceInsertionAllowed(false) + .on(this.eventKeyDown, e => this._onEnterDown(e)); + } + + setEditorMode(mode: UrlTextEditorMode): this { + if (this._mode == mode) return this; + + this._mode = mode; + this._redraw(); + return this; + } + + getEditorMode(): UrlTextEditorMode { + return this._mode; + } + + getUrl(): HttpUrl { + return HttpUrl.parse(this.getText()); + } + + getNumberOfQueryLines(): number { + return this._numberOfQueryLines; + } + + getMacroNames(): Array { + return this._macros; + } + + setMacroContext(macroContext: IReadOnlyMacroContext): this { + if (this._macroContext != null) + this._macroContext.off(this._macroContext.eventMacroValueChanged, this._onMacroChanged); + this._macroContext = macroContext; + this._macroContext.on(this._macroContext.eventMacroValueChanged, this._onMacroChanged); + this._redraw(); + return this; + } + + getMacroContext(): IReadOnlyMacroContext { + return this._macroContext; + } + + protected _tokenize(newText: string): Array { + this._macros = []; + + let lines: Array = []; + let tokens: Array = []; + + const append = (newTokens: Array, transparent: boolean = false): void => { + if (this._mode == UrlTextEditorMode.SingleLine) { + if (transparent) { + newTokens.forEach(token => token.setInlineCss({ + opacity: "0.5" + })); + } + tokens = tokens.concat(newTokens); + } else { + let line = new Line().setTokens(newTokens); + if (transparent) { + line.setInlineCss({ + opacity: "0.5" + }); + } + lines.push(line); + } + }; + + try { + const url = HttpUrl.parse(newText); + this._currentUrl = url; + + if (url.protocol) { + if (url.protocol.endsWith("/")) { + append([ ...this._tokenizeMacroedText(url.protocol.substring(0, url.protocol.length - 2))[0], new AccentToken("//") ]); + } else { + append(this._tokenizeMacroedText(url.protocol)[0]); + } + } + + if (url.hostname) { + append(this._tokenizeMacroedText(url.hostname)[0]); + } + + if (url.path) { + append([ new AccentToken("/"), ...this._tokenizeMacroedText(url.path.substring(1))[0] ]); + } + + if (url.query) { + const strippedQuery = url.query.substring(1).split("&"); + + this._numberOfQueryLines = strippedQuery.length; + + strippedQuery.map((keyValuePair, index) => { + let toks = this._tokenizeMacroedText(keyValuePair); + + if (index == 0) + append([ new AccentToken("?"), ...(toks[0]) ], toks[1]); + else + append([ new AccentToken("&"), ...(toks[0]) ], toks[1]); + }); + } + + if (url.hash) { + append([ new AccentToken("#"), ...this._tokenizeMacroedText(url.hash.substring(1))[0] ]); + } + + } catch (ex) { + this._currentUrl = new HttpUrl(); + this._currentUrl.hostname = newText; + return [ new Line().setTokens(this._tokenizeMacroedText(newText)[0]) ]; + } + + if (this._mode == UrlTextEditorMode.MultiLine) { + return lines; + } else { + lines = [ new Line().setTokens(tokens) ]; + return lines; + } + } + + protected _toStringCaretPosition(lines: Array, position: CaretPosition): number { + if (lines.length == 0) + return 0; + + const focusLine = lines[position.row]; + + if (position.column >= focusLine.length && focusLine.index != lines.length - 1) + return lines[position.row + 1].offset; + + return lines[position.row].offset + position.column; + } + + protected _toTokenCaretPosition(lines: Array, position: number): CaretPosition { + if (position <= 0 || lines.length == 0) return { + row: 0, column: 0 + }; + + let focusLine: Line = null; + + for (let i = 0; i <= lines.length - 1; i++) { + const currentLine = lines[i]; + if (currentLine.offset <= position && position <= currentLine.length + currentLine.offset) { + focusLine = currentLine; + break; + } + } + + if (focusLine == null) + return { + row: lines.length - 1, + column: lines[lines.length - 1].length + }; + + return { + row: focusLine.index, + column: position - focusLine.offset + }; + } + + protected _onLeavingDom(): void { + if (this._macroContext == null) return; + this._macroContext.off(this._macroContext.eventMacroValueChanged, this._onMacroChanged); + } + + protected _onAboutPaste(pasteText: string): boolean { + let request = CurlCommandConverter.convert(pasteText); + + if (request != null) { + this.raise(this.eventCurlCommandPasted, { request }); + return false; + } + + return true; + } + + private _onMacroChanged = (e: SimpleEvent): void => { + let macroName = (e["detail"]["macroName"]); + if (this._macros.indexOf(macroName) != -1) { + this._redraw(); + } + }; + + private _onEnterDown(e): void { + if ((e).key != "Enter") return; + this.raise(this.eventSendRequested); + } + + private _tokenizeMacroedText(text: string): [Array, boolean] { + let result = MacroedText.parse(text, true); + + let eqPassed = false; + let isEqInTheEnd = false; + result.getMacroNames().forEach(name => { + if (this._macros.indexOf(name) == -1) { + this._macros.push(name); + } + }); + + let tokens = result.getParts().map((part) => { + if (part.type == MacroedTextPartType.Parameter) { + let macroName = ""; + + if (part.text.length >= 3) + macroName = part.text.substring(2, part.text.length - 1); + + if (eqPassed && isEqInTheEnd && this._macroContext != null) { + isEqInTheEnd = this._macroContext.isMacroEmpty(macroName); + } + return new ParameterToken(part.text); + } else if (part.type == MacroedTextPartType.PlainText) { + if (eqPassed) isEqInTheEnd = false; + return new RegularToken(part.text); + } else if (part.type == MacroedTextPartType.EqualityChar) { + eqPassed = true; + isEqInTheEnd = true; + return new AccentToken(part.text); + } else + throw new Error(`MacroedTextPartType ${part.type} is not supported.`); + }); + return [tokens, isEqInTheEnd]; + } +} diff --git a/src/renderer/ui/HttpRequestControl/index.ts b/src/renderer/ui/HttpRequestControl/index.ts new file mode 100644 index 0000000..14009d7 --- /dev/null +++ b/src/renderer/ui/HttpRequestControl/index.ts @@ -0,0 +1,2 @@ +export { HttpRequestControl } from "./HttpRequestControl"; +export { UrlTextEditor } from "./UrlTextEditor"; diff --git a/src/renderer/ui/IKeyboardNavigable.ts b/src/renderer/ui/IKeyboardNavigable.ts new file mode 100644 index 0000000..6598655 --- /dev/null +++ b/src/renderer/ui/IKeyboardNavigable.ts @@ -0,0 +1,15 @@ +export enum FocusLeaveDirection { + Up = "Up", Down = "Down", Left = "Left", Right = "Right", Backspace = "Backspace" +} + +export class CaretPosition { + row: number = -1; + column: number = -1; +} + +export interface IKeyboardNavigable { + eventFocusLeaveRequested: string; + + setCaretPosition(position: CaretPosition): this; + getCaretPosition(): CaretPosition; +} diff --git a/src/renderer/ui/Icon.ts b/src/renderer/ui/Icon.ts new file mode 100644 index 0000000..060f1d9 --- /dev/null +++ b/src/renderer/ui/Icon.ts @@ -0,0 +1,21 @@ +import { Span } from "aflon"; + +export class Icon extends Span { + private _name: string; + + constructor(iconName: string) { + super(); + this.setName(iconName); + } + + getName(): string { + return this._name; + } + + setName(iconName: string): this { + this.removeClass(`ri-${this._name}`); + this._name = iconName; + this.addClass(`ri-${this._name}`); + return this; + } +} diff --git a/src/renderer/ui/IconButton.ts b/src/renderer/ui/IconButton.ts new file mode 100644 index 0000000..c8a2fc2 --- /dev/null +++ b/src/renderer/ui/IconButton.ts @@ -0,0 +1,88 @@ +import { AbstractButton, Div } from "aflon"; + +import { Colors } from "./StyleConstants"; +import { Icon } from "./Icon"; +import { Tooltip } from "./ContextMenu"; + +export class IconButton extends Div implements AbstractButton { + eventChange: string; + eventInput: string; + + private _tooltip: Tooltip = null; + + constructor(iconName: string) { + super(); + + this.append([ + new Icon(iconName) + ]) + .on(this.eventClick, e => this._onClick(e)); + + if (iconName == "new-req") + this.setInlineCss({ fontSize: "12px" }); + } + + setDisabled(disabled: boolean): this { + if (disabled) { + this.addAttr("disabled"); + this.setInlineCss({ + cursor: "default", + color: Colors.workspacePlaceholder + }); + } else { + this.removeAttr("disabled"); + this.setInlineCss({ + cursor: "pointer", + color: "inherit" + }); + } + + return this; + } + + getDisabled(): boolean { + return this.hasAttr("disabled"); + } + + focus(): void { + // do nothing, this button is not focusable + return; + } + + blur(): void { + this.getHtmlElement().blur(); + } + + setTooltip(text: string): this { + if (this._tooltip == null) + this._tooltip = new Tooltip(this); + + this._tooltip.setText(text); + return this; + } + + getTooltip(): string { + return this._tooltip.getText(); + } + + private _onClick(e: Event): void { + if (!this.getDisabled()) return; + e.stopImmediatePropagation(); + } +} + +IconButton.style = { + _: { + width: "21px", + height: "25px", + lineHeight: "25px", + textAlign: "center", + paddingTop: "1px", + color: Colors.browserDefault, + fontSize: "13px", + cursor: "pointer", + "&:hover": { + color: Colors.workspaceDescriptor + } + } +}; diff --git a/src/renderer/ui/MarcoPresetsControl/MacroNameTextBox.ts b/src/renderer/ui/MarcoPresetsControl/MacroNameTextBox.ts new file mode 100644 index 0000000..580e30b --- /dev/null +++ b/src/renderer/ui/MarcoPresetsControl/MacroNameTextBox.ts @@ -0,0 +1,40 @@ +import { CSS } from "aflon"; + +import { Colors } from "../StyleConstants"; +import { Line, ParameterToken, TokenTextEditor } from "../TokenTextEditor"; + + +export class MacroNameTextBox extends TokenTextEditor { + + constructor() { + super(); + + this.setPlaceholder("Enter parameter name"); + } + + protected _tokenize(text: string): Array { + return [ new Line().setTokens([ new ParameterToken(text) ]) ]; + } +} + +MacroNameTextBox.style = CSS.extendAflonCss(TokenTextEditor.style, { + _: { + width: "100%", + height: "30px", + lineHeight: "29px", + color: Colors.workspaceParameter, + paddingLeft: "8px", + overflowX: "scroll", + borderBottom: `1px solid ${Colors.workspaceLineWeak}`, + borderLeft: `1px solid ${Colors.workspaceLineWeak}`, + "&:focus": { + border: "none", + outline: "none", + borderBottom: `1px solid ${Colors.workspaceLineWeak}`, + borderLeft: `1px solid ${Colors.workspaceLineWeak}` + }, + "&::-webkit-scrollbar": { + display: "none" + } + } +}); diff --git a/src/renderer/ui/MarcoPresetsControl/MacroNamesControl.ts b/src/renderer/ui/MarcoPresetsControl/MacroNamesControl.ts new file mode 100644 index 0000000..da9a9fd --- /dev/null +++ b/src/renderer/ui/MarcoPresetsControl/MacroNamesControl.ts @@ -0,0 +1,101 @@ +import { Div, typeAflonTarget } from "aflon"; + +import { TokenTextEditor } from "../TokenTextEditor"; +import { Colors } from "../StyleConstants"; +import { SimpleModals } from "../Modals"; + +import { MacroNameTextBox } from "./MacroNameTextBox"; + +export class MacroNamesControl extends Div { + public eventChange = "change"; + + private _header: Div; + private _names: Div; + + constructor() { + super(); + + this.append([ + (this._header = new Div()), + (this._names = new Div()) + .append([ this._createNewNameTokenTextEditor("") ]) + ]); + } + + setNames(names: Array): this { + this._names.empty(); + + this._names.append( + names.map(name => this._createNewNameTokenTextEditor(name)) + ); + + this._names.append([ this._createNewNameTokenTextEditor("") ]); + + return this; + } + + getNames(): Array { + let result = this._names.children() + .filter(child => child instanceof MacroNameTextBox) + .map(child => child.getText()); + + result.pop(); + + return result; + } + + private _createNewNameTokenTextEditor(name: string): TokenTextEditor { + let editor = new MacroNameTextBox(); + + editor + .setText(name) + .on(editor.eventInput, () => this._onEditorInput()) + .on(editor.eventFocusOut, e => this._onFocusOut(e)); + + return editor; + } + + private _onEditorInput(): void { + let children = this._names.children(); + if (children.length != 0) { + let lastChild = children[children.length - 1]; + if (lastChild.getText().length != 0) + this._names.append([ this._createNewNameTokenTextEditor("") ]); + } + this.raise(this.eventChange); + } + + private async _onFocusOut(e: Event): Promise { + let sender = typeAflonTarget(e, TokenTextEditor); + + if (!sender) return; + + if (sender == this._names.children()[this._names.children().length - 1]) return; + + if (this.getNames().filter(name => name == sender.getText()).length > 1) { + await SimpleModals.alert(`Parameter ${sender.getText()} is already configured.`); + setTimeout(() => sender.focus(), 0); + return; + } + + if (sender.getText().length != 0) return; + + this._names.removeChild(sender); + this.raise(this.eventChange); + } +} + +MacroNamesControl.style = { + _: { + display: "flex", + flexFlow: "column nowrap" + }, + _header: { + height: "30px", + borderBottom: `1px solid ${Colors.workspaceLineWeak}` + }, + _names: { + display: "flex", + flexFlow: "column nowrap" + } +}; diff --git a/src/renderer/ui/MarcoPresetsControl/MacroPresetControl.ts b/src/renderer/ui/MarcoPresetsControl/MacroPresetControl.ts new file mode 100644 index 0000000..521c064 --- /dev/null +++ b/src/renderer/ui/MarcoPresetsControl/MacroPresetControl.ts @@ -0,0 +1,176 @@ +import { Div, Container, TextBox } from "aflon"; + +import { MacroPreset, MacroRecord } from "../../lib/http"; + +import { SimpleModals } from "../Modals"; +import { Colors, FontStyles } from "../StyleConstants"; +import { IconButton } from "../IconButton"; +import { ContextMenu, ContextMenuItemType, ContextMenuShowTrigger } from "../ContextMenu"; + +import { MacroPresetValueTextBox } from "./MacroPresetValueTextBox"; +import { SimpleEvent } from "../../lib/SimpleEvent"; + +export class MacroPresetControl extends Div { + public eventChange = "change"; + + private _header: Div; + private _nameTxtBx: TextBox; + private _optionsBtn: IconButton; + private _deleteBtn: IconButton; + private _values: Container; + + private _contextMenu: ContextMenu; + + constructor() { + super(); + + this.append([ + (this._header = new Div()) + .append([ + (this._nameTxtBx = new TextBox()) + .setDisabled(true) + .on(this._nameTxtBx.eventBlur, () => this._onNameTxtBxBlur()) + .on(this._nameTxtBx.eventKeyDown, e => this._nameTxtBxKeyDown(e)), + (this._optionsBtn = new IconButton("options")) + ]), + (this._values = new Container()) + ]); + + (this._contextMenu = new ContextMenu(this._optionsBtn, [ + { id: "rename", text: "Rename", type: ContextMenuItemType.Button }, + { id: "delete", text: "Delete", type: ContextMenuItemType.Button, iconName: "delete" } + ], ContextMenuShowTrigger.OnClickEvent)) + .on(this._contextMenu.eventSelected, e => this._onContextMenuSelected(e)); + } + + updateNames(names: Array): this { + let presetControls: Record = {}; + + this._values.children().forEach(child => presetControls[child.getMacroRecord().name] = child); + + this._values.empty(); + + this._values.append(names.map(name => { + if (presetControls[name]) + return presetControls[name]; + else + return this._createNewMacroPresetValueTextEditor({ name, value: "" }); + })); + + return this; + } + + setMacroPreset(preset: MacroPreset): this { + this._nameTxtBx.setText(preset.name); + this._values.empty(); + this._values.append(preset.macros.map(macro => this._createNewMacroPresetValueTextEditor(macro))); + return this; + } + + getMacroPreset(): MacroPreset { + return { + name: this._nameTxtBx.getText(), + macros: this._values.children().map(value => value.getMacroRecord()) + }; + } + + enterRenameMode(): this { + this._nameTxtBx.setDisabled(false); + setTimeout(() => { + ( this._nameTxtBx.getHtmlElement()).setSelectionRange(0, + this._nameTxtBx.getText().length); + this._nameTxtBx.focus(); + }, 0); + + return this; + } + + private _nameTxtBxKeyDown(e: Event): void { + let keyEvent = e; + + if (keyEvent.key != "Enter") return; + + this._nameTxtBx.blur(); + } + + private async _onContextMenuSelected(e: SimpleEvent): Promise { + let selectedId = e.detail["id"]; + + if (!selectedId) return; + + if (selectedId == "delete") { + let result = await SimpleModals.confirm(`Are you sure you want to delete preset ${this._nameTxtBx.getText()}?`); + + if (result) + this.parent().removeChild(this); + } else if (selectedId == "rename") { + this.enterRenameMode(); + } + } + + private _createNewMacroPresetValueTextEditor(macro: MacroRecord): MacroPresetValueTextBox { + let editor = new MacroPresetValueTextBox(); + + editor + .setMacroRecord(macro) + .on(editor.eventChange, () => this._onEditorChange()); + + return editor; + } + + private _onEditorChange(): void { + this.raise(this.eventChange); + } + + private _onNameTxtBxBlur(): void { + setTimeout(() => this._nameTxtBx.setDisabled(true), 0); + } +} + +MacroPresetControl.style = { + _: { + display: "flex", + flexFlow: "column nowrap", + flex: "1 0 200px" + }, + _header: { + display: "flex", + flexFlow: "row nowrap", + alignItems: "center", + height: "30px", + paddingLeft: "5px", + paddingRight: "3px", + borderBottom: `1px solid ${Colors.workspaceLineWeak}`, + borderLeft: `1px solid ${Colors.workspaceLineWeak}` + }, + _nameTxtBx: { + ...FontStyles.sansSerifBold, + color: Colors.workspaceDescriptor, + outline: "none", + border: "none", + paddingLeft: "5px", + marginRight: "3px", + height: "20px", + flex: "1 1 1px", + padding: "0", + borderRadius: "3px", + fontSize: "11px", + background: "none", + "&:focus": { + outline: "none" + }, + "&:disabled": { + userSelect: "none", + pointerEvents: "none" + } + }, + _optionsBtn: { + fontSize: "15px", + color: Colors.workspacePlaceholder + }, + _values: { + display: "flex", + flexFlow: "column nowrap", + width: "100%" + } +}; diff --git a/src/renderer/ui/MarcoPresetsControl/MacroPresetValueTextBox.ts b/src/renderer/ui/MarcoPresetsControl/MacroPresetValueTextBox.ts new file mode 100644 index 0000000..7b9cbde --- /dev/null +++ b/src/renderer/ui/MarcoPresetsControl/MacroPresetValueTextBox.ts @@ -0,0 +1,53 @@ +import { CSS } from "aflon"; + +import { MacroRecord } from "../../lib/http"; + +import { Colors } from "../StyleConstants"; +import { TokenTextEditor } from "../TokenTextEditor"; + + +export class MacroPresetValueTextBox extends TokenTextEditor { + private _macroName: string; + + constructor() { + super(); + + this.setPlaceholder("No value"); + } + + setMacroRecord(record: MacroRecord): this { + this._macroName = record.name; + this.setText(record.value); + return this; + } + + getMacroRecord(): MacroRecord { + return { + name: this._macroName, value: this.getText() + }; + } +} + +MacroPresetValueTextBox.style = CSS.extendAflonCss(TokenTextEditor.style, { + _: { + width: "100%", + height: "30px", + lineHeight: "29px", + overflowX: "scroll", + paddingLeft: "8px", + borderBottom: `1px solid ${Colors.workspaceLineWeak}`, + borderLeft: `1px solid ${Colors.workspaceLineWeak}`, + "&:focus": { + ...TokenTextEditor.style["&:focus"], + borderBottom: `1px solid ${Colors.workspaceLineWeak}`, + borderLeft: `1px solid ${Colors.workspaceLineWeak}`, + outline: "none" + }, + "&::-webkit-scrollbar": { + display: "none" + } + }, + _placeholderSpan: { + color: Colors.tooltipBackgroundDefault + } +}); diff --git a/src/renderer/ui/MarcoPresetsControl/MacroPresetsControl.ts b/src/renderer/ui/MarcoPresetsControl/MacroPresetsControl.ts new file mode 100644 index 0000000..3b2e9a7 --- /dev/null +++ b/src/renderer/ui/MarcoPresetsControl/MacroPresetsControl.ts @@ -0,0 +1,176 @@ +import { Div, Container } from "aflon"; + +import { IMacroContext, MacroPreset } from "../../lib/http"; + +import { Modal, ModalContent } from "../Modals"; +import { Colors, ZIndexLayers } from "../StyleConstants"; +import { Button } from "../BasicControls"; + +import { MacroNamesControl } from "./MacroNamesControl"; +import { MacroPresetControl } from "./MacroPresetControl"; + +export class MacroPresetsControl extends Div implements ModalContent> { + public eventResultReady = "resultReady"; + + private _content: Div; + private _macroNames: MacroNamesControl; + private _macroPresetControls: Container; + private _footer: Div; + private _newPresetButton: Button; + private _footerBuffer: Div; + private _submitButton: Button; + private _cancelButton: Button; + + constructor(macroContext: IMacroContext) { + super(); + + this.append([ + (this._content = new Div()) + .append([ + (this._macroNames = new MacroNamesControl()) + .on(this._macroNames.eventChange, () => this._onMacroNamesChanged()), + (this._macroPresetControls = new Container()) + ]), + (this._footer = new Div()) + .append([ + (this._newPresetButton = new Button()) + .setText("New preset") + .on(this._newPresetButton.eventClick, () => this._onNewPresetButtonClick()), + (this._footerBuffer = new Div()), + (this._submitButton = new Button()) + .setText("OK") + .on(this._submitButton.eventClick, () => this.raise(this.eventResultReady)), + (this._cancelButton = new Button()) + .setText("Cancel") + .on(this._cancelButton.eventClick, () => this.raise(this.eventResultReady)) + ]) + ]); + + this.setMacroPresets(macroContext.getMacroPresets()); + } + + static async showAsModal(macroContext: IMacroContext): Promise { + let presets = await new Modal().show(new MacroPresetsControl(macroContext)); + macroContext.setMacroPresets(presets); + } + + public getResult(): Array { + let presets = this.getMacroPresets(); + return presets; + } + + setMacroPresets(presets: Array): this { + let names: Array = []; + + presets.forEach(preset => { + preset.macros.map(macro => macro.name).forEach(name => { + if (!names.includes(name)) + names.push(name); + }); + }); + + this._macroNames.setNames(names); + + this._macroPresetControls.append(presets.map(preset => + new MacroPresetControl() + .setMacroPreset(preset) + .updateNames(names) + )); + + return this; + } + + getMacroPresets(): Array { + return this._macroPresetControls.children().map(child => child.getMacroPreset()); + } + + private _onMacroNamesChanged(): void { + this._macroPresetControls.children().forEach(child => + child.updateNames(this._macroNames.getNames()) + ); + } + + private _onNewPresetButtonClick(): void { + this._macroPresetControls.append([ + new MacroPresetControl() + .setMacroPreset({ name: this._generateNewPresetName(), macros: [] }) + .updateNames(this._macroNames.getNames()) + .enterRenameMode() + ]); + } + + private _generateNewPresetName(): string { + let names = this._macroPresetControls.children().map(ctrl => ctrl.getMacroPreset().name); + + let baseName = "Untitled preset"; + + if (!names.includes(baseName)) + return baseName; + + let counter = 1; + + while (names.includes(`${baseName} (${counter})`)) { + counter++; + } + + return `${baseName} (${counter})`; + } +} + +MacroPresetsControl.style = { + _: { + display: "flex", + flexFlow: "column nowrap", + width: "70vw", + height: "70vh" + }, + _content: { + display: "flex", + flexFlow: "row nowrap", + alignItems: "flex-start", + flex: "1 1 1px", + minHeight: "250px", + overflowY: "auto", + overflowX: "hidden", + borderBottom: `3px solid ${Colors.workspaceLineWeak}`, + borderTop: `3px solid ${Colors.workspaceLineWeak}`, + "&::-webkit-scrollbar": { + borderRight: `solid 1px ${Colors.workspaceLineWeak}` + } + }, + _macroNames: { + flex: "0 0 200px", + minHeight: "100%", + borderRight: `3px solid ${Colors.workspaceLineWeak}`, + zIndex: ZIndexLayers.modal + 1 + }, + _macroPresetControls: { + display: "flex", + flexFlow: "row nowrap", + flex: "1 1 1px", + overflowX: "auto", + overflowY: "hidden", + marginLeft: "-1px", + borderRight: `1px solid ${Colors.workspaceLineWeak}`, + "&::-webkit-scrollbar": { + borderBottom: `solid 1px ${Colors.workspaceLineWeak}` + } + }, + _footer: { + display: "flex", + flexFlow: "row nowrap", + justifyContent: "flex-end", + marginTop: "20px" + }, + _newPresetButton: { + }, + _footerBuffer: { + flex: "1 1 1px" + }, + _submitButton: { + + }, + _cancelButton: { + marginLeft: "10px" + } +}; diff --git a/src/renderer/ui/MarcoPresetsControl/index.ts b/src/renderer/ui/MarcoPresetsControl/index.ts new file mode 100644 index 0000000..094ef9b --- /dev/null +++ b/src/renderer/ui/MarcoPresetsControl/index.ts @@ -0,0 +1 @@ +export { MacroPresetsControl } from "./MacroPresetsControl"; diff --git a/src/renderer/ui/Modals/Modal.ts b/src/renderer/ui/Modals/Modal.ts new file mode 100644 index 0000000..6d2d7a6 --- /dev/null +++ b/src/renderer/ui/Modals/Modal.ts @@ -0,0 +1,109 @@ +import { Div, Element } from "aflon"; +import { BoxShadowValues, Colors, FontStyles, ZIndexLayers } from "../StyleConstants"; + +export abstract class ModalContent extends Element { + public eventResultReady; + public abstract getResult(): TResult; +} + +export class Modal extends Div { + private _background: Div; + private _container: Div; + + constructor() { + super(); + + this.append([ + (this._background = new Div()) + .on(this.eventClick, async () => { + await this.animations("pingOut").startAsync(); + this.animations("pingIn").start(); + }), + (this._container = new Div()) + ]); + } + + async show(content: ModalContent): Promise { + return new Promise((resolve) => { + content.on(content.eventResultReady, () => { + this.hide(); + resolve(content.getResult()); + }); + + this._container.append([ content ]); + document.body.append(this.getHtmlElement()); + this.animations("show").start(); + }); + } + + async hide(): Promise { + await this.animations("hide").startAsync(); + document.body.removeChild(this.getHtmlElement()); + } +} + +Modal.style = { + _: { + position: "fixed", + display: "flex", + alignItems: "center", + justifyContent: "center", + width: "100vw", + height: "100vh", + top: 0, + zIndex: ZIndexLayers.modal + }, + _background: { + position: "absolute", + width: "100%", + height: "100%" + }, + _container: { + ...FontStyles.sansSerifNormal, + background: Colors.consoleBackground, + borderTop: `solid 1px ${Colors.consoleBorder}`, + boxShadow: BoxShadowValues.consoleExtended, + color: Colors.browserDefault, + display: "flex", + flexFlow: "column nowrap", + borderRadius: "20px 20px 20px 20px", + padding: "20px", + opacity: 0, + transform: "scale(0.9)", + maxWidth: "80%", + maxHeight: "80%" + } +}; + +Modal.animations = { + show: { + animations: [ + { track: "opacity", to: 1, duration: 75, ease: "linear", target: "_container"}, + { track: "transform", to: "scale(1.0)", target: "_container" } + ], + ease: "easeOut", + duration: 150 + }, + hide: { + animations: [ + { track: "opacity", to: 0, duration: 75, delay: 75, ease: "linear", target: "_container"}, + { track: "transform", to: "scale(0.7)", target: "_container" } + ], + ease: "easeIn", + duration: 150 + }, + pingOut: { + animations: [ + { track: "transform", to: "scale(1.05)", target: "_container" } + ], + ease: "easeOut", + duration: 50 + }, + pingIn: { + animations: [ + { track: "transform", to: "scale(1.0)", target: "_container" } + ], + ease: "easeIn", + duration: 50 + } +}; diff --git a/src/renderer/ui/Modals/SimpleModals.ts b/src/renderer/ui/Modals/SimpleModals.ts new file mode 100644 index 0000000..b3363d9 --- /dev/null +++ b/src/renderer/ui/Modals/SimpleModals.ts @@ -0,0 +1,96 @@ +import { Div } from "aflon"; + +import { Button } from "../BasicControls"; +import { FontStyles } from "../StyleConstants"; + +import { Modal, ModalContent } from "./Modal"; + +class SimpleModalContentButton { + text: string; + result?: TResult; + default?: boolean; +} + +class SimpleModalContent extends Div implements ModalContent { + public eventResultReady = "resultReady"; + + private _logo: Div; + private _message: Div; + private _button: Button; + + private _result: TResult = null; + + constructor(message: string, buttons: Array> = [{ text: "OK" }]) { + super(); + + this + .append([ + (this._logo = new Div()), + (this._message = new Div()) + .setText(message) + ]) + .append(buttons.map(button => { + let btnElem = new Button() + .addCssClass({ marginBottom: "10px" }) + .setText(button.text) + .on("click", () => { + this._result = button.result; + this.raise(this.eventResultReady); + }); + if (button.default) { + setTimeout(() => btnElem.focus(), 0); + } + return btnElem; + })); + } + + public getResult(): TResult { + return this._result; + } +} + +SimpleModalContent.style = { + _: { + display: "flex", + flexFlow: "column nowrap", + alignItems: "stretch", + minWidth: "150px", + maxWidth: "300px" + }, + _logo: { + height: "40px", + backgroundImage: "url(./resources/images/WelcomeIcon.svg)", + backgroundPosition: "center", + backgroundSize: "contain", + backgroundRepeat: "no-repeat", + opacity: "0.5", + marginTop: "20px", + marginBottom: "40px" + }, + _message: { + ...FontStyles.sansSerifNormal, + fontSize: "12px", + marginBottom: "40px", + overflowWrap: "break-word", + whiteSpace: "pre-line", + textAlign: "center" + } +}; + +export class SimpleModals { + static async confirm(text: string): Promise { + return await new Modal().show(new SimpleModalContent(text, + [{ text: "Yes", result: true }, { text: "No", result: false }]) + ); + } + + static async choose(text: string, options: Array<{ text: string, id: string, default?: boolean }>): Promise { + return await new Modal().show(new SimpleModalContent(text, + options.map>(option => ({ text: option.text, result: option.id, default: option.default }))) + ); + } + + static async alert(text: string, button: string = "OK"): Promise { + return await new Modal().show(new SimpleModalContent(text, [{ text: button }])); + } +} diff --git a/src/renderer/ui/Modals/index.ts b/src/renderer/ui/Modals/index.ts new file mode 100644 index 0000000..e81c71a --- /dev/null +++ b/src/renderer/ui/Modals/index.ts @@ -0,0 +1,2 @@ +export { ModalContent, Modal } from "./Modal"; +export { SimpleModals } from "./SimpleModals"; diff --git a/src/renderer/ui/PreferenceStore.ts b/src/renderer/ui/PreferenceStore.ts new file mode 100644 index 0000000..bebd4f4 --- /dev/null +++ b/src/renderer/ui/PreferenceStore.ts @@ -0,0 +1,125 @@ +import { ISimpleEventable, SimpleEventListener, SimpleEventBroker } from "../lib/SimpleEvent"; + +import { ColorTheme } from "./StyleConstants"; + +export enum ResponseControlLocation { + Console = "Console", Workspace = "Workspace", Automatic = "Automatic" +} + +class HttpinessPreferences implements ISimpleEventable { + public eventHideRequestDescriptorChanged = "hideRequestLabelsChanged"; + public eventHideResponseDescriptorChanged = "hideResponseLabelsChanged"; + public eventPreferSingleLineUrlChanged = "preferSingleLineUrlChanged"; + public eventCloseConsoleOnMouseLeaveChanged = "closeConsoleOnMouseLeaveChanged"; + public eventColorThemeChanged = "colorThemeChanged"; + public eventResponseLocationChanged = "responseLocationChanged"; + public eventAutoHideParametersChanged = "autoHideParametersChanged"; + + private _eventBroker: SimpleEventBroker = new SimpleEventBroker(this); + + on(eventName: string, handler: SimpleEventListener): this { + this._eventBroker.on(eventName, handler); + return this; + } + + off(eventName: string, handler: SimpleEventListener): this { + this._eventBroker.off(eventName, handler); + return this; + } + + raise(eventName: string, args: Record = {}): void { + this._eventBroker.raise(eventName, args); + } + + setHideRequestDescriptor(hide: boolean): this { + if (hide == this.getHideRequestDescriptor()) return this; + + localStorage.setItem("hide-labels", String(hide)); + this.raise(this.eventHideRequestDescriptorChanged); + return this; + } + + getHideRequestDescriptor(): boolean { + return localStorage.getItem("hide-labels") == "true"; + } + + setHideResponseDescriptor(hide: boolean): this { + if (hide == this.getHideResponseDescriptor()) return this; + + localStorage.setItem("hide-res-labels", String(hide)); + this.raise(this.eventHideResponseDescriptorChanged); + return this; + } + + getHideResponseDescriptor(): boolean { + return localStorage.getItem("hide-res-labels") == "true"; + } + + setPreferSingleLineUrl(prefer: boolean): this { + if (prefer == this.getPreferSingleLineUrl()) return this; + + localStorage.setItem("single-line-url", String(prefer)); + this.raise(this.eventPreferSingleLineUrlChanged); + return this; + } + + getPreferSingleLineUrl(): boolean { + return localStorage.getItem("single-line-url") == "true"; + } + + setColorTheme(theme: ColorTheme): this { + if (theme == this.getColorTheme()) return this; + + localStorage.setItem("color-theme", theme); + this.raise(this.eventColorThemeChanged); + return this; + } + + getColorTheme(): ColorTheme { + let theme = localStorage.getItem("color-theme"); + + if (theme == ColorTheme.Light) return ColorTheme.Light; + return ColorTheme.Dark; + } + + setCloseConsoleOnMouseLeave(close: boolean): this { + if (close == this.getCloseConsoleOnMouseLeave()) return this; + + localStorage.setItem("close-console-on-mouse-leave", String(close)); + this.raise(this.eventCloseConsoleOnMouseLeaveChanged); + return this; + } + + getCloseConsoleOnMouseLeave(): boolean { + return localStorage.getItem("close-console-on-mouse-leave") == "true"; + } + + setResponseLocation(location: ResponseControlLocation): this { + if (location == this.getResponseLocation()) return this; + localStorage.setItem("response-location", location); + this.raise(this.eventResponseLocationChanged); + return this; + } + + getResponseLocation(): ResponseControlLocation { + let location = localStorage.getItem("response-location"); + if (!Object.values(ResponseControlLocation).includes(location)) + return ResponseControlLocation.Automatic; + return location; + } + + setAutoHideParameters(autoHide: boolean): this { + if (autoHide == this.getAutoHideParameters()) return this; + + localStorage.setItem("auto-hide-parameters", String(autoHide)); + this.raise(this.eventAutoHideParametersChanged); + return this; + } + + getAutoHideParameters(): boolean { + return localStorage.getItem("auto-hide-parameters") == "true"; + } + +} + +export let PreferenceStore = new HttpinessPreferences(); diff --git a/src/renderer/ui/RapidsApp.ts b/src/renderer/ui/RapidsApp.ts new file mode 100644 index 0000000..c1218c9 --- /dev/null +++ b/src/renderer/ui/RapidsApp.ts @@ -0,0 +1,206 @@ +import { Div, CSS, NestedCSSProperties } from "aflon"; + +import { HttpAuth, HttpReqt, HttpCollection, HttpDir, HttpCollectionItem } from "../lib/http"; +import { currentPlatform, Platform } from "../lib/Platform"; +import { ZoomManager } from "../lib/WindowManipulation"; + +import { Colors, ZIndexLayers } from "./StyleConstants"; +import { RequestBrowser } from "./RequestBrowser"; +import { WorkspaceControl } from "./WorkspaceControl"; +import { ControlBar } from "./ControlBar"; +import { VResizer } from "./VResizer"; +import { PreferenceStore } from "./PreferenceStore"; +import { AppStatePersister } from "./AppStatePersister"; + +CSS.createRule("html, body", { + margin: 0, + padding: 0, + backgroundColor: Colors.backgroundDefault, + userSelect: "none", + width: "100%", + height: "100%", + "color-scheme": PreferenceStore.getColorTheme() +}); + +CSS.createRule("*", { + boxSizing: "border-box" +}); + +CSS.createRule(".control-bar", { + "-webkit-app-region": "drag" +}); + +CSS.createRule("option", { + background: Colors.tooltipBackgroundDefault, + color: Colors.tooltipText +}); + +CSS.createRule("::selection", { + backgroundColor: Colors.textSelection +}); + +if (currentPlatform() == Platform.Win32) { + CSS.createRule("::-webkit-scrollbar", { + width: "8px", height: "8px" + }); + + CSS.createRule("::-webkit-scrollbar-thumb", { + background: Colors.scrollThumb + }); + + CSS.createRule("::-webkit-scrollbar-corner", { + display: "none" + }); + + CSS.createRule("::-webkit-resizer", { + display: "none" + }); +} + +window.addEventListener("keydown", e => { + const modifierKeyPressed = + (currentPlatform() == Platform.MacOS && e.metaKey) || + (currentPlatform() != Platform.MacOS && e.ctrlKey); + + if (!modifierKeyPressed) return; + + if (e.key == "+" || e.key == "=") { + e.preventDefault(); + ZoomManager.zoomIn(); + } else if (e.key == "-" || e.key == "_") { + e.preventDefault(); + ZoomManager.zoomOut(); + } +}); + +PreferenceStore.on(PreferenceStore.eventColorThemeChanged, () => { + setTimeout(() => location.reload(), 0); +}); + +export default class RapidsApp extends Div { + private _workspaceControl: WorkspaceControl; + private _httpRequestBrowser: RequestBrowser; + private _reqBrowserSeparator: VResizer; + private _controlBar: ControlBar; + + constructor() { + super(); + + this.append([ + (this._workspaceControl = new WorkspaceControl()) + .on(this._workspaceControl.eventActionRequested, e => this._onWorkspaceActionRequested(e)), + (this._httpRequestBrowser = new RequestBrowser()) + .setInlineCss({ width: `${AppStatePersister.getRequestBrowserWidth()}px`}) + .on(this._httpRequestBrowser.eventItemSelected, e => this._onItemSelected(e)) + .on(this._httpRequestBrowser.eventItemPinSelected, e => this._onItemPinSelected(e)) + .on(this._httpRequestBrowser.eventReqtSendRequested, e => this._onReqtSendRequested(e)) + .on(this._httpRequestBrowser.eventItemAboutToBeDeleted, e => this._onItemAboutToBeDeleted(e)) + .on(this._httpRequestBrowser.eventClosingCollection, e => this._onClosingCollection(e)), + (this._controlBar = new ControlBar()), + (this._reqBrowserSeparator = new VResizer()) + .setResizingCallback(this._onBrowserResize) + ]); + } + + private _onBrowserResize = (e: MouseEvent): void => { + let rect = this.getHtmlElement().getBoundingClientRect(); + let width = e.clientX - rect.left; + AppStatePersister.setRequestBrowserWidth(width); + this._httpRequestBrowser.setInlineCss({ + width: `${e.clientX - rect.left}px` + }); + }; + + private _onItemSelected(e: Event): void { + let request = (e["detail"]["item"]); + let makeNameEditable = (e["detail"]["makeWorkspaceNameEditable"]); + this._workspaceControl.setPreviewItem(request, makeNameEditable); + } + + private _onItemPinSelected(e: Event): void { + let colItem = (e["detail"]["item"]); + this._workspaceControl.addPinnedItem(colItem); + } + + private async _onReqtSendRequested(e: Event): Promise { + let reqt = (e["detail"]["item"]); + if (!(reqt instanceof HttpReqt)) + throw new Error("Payload of eventReqtSendRequested in not HttpReqt."); + this._workspaceControl.sendRequest(reqt); + } + + private _onClosingCollection(e: Event): void { + let collection = (e["detail"]["collection"]); + this._workspaceControl.closeAllItemsFromCollection(collection); + } + + private _onWorkspaceActionRequested(e: Event): void { + let action = e["detail"]["action"]; + + if (action == "send-request") { + this._httpRequestBrowser.loadSampleRequest(); + return; + } + + if (action == "create") { + this._httpRequestBrowser.createCollection(); + return; + } + + if (action == "import") { + this._httpRequestBrowser.importCollection(); + return; + } + + if (action == "docs") { + this._controlBar.showHelp(); + return; + } + } + + private _onItemAboutToBeDeleted(e: Event): void { + let details = e["detail"]; + + if (details["item"] && (details["item"] instanceof HttpReqt || details["item"] instanceof HttpAuth)) { + this._workspaceControl.closeItem(details["item"]); + return; + } + + if (details["item"] && details["item"] instanceof HttpDir) { + this._workspaceControl.closeAllDescendantsOf(details["item"]); + return; + } + } +} + +RapidsApp.style = { + _: { + fontSize: "20px", + fontWeight: "bolder", + fontFamily: "Open Sans", + display: "grid", + height: "100%", + width: "100%", + overflow: "hidden", + grid: + `"header header header" 38px + "browser separator viewer" 1fr + / auto 0px 1fr` + }, + _reqBrowserSeparator: { + gridArea: "separator", + zIndex: ZIndexLayers.base + 1 + }, + _httpRequestBrowser: { + gridArea: "browser", + width: "300px", + minWidth: "300px", + maxWidth: "500px" + }, + _workspaceControl: { + gridArea: "viewer" + }, + _controlBar: { + gridArea: "header" + } +}; diff --git a/src/renderer/ui/RequestBrowser/BrowserAuthItem.ts b/src/renderer/ui/RequestBrowser/BrowserAuthItem.ts new file mode 100644 index 0000000..4c57083 --- /dev/null +++ b/src/renderer/ui/RequestBrowser/BrowserAuthItem.ts @@ -0,0 +1,180 @@ +import { Div } from "aflon"; + +import { HttpAuth, HttpDir } from "../../lib/http"; +import { SimpleEvent } from "../../lib/SimpleEvent"; + +import { ContextMenu, ContextMenuItemType } from "../ContextMenu"; +import { Colors, FontStyles } from "../StyleConstants"; +import { SimpleModals } from "../Modals"; +import { IconButton } from "../IconButton"; + +import { BrowserItemName } from "./BrowserItemName"; +import { RequestBrowserClipboard } from "./RequestBrowserClipboard"; + +export class BrowserAuthItem extends Div { + public eventSelected = "selected"; + public eventPinSelected = "pinSelected"; + public eventDeleteRequested = "deleteRequested"; + public eventDuplicateRequested = "duplicateRequested"; + + private _typeDiv: Div; + private _browserItemName: BrowserItemName; + private _optionsDiv: Div; + + private _contextMenu: ContextMenu; + + private _auth: HttpAuth; + + constructor(auth: HttpAuth) { + super(); + + this._auth = auth; + + this.append([ + (this._typeDiv = new Div()) + .setText("Auth"), + (this._browserItemName = new BrowserItemName()) + .setText(auth.getName()) + .on(this._browserItemName.eventClick, () => this._onNameClick()) + .on(this._browserItemName.eventChange, () => this._onBrowserItemNameChange()), + (this._optionsDiv = new Div()) + .append([ + new IconButton("pin") + .setTooltip("Pin to workspace") + .on("click", () => this._onPinClick()), + new IconButton("delete") + .setTooltip("Delete request") + .on("click", () => this._onDeleteClick()) + ]) + ]) + .on(this.eventMouseEnter, () => this._onMouseEnter()) + .on(this.eventMouseLeave, () => this._onMouseLeave()) + .on(this.eventDblClick, () => this._onDblClick()); + + (this._contextMenu = new ContextMenu(this, [ + { id: "preview", type: ContextMenuItemType.Button, text: "Preview" }, + { id: "pin", type: ContextMenuItemType.Button, text: "Pin", iconName: "pin" }, + { id: "divider", type: ContextMenuItemType.Divider }, + { id: "cut", type: ContextMenuItemType.Button, text: "Cut", disabled: true }, + { id: "copy", type: ContextMenuItemType.Button, text: "Copy", disabled: true }, + { id: "duplicate", type: ContextMenuItemType.Button, text: "Duplicate", disabled: true }, + { id: "divider", type: ContextMenuItemType.Divider }, + { id: "rename", type: ContextMenuItemType.Button, text: "Rename" }, + { id: "delete", type: ContextMenuItemType.Button, text: "Delete", iconName: "delete" } + ])) + .on(this._contextMenu.eventSelected, e => this._onContextMenuSelected(e)); + + this._auth + .on(this._auth.eventNameChanged, () => this._onReqtNameChanged()); + } + + public getHttpAuth(): HttpAuth { + return this._auth; + } + + public getName(): string { + return this._auth.getName(); + } + + private _onMouseEnter(): void { + this._optionsDiv.setInlineCss({ + display: "flex" + }); + } + + private _onMouseLeave(): void { + this._optionsDiv.setInlineCss({ + display: "none" + }); + } + + private _onDblClick(): void { + this.raise(this.eventPinSelected); + } + + private _onPinClick(): void { + this.raise(this.eventPinSelected); + } + + private _onNameClick(): void { + this.raise(this.eventSelected); + } + + private _onDeleteClick(): void { + this.raise(this.eventDeleteRequested); + } + + private _onReqtNameChanged(): void { + this._browserItemName.setText(this._auth.getName()); + } + + private _onBrowserItemNameChange(): void { + let newName = this._browserItemName.getText(); + + let parent = this._auth.getParent(); + if (parent instanceof HttpDir && parent.containsChild(newName)) { + SimpleModals.alert(`Cannot rename item: another item with name ${newName} already exists.`); + this._browserItemName.setText(this._auth.getName()); + return; + } + + this._auth.setName(this._browserItemName.getText()); + } + + private _onRenameRequested(): void { + setTimeout(() => this._browserItemName.makeChangeReady(), 10); + } + + private _onContextMenuSelected(e: SimpleEvent): void { + const id = e["detail"]["id"]; + if (!id) return; + + if (id == "preview") { + this.raise(this.eventSelected); + } else if (id == "pin") { + this.raise(this.eventPinSelected); + } else if (id == "cut") { + RequestBrowserClipboard.setValueToCut(this); + } else if (id == "copy") { + RequestBrowserClipboard.setValueToCopy(this); + } else if (id == "duplicate") { + this.raise(this.eventDuplicateRequested); + } else if (id == "rename") { + this._onRenameRequested(); + } else if (id == "delete") { + this.raise(this.eventDeleteRequested); + } + } +} + +BrowserAuthItem.style = { + _: { + display: "flex", + flexFlow: "row nowrap", + alignItems: "center", + height: "25px", + width: "100%", + paddingRight: "10px", + userSelect: "none", + position: "relative", + "&:hover": { + background: Colors.browserBackHover + } + }, + _typeDiv: { + ...FontStyles.sansSerifExtraBold, + flex: "0 0 28px", + color: Colors.browserDefault, + fontSize: "8px", + //lineHeight: "25px", + textAlign: "center" + }, + _browserItemName: { + flex: "1 1 100px", + height: "100%" + }, + _optionsDiv: { + display: "none", + flexFlow: "row nowrap" + } +}; diff --git a/src/renderer/ui/RequestBrowser/BrowserCollectionItem.ts b/src/renderer/ui/RequestBrowser/BrowserCollectionItem.ts new file mode 100644 index 0000000..15caff1 --- /dev/null +++ b/src/renderer/ui/RequestBrowser/BrowserCollectionItem.ts @@ -0,0 +1,614 @@ +import { Div, typeAflonTarget } from "aflon"; + +import { HttpReqt, HttpCollection, HttpDir, HttpAuth, AuthType } from "../../lib/http"; +import { SimpleEvent } from "../../lib/SimpleEvent"; + +import { ContextMenu, ContextMenuItemType } from "../ContextMenu"; +import { Icon } from "../Icon"; +import { Colors, FontStyles } from "../StyleConstants"; +import { SimpleModals } from "../Modals"; +import { MacroPresetsControl } from "../MarcoPresetsControl"; +import { IconButton } from "../IconButton"; + +import { BrowserAuthItem } from "./BrowserAuthItem"; +import { BrowserDirItem } from "./BrowserDirItem"; +import { BrowserItemName } from "./BrowserItemName"; +import { BrowserRequestItem } from "./BrowserRequestItem"; +import { RequestBrowserClipboard, RequestBrowserClipboardAction } from "./RequestBrowserClipboard"; + +export class BrowserCollectionItem extends Div { + private static _defaultNewDirName = "Untitled directory"; + private static _defaultNewRequestName = "Untitled request"; + private static _defaultNewAuthName = "Untitled auth"; + + public eventRequestSelected = "requestSelected"; + public eventAuthSelected = "authSelected"; + public eventRequestPinSelected = "requestPinSelected"; + public eventAuthPinSelected = "authPinSelected"; + public eventCloseRequested = "closeRequested"; + public eventItemAboutToBeDeleted = "itemAboutToBeDeleted"; + + private _itemContainer: Div; + private _documentIcon: Div; + private _nameDiv: BrowserItemName; + private _optionsDiv: Div; + private _authContainer: Div; + private _dirContainer: Div; + private _reqtContainer: Div; + + private _contextMenu: ContextMenu; + + private _collection: HttpCollection; + private _expanded: boolean = false; + + constructor(collection: HttpCollection) { + super(); + + this._collection = collection; + + let icon = "file-void"; + if (this._collection.isLocalExperimental()) + icon = "experiment"; + + this.append([ + (this._itemContainer = new Div()) + .append([ + (this._documentIcon = new Div()) + .append([ new Icon(icon) ]) + .on(this._documentIcon.eventClick, () => this._onClick()), + (this._nameDiv = new BrowserItemName()) + .setText(collection.getName()) + .on(this._nameDiv.eventClick, () => this._onClick()), + (this._optionsDiv = new Div()) + .append([ + new IconButton("new-req") + .setTooltip("Create new request") + .on("click", () => this._onNewReqtRequested()), + new IconButton("new-dir") + .setTooltip("Create new directory") + .on("click", () => this._onNewDirRequested()), + new IconButton("close") + .setTooltip("Close this collection") + .setInlineCss({ fontSize: "11px", paddingTop: "1px" }) + .on("click", () => this._onCloseRequested()) + ]) + ]) + .on(this.eventMouseEnter, () => this._onMouseEnter()) + .on(this.eventMouseLeave, () => this._onMouseLeave()) + .on(this.eventDragOver, e => this._onDragOver(e)) + .on(this.eventDragEnter, () => this._onDragEnter()) + .on(this.eventDragLeave, () => this._onDragLeave()) + .on(this.eventDrop, e => this._onDrop(e)), + (this._dirContainer = new Div()), + (this._authContainer = new Div()), + (this._reqtContainer = new Div()) + ]); + + (this._contextMenu = new ContextMenu(this._itemContainer, [ + { id: "new-req", type: ContextMenuItemType.Button, text: "New Request", iconName: "new-req" }, + { id: "new-dir", type: ContextMenuItemType.Button, text: "New Directory", iconName: "new-dir" }, + { id: "new-auth", type: ContextMenuItemType.Button, text: "New Authentication" }, + { id: "divider", type: ContextMenuItemType.Divider }, + { id: "paste", type: ContextMenuItemType.Button, text: "Paste", disabled: true }, + { id: "divider", type: ContextMenuItemType.Divider }, + { id: "presets", type: ContextMenuItemType.Button, text: "Configure presets" }, + { id: "close", type: ContextMenuItemType.Button, text: "Close", iconName: "close", iconSizeAdjustment: -0.5 } + ])) + .on(this._contextMenu.eventSelected, e => this._onContextMenuSelected(e)) + .on(this._contextMenu.eventAboutToBeShown, () => this._onContextMenuAboutToBeShown()); + } + + getCollection(): HttpCollection { + return this._collection; + } + + setExpanded(expanded: boolean): this { + if (this._expanded == expanded) return this; + + this._expanded = expanded; + + if (this._expanded) { + if (!this._collection.isLocalExperimental()) { + this._documentIcon.empty(); + this._documentIcon.append([ new Icon("file-solid") ]); + } + this._authContainer.append( + this._collection.getAuths() + .map(auth => { + let authItem = new BrowserAuthItem(auth); + this._attachAllEventHandlersToAuthItem(authItem); + return authItem; + }) + ); + this._dirContainer.append( + this._collection.getDirs() + .map(dir => { + let dirItem = new BrowserDirItem(dir); + this._attachAllEventHandlersToDirItem(dirItem); + return dirItem; + }) + ); + this._reqtContainer.append( + this._collection.getReqts() + .map(reqt => { + let reqItem = new BrowserRequestItem(reqt); + this._attachAllEventHandlersToReqtItem(reqItem); + return reqItem; + }) + ); + } else { + if (!this._collection.isLocalExperimental()) { + this._documentIcon.empty(); + this._documentIcon.append([ new Icon("file-void") ]); + } + this._authContainer.children().forEach(element => { + if (element instanceof BrowserAuthItem) + this._removeAllEventHandlersFromAuthItem(element); + }); + this._dirContainer.children().forEach(element => { + if (element instanceof BrowserDirItem) + this._removeAllEventHandlerFromDirItem(element); + }); + this._reqtContainer.children().forEach(element => { + if (element instanceof BrowserRequestItem) + this._removeAllEventHandlersFromReqtItem(element); + }); + this._authContainer.empty(); + this._dirContainer.empty(); + this._reqtContainer.empty(); + } + + return this; + } + + getExpanded(): boolean { + return this._expanded; + } + + private _generateNewDefaultNewReqtName(): string { + const existingUntitledReqtNames = + this._collection.getReqts() + .map(reqt => reqt.getName()) + .filter(name => name.indexOf(BrowserCollectionItem._defaultNewRequestName) == 0); + + if (existingUntitledReqtNames.length == 0) return BrowserCollectionItem._defaultNewRequestName; + + for (let i = 1; i <= 10000; i++) { + const candidate = `${BrowserCollectionItem._defaultNewRequestName} (${i})`; + if (!existingUntitledReqtNames.includes(candidate)) + return candidate; + } + + return BrowserCollectionItem._defaultNewRequestName; + } + + private _generateNewDefaultNewDirName(): string { + const existingUntitledDirNames = + this._collection.getDirs() + .map(dir => dir.getName()) + .filter(name => name.indexOf(BrowserCollectionItem._defaultNewDirName) == 0); + + if (existingUntitledDirNames.length == 0) return BrowserCollectionItem._defaultNewDirName; + + for (let i = 1; i <= 10000; i++) { + const candidate = `${BrowserCollectionItem._defaultNewDirName} (${i})`; + if (!existingUntitledDirNames.includes(candidate)) + return candidate; + } + + return BrowserCollectionItem._defaultNewDirName; + } + + private _generateNewDefaultNewAuthName(): string { + if (this._collection.getAuths().length == 0) + return "Default Auth"; + + const existingUntitledAuthNames = + this._collection.getAuths() + .map(auth => auth.getName()) + .filter(name => name.indexOf(BrowserCollectionItem._defaultNewAuthName) == 0); + + if (existingUntitledAuthNames.length == 0) return BrowserCollectionItem._defaultNewAuthName; + + for (let i = 1; i <= 10000; i++) { + const candidate = `${BrowserCollectionItem._defaultNewAuthName} (${i})`; + if (!existingUntitledAuthNames.includes(candidate)) + return candidate; + } + + return BrowserCollectionItem._defaultNewAuthName; + } + + private _attachAllEventHandlersToAuthItem(item: BrowserAuthItem): void { + item.on(item.eventDeleteRequested, this._onAuthItemDeleteRequested); + item.on(item.eventSelected, this._onAuthItemSelected); + item.on(item.eventPinSelected, this._onAuthItemPinSelected); + } + + private _removeAllEventHandlersFromAuthItem(item: BrowserAuthItem): void { + item.off(item.eventDeleteRequested, this._onAuthItemDeleteRequested); + item.off(item.eventSelected, this._onAuthItemSelected); + item.off(item.eventPinSelected, this._onAuthItemPinSelected); + } + + private _attachAllEventHandlersToReqtItem(item: BrowserRequestItem): void { + item.on(item.eventDeleteRequested, this._onReqItemDeleteRequested); + item.on(item.eventSelected, this._onReqItemSelected); + item.on(item.eventPinSelected, this._onReqItemPinSelected); + item.on(item.eventDuplicateRequested, this._onReqItemDuplicateRequested); + } + + private _removeAllEventHandlersFromReqtItem(item: BrowserRequestItem): void { + item.off(item.eventDeleteRequested, this._onReqItemDeleteRequested); + item.off(item.eventSelected, this._onReqItemSelected); + item.off(item.eventPinSelected, this._onReqItemPinSelected); + item.off(item.eventDuplicateRequested, this._onReqItemDuplicateRequested); + } + + private _attachAllEventHandlersToDirItem(item: BrowserDirItem): void { + item.on(item.eventDeleteRequested, this._onDirItemDeleteRequested); + item.on(item.eventRequestSelected, this._onDirRequestSelected); + item.on(item.eventRequestPinSelected, this._onDirRequestPinSelected); + item.on(item.eventAuthSelected, this._onDirAuthSelected); + item.on(item.eventAuthPinSelected, this._onDirAuthPinSelected); + item.on(item.eventItemAboutToBeDeleted, this._onItemAboutToBeDeleted); + } + + private _removeAllEventHandlerFromDirItem(item: BrowserDirItem): void { + item.off(item.eventDeleteRequested, this._onDirItemDeleteRequested); + item.off(item.eventRequestSelected, this._onDirRequestSelected); + item.off(item.eventRequestPinSelected, this._onDirRequestPinSelected); + item.off(item.eventAuthSelected, this._onDirAuthSelected); + item.off(item.eventAuthPinSelected, this._onDirAuthPinSelected); + item.off(item.eventItemAboutToBeDeleted, this._onItemAboutToBeDeleted); + } + + private _onNewAuthRequested(): void { + this.setExpanded(true); + + let auth = new HttpAuth() + .setAuthDefinition({ type: AuthType.Bearer, bearerToken: "" }) + .setName(this._generateNewDefaultNewAuthName()); + + this._collection.addAuth(auth); + + let newItem = new BrowserAuthItem(auth); + this._attachAllEventHandlersToAuthItem(newItem); + this._authContainer.append([ newItem ]); + } + + private _onNewReqtRequested(): void { + this.setExpanded(true); + + let newRequest: HttpReqt = new HttpReqt() + .setName(this._generateNewDefaultNewReqtName()); + + this._collection.addReqt(newRequest); + + let newItem = new BrowserRequestItem(newRequest); + this._attachAllEventHandlersToReqtItem(newItem); + this._reqtContainer.append([ newItem ]); + this.raise(this.eventRequestSelected, { request: newItem, makeNameEditable: true }); + } + + private _onNewDirRequested(): void { + this.setExpanded(true); + + let newDir: HttpDir = new HttpDir() + .setName(this._generateNewDefaultNewDirName()); + + this._collection.addDir(newDir); + + let newItem = new BrowserDirItem(newDir); + this._attachAllEventHandlersToDirItem(newItem); + this._dirContainer.append([ newItem ]); + } + + private _onReqItemDeleteRequested = async (e: Event): Promise => { + let reqItem: BrowserRequestItem = typeAflonTarget(e, BrowserRequestItem); + if (reqItem == null) return; + + let reqt = reqItem.getHttpReqt(); + + if (! await SimpleModals.confirm(`Are you sure you want to delete request '${reqt.getName()}'?`)) return; + + this.raise(this.eventItemAboutToBeDeleted, { reqItem }); + this._removeAllEventHandlersFromReqtItem(reqItem); + this._reqtContainer.removeChild(reqItem); + this._collection.removeReqt(reqt); + }; + + private _onAuthItemDeleteRequested = async (e: Event): Promise => { + let authItem: BrowserAuthItem = typeAflonTarget(e, BrowserAuthItem); + if (authItem == null) return; + + let auth = authItem.getHttpAuth(); + + if (! await SimpleModals.confirm(`Are you sure you want to delete authorization '${auth.getName()}'?`)) return; + + this.raise(this.eventItemAboutToBeDeleted, { authItem }); + this._removeAllEventHandlersFromAuthItem(authItem); + this._authContainer.removeChild(authItem); + this._collection.removeAuth(auth); + }; + + private _onDirItemDeleteRequested = async (e: Event): Promise => { + let dirItem: BrowserDirItem = typeAflonTarget(e, BrowserDirItem); + if (dirItem == null) return; + + let dir = dirItem.getHttpDir(); + + if (! await SimpleModals.confirm(`Are you sure you want to delete directory '${dir.getName()}'?`)) return; + + this.raise(this.eventItemAboutToBeDeleted, { dirItem }); + this._removeAllEventHandlerFromDirItem(dirItem); + this._dirContainer.removeChild(dirItem); + this._collection.removeDir(dir); + }; + + private _onClick(): void { + this.setExpanded(!this.getExpanded()); + } + + private _onMouseEnter(): void { + this._optionsDiv.setInlineCss({ + display: "flex" + }); + } + + private _onMouseLeave(): void { + this._optionsDiv.setInlineCss({ + display: "none" + }); + } + + private _onCloseRequested(): void { + this.raise(this.eventCloseRequested); + } + + private _onPaste(): void { + const item = RequestBrowserClipboard.getValue(); + const action = RequestBrowserClipboard.getAction(); + + if (this._collection.containsChild(item.getName())) { + SimpleModals.alert(`Cannot paste here because item with name ${item.getName()} already exists.`); + return; + } + + let parent = null; + if (item && item.parent()) + parent = item.parent().parent(); + + if (parent == this) return; + + if (action == RequestBrowserClipboardAction.Cut) { + if (item instanceof BrowserRequestItem) { + item.getHttpReqt().getParent().removeReqt(item.getHttpReqt()); + item.parent().removeChild(item); + this._collection.addReqt(item.getHttpReqt()); + if (this._expanded) { + let reqItem = new BrowserRequestItem(item.getHttpReqt()); + this._attachAllEventHandlersToReqtItem(reqItem); + this._reqtContainer.append([ reqItem ]); + } + } else if (item instanceof BrowserDirItem) { + item.getHttpDir().getParent().removeDir(item.getHttpDir()); + item.parent().removeChild(item); + this._collection.addDir(item.getHttpDir()); + if (this._expanded) { + let dirItem = new BrowserDirItem(item.getHttpDir()); + this._attachAllEventHandlersToDirItem(dirItem); + this._dirContainer.append([ dirItem ]); + } + } + RequestBrowserClipboard.clearValue(); + } else if (action == RequestBrowserClipboardAction.Copy) { + if (item instanceof BrowserRequestItem) { + let itemClone = item.getHttpReqt().clone(); + this._collection.addReqt(itemClone); + if (this._expanded) { + let reqItem = new BrowserRequestItem(itemClone); + this._attachAllEventHandlersToReqtItem(reqItem); + this._reqtContainer.append([ reqItem ]); + } + } else if (item instanceof BrowserDirItem) { + let itemClone = item.getHttpDir().clone(); + this._collection.addDir(itemClone); + if (this._expanded) { + let dirItem = new BrowserDirItem(itemClone); + this._attachAllEventHandlersToDirItem(dirItem); + this._dirContainer.append([ dirItem ]); + } + } + } + } + + private _onReqItemSelected = (e: Event): void => { + let reqItem: BrowserRequestItem = typeAflonTarget(e, BrowserRequestItem); + if (reqItem == null) return; + this.raise(this.eventRequestSelected, { request: reqItem }); + }; + + private _onAuthItemSelected = (e: Event): void => { + let authItem: BrowserAuthItem = typeAflonTarget(e, BrowserAuthItem); + if (authItem == null) return; + this.raise(this.eventAuthSelected, { auth: authItem }); + }; + + private _onReqItemPinSelected = (e: Event): void => { + let reqItem: BrowserRequestItem = typeAflonTarget(e, BrowserRequestItem); + if (reqItem == null) return; + this.raise(this.eventRequestPinSelected, { request: reqItem }); + }; + + private _onAuthItemPinSelected = (e: Event): void => { + let authItem: BrowserAuthItem = typeAflonTarget(e, BrowserAuthItem); + if (authItem == null) return; + this.raise(this.eventAuthPinSelected, { auth: authItem }); + }; + + private _onReqItemDuplicateRequested = (e: Event): void => { + let reqItem: BrowserRequestItem = typeAflonTarget(e, BrowserRequestItem); + if (reqItem == null) return; + let itemClone = reqItem.getHttpReqt().clone(); + let name: string = itemClone.getName(); + let suffix = " - Copy"; + + while (this._collection.containsChild(name)) { + name += suffix; + } + + itemClone.setName(name); + this._collection.addReqt(itemClone); + if (this._expanded) { + let reqItem = new BrowserRequestItem(itemClone); + this._attachAllEventHandlersToReqtItem(reqItem); + this._reqtContainer.append([ reqItem ]); + } + }; + + private _onDirRequestSelected = + (e: Event): this => this.raise(this.eventRequestSelected, { ... e["detail"] }); + + private _onDirRequestPinSelected = + (e: Event): this => this.raise(this.eventRequestPinSelected, { ... e["detail"] }); + + private _onDirAuthSelected = + (e: Event): this => this.raise(this.eventAuthSelected, { ... e["detail"] }); + + private _onDirAuthPinSelected = + (e: Event): this => this.raise(this.eventAuthPinSelected, { ... e["detail"] }); + + private _onItemAboutToBeDeleted = + (e: Event): this => this.raise(this.eventItemAboutToBeDeleted, { ... e["detail"] }); + + private _onContextMenuSelected(e: SimpleEvent): void { + const id = e["detail"]["id"]; + if (!id) return; + + if (id == "new-req") { + this._onNewReqtRequested(); + } else if (id == "new-dir") { + this._onNewDirRequested(); + } else if (id == "new-auth") { + this._onNewAuthRequested(); + } else if (id == "presets") { + MacroPresetsControl.showAsModal(this._collection); + } else if (id == "paste") { + this._onPaste(); + } else if (id == "close") { + this.raise(this.eventCloseRequested); + } + } + + private _onContextMenuAboutToBeShown(): void { + if (RequestBrowserClipboard.getValue() == null || + RequestBrowserClipboard.getAction() == RequestBrowserClipboardAction.None) { + this._contextMenu.setItemDisabled("paste", true); + } else { + this._contextMenu.setItemDisabled("paste", false); + } + } + + private _onDragOver(e: Event): void { + e.preventDefault(); + let dragEvent = e; + + let clipboardValue = RequestBrowserClipboard.getValue(); + + let parent = null; + if (clipboardValue && clipboardValue.parent()) + parent = clipboardValue.parent().parent(); + + if (parent == this) { + dragEvent.dataTransfer.effectAllowed = "none"; + } else { + dragEvent.dataTransfer.effectAllowed = "move"; + } + } + + private _onDragEnter(): void { + let clipboardValue = RequestBrowserClipboard.getValue(); + + let parent = null; + if (clipboardValue && clipboardValue.parent()) + parent = clipboardValue.parent().parent(); + + if (parent == this) return; + + this._itemContainer.setInlineCss({ + outline: `solid 1px ${Colors.workspaceLine}`, + outlineOffset: "-1px" + }); + } + + private _onDragLeave(): void { + this._itemContainer.setInlineCss({ + outline: "none", + outlineOffset: "0" + }); + } + + private _onDrop(e: Event): void { + e.preventDefault(); + this._itemContainer.setInlineCss({ + outline: "none", + outlineOffset: "0" + }); + this._onPaste(); + } +} + +BrowserCollectionItem.style = { + _: { + display: "flex", + flexFlow: "column nowrap", + width: "100%", + position: "relative" + }, + _itemContainer: { + display: "flex", + flexFlow: "row nowrap", + height: "25px", + width: "100%", + paddingRight: "10px", + "&:hover": { + background: Colors.browserBackHover + } + }, + _authContainer: { + display: "flex", + flexFlow: "column nowrap", + paddingLeft: "23px" + }, + _dirContainer: { + display: "flex", + flexFlow: "column nowrap", + paddingLeft: "23px" + }, + _reqtContainer: { + display: "flex", + flexFlow: "column nowrap", + paddingLeft: "23px" + }, + _documentIcon: { + ...FontStyles.sansSerifExtraBold, + flex: "0 0 28px", + paddingLeft: "5px", + paddingTop: "1px", + height: "100%", + color: Colors.browserDefault, + fontSize: "12px", + lineHeight: "25px", + textAlign: "center", + cursor: "pointer" + }, + _nameDiv: { + flex: "1 1 100px", + height: "100%" + }, + _optionsDiv: { + display: "none", + flexFlow: "row nowrap" + } +}; diff --git a/src/renderer/ui/RequestBrowser/BrowserDirItem.ts b/src/renderer/ui/RequestBrowser/BrowserDirItem.ts new file mode 100644 index 0000000..7147941 --- /dev/null +++ b/src/renderer/ui/RequestBrowser/BrowserDirItem.ts @@ -0,0 +1,631 @@ +import { Div, typeAflonTarget } from "aflon"; + +import { AuthType, HttpAuth, HttpDir, HttpReqt } from "../../lib/http"; +import { SimpleEvent } from "../../lib/SimpleEvent"; + +import { ContextMenu, ContextMenuItemType } from "../ContextMenu"; +import { Colors, FontStyles } from "../StyleConstants"; +import { SimpleModals } from "../Modals"; + +import { BrowserAuthItem } from "./BrowserAuthItem"; +import { BrowserItemName } from "./BrowserItemName"; +import { BrowserRequestItem } from "./BrowserRequestItem"; +import { IconButton } from "../IconButton"; +import { RequestBrowserClipboard, RequestBrowserClipboardAction } from "./RequestBrowserClipboard"; + +export class BrowserDirItem extends Div { + private static _defaultNewDirName = "Untitled directory"; + private static _defaultNewRequestName = "Untitled request"; + private static _defaultNewAuthName = "Untitled auth"; + + public eventDeleteRequested = "deleteRequested"; + public eventAuthSelected = "authSelected"; + public eventRequestSelected = "requestSelected"; + public eventAuthPinSelected = "authPinSelected"; + public eventRequestPinSelected = "requestPinSelected"; + public eventItemAboutToBeDeleted = "itemAboutToBeDeleted"; + + private _itemContainer: Div; + private _triangle: Div; + private _browserItemName: BrowserItemName; + private _optionsDiv: Div; + private _authContainer: Div; + private _dirContainer: Div; + private _reqtContainer: Div; + + private _contextMenu: ContextMenu; + + private _dir: HttpDir; + private _expanded: boolean = false; + + constructor(dir: HttpDir) { + super(); + + this._dir = dir; + + this.append([ + (this._itemContainer = new Div()) + .append([ + (this._triangle = new Div()) + .setText("โ–ถ") + .on(this._triangle.eventClick, () => this._onClick()), + (this._browserItemName = new BrowserItemName()) + .setText(dir.getName()) + .on(this._browserItemName.eventChange, () => this._onBrowserItemChange()) + .on(this._browserItemName.eventClick, () => this._onClick()), + (this._optionsDiv = new Div()) + .append([ + new IconButton("new-req") + .setTooltip("Create new request") + .on("click", () => this._onNewReqtRequested()), + new IconButton("new-dir") + .setTooltip("Create new directory") + .on("click", () => this._onNewDirRequested()), + new IconButton("delete") + .setTooltip("Delete directory") + .on("click", () => this._onDeleteRequested()) + ]) + ]) + .addAttr("draggable", "true") + .on(this.eventMouseEnter, () => this._onMouseEnter()) + .on(this.eventMouseLeave, () => this._onMouseLeave()) + .on(this.eventDragStart, () => this._onDragStart()) + .on(this.eventDragOver, e => this._onDragOver(e)) + .on(this.eventDragEnter, () => this._onDragEnter()) + .on(this.eventDragLeave, () => this._onDragLeave()) + .on(this.eventDrop, e => this._onDrop(e)), + (this._dirContainer = new Div()), + (this._authContainer = new Div()), + (this._reqtContainer = new Div()) + ]); + + (this._contextMenu = new ContextMenu(this._itemContainer, [ + { id: "new-req", type: ContextMenuItemType.Button, text: "New Request", iconName: "new-req" }, + { id: "new-dir", type: ContextMenuItemType.Button, text: "New Directory", iconName: "new-dir" }, + { id: "new-auth", type: ContextMenuItemType.Button, text: "New Authentication" }, + { id: "divider", type: ContextMenuItemType.Divider }, + { id: "cut", type: ContextMenuItemType.Button, text: "Cut" }, + { id: "copy", type: ContextMenuItemType.Button, text: "Copy" }, + { id: "paste", type: ContextMenuItemType.Button, text: "Paste", disabled: true }, + { id: "divider", type: ContextMenuItemType.Divider }, + { id: "rename", type: ContextMenuItemType.Button, text: "Rename" }, + { id: "delete", type: ContextMenuItemType.Button, text: "Delete", iconName: "delete" } + ])) + .on(this._contextMenu.eventSelected, e => this._onContextMenuSelected(e)) + .on(this._contextMenu.eventAboutToBeShown, () => this._onContextMenuAboutToBeShown()); + } + + getHttpDir(): HttpDir { + return this._dir; + } + + public getName(): string { + return this._dir.getName(); + } + + setExpanded(expanded: boolean): this { + if (this._expanded == expanded) return this; + + this._expanded = expanded; + + if (this._expanded) { + this._triangle.setInlineCss({ transform: "rotate(90deg)" }); + this._authContainer.append( + this._dir.getAuths() + .map(auth => { + let authItem = new BrowserAuthItem(auth); + this._attachAllEventHandlersToAuthItem(authItem); + return authItem; + }) + ); + this._dirContainer.append( + this._dir.getDirs() + .map(dir => { + let dirItem = new BrowserDirItem(dir); + this._attachAllEventHandlersToDirItem(dirItem); + return dirItem; + }) + ); + this._reqtContainer.append( + this._dir.getReqts() + .map(reqt => { + let reqItem = new BrowserRequestItem(reqt); + this._attachAllEventHandlersToReqtItem(reqItem); + return reqItem; + }) + ); + } else { + this._triangle.setInlineCss({ transform: "none" }); + this._authContainer.children().forEach(element => { + if (element instanceof BrowserAuthItem) + this._removeAllEventHandlersFromAuthItem(element); + }); + this._dirContainer.children().forEach(element => { + if (element instanceof BrowserDirItem) + this._removeAllEventHandlerFromDirItem(element); + }); + this._reqtContainer.children().forEach(element => { + if (element instanceof BrowserRequestItem) + this._removeAllEventHandlersFromReqtItem(element); + }); + this._authContainer.empty(); + this._dirContainer.empty(); + this._reqtContainer.empty(); + } + + return this; + } + + getExpanded(): boolean { + return this._expanded; + } + + private _attachAllEventHandlersToAuthItem(item: BrowserAuthItem): void { + item.on(item.eventDeleteRequested, this._onAuthItemDeleteRequested); + item.on(item.eventSelected, this._onAuthItemSelected); + item.on(item.eventPinSelected, this._onAuthItemPinSelected); + } + + private _removeAllEventHandlersFromAuthItem(item: BrowserAuthItem): void { + item.off(item.eventDeleteRequested, this._onAuthItemDeleteRequested); + item.off(item.eventSelected, this._onAuthItemSelected); + item.off(item.eventPinSelected, this._onAuthItemPinSelected); + } + + private _attachAllEventHandlersToReqtItem(item: BrowserRequestItem): void { + item.on(item.eventDeleteRequested, this._onReqItemDeleteRequested); + item.on(item.eventSelected, this._onReqItemSelected); + item.on(item.eventPinSelected, this._onReqItemPinSelected); + item.on(item.eventDuplicateRequested, this._onReqItemDuplicateRequested); + } + + private _removeAllEventHandlersFromReqtItem(item: BrowserRequestItem): void { + item.off(item.eventDeleteRequested, this._onReqItemDeleteRequested); + item.off(item.eventSelected, this._onReqItemSelected); + item.off(item.eventPinSelected, this._onReqItemPinSelected); + item.off(item.eventDuplicateRequested, this._onReqItemDuplicateRequested); + } + + private _attachAllEventHandlersToDirItem(item: BrowserDirItem): void { + item.on(item.eventDeleteRequested, this._onDirItemDeleteRequested); + item.on(item.eventRequestSelected, this._onDirRequestSelected); + item.on(item.eventRequestPinSelected, this._onDirRequestPinSelected); + item.on(item.eventAuthSelected, this._onDirAuthSelected); + item.on(item.eventAuthPinSelected, this._onDirAuthPinSelected); + item.on(item.eventItemAboutToBeDeleted, this._onItemAboutToBeDeleted); + } + + private _removeAllEventHandlerFromDirItem(item: BrowserDirItem): void { + item.off(item.eventDeleteRequested, this._onDirItemDeleteRequested); + item.off(item.eventRequestSelected, this._onDirRequestSelected); + item.off(item.eventRequestPinSelected, this._onDirRequestPinSelected); + item.off(item.eventAuthSelected, this._onDirAuthSelected); + item.off(item.eventAuthPinSelected, this._onDirAuthPinSelected); + item.off(item.eventItemAboutToBeDeleted, this._onItemAboutToBeDeleted); + } + + private _generateDefaultNewRequestName(): string { + const existingUntitledDirNames = + this._dir.getReqts() + .map(dir => dir.getName()) + .filter(name => name.indexOf(BrowserDirItem._defaultNewRequestName) == 0); + + if (existingUntitledDirNames.length == 0) return BrowserDirItem._defaultNewRequestName; + + for (let i = 1; i <= 1000; i++) { + const candidate = `${BrowserDirItem._defaultNewRequestName} (${i})`; + if (!existingUntitledDirNames.includes(candidate)) + return candidate; + } + + return BrowserDirItem._defaultNewRequestName; + } + + private _generateDefaultNewAuthName(): string { + if (this._dir.getAuths().length == 0) + return "Default Auth"; + + const existingUntitledAuthNames = + this._dir.getAuths() + .map(auth => auth.getName()) + .filter(name => name.indexOf(BrowserDirItem._defaultNewAuthName) == 0); + + if (existingUntitledAuthNames.length == 0) return BrowserDirItem._defaultNewAuthName; + + for (let i = 1; i <= 10000; i++) { + const candidate = `${BrowserDirItem._defaultNewAuthName} (${i})`; + if (!existingUntitledAuthNames.includes(candidate)) + return candidate; + } + + return BrowserDirItem._defaultNewAuthName; + } + + private _generateDefaultNewDirName(): string { + const existingUntitledDirNames = + this._dir.getDirs() + .map(dir => dir.getName()) + .filter(name => name.indexOf(BrowserDirItem._defaultNewDirName) == 0); + + if (existingUntitledDirNames.length == 0) return BrowserDirItem._defaultNewDirName; + + for (let i = 1; i <= 1000; i++) { + const candidate = `${BrowserDirItem._defaultNewDirName} (${i})`; + if (!existingUntitledDirNames.includes(candidate)) + return candidate; + } + + return BrowserDirItem._defaultNewDirName; + } + + private _onNewAuthRequested(): void { + this.setExpanded(true); + + let auth = new HttpAuth() + .setAuthDefinition({ type: AuthType.Bearer, bearerToken: "" }) + .setName(this._generateDefaultNewAuthName()); + + this._dir.addAuth(auth); + + let newItem = new BrowserAuthItem(auth); + this._attachAllEventHandlersToAuthItem(newItem); + this._authContainer.append([ newItem ]); + } + + private _onAuthItemDeleteRequested = async (e: Event): Promise => { + let authItem: BrowserAuthItem = typeAflonTarget(e, BrowserAuthItem); + if (authItem == null) return; + + let auth = authItem.getHttpAuth(); + + if (! await SimpleModals.confirm(`Are you sure you want to delete authorization '${auth.getName()}'?`)) return; + + this.raise(this.eventItemAboutToBeDeleted, { authItem }); + this._removeAllEventHandlersFromAuthItem(authItem); + this._authContainer.removeChild(authItem); + this._dir.removeAuth(auth); + }; + + private _onNewReqtRequested(): void { + this.setExpanded(true); + + let newRequest: HttpReqt = new HttpReqt() + .setName(this._generateDefaultNewRequestName()); + + this._dir.addReqt(newRequest); + + let newItem = new BrowserRequestItem(newRequest); + this._attachAllEventHandlersToReqtItem(newItem); + + this._reqtContainer.append([ newItem ]); + this.raise(this.eventRequestPinSelected, { request: newItem, makeNameEditable: true }); + } + + private _onReqItemDeleteRequested = async (e: Event): Promise => { + let reqItem: BrowserRequestItem = typeAflonTarget(e, BrowserRequestItem); + if (reqItem == null) return; + let reqt = reqItem.getHttpReqt(); + + if (! await SimpleModals.confirm(`Are you sure you want to delete request '${reqt.getName()}'?`)) return; + + this.raise(this.eventItemAboutToBeDeleted, { reqItem }); + this._removeAllEventHandlersFromReqtItem(reqItem); + this._reqtContainer.removeChild(reqItem); + this._dir.removeReqt(reqt); + }; + + private _onNewDirRequested(): void { + this.setExpanded(true); + + let newDir: HttpDir = new HttpDir() + .setName(this._generateDefaultNewDirName()); + + this._dir.addDir(newDir); + + let newItem = new BrowserDirItem(newDir); + this._attachAllEventHandlersToDirItem(newItem); + this._dirContainer.append([ newItem ]); + } + + private _onDirItemDeleteRequested = async (e: Event): Promise => { + let dirItem: BrowserDirItem = typeAflonTarget(e, BrowserDirItem); + if (dirItem == null) return; + let dir = dirItem.getHttpDir(); + + if (! await SimpleModals.confirm(`Are you sure you want to delete directory '${dir.getName()}'?`)) return; + + this.raise(this.eventItemAboutToBeDeleted, { dirItem }); + this._removeAllEventHandlerFromDirItem(dirItem); + this._dirContainer.removeChild(dirItem); + this._dir.removeDir(dir); + }; + + private _onDeleteRequested(): void { + this.raise(this.eventDeleteRequested); + } + + private _onClick(): void { + this.setExpanded(!this.getExpanded()); + } + + private _onMouseEnter(): void { + this._optionsDiv.setInlineCss({ + display: "flex" + }); + } + + private _onMouseLeave(): void { + this._optionsDiv.setInlineCss({ + display: "none" + }); + } + + private _onBrowserItemChange(): void { + let newName = this._browserItemName.getText(); + + if (this._dir.getParent().containsChild(newName)) { + SimpleModals.alert(`Cannot rename item: another item with name ${newName} already exists.`); + this._browserItemName.setText(this._dir.getName()); + return; + } + + this._dir.setName(this._browserItemName.getText()); + } + + private _onRenameRequested(): void { + setTimeout(() => this._browserItemName.makeChangeReady(), 10); + } + + private _onPaste(): void { + const item = RequestBrowserClipboard.getValue(); + const action = RequestBrowserClipboard.getAction(); + + if (this._dir.containsChild(item.getName())) { + SimpleModals.alert(`Cannot paste here because item with name ${item.getName()} already exists.`); + return; + } + + let parent = null; + if (item && item.parent()) + parent = item.parent().parent(); + + if (item == this || parent == this) return; + + if (action == RequestBrowserClipboardAction.Cut) { + if (item instanceof BrowserRequestItem) { + item.getHttpReqt().getParent().removeReqt(item.getHttpReqt()); + item.parent().removeChild(item); + this._dir.addReqt(item.getHttpReqt()); + if (this._expanded) { + let reqItem = new BrowserRequestItem(item.getHttpReqt()); + this._attachAllEventHandlersToReqtItem(reqItem); + this._reqtContainer.append([ reqItem ]); + } + } else if (item instanceof BrowserDirItem) { + item.getHttpDir().getParent().removeDir(item.getHttpDir()); + item.parent().removeChild(item); + this._dir.addDir(item.getHttpDir()); + if (this._expanded) { + let dirItem = new BrowserDirItem(item.getHttpDir()); + this._attachAllEventHandlersToDirItem(dirItem); + this._dirContainer.append([ dirItem ]); + } + } + RequestBrowserClipboard.clearValue(); + } else if (action == RequestBrowserClipboardAction.Copy) { + if (item instanceof BrowserRequestItem) { + let itemClone = item.getHttpReqt().clone(); + this._dir.addReqt(itemClone); + if (this._expanded) { + let reqItem = new BrowserRequestItem(itemClone); + this._attachAllEventHandlersToReqtItem(reqItem); + this._reqtContainer.append([ reqItem ]); + } + } else if (item instanceof BrowserDirItem) { + let itemClone = item.getHttpDir().clone(); + this._dir.addDir(itemClone); + if (this._expanded) { + let dirItem = new BrowserDirItem(itemClone); + this._attachAllEventHandlersToDirItem(dirItem); + this._dirContainer.append([ dirItem ]); + } + } + } + } + + private _onReqItemSelected = (e: Event): void => { + let reqItem: BrowserRequestItem = typeAflonTarget(e, BrowserRequestItem); + if (reqItem == null) return; + this.raise(this.eventRequestSelected, { request: reqItem }); + }; + + private _onAuthItemSelected = (e: Event): void => { + let authItem: BrowserAuthItem = typeAflonTarget(e, BrowserAuthItem); + if (authItem == null) return; + this.raise(this.eventAuthSelected, { auth: authItem }); + }; + + private _onReqItemPinSelected = (e: Event): void => { + let reqItem: BrowserRequestItem = typeAflonTarget(e, BrowserRequestItem); + if (reqItem == null) return; + this.raise(this.eventRequestPinSelected, { request: reqItem }); + }; + + private _onAuthItemPinSelected = (e: Event): void => { + let authItem: BrowserAuthItem = typeAflonTarget(e, BrowserAuthItem); + if (authItem == null) return; + this.raise(this.eventAuthPinSelected, { auth: authItem }); + }; + + private _onReqItemDuplicateRequested = (e: Event): void => { + let reqItem: BrowserRequestItem = typeAflonTarget(e, BrowserRequestItem); + if (reqItem == null) return; + let itemClone = reqItem.getHttpReqt().clone(); + let name: string = itemClone.getName(); + let suffix = " - Copy"; + + while (this._dir.containsChild(name)) { + name += suffix; + } + + itemClone.setName(name); + this._dir.addReqt(itemClone); + if (this._expanded) { + let reqItem = new BrowserRequestItem(itemClone); + this._attachAllEventHandlersToReqtItem(reqItem); + this._reqtContainer.append([ reqItem ]); + } + }; + + private _onDirAuthSelected = + (e: Event): this => this.raise(this.eventAuthSelected, { ... e["detail"] }); + + private _onDirAuthPinSelected = + (e: Event): this => this.raise(this.eventAuthPinSelected, { ... e["detail"] }); + + private _onDirRequestSelected = + (e: Event): this => this.raise(this.eventRequestSelected, { ... e["detail"] }); + + private _onDirRequestPinSelected = + (e: Event): this => this.raise(this.eventRequestPinSelected, { ... e["detail"] }); + + private _onItemAboutToBeDeleted = + (e: Event): this => this.raise(this.eventItemAboutToBeDeleted, { ... e["detail"] }); + + private _onContextMenuSelected(e: SimpleEvent): void { + const id = e["detail"]["id"]; + if (!id) return; + + if (id == "new-req") { + this._onNewReqtRequested(); + } else if (id == "new-dir") { + this._onNewDirRequested(); + } else if (id == "new-auth") { + this._onNewAuthRequested(); + } else if (id == "cut") { + RequestBrowserClipboard.setValueToCut(this); + } else if (id == "copy") { + RequestBrowserClipboard.setValueToCopy(this); + } else if (id == "paste") { + this._onPaste(); + } else if (id == "rename") { + this._onRenameRequested(); + } else if (id == "delete") { + this._onDeleteRequested(); + } + } + + private _onContextMenuAboutToBeShown(): void { + if (RequestBrowserClipboard.getValue() == null || + RequestBrowserClipboard.getAction() == RequestBrowserClipboardAction.None) { + this._contextMenu.setItemDisabled("paste", true); + } else { + this._contextMenu.setItemDisabled("paste", false); + } + } + + private _onDragStart(): void { + RequestBrowserClipboard.setValueToCut(this); + } + + private _onDragOver(e: Event): void { + e.preventDefault(); + let dragEvent = e; + + let clipboardValue = RequestBrowserClipboard.getValue(); + + let parent = null; + if (clipboardValue && clipboardValue.parent()) + parent = clipboardValue.parent().parent(); + + if (clipboardValue == this || parent == this) { + dragEvent.dataTransfer.effectAllowed = "none"; + } else { + dragEvent.dataTransfer.effectAllowed = "move"; + } + + } + + private _onDragEnter(): void { + let clipboardValue = RequestBrowserClipboard.getValue(); + + let parent = null; + if (clipboardValue && clipboardValue.parent()) + parent = clipboardValue.parent().parent(); + + if (clipboardValue == this || parent == this) return; + + this._itemContainer.setInlineCss({ + outline: `solid 1px ${Colors.workspaceLine}`, + outlineOffset: "-1px" + }); + } + + private _onDragLeave(): void { + this._itemContainer.setInlineCss({ + outline: "none", + outlineOffset: "0" + }); + } + + private _onDrop(e: Event): void { + e.preventDefault(); + this._itemContainer.setInlineCss({ + outline: "none", + outlineOffset: "0" + }); + this._onPaste(); + } +} + +BrowserDirItem.style = { + _: { + display: "flex", + flexFlow: "column nowrap", + width: "100%", + position: "relative" + }, + _itemContainer: { + display: "flex", + flexFlow: "row nowrap", + height: "25px", + width: "100%", + paddingRight: "10px", + "&:hover": { + background: Colors.browserBackHover + } + }, + _authContainer: { + display: "flex", + flexFlow: "column nowrap", + paddingLeft: "23px" + }, + _dirContainer: { + display: "flex", + flexFlow: "column nowrap", + paddingLeft: "23px" + }, + _reqtContainer: { + display: "flex", + flexFlow: "column nowrap", + paddingLeft: "23px" + }, + _triangle: { + ...FontStyles.sansSerifExtraBold, + flex: "0 0 28px", + height: "100%", + color: Colors.browserDefault, + fontSize: "10px", + lineHeight: "25px", + textAlign: "center", + cursor: "pointer" + }, + _browserItemName: { + flex: "1 1 100px", + height: "100%" + }, + _optionsDiv: { + display: "none", + flexFlow: "row nowrap" + } +}; diff --git a/src/renderer/ui/RequestBrowser/BrowserItemName.ts b/src/renderer/ui/RequestBrowser/BrowserItemName.ts new file mode 100644 index 0000000..6640726 --- /dev/null +++ b/src/renderer/ui/RequestBrowser/BrowserItemName.ts @@ -0,0 +1,84 @@ +import { TextBox, Div } from "aflon"; + +import { FontStyles, Colors } from "../StyleConstants"; + +export class BrowserItemName extends Div { + public eventChange = "change"; + + private _textBox: TextBox; + private _overlay: Div; + + constructor() { + super(); + + this.append([ + (this._textBox = new TextBox()) + .setDisabled(true) + .on(this._textBox.eventFocusOut, () => this._onTextBoxFocusOut()) + .on(this._textBox.eventClick, (e) => this._onTextBoxClick(e)) + .on(this._textBox.eventKeyDown, (e) => this._onTextBoxKeyDown(e)), + (this._overlay = new Div()) + ]); + } + + makeChangeReady(): this { + this._overlay.setInlineCss({ display: "none" }); + this._textBox.setDisabled(false); + this._textBox.focus(); + ( this._textBox.getHtmlElement()).setSelectionRange(0, this.getText().length); + return this; + } + + setText(text: string): this { + this._textBox.setText(text); + return this; + } + + getText(): string { + return this._textBox.getText(); + } + + private _onTextBoxClick(e: Event): void { + e.stopPropagation(); + } + + private _onTextBoxFocusOut(): void { + this._overlay.setInlineCss({ display: "block" }); + this._textBox.setDisabled(true); + } + + private _onTextBoxKeyDown(e: Event): void { + const key = ( e).key; + + if (key == "Enter") { + this._onTextBoxFocusOut(); + } + } +} + +BrowserItemName.style = { + _: { + position: "relative" + }, + _textBox: { + ...FontStyles.sansSerifNormal, + fontSize: "12px", + color: Colors.browserDefault, + border: "none", + outline: "none", + background: "none", + position: "absolute", + width: "100%", + top: "3px", + bottom: "3px" + }, + _overlay: { + background: "transparent", + position: "absolute", + top: 0, + left: 0, + right: 0, + bottom: 0, + cursor: "pointer" + } +}; diff --git a/src/renderer/ui/RequestBrowser/BrowserRequestItem.ts b/src/renderer/ui/RequestBrowser/BrowserRequestItem.ts new file mode 100644 index 0000000..55e5b58 --- /dev/null +++ b/src/renderer/ui/RequestBrowser/BrowserRequestItem.ts @@ -0,0 +1,276 @@ +import { Element, Div } from "aflon"; + +import { HttpReqt } from "../../lib/http"; +import { SimpleEvent } from "../../lib/SimpleEvent"; + +import { ContextMenu, ContextMenuItemType } from "../ContextMenu"; +import { Colors, FontStyles, getMethodColor, getShortMethodDesignation } from "../StyleConstants"; +import { SimpleModals } from "../Modals"; + +import { BrowserItemName } from "./BrowserItemName"; +import { IconButton } from "../IconButton"; +import { RequestBrowser } from "./RequestBrowser"; +import { RequestBrowserClipboard, RequestBrowserClipboardAction } from "./RequestBrowserClipboard"; + +export class BrowserRequestItem extends Div { + public eventSelected = "selected"; + public eventPinSelected = "pinSelected"; + public eventDeleteRequested = "deleteRequested"; + public eventDuplicateRequested = "duplicateRequested"; + + private _typeDiv: Div; + private _browserItemName: BrowserItemName; + private _optionsDiv: Div; + + private _contextMenu: ContextMenu; + + private _reqt: HttpReqt; + + constructor(reqt: HttpReqt) { + super(); + + this._reqt = reqt; + + this.append([ + (this._typeDiv = new Div()) + .setText(getShortMethodDesignation(reqt.getRawHttpRequest().method)) + .setInlineCss({ color: getMethodColor(reqt.getRawHttpRequest().method) }), + (this._browserItemName = new BrowserItemName()) + .setText(reqt.getName()) + .on(this._browserItemName.eventClick, () => this._onNameClick()) + .on(this._browserItemName.eventChange, () => this._onBrowserItemNameChange()), + (this._optionsDiv = new Div()) + .append([ + new IconButton("send") + .setTooltip("Send request") + .on("click", () => this._onSendClick()), + new IconButton("pin") + .setTooltip("Pin to workspace") + .on("click", () => this._onPinClick()), + new IconButton("delete") + .setTooltip("Delete request") + .on("click", () => this._onDeleteClick()) + + ]) + ]) + .addAttr("draggable", "true") + .on(this.eventMouseEnter, () => this._onMouseEnter()) + .on(this.eventMouseLeave, () => this._onMouseLeave()) + .on(this.eventDblClick, () => this._onDblClick()) + .on(this.eventDragStart, () => this._onDragStart()) + .on(this.eventDragOver, e => this._onDragOver(e)) + .on(this.eventDragEnter, () => this._onDragEnter()) + .on(this.eventDragLeave, () => this._onDragLeave()) + .on(this.eventDrop, e => this._onDrop(e)); + + (this._contextMenu = new ContextMenu(this, [ + { id: "send", type: ContextMenuItemType.Button, text: "Send", iconName: "send" }, + { id: "divider", type: ContextMenuItemType.Divider }, + { id: "preview", type: ContextMenuItemType.Button, text: "Preview" }, + { id: "pin", type: ContextMenuItemType.Button, text: "Pin", iconName: "pin" }, + { id: "divider", type: ContextMenuItemType.Divider }, + { id: "cut", type: ContextMenuItemType.Button, text: "Cut" }, + { id: "copy", type: ContextMenuItemType.Button, text: "Copy" }, + { id: "duplicate", type: ContextMenuItemType.Button, text: "Duplicate" }, + { id: "divider", type: ContextMenuItemType.Divider }, + { id: "rename", type: ContextMenuItemType.Button, text: "Rename" }, + { id: "delete", type: ContextMenuItemType.Button, text: "Delete", iconName: "delete" } + ])) + .on(this._contextMenu.eventSelected, e => this._onContextMenuSelected(e)); + + this._reqt + .on(this._reqt.eventNameChanged, () => this._onReqtNameChanged()) + .on(this._reqt.eventMethodChanged, () => this._onReqtMethodChanged()); + } + + public getHttpReqt(): HttpReqt { + return this._reqt; + } + + public getName(): string { + return this._reqt.getName(); + } + + private _findParentRequestBrowser(): RequestBrowser { + // eslint-disable-next-line @typescript-eslint/no-this-alias + let e: Element = this; + + while (e != null) { + if (e instanceof RequestBrowser) + return e; + + e = e.parent(); + } + + throw new Error("Parent RequestBrowser cannot be found."); + } + + private _onMouseEnter(): void { + this._optionsDiv.setInlineCss({ + display: "flex" + }); + } + + private _onMouseLeave(): void { + this._optionsDiv.setInlineCss({ + display: "none" + }); + } + + private _onDblClick(): void { + this.raise(this.eventPinSelected); + } + + private _onSendClick(): void { + this._findParentRequestBrowser().reportReqtSendRequested(this._reqt); + } + + private _onPinClick(): void { + this.raise(this.eventPinSelected); + } + + private _onNameClick(): void { + this.raise(this.eventSelected); + } + + private _onDeleteClick(): void { + this.raise(this.eventDeleteRequested); + } + + private _onReqtNameChanged(): void { + this._browserItemName.setText(this._reqt.getName()); + } + + private _onReqtMethodChanged(): void { + this._typeDiv + .setText(getShortMethodDesignation(this._reqt.getRawHttpRequest().method)) + .setInlineCss({ color: getMethodColor(this._reqt.getRawHttpRequest().method) }); + } + + private _onBrowserItemNameChange(): void { + let newName = this._browserItemName.getText(); + + if (this._reqt.getParent().containsChild(newName)) { + SimpleModals.alert(`Cannot rename item: another item with name ${newName} already exists.`); + this._browserItemName.setText(this._reqt.getName()); + return; + } + + this._reqt.setName(this._browserItemName.getText()); + } + + private _onRenameRequested(): void { + setTimeout(() => this._browserItemName.makeChangeReady(), 10); + } + + private _onContextMenuSelected(e: SimpleEvent): void { + const id = e["detail"]["id"]; + if (!id) return; + + if (id == "send") { + this._findParentRequestBrowser().reportReqtSendRequested(this._reqt); + } else if (id == "preview") { + this.raise(this.eventSelected); + } else if (id == "pin") { + this.raise(this.eventPinSelected); + } else if (id == "cut") { + RequestBrowserClipboard.setValueToCut(this); + } else if (id == "copy") { + RequestBrowserClipboard.setValueToCopy(this); + } else if (id == "duplicate") { + this.raise(this.eventDuplicateRequested); + } else if (id == "rename") { + this._onRenameRequested(); + } else if (id == "delete") { + this.raise(this.eventDeleteRequested); + } + } + + private _onDragStart(): void { + RequestBrowserClipboard.setValueToCut(this); + } + + private _onDragOver(e: Event): void { + e.preventDefault(); + let dragEvent = e; + + let clipboardValue = RequestBrowserClipboard.getValue(); + + if (clipboardValue.parent() == this.parent() && clipboardValue instanceof BrowserRequestItem) { + dragEvent.dataTransfer.effectAllowed = "move"; + } else { + dragEvent.dataTransfer.effectAllowed = "none"; + } + } + + private _onDragEnter(): void { + this.setInlineCss({ + borderBottom: `solid 1px ${Colors.workspaceLine}` + }); + } + + private _onDragLeave(): void { + this.setInlineCss({ + borderBottom: "none" + }); + } + + private _onDrop(e: Event): void { + e.preventDefault(); + this.setInlineCss({ + borderBottom: "none" + }); + + const item = RequestBrowserClipboard.getValue(); + const action = RequestBrowserClipboard.getAction(); + + if (action != RequestBrowserClipboardAction.Cut) return; + if (!(item instanceof BrowserRequestItem)) return; + if (item == this) return; + + if (this.parent() != item.parent() && this.getHttpReqt().getParent().containsChild(item.getName())) { + SimpleModals.alert(`Cannot move item ${item.getName()} because item with same name already exists.`); + return; + } + + item.getHttpReqt().getParent().removeReqt(item.getHttpReqt()); + this.getHttpReqt().getParent().addReqt(item.getHttpReqt(), this.getHttpReqt()); + + item.parent().removeChild(item); + this.insertAfter(item); + + RequestBrowserClipboard.clearValue(); + } +} + +BrowserRequestItem.style = { + _: { + display: "flex", + flexFlow: "row nowrap", + alignItems: "center", + height: "25px", + width: "100%", + paddingRight: "10px", + userSelect: "none", + position: "relative", + "&:hover": { + background: Colors.browserBackHover + } + }, + _typeDiv: { + ...FontStyles.sansSerifExtraBold, + flex: "0 0 28px", + color: Colors.browserDefault, + fontSize: "8px", + //lineHeight: "25px", + textAlign: "center" + }, + _browserItemName: { + flex: "1 1 100px", + height: "100%" + }, + _optionsDiv: { + display: "none", + flexFlow: "row nowrap" + } +}; diff --git a/src/renderer/ui/RequestBrowser/RequestBrowser.ts b/src/renderer/ui/RequestBrowser/RequestBrowser.ts new file mode 100644 index 0000000..34a5709 --- /dev/null +++ b/src/renderer/ui/RequestBrowser/RequestBrowser.ts @@ -0,0 +1,572 @@ +import { Br, Div, typeAflonTarget, Span } from "aflon"; + +import { HttpCollection, CollectionOpeningError, HttpReqt, HttpRequestMethod, HttpTextBody, HttpBodyContentType } from "../../lib/http"; +import { Postman2dot1CollectionConverter } from "../../lib/interop/collections/Postman2dot1CollectionConverter"; +import { error } from "../../lib/Logger"; +import { ConversionError, IThirdPartyCollectionConverter } from "../../lib/interop/collections/IThirdPartyCollectionConverter"; +import { showOpenDialog, showSaveDialog } from "../../lib/SystemDialogs"; + +import { Colors, FontStyles } from "../StyleConstants"; +import { AppStatePersister } from "../AppStatePersister"; +import { IconButton } from "../IconButton"; +import { SimpleModals } from "../Modals"; + +import { BrowserRequestItem } from "./BrowserRequestItem"; +import { BrowserCollectionItem } from "./BrowserCollectionItem"; +import { BrowserDirItem } from "./BrowserDirItem"; +import { BrowserAuthItem } from "./BrowserAuthItem"; + +export class RequestBrowser extends Div { + private static _saveInterval = 5000; + + public eventItemSelected = "itemSelected"; + public eventItemPinSelected = "itemPinSelected"; + public eventItemAboutToBeDeleted = "itemAboutToBeDeleted"; + public eventReqtSendRequested = "reqtSendRequested"; + public eventClosingCollection = "closingCollection"; + + private _header: Div; + private _title: Div; + private _content: Div; + private _placeholder: Div; + private _importIcon: IconButton; + private _newFileIcon: IconButton; + private _experimentIcon: IconButton; + + private _saveTimer: NodeJS.Timeout; + private _externallyModifiedCollections: Array = []; + private _externalModificationPromptShown: boolean = false; + + constructor() { + super(); + + this.append([ + (this._header = new Div()) + .append([ + (this._title = new Div()) + .setText("Request Browser"), + (this._importIcon = new IconButton("import")) + .setTooltip("Import httpiness or third-party collection") + .on("click", () => this._onImportButtonClick()), + (this._newFileIcon = new IconButton("new-file")) + .setTooltip("Create new collection") + .on("click", () => this._onNewCollectionButtonClick()), + (this._experimentIcon = new IconButton("experiment")) + .setTooltip("Experiment. Show local collection for experimenting with requests. " + + "This special collection is not stored in file and cannot be shared.") + .on("click", () => this.importLSExpCollection()) + ]), + (this._content = new Div()), + (this._placeholder = new Div()) + .append([ + new Span().setText("There are no collections opened."), + new Br(), + new Span().setText("To get started, "), + new Span() + .setText("import") + .setInlineCss({ fontWeight: "bold", cursor: "pointer", textDecoration: "underline" }) + .on("click", () => this._onImportButtonClick()), + new Span().setText(" existing collection or "), + new Span() + .setText("create") + .setInlineCss({ fontWeight: "bold", cursor: "pointer", textDecoration: "underline" }) + .on("click", () => this._onNewCollectionButtonClick()), + new Span().setText(" new one.") + ]) + ]); + + this._loadStoredCollections(); + this._updatePlaceholderVisibility(); + + this._saveTimer = setInterval(() => this._onSaveTimeout(), RequestBrowser._saveInterval); + window.addEventListener("beforeunload", this._onBeforeUnload); + } + + createCollection(): void { + this._onNewCollectionButtonClick(); + } + + importCollection(): void { + this._onImportButtonClick(); + } + + importLSExpCollection(): void { + this._importCollection(HttpCollection.localStorageExpCollectionPath); + } + + loadSampleRequest(): void { + let experimentalCollectionItem = this._content.children() + .filter(child => child instanceof BrowserCollectionItem) + .map(item => item) + .find(item => item.getCollection().isLocalExperimental()); + + let experimentalCollection: HttpCollection = null; + + if (experimentalCollectionItem) { + experimentalCollection = experimentalCollectionItem.getCollection(); + this._closeCollection(experimentalCollectionItem); + } else { + const cff = HttpCollection.fromFile(HttpCollection.localStorageExpCollectionPath); + if (cff.error != CollectionOpeningError.Success) return; + experimentalCollection = cff.collection; + } + + let sampleRequest = experimentalCollection.getReqts() + .find(reqt => reqt.getName() == "Sample request"); + + if (!sampleRequest) { + let body = new HttpTextBody(); + body.value = "{ \"message\": \"Hello world from ${HTTPINESS}!\" }"; + body.valueType = HttpBodyContentType.Text; + + sampleRequest = + new HttpReqt() + .setName("Sample request") + .setMethod(HttpRequestMethod.POST) + .setUrl("https://www.httpbin.org/anything") + .setHeaders([ { name: "Content-Type", value: "application/json" } ]) + .setBody(body); + + experimentalCollection.addReqt(sampleRequest); + experimentalCollection.setMacro("HTTPINESS", "httpiness", false); + } + + let newCollectionItem = new BrowserCollectionItem(experimentalCollection); + this._attachEventHandlers(newCollectionItem); + this._content.prepend([ newCollectionItem ]); + newCollectionItem.setExpanded(true); + + this._updateOpenCollectionsCache(); + this._updatePlaceholderVisibility(); + + this.raise(this.eventItemPinSelected, { + item: sampleRequest + }); + } + + reportReqtSendRequested(reqt: HttpReqt): void { + this.raise(this.eventReqtSendRequested, { item: reqt }); + } + + private _updatePlaceholderVisibility(): void { + if (this._content.getChildrenNumber() == 0) { + this._content.setVisibility(false); + this._placeholder.setVisibility(true); + } else { + this._content.setVisibility(true); + this._placeholder.setVisibility(false); + } + } + + private async _promptUserForExternallyModifiedCollections(): Promise { + if (this._externalModificationPromptShown) return; + if (this._externallyModifiedCollections.length == 0) return; + + this._externalModificationPromptShown = true; + + let result = await SimpleModals.choose( + `Some collection files have been modified outside httpiness. + + How would you like to resolve conflicts?`, + [ + { text: "Reload collections from modified files", id: "reload", default: true }, + { text: "Discard changes in modified files", id: "discard" } + ] + ); + + if (result == "reload") { + this._externallyModifiedCollections.forEach(collection => this._reloadCollection(collection)); + } else if (result == "discard") { + this._externallyModifiedCollections.forEach(collection => collection.getCollection().save()); + } + + this._externalModificationPromptShown= false; + this._externallyModifiedCollections = []; + } + + private _saveCollections(): void { + this._content.children().forEach(child => { + if (!(child instanceof BrowserCollectionItem)) return; + + let collectionItem = child; + + if (collectionItem.getCollection().isModifiedExternally()) { + if (!this._externallyModifiedCollections.includes(collectionItem)) + this._externallyModifiedCollections.push(collectionItem); + } else if (collectionItem.getCollection().isDirty()) + collectionItem.getCollection().save(); + }); + } + + private _updateOpenCollectionsCache(): void { + let openCollections: Array = []; + + this._content.children().forEach(child => { + if (!(child instanceof BrowserCollectionItem)) return; + let collectionItem = child; + openCollections.push(collectionItem.getCollection().getFilePath()); + }); + + AppStatePersister.setOpenedCollections(openCollections); + } + + private _loadStoredCollections(): void { + try { + let rawCollectionPaths: Array = AppStatePersister.getOpenedCollections(); + + if (!rawCollectionPaths.length) return; + + let collections: Array = []; + + rawCollectionPaths.forEach(path => { + if (!HttpCollection.isValidPath(path)) return; + + const result = HttpCollection.fromFile(path); + if (result.error != CollectionOpeningError.Success) return; + + let newCollectionItem = new BrowserCollectionItem(result.collection); + this._attachEventHandlers(newCollectionItem); + + collections.push(newCollectionItem); + }); + + this._content.append(collections); + + setTimeout(() => { + AppStatePersister.getOpenedWItems().reverse().forEach(record => { + let targetCollection = collections.find(collection => collection.getCollection().getUuid() == record.collectionUuid); + if (!targetCollection) return; + + let item = targetCollection.getCollection().findFromAbsolutePath(record.path); + if (!item) return; + + if (record.pinned) + this.raise(this.eventItemPinSelected, { item }); + else + this.raise(this.eventItemSelected, { item }); + }); + }, 0); + } catch (ex) { + error(`There was an error loading previously opened collections: ${ex}`, {error: ex as Error}); + this._content.empty(); + } + } + + private _reloadCollection(collectionItem: BrowserCollectionItem): void { + this.raise(this.eventClosingCollection, { + collection: collectionItem.getCollection() + }); + + this._removeEventHandlers(collectionItem); + + let path = collectionItem.getCollection().getFilePath(); + + if (!HttpCollection.isValidPath(path)) return; + + const result = HttpCollection.fromFile(path); + if (result.error != CollectionOpeningError.Success) return; + + let newCollectionItem = new BrowserCollectionItem(result.collection); + this._attachEventHandlers(newCollectionItem); + + collectionItem.insertAfter(newCollectionItem); + this._content.removeChild(collectionItem); + } + + private async _tryThirdPartyCollectionConversion(pathToCollection: string): Promise { + const convertor: IThirdPartyCollectionConverter = new Postman2dot1CollectionConverter(pathToCollection); + const result = convertor.convert(); + + if (result.error != ConversionError.Success) { + await SimpleModals.alert(`There was an error opening and converting third-party collection ${pathToCollection}.`); + return null; + } + + if (result.notSupported.size == 0) { + await SimpleModals.alert("Conversion of third-party collection has been successful. " + + "Where would you like to save converted httpiness collection?", "Browse"); + } else { + let skipped = ""; + result.notSupported.forEach(feature => skipped += " " + feature + ","); + skipped = skipped.slice(0, -1); + + + await SimpleModals.alert("Conversion of third-party collection has been successful, " + + "but some features might have been skipped because httpiness does not support them yet. " + + "These features are:" + skipped + ".\n\n" + + "Where would you like to save converted httpiness collection?", "Browse"); + } + + const sfdResult = await showSaveDialog({ + title: "Save converted httpiness collection...", + filters: [{ name: "httpiness JSON Collection", extensions: ["json"] }], + properties: [ "createDirectory", "showOverwriteConfirmation" ] + }); + + if (sfdResult.canceled) return null; + + const sResult = convertor.save(sfdResult.filePath, sfdResult.filePath + ".conversion.log"); + + if (!sResult) { + await SimpleModals.alert("There was an error saving converted collection."); + return null; + } + + const cff = HttpCollection.fromFile(sfdResult.filePath); + + if (cff.error != CollectionOpeningError.Success) { + await SimpleModals.alert("There was an error opening converted collection."); + return null; + } + + return cff.collection; + } + + private async _onImportButtonClick(): Promise { + const result = await showOpenDialog({ + title: "Open collection file...", + filters: [{ name: "httpiness or third-party JSON Collection", extensions: ["json"] }], + properties: [ "openFile", "createDirectory" ] + }); + + if (result.canceled) return; + + await this._importCollection(result.filePaths[0]); + } + + private async _importCollection(pathToCollection: string): Promise { + let collection = null; + const cff = HttpCollection.fromFile(pathToCollection); + + if (cff.error == CollectionOpeningError.Success) { + collection = cff.collection; + } else if (cff.error == CollectionOpeningError.UnknownError) { + await SimpleModals.alert(`There was an error opening httpiness collection ${pathToCollection}. ` + + "Collection file might be corrupted."); + return; + } else if (cff.error == CollectionOpeningError.UnsupportedVersion) { + await SimpleModals.alert(`There was an error opening httpiness collection ${pathToCollection}. ` + + "Collection schema is not supported. Please download latest version of httpiness and try again."); + return; + } else if (cff.error == CollectionOpeningError.UnknownVersion) { + collection = await this._tryThirdPartyCollectionConversion(pathToCollection); + } else { + throw new Error(`Cannot handle CollectionFromFileError ${cff.error}.`); + } + + if (collection == null) return; + + let indexOfAlreadyImportedCollection = this._content.children() + .filter(child => child instanceof BrowserCollectionItem) + .map(item => (item).getCollection()) + .findIndex(col => col.getFilePath() == collection.getFilePath()); + + if (indexOfAlreadyImportedCollection != -1) { + await SimpleModals.alert("Cannot import collection because it has already been imported."); + return; + } + + let newCollectionItem = new BrowserCollectionItem(collection); + this._attachEventHandlers(newCollectionItem); + + if (collection.isLocalExperimental()) { + this._content.prepend([newCollectionItem]); + } else { + this._content.append([newCollectionItem]); + } + + newCollectionItem.setExpanded(true); + + this._updateOpenCollectionsCache(); + this._updatePlaceholderVisibility(); + } + + private async _onNewCollectionButtonClick(): Promise { + const result = await showSaveDialog({ + title: "Create new collection...", + filters: [{ name: "httpiness JSON Collection", extensions: ["json"] }], + properties: [ "createDirectory", "showOverwriteConfirmation" ] + }); + + if (result.canceled) return; + + let collectionResult = HttpCollection.fromFile(result.filePath); + if (collectionResult.error != CollectionOpeningError.Success) return; + + let newCollectionItem = new BrowserCollectionItem(collectionResult.collection); + this._attachEventHandlers(newCollectionItem); + this._content.append([newCollectionItem]); + + this._updateOpenCollectionsCache(); + this._updatePlaceholderVisibility(); + } + + private _attachEventHandlers(collectionItem: BrowserCollectionItem): void { + collectionItem + .on(collectionItem.eventRequestSelected, this._onRequestSelected) + .on(collectionItem.eventRequestPinSelected, this._onRequestPinSelected) + .on(collectionItem.eventCloseRequested, this._onCloseRequested) + .on(collectionItem.eventItemAboutToBeDeleted, this._onItemAboutToBeDeleted) + .on(collectionItem.eventAuthSelected, this._onAuthSelected) + .on(collectionItem.eventAuthPinSelected, this._onAuthPinSelected); + } + + private _removeEventHandlers(collectionItem: BrowserCollectionItem): void { + collectionItem + .off(collectionItem.eventRequestSelected, this._onRequestSelected) + .off(collectionItem.eventRequestPinSelected, this._onRequestPinSelected) + .off(collectionItem.eventCloseRequested, this._onCloseRequested) + .off(collectionItem.eventItemAboutToBeDeleted, this._onItemAboutToBeDeleted) + .off(collectionItem.eventAuthSelected, this._onAuthSelected) + .off(collectionItem.eventAuthPinSelected, this._onAuthPinSelected); + } + + private _closeCollection(collectionItem: BrowserCollectionItem): void { + this.raise(this.eventClosingCollection, { + collection: collectionItem.getCollection() + }); + + this._removeEventHandlers(collectionItem); + this._content.removeChild(collectionItem); + this._updateOpenCollectionsCache(); + this._updatePlaceholderVisibility(); + + collectionItem.getCollection().save(); + } + + private _onRequestSelected = (e: Event): void => { + this.raise(this.eventItemSelected, { + item: (e["detail"]["request"]).getHttpReqt(), + makeWorkspaceNameEditable: (e["detail"]["makeNameEditable"]) + }); + }; + + private _onRequestPinSelected = (e: Event): void => { + this.raise(this.eventItemPinSelected, { + item: (e["detail"]["request"]).getHttpReqt() + }); + }; + + private _onItemAboutToBeDeleted = (e: Event): void => { + const details = e["detail"]; + + if (details["reqItem"] && details["reqItem"] instanceof BrowserRequestItem) { + this.raise(this.eventItemAboutToBeDeleted, { item: (details["reqItem"]).getHttpReqt() }); + return; + } + + if (details["dirItem"] && details["dirItem"] instanceof BrowserDirItem) { + this.raise(this.eventItemAboutToBeDeleted, { item: (details["dirItem"]).getHttpDir() }); + return; + } + + if (details["authItem"] && details["authItem"] instanceof BrowserAuthItem) { + this.raise(this.eventItemAboutToBeDeleted, { item: (details["authItem"]).getHttpAuth() }); + return; + } + }; + + private _onCloseRequested = (e: Event): void => { + let sender = typeAflonTarget(e, BrowserCollectionItem); + if (sender == null) return; + + this._closeCollection(sender); + }; + + private _onAuthSelected = (e: Event): void => { + this.raise(this.eventItemSelected, { + item: (e["detail"]["auth"]).getHttpAuth() + }); + }; + + private _onAuthPinSelected = (e: Event): void => { + this.raise(this.eventItemPinSelected, { + item: (e["detail"]["auth"]).getHttpAuth() + }); + }; + + private _onSaveTimeout(): void { + this._saveCollections(); + this._promptUserForExternallyModifiedCollections(); + } + + private _onBeforeUnload = (): void => { + this._saveCollections(); + clearInterval(this._saveTimer); + + this._updateOpenCollectionsCache(); + + window.removeEventListener("beforeunload", this._onBeforeUnload); + }; +} + +RequestBrowser.style = { + _: { + backgroundColor: Colors.browserBackDefault, + minWidth: "200px", + display: "flex", + flexFlow: "column nowrap", + height: "100%", + borderRight: `solid 1px ${Colors.workspaceLine}` + }, + _header: { + display: "flex", + flexFlow: "row nowrap", + padding: "5px 10px" + }, + _title: { + ...FontStyles.sansSerifBold, + fontSize: "12px", + lineHeight: "25px", + flex: "1 1 100px", + color: Colors.consoleDominant + }, + _content: { + display: "flex", + flexFlow: "column nowrap", + flex: "1 0 1px", + overflowY: "auto" + }, + _placeholder: { + ...FontStyles.sansSerifNormal, + color: Colors.browserDefault, + transition: "opacity 0.5s", + opacity: "0.6", + fontSize: "12px", + textAlign: "center", + padding: "20px", + paddingTop: "50px", + flex: "1 1 200px", + "&:hover": { + opacity: "1.0", + transition: "opacity 0.5s" + } + }, + _importIcon: { + fontSize: "13px", + borderRadius: "4px", + width: "25px", + height: "25px", + "&:hover": { + background: Colors.browserBackSelected + } + }, + _newFileIcon: { + fontSize: "14px", + borderRadius: "4px", + width: "25px", + height: "25px", + "&:hover": { + background: Colors.browserBackSelected + } + }, + _experimentIcon: { + fontSize: "14px", + borderRadius: "4px", + width: "25px", + height: "25px", + "&:hover": { + background: Colors.browserBackSelected + } + } +}; diff --git a/src/renderer/ui/RequestBrowser/RequestBrowserClipboard.ts b/src/renderer/ui/RequestBrowser/RequestBrowserClipboard.ts new file mode 100644 index 0000000..548ff69 --- /dev/null +++ b/src/renderer/ui/RequestBrowser/RequestBrowserClipboard.ts @@ -0,0 +1,37 @@ +import { BrowserAuthItem } from "./BrowserAuthItem"; +import { BrowserDirItem } from "./BrowserDirItem"; +import { BrowserRequestItem } from "./BrowserRequestItem"; + +export enum RequestBrowserClipboardAction { + Cut, Copy, None +} + +export type BrowserItem = BrowserDirItem | BrowserRequestItem | BrowserAuthItem; + +export class RequestBrowserClipboard { + private static _value: BrowserItem = null; + private static _action: RequestBrowserClipboardAction = RequestBrowserClipboardAction.None; + + static setValueToCopy(item: BrowserItem): void { + RequestBrowserClipboard._value = item; + RequestBrowserClipboard._action = RequestBrowserClipboardAction.Copy; + } + + static setValueToCut(item: BrowserItem): void { + RequestBrowserClipboard._value = item; + RequestBrowserClipboard._action = RequestBrowserClipboardAction.Cut; + } + + static getValue(): BrowserItem { + return RequestBrowserClipboard._value; + } + + static getAction(): RequestBrowserClipboardAction { + return RequestBrowserClipboard._action; + } + + static clearValue(): void { + RequestBrowserClipboard._value = null; + RequestBrowserClipboard._action = RequestBrowserClipboardAction.None; + } +} diff --git a/src/renderer/ui/RequestBrowser/index.ts b/src/renderer/ui/RequestBrowser/index.ts new file mode 100644 index 0000000..bf42dc4 --- /dev/null +++ b/src/renderer/ui/RequestBrowser/index.ts @@ -0,0 +1 @@ +export { RequestBrowser } from "./RequestBrowser"; diff --git a/src/renderer/ui/StyleConstants.ts b/src/renderer/ui/StyleConstants.ts new file mode 100644 index 0000000..327daa4 --- /dev/null +++ b/src/renderer/ui/StyleConstants.ts @@ -0,0 +1,174 @@ +import { CSSProperties } from "aflon"; +import { HttpRequestMethod } from "../lib/http"; +import { PreferenceStore } from "./PreferenceStore"; + +export enum ColorTheme { + Dark = "dark", Light = "light" +} + +const Themes = { + [ColorTheme.Dark]: { + colors: { + controlBarBackground: "#1A1A1A", + textSelection: "#234854", + backgroundDefault: "#1A1A1A", + workspaceDefault: "#C5C5C5", + workspacePlaceholder: "#6C6C6C", + workspaceAccent: "#00C2FF", + workspaceParameter: "#FF5252", + workspaceDescriptor: "#FFFFFF", + workspaceLine: "#4E4E4E", + workspaceLineWeak: "#323232", + workspaceSearch: "#FFF500", + workspaceSearchFocus: "#FF5252", + browserBackDefault: "#272727", + browserBackSelected: "#1A1A1A", + browserBackHover: "#000000", + browserDefault: "#C5C5C5", + browserTextSelection: "#2522B5", + methodGet: "#00BB13", + methodPost: "#FF8A00", + methodPut: "#3AB4BC", + methodDelete: "#E40000", + methodOther: "#B5B5B5", + consoleBackground: "#272727", + consoleBorder: "#4E4E4E", + consoleDominant: "#FFFFFF", + consoleDefault: "#C5C5C5", + statusOK: "#00BB13", + statusWarn: "#FF8A00", + statusError: "#E40000", + statusForeground: "#FFFFFF", + tooltipText: "#C5C5C5", + tooltipDisabled: "#8B8B8B", + tooltipBackgroundDefault: "#3C3C3C", + tooltipBackgroundHover: "#1A1A1A", + scrollThumb: "#40404044" + }, + boxShadowValues: { + consoleCollapsed: "0px 0px 10px 0px rgba(0,0,0,1)", + consoleExtended: "0px 0px 100px 0px rgba(0,0,0,1)", + contextMenu: "0px 0px 10px 0px rgba(0,0,0,0.7)", + button: "0px 3px 3px 0px rgba(0,0,0,0.5)" + }, + imageUrls: { + wordmark: "./resources/images/HttpinessWordMark.darktheme.svg" + } + }, + [ColorTheme.Light]: { + colors: { + controlBarBackground: "#ffffff", + textSelection: "#e5e5e5", + backgroundDefault: "#ffffff", + workspaceDefault: "#5a5a5a", + workspacePlaceholder: "#939393", + workspaceAccent: "#00C2FF", + workspaceParameter: "#F02D2D", + workspaceDescriptor: "#000000", + workspaceLine: "#d1d1d1", + workspaceLineWeak: "#e1e1e1", + workspaceSearch: "#FFB800", + workspaceSearchFocus: "#FF5252", + browserBackDefault: "#f4f4f4", + browserBackSelected: "#f4f4f4", + browserBackHover: "#ffffff", + browserDefault: "#5a5a5a", + methodGet: "#00BB13", + methodPost: "#FF9900", + methodPut: "#25CDD8", + methodDelete: "#E40000", + methodOther: "#4a4a4a", + consoleBackground: "#f4f4f4", + consoleBorder: "#c1c1c1", + consoleDominant: "#000000", + consoleDefault: "#5a5a5a", + statusOK: "#00BB13", + statusWarn: "#FF8A00", + statusError: "#E40000", + statusForeground: "#FFFFFF", + tooltipText: "#5a5a5a", + tooltipDisabled: "#b0b0b0", + tooltipBackgroundDefault: "#f4f4f4", + tooltipBackgroundHover: "#ffffff", + scrollThumb: "#c0c0c044" + }, + boxShadowValues: { + consoleCollapsed: "0px 0px 7px 0px rgba(0,0,0,0.25)", + consoleExtended: "0px 0px 100px 0px rgba(0,0,0,0.4)", + contextMenu: "0px 3px 10px 0px rgba(0,0,0,0.32)", + button: "0px 3px 3px 0px rgba(0,0,0,0.25)" + }, + imageUrls: { + wordmark: "./resources/images/HttpinessWordMark.lighttheme.svg" + } + } +}; + +let theme: ColorTheme = PreferenceStore.getColorTheme(); + +export const Colors = Themes[theme].colors; +export const BoxShadowValues = Themes[theme].boxShadowValues; +export const ImageUrls = Themes[theme].imageUrls; + +export function getMethodColor(method: HttpRequestMethod): string { + if (method == HttpRequestMethod.GET) { + return Colors.methodGet; + } else if (method == HttpRequestMethod.POST) { + return Colors.methodPost; + } else if (method == HttpRequestMethod.PUT) { + return Colors.methodPut; + } else if (method == HttpRequestMethod.DELETE) { + return Colors.methodDelete; + } else { + return Colors.workspaceDefault; + } +} + +export function getShortMethodDesignation(method: HttpRequestMethod): string { + if (method == HttpRequestMethod.GET) return "GET"; + else if (method == HttpRequestMethod.POST) return "POS"; + else if (method == HttpRequestMethod.PUT) return "PUT"; + else if (method == HttpRequestMethod.DELETE) return "DEL"; + else if (method == HttpRequestMethod.CONNECT) return "CON"; + else if (method == HttpRequestMethod.HEAD) return "HED"; + else if (method == HttpRequestMethod.OPTIONS) return "OPT"; + else if (method == HttpRequestMethod.PATCH) return "PAT"; + else if (method == HttpRequestMethod.TRACE) return "TRC"; + else return "OTH"; +} + +export function getStatusColor(status: number): string { + if (status >= 500) return Colors.statusError; + if (status >= 200 && status < 300) return Colors.statusOK; + return Colors.statusWarn; +} + +export const FontStyles: Record = { + monoSpace: { + fontFamily: "Ubuntu Mono", + fontWeight: 400, + fontSize: "15px", + lineHeight: "20px" + }, + sansSerifNormal: { + fontFamily: "Open Sans", + fontWeight: 400 + }, + sansSerifBold: { + fontFamily: "Open Sans", + fontWeight: 700 + }, + sansSerifExtraBold: { + fontFamily: "Open Sans", + fontWeight: 800 + } +}; + +export const ZIndexLayers = { + base: 0, + consoleBackground: 40, + console: 50, + modal: 100, + context: 150, + tooltip: 200 +}; diff --git a/src/renderer/ui/Toast.ts b/src/renderer/ui/Toast.ts new file mode 100644 index 0000000..6b8a266 --- /dev/null +++ b/src/renderer/ui/Toast.ts @@ -0,0 +1,141 @@ +import { Div, Element } from "aflon"; +import { BoxShadowValues, Colors, FontStyles, ZIndexLayers } from "./StyleConstants"; +import { Icon } from "./Icon"; + +export abstract class ToastContent extends Element { + public eventResultReady: string; + public abstract getResult(): TResult; +} + +abstract class ToastElementBase extends Div { + abstract cancel(): void; +} + +class ToastElement extends ToastElementBase { + public eventAboutToBeHidden = "aboutToBeHidden"; + + private _closeBtn: Icon; + private _content: ToastContent; + + private _hideAfterSeconds: number = 10; + private _resolveToastResult: () => void = null; + + constructor(content: ToastContent, hideAfterSeconds: number = 10) { + super(); + + this.append([ + this._closeBtn = new Icon("result") + .on(this.eventClick, () => this.cancel()), + this._content = content + ]); + + this._hideAfterSeconds = hideAfterSeconds; + } + + getResult(): TResult { + return this._content.getResult(); + } + + async show(): Promise { + return new Promise((resolve) => { + let timeoutId: NodeJS.Timeout = null; + + this._resolveToastResult = (): void => { + this._content.off(this._content.eventResultReady, this._resolveToastResult); + + if (timeoutId != null) + clearTimeout(timeoutId); + + this._hide(); + resolve(this._content.getResult()); + }; + + this._content.on(this._content.eventResultReady, this._resolveToastResult); + timeoutId = setTimeout(this._resolveToastResult, this._hideAfterSeconds * 1000); + + document.body.append(this.getHtmlElement()); + this.animations("show").start(); + }); + } + + cancel(): void { + if (this._resolveToastResult) + this._resolveToastResult(); + } + + private async _hide(): Promise { + await this.animations("hide").startAsync(); + document.body.removeChild(this.getHtmlElement()); + } +} + +ToastElement.style = { + _: { + ...FontStyles.sansSerifNormal, + background: Colors.consoleBackground, + borderTop: `solid 1px ${Colors.consoleBorder}`, + boxShadow: BoxShadowValues.consoleExtended, + color: Colors.browserDefault, + position: "fixed", + display: "flex", + flexFlow: "column nowrap", + borderRadius: "20px 20px 20px 20px", + padding: "20px", + paddingTop: "60px", + zIndex: ZIndexLayers.modal, + right: "-400px", + width: "300px", + minHeight: "200px", + maxHeight: "500px", + bottom: "20px" + }, + _closeBtn: { + position: "absolute", + right: "20px", + top: "20px", + cursor: "pointer", + color: Colors.workspaceDefault, + "&:hover": { + color: Colors.workspaceDescriptor + } + } +}; + +ToastElement.animations = { + show: { + animations: [ + { track: "right", from: "-400px", to: "20px" } + ], + ease: "circOut", + duration: 300 + }, + hide: { + animations: [ + { track: "right", to: "-400px" } + ], + ease: "easeIn", + duration: 150 + } +}; + +export class Toast { + private static _currentToastElement: ToastElementBase = null; + + public static async show(content: ToastContent, hideAfterSeconds: number = 10): Promise { + let toast = new ToastElement(content, hideAfterSeconds); + + if (Toast._currentToastElement != null) { + Toast._currentToastElement.cancel(); + } + + Toast._currentToastElement = toast; + + return new Promise((resolve) => { + toast.show().then(result => { + if (Toast._currentToastElement == toast) + Toast._currentToastElement = null; + resolve(result); + }); + }); + } +} diff --git a/src/renderer/ui/TokenTextEditor/Line.ts b/src/renderer/ui/TokenTextEditor/Line.ts new file mode 100644 index 0000000..554407c --- /dev/null +++ b/src/renderer/ui/TokenTextEditor/Line.ts @@ -0,0 +1,49 @@ +import { Element, Span } from "aflon"; + +import { Token } from "./Token"; + +class Br extends Element { + _createElement(): HTMLElement { + return document.createElement("br"); + } +} + +export class Line extends Span { + public index: number; + public length: number; + public offset: number; + + getTokens(): Array { + return this.children() + .filter(child => child instanceof Token) + .map(child => child); + } + + setTokens(tokens: Array): this { + let offset = 0; + + let nonEmptyTokens = []; + + tokens.forEach((token) => { + if (token.length == 0) return; + nonEmptyTokens.push(token); + token.offset = offset; + offset += token.length; + }); + + + this.length = offset; + + this.empty(); + this.append(nonEmptyTokens); + this.append([ new Br() ]); + return this; + } +} + +Line.style = { + _: { + height: "20px", + whiteSpace: "nowrap" + } +}; diff --git a/src/renderer/ui/TokenTextEditor/MacroedTextEditor.ts b/src/renderer/ui/TokenTextEditor/MacroedTextEditor.ts new file mode 100644 index 0000000..34edc96 --- /dev/null +++ b/src/renderer/ui/TokenTextEditor/MacroedTextEditor.ts @@ -0,0 +1,35 @@ +import { IMacroSource, MacroedText, MacroedTextPartType } from "../../lib/http"; + +import { TokenTextEditor } from "./TokenTextEditor"; +import { Line } from "./Line"; +import { Token, RegularToken, ParameterToken } from "./Token"; + +export class MacroedTextEditor extends TokenTextEditor implements IMacroSource { + private _macros: Array = []; + + constructor() { + super(); + + this.setPlaceholder("..."); + } + + getMacroNames(): Array { + return this._macros; + } + + protected _tokenize(newText: string): Array { + this._macros = []; + return [ new Line().setTokens(this._tokenizeMacroedText(newText)) ]; + } + + private _tokenizeMacroedText(text: string): Array { + let result = MacroedText.parse(text); + this._macros = result.getMacroNames(); + return result.getParts().map(part => { + if (part.type == MacroedTextPartType.Parameter) { + return new ParameterToken(part.text); + } else if (part.type == MacroedTextPartType.PlainText) return new RegularToken(part.text); + else throw new Error(`MacroedTextPartType ${part.type} is not supported.`); + }); + } +} diff --git a/src/renderer/ui/TokenTextEditor/StringProcessor.ts b/src/renderer/ui/TokenTextEditor/StringProcessor.ts new file mode 100644 index 0000000..a53cee7 --- /dev/null +++ b/src/renderer/ui/TokenTextEditor/StringProcessor.ts @@ -0,0 +1,140 @@ +import { Clipboard } from "../../lib/Clipboard"; + +export class StringSelection { + start: number = -1; + end: number = -1; +} + +export class StringProcessorState { + text: string; + selection: StringSelection = new StringSelection(); +} + +export enum StringProcessorDeltaType { + None, Backspace, Delete, Insert, Copy, Cut, Paste +} + +export class StringProcessorDelta { + type: StringProcessorDeltaType = StringProcessorDeltaType.None; + insertionText: string; + skipNewlinesFromClipboard: boolean = false; +} + +export class StringProcessor { + public static process(state: StringProcessorState, delta: StringProcessorDelta): StringProcessorState { + + if (delta.type == StringProcessorDeltaType.Insert) + return StringProcessor._processCharacter(state, delta.insertionText); + else if (delta.type == StringProcessorDeltaType.Backspace) + return StringProcessor._processBackspace(state); + else if (delta.type == StringProcessorDeltaType.Delete) + return StringProcessor._processDelete(state); + else if (delta.type == StringProcessorDeltaType.Cut) + return StringProcessor._processCut(state); + else if (delta.type == StringProcessorDeltaType.Copy) + return StringProcessor._processCopy(state); + else if (delta.type == StringProcessorDeltaType.Paste) + return StringProcessor._processPaste(state, delta.skipNewlinesFromClipboard); + + return null; + } + + private static _processCopy(currentState: StringProcessorState): StringProcessorState { + if (currentState.selection.start == currentState.selection.end) return null; + const selectedText = currentState.text.substring(currentState.selection.start, currentState.selection.end); + if (!selectedText) return null; + Clipboard.setText(selectedText); + return null; + } + + private static _processPaste(currentState: StringProcessorState, skipNewLines: boolean): StringProcessorState { + let clipboardText = Clipboard.getText(); + if (skipNewLines) + clipboardText = clipboardText.split("\n").join(""); + + if (!clipboardText) return null; + + let newState = new StringProcessorState(); + + if (currentState.selection.start == currentState.selection.end) { + newState.text = StringProcessor._modifyString(currentState.text, currentState.selection.start, currentState.selection.start, clipboardText); + newState.selection = { start: currentState.selection.start + clipboardText.length, end: currentState.selection.start + clipboardText.length }; + } else { + newState.text = StringProcessor._modifyString(currentState.text, currentState.selection.start, currentState.selection.end, clipboardText); + newState.selection = { start: currentState.selection.start + clipboardText.length, end: currentState.selection.start + clipboardText.length }; + } + + return newState; + } + + private static _processCut(currentState: StringProcessorState): StringProcessorState { + if (currentState.selection.start == currentState.selection.end) return null; + + const selectedText = currentState.text.substring(currentState.selection.start, currentState.selection.end); + if (!selectedText) return null; + Clipboard.setText(selectedText); + + let newState = new StringProcessorState(); + + newState.text = StringProcessor._modifyString(currentState.text, currentState.selection.start, currentState.selection.end, ""); + newState.selection = { start: currentState.selection.start, end: currentState.selection.start }; + + return newState; + } + + private static _processBackspace(currentState: StringProcessorState): StringProcessorState { + let newState = new StringProcessorState(); + + if (currentState.selection.start == currentState.selection.end) { + if (currentState.selection.start >= 1) { + newState.text = StringProcessor._modifyString(currentState.text, currentState.selection.start - 1, currentState.selection.start, ""); + newState.selection.start = currentState.selection.start - 1; + newState.selection.end = currentState.selection.start - 1; + } + } else { + newState.text = StringProcessor._modifyString(currentState.text, currentState.selection.start, currentState.selection.end, ""); + newState.selection.start = currentState.selection.start; + newState.selection.end = currentState.selection.start; + } + + return newState; + } + + private static _processDelete(currentState: StringProcessorState): StringProcessorState { + let newState = new StringProcessorState(); + + if (currentState.selection.start == currentState.selection.end) { + if (currentState.selection.start < currentState.text.length) { + newState.text = StringProcessor._modifyString(currentState.text, currentState.selection.start, currentState.selection.start + 1, ""); + newState.selection.start = currentState.selection.start; + newState.selection.end = currentState.selection.start; + } + } else { + newState.text = StringProcessor._modifyString(currentState.text, currentState.selection.start, currentState.selection.end, ""); + newState.selection.start = currentState.selection.start; + newState.selection.end = currentState.selection.start; + } + + return newState; + } + + private static _processCharacter(currentState: StringProcessorState, character: string): StringProcessorState { + let newState = new StringProcessorState(); + + if (currentState.selection.start == currentState.selection.end) { + newState.text = StringProcessor._modifyString(currentState.text, currentState.selection.start, currentState.selection.end, character); + newState.selection.start = currentState.selection.start + character.length; + newState.selection.end = currentState.selection.start + character.length; + } else { + newState.text = StringProcessor._modifyString(currentState.text, currentState.selection.start, currentState.selection.end, character); + newState.selection.start = currentState.selection.start + character.length; + newState.selection.end = currentState.selection.start + character.length; + } + + return newState; + } + + private static _modifyString(originalString: string, from: number, to: number, replaceString: string): string { + return originalString.substring(0, from) + replaceString + originalString.substring(to); + } +} diff --git a/src/renderer/ui/TokenTextEditor/Token.ts b/src/renderer/ui/TokenTextEditor/Token.ts new file mode 100644 index 0000000..b866f8a --- /dev/null +++ b/src/renderer/ui/TokenTextEditor/Token.ts @@ -0,0 +1,71 @@ +import { Span, CSS } from "aflon"; + +import { Colors } from "../StyleConstants"; + +export class Token extends Span { + private static _noWrapClass: string = CSS.class({ + whiteSpace: "pre" + }); + + public offset: number; + public readonly length: number; + + constructor(text: string, wrapLine: boolean = false) { + super(); + + this.setText(text); + this.length = text.length; + + if (!wrapLine) { + this.addClass(Token._noWrapClass); + } + } +} + +export class RegularToken extends Token { + private static _cssColorClass = CSS.class({ + color: Colors.workspaceDefault + }); + + constructor(text: string, wrapLine: boolean = false) { + super(text, wrapLine); + + this.addClass(RegularToken._cssColorClass); + } +} + +export class AccentToken extends Token { + private static _cssColorClass = CSS.class({ + color: Colors.workspaceAccent + }); + + constructor(text: string, wrapLine: boolean = false) { + super(text, wrapLine); + + this.addClass(AccentToken._cssColorClass); + } +} + +export class ParameterToken extends Token { + private static _cssColorClass = CSS.class({ + color: Colors.workspaceParameter + }); + + constructor(text: string, wrapLine: boolean = false) { + super(text, wrapLine); + + this.addClass(ParameterToken._cssColorClass); + } +} + +export class SuppressedToken extends Token { + private static _cssColorClass = CSS.class({ + color: Colors.workspacePlaceholder + }); + + constructor(text: string, wrapLine: boolean = false) { + super(text, wrapLine); + + this.addClass(SuppressedToken._cssColorClass); + } +} diff --git a/src/renderer/ui/TokenTextEditor/TokenTextEditor.ts b/src/renderer/ui/TokenTextEditor/TokenTextEditor.ts new file mode 100644 index 0000000..e05118c --- /dev/null +++ b/src/renderer/ui/TokenTextEditor/TokenTextEditor.ts @@ -0,0 +1,710 @@ +import { Element as AflonElement, isAflonElement, Div, Span, AflonHtmlElement, AbstractTextBox } from "aflon"; + +import { Platform, currentPlatform } from "../../lib/Platform"; +import { SimpleEvent } from "../../lib/SimpleEvent"; +import { Clipboard } from "../../lib/Clipboard"; + +import { Colors, FontStyles } from "../StyleConstants"; +import { ContextMenu, ContextMenuItemType } from "../ContextMenu"; +import { FocusLeaveDirection, CaretPosition, IKeyboardNavigable } from "../IKeyboardNavigable"; + +import { StringProcessor, StringProcessorState, StringProcessorDelta, StringProcessorDeltaType } from "./StringProcessor"; +import { Token, RegularToken } from "./Token"; +import { Line } from "./Line"; + +export class SelectionInterval { + public start: CaretPosition = null; + public end: CaretPosition = null; +} + +export class TokenTextEditor extends Div implements AbstractTextBox, IKeyboardNavigable { + public eventChange = "change"; + public eventInput = "input"; + public eventFocusLeaveRequested = "focusLeaveRequested"; + + private _clipboardOperationsAllowed: boolean = true; + private _newLineInsertionAllowed: boolean = false; + private _spaceInsertionAllowed: boolean = true; + private _tabKeyInsertionString: string = null; + + private _placeholder: string = ""; + private _placeholderShown: boolean = false; + private _placeholderSpan: Span = null; + + private _readOnly: boolean = false; + private _disabled: boolean = false; + + private _textOnFocusIn: string = ""; + + private _currentText: string = ""; + private _textFilter: (text: string) => string = null; + + private _contextMenu: ContextMenu = null; + + constructor() { + super(); + + this.addAttr("contenteditable", "true") + .addAttr("spellcheck", "false") + .addAttr("tabindex", "0") + .setInlineCss({ userSelect: "text" }) + .on(this.eventKeyDown, e => this._onKeyDown(e)) + .on(this.eventFocusIn, () => this._onFocusIn()) + .on(this.eventFocusOut, () => this._onFocusOut()); + + (this._contextMenu = new ContextMenu(this, [ + { id: "cut", type: ContextMenuItemType.Button, text: "Cut" }, + { id: "copy", type: ContextMenuItemType.Button, text: "Copy" }, + { id: "paste", type: ContextMenuItemType.Button, text: "Paste" } + ])) + .on(this._contextMenu.eventAboutToBeShown, () => this._onContextMenuAboutToBeShown()) + .on(this._contextMenu.eventSelected, e => this._onContextMenuSelected(e)); + + this._placeholderSpan = new Span(); + } + + setClipboardOperationsAllowed(allowed: boolean): this { + this._clipboardOperationsAllowed = allowed; + return this; + } + + getClipboardOperationsAllowed(): boolean { + return this._clipboardOperationsAllowed; + } + + setNewLineInsertionAllowed(allowed: boolean): this { + this._newLineInsertionAllowed = allowed; + return this; + } + + getNewLineInsertionAllowed(): boolean { + return this._newLineInsertionAllowed; + } + + setSpaceInsertionAllowed(allowed: boolean): this { + this._spaceInsertionAllowed = allowed; + return this; + } + + getSpaceInsertionAllowed(): boolean { + return this._spaceInsertionAllowed; + } + + setTabKeyInsertionString(value: string): this { + this._tabKeyInsertionString = value; + return this; + } + + getTabKeyInsertionString(): string { + return this._tabKeyInsertionString; + } + + setText(text: string): this { + if (this._textFilter != null) + this._currentText = this._textFilter(text); + else + this._currentText = text; + + let tokens = this._tokenize(this._currentText); + this._populate(tokens); + + this._showPlaceholderIfNecessary(); + + setTimeout(() => this.raise(this.eventInput), 1); + return this; + } + + getText(): string { + if (this._placeholderShown) return ""; + return this._currentText; + } + + setTextFilter(textFilter: (text: string) => string): this { + this._textFilter = textFilter; + return this; + } + + getTextFilter(): (text: string) => string { + return this._textFilter; + } + + setPlaceholder(placeholder: string): this { + this._placeholder = placeholder; + this._showPlaceholderIfNecessary(); + return this; + } + + getPlaceholder(): string { + return this._placeholder; + } + + setReadOnly(readOnly: boolean): this { + return this._setDisabledAndReadOnly(this._disabled, readOnly); + } + + getReadOnly(): boolean { + return this._readOnly; + } + + setDisabled(disabled: boolean): this { + return this._setDisabledAndReadOnly(disabled, this._readOnly); + } + + getDisabled(): boolean { + return this._disabled; + } + + focus(): void { + this.getHtmlElement().focus(); + } + + blur(): void { + this.getHtmlElement().blur(); + } + + getLines(): Array { + let lines: Array = []; + this.children().forEach(child => { + if (child instanceof Line) + lines.push(child); + }); + return lines; + } + + getSelection(): SelectionInterval { + let interval: SelectionInterval = new SelectionInterval(); + + let selection = document.getSelection(); + + if (!selection) return null; + + if (selection.type == "None") + return null; + + if (selection.rangeCount != 1) return null; + + let range = selection.getRangeAt(0); + + interval.start = this._findCaretPositionInTextNodeWithOffset(range.startContainer, range.startOffset); + interval.end = this._findCaretPositionInTextNodeWithOffset(range.endContainer, range.endOffset); + + return interval; + } + + setSelection(interval: SelectionInterval): this { + + let start = this._findNodeWhichContainsCaretPosition(interval.start); + let end = this._findNodeWhichContainsCaretPosition(interval.end); + + if (start == null || end == null) return this; + + let range = document.createRange(); + let sel = window.getSelection(); + if (sel == null) return this; + range.setStart(start.node, start.offset); + range.setEnd(end.node, end.offset); + + sel.removeAllRanges(); + sel.addRange(range); + + return this; + } + + selectAll(): this { + return this.setSelection({ + start: this._toTokenCaretPosition(this.getLines(), 0), + end: this._toTokenCaretPosition(this.getLines(), this._currentText.length) + }); + } + + setCaretPosition(position: CaretPosition): this { + let pos = this._findNodeWhichContainsCaretPosition(position); + if (pos == null) { + this.focus(); + return this; + } + + let range = document.createRange(); + let sel = window.getSelection(); + if (sel == null) return this; + range.setStart(pos.node, pos.offset); + range.collapse(true); + + sel.removeAllRanges(); + sel.addRange(range); + + let editorRect = this.getHtmlElement().getBoundingClientRect(); + let rangeRect = range.getBoundingClientRect(); + this.getHtmlElement().scrollTo( + rangeRect.x - editorRect.x - editorRect.width + this.getHtmlElement().scrollLeft, + rangeRect.y - editorRect.y - editorRect.height + this.getHtmlElement().scrollTop); + + return this; + } + + getCaretPosition(): CaretPosition { + let selection = this.getSelection(); + + if (!selection.start || !selection.end) return null; + + if (selection.start.column != selection.end.column || + selection.start.row != selection.start.row) return null; + + return selection.start; + } + + protected _redraw(): void { + this.setText(this.getText()); + } + + protected _tokenize(text: string): Array { + const lineStrings = text.split("\n"); + + let lines: Array = []; + + for (let i = 0; i <= lineStrings.length - 1; i++) { + lines.push(new Line().setTokens([ + new RegularToken(lineStrings[i])] + )); + } + + return lines; + } + + protected _toStringCaretPosition(lines: Array, position: CaretPosition): number { + if (lines.length == 0) + return 0; + + if (position == null) + return lines[lines.length - 1].offset + lines[lines.length - 1].length; + + return lines[position.row].offset + position.column + position.row; + } + + protected _toTokenCaretPosition(lines: Array, position: number): CaretPosition { + if (position <= 0) return { + row: 0, column: 0 + }; + + let focusLine: Line = null; + + for (let i = 0; i <= lines.length - 1; i++) { + const currentLine = lines[i]; + if (currentLine.offset <= position && position <= currentLine.length + currentLine.offset) { + focusLine = currentLine; + break; + } + position--; + } + + if (focusLine == null) + return { + row: lines.length - 1, + column: lines[lines.length - 1].length + }; + + return { + row: focusLine.index, + column: position - focusLine.offset + }; + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + protected _onAboutPaste(pasteText: string): boolean { + return true; + } + + private _onContextMenuSelected(e: SimpleEvent): void { + const id = e["detail"]["id"]; + if (!id) return; + + let key = ""; + + if (id == "cut") { + key = "X"; + } else if (id == "copy") { + key = "C"; + } else if (id == "paste") { + key = "V"; + } + + this._onKeyDown(new KeyboardEvent("emulatedKeyboardClipboardEvent", { + key, ctrlKey: true, metaKey: true + })); + } + + private _onContextMenuAboutToBeShown(): void { + const selection = this.getSelection(); + + + if (!selection || !selection.start || (!selection.end && selection.start.column != 0 && selection.start.row != 0) || + (selection.end && selection.start.column == selection.end.column && selection.start.row == selection.end.row)) { + this._contextMenu.setItemDisabled("copy", true); + this._contextMenu.setItemDisabled("cut", true); + } else { + this._contextMenu.setItemDisabled("copy", false); + + if (this.getReadOnly()) + this._contextMenu.setItemDisabled("cut", true); + else + this._contextMenu.setItemDisabled("cut", false); + } + + const clipboardText = Clipboard.getText(); + if (!clipboardText || this.getReadOnly()) { + this._contextMenu.setItemDisabled("paste", true); + } else { + this._contextMenu.setItemDisabled("paste", false); + } + } + + private _setDisabledAndReadOnly(disabled: boolean, readOnly: boolean): this { + this._disabled = disabled; + this._readOnly = readOnly; + + if (this._disabled) { + this.removeAttr("contenteditable") + .removeAttr("tabindex") + .addAttr("disabled") + .removeAttr("readonly") + .setInlineCss({ + pointerEvents: "none" + }); + } else if (this._readOnly) { + this.removeAttr("contenteditable") + .removeAttr("tabindex") + .removeAttr("disabled") + .addAttr("readonly") + .setInlineCss({ + pointerEvents: "auto" + }); + } else { + this.addAttr("contenteditable", "true") + .addAttr("tabindex", "0") + .removeAttr("disabled") + .removeAttr("readonly") + .setInlineCss({ + pointerEvents: "auto" + }); + } + + return this; + } + + private _calculateNewEditorStateOnKeyPress(e: KeyboardEvent, currentState: StringProcessorState) : StringProcessorState { + const key = e.key; + const modifierKeyPressed = (currentPlatform() == Platform.MacOS && e.metaKey) || + (currentPlatform() != Platform.MacOS && e.ctrlKey); + + let delta: StringProcessorDelta = new StringProcessorDelta(); + + if (modifierKeyPressed) { + if (this._clipboardOperationsAllowed) { + if (key.toUpperCase() == "C") delta.type = StringProcessorDeltaType.Copy; + else if (key.toUpperCase() == "V") delta.type = StringProcessorDeltaType.Paste; + else if (key.toUpperCase() == "X") delta.type = StringProcessorDeltaType.Cut; + + delta.skipNewlinesFromClipboard = !this._newLineInsertionAllowed; + } + } else { + if (key == "Backspace") delta.type = StringProcessorDeltaType.Backspace; + else if (key == "Delete") delta.type = StringProcessorDeltaType.Delete; + else if (key == "Enter") { + if (this._newLineInsertionAllowed) { + delta.type = StringProcessorDeltaType.Insert; + delta.insertionText = "\n"; + } + } else if (key == " ") { + if (this._spaceInsertionAllowed) { + delta.type = StringProcessorDeltaType.Insert; + delta.insertionText = " "; + } + } else if (key == "Tab") { + if (this._tabKeyInsertionString != null) { + delta.type = StringProcessorDeltaType.Insert; + delta.insertionText = this._tabKeyInsertionString; + } + } else if (key.length == 1) { + delta.type = StringProcessorDeltaType.Insert; + delta.insertionText = key; + } + } + + if (delta.type == StringProcessorDeltaType.None) return null; + + return StringProcessor.process(currentState, delta); + } + + private _findCaretPositionInTextNodeWithOffset(container: Node, offset: number): CaretPosition { + + let textNode: Text = null; + let tokenNode: Token = null; + let lineNode: Line = null; + let tokenTextEditorNode: TokenTextEditor = null; + + if (container.nodeType == Node.TEXT_NODE) { + textNode = container; + + let textParent: AflonElement = null; + if (isAflonElement(textNode.parentNode["aflonElement"])) { + textParent = ((textNode.parentNode).aflonElement); + + if (textParent instanceof Token) { + tokenNode = textParent; + + if (tokenNode.parent() instanceof Line) { + lineNode = tokenNode.parent(); + + if (lineNode.parent() instanceof TokenTextEditor) + tokenTextEditorNode = lineNode.parent(); + } + } + } + + } else if (isAflonElement(container["aflonElement"])) { + let elem: AflonElement = ((container).aflonElement); + + if (elem instanceof Token) { + tokenNode = elem; + + if (tokenNode.parent() instanceof Line) { + lineNode = tokenNode.parent(); + + if (lineNode.parent() instanceof TokenTextEditor) + tokenTextEditorNode = lineNode.parent(); + } + } else if (elem instanceof Line) { + lineNode = elem; + + if (lineNode.parent() instanceof TokenTextEditor) + tokenTextEditorNode = lineNode.parent(); + } else if (elem instanceof TokenTextEditor) { + tokenTextEditorNode = elem; + } + } + + if (tokenTextEditorNode == null || tokenTextEditorNode != this) return null; + + if (lineNode == null) return { row: 0, column: 0 }; + + if (tokenNode == null) { + if (offset == 0) return { + row: lineNode.index, + column: 0 + }; + else if (offset == lineNode.getChildrenNumber()) return { + row: lineNode.index, + column: lineNode.length + }; + else return { + row: lineNode.index, + column: lineNode.getTokens()[offset - 1].offset + lineNode.getTokens()[offset - 1].length + }; + } + + if (textNode == null) return { row: 0, column: 0 }; + + return { + row: lineNode.index, + column: tokenNode.offset + offset + }; + } + + private _findNodeWhichContainsCaretPosition(position: CaretPosition): { node: Node, offset: number } { + let lines = this.getLines(); + + if (lines.length == 0) return null; + + if (position.row == -1 || position.row > lines.length - 1) + position.row = lines.length - 1; + + if (position.row < 0) + position.row = 0; + + const targetLine = lines[position.row]; + + if (position.column == -1 || position.column > targetLine.length) + position.column = targetLine.length; + + if (position.column < 0) + position.column = 0; + + if (position.column == 0 && targetLine.length == 0) + return { node: targetLine.getHtmlElement(), offset: 0 }; + + let targetToken: Token = null; + const targetLineTokens = targetLine.getTokens(); + + if (position.column == targetLine.length) { + targetToken = (targetLineTokens[targetLineTokens.length - 1]); + } else { + for (let i = 0; i <= targetLineTokens.length - 1; i++) { + const token = targetLineTokens[i]; + if (token.offset <= position.column && position.column < token.offset + token.length) { + targetToken = token; + break; + } + } + } + + const targetTokenChildren = targetToken.getHtmlElement().childNodes; + + if (targetTokenChildren.length == 1 && + targetTokenChildren[0].nodeType == 3) { + return { node: (targetTokenChildren[0]), offset: position.column - targetToken.offset}; + } + return null; + } + + private _onKeyDown(e: Event): void { + let keyEvent = e; + + this._raiseEventFocusLeaveRequestedIfNecessary(keyEvent); + + if (keyEvent.key == "ArrowUp" || + keyEvent.key == "ArrowDown" || + keyEvent.key == "ArrowLeft" || + keyEvent.key == "ArrowRight") + return; + + if (keyEvent.key == "Tab" && + this._tabKeyInsertionString == null) + return; + + e.preventDefault(); + + const modifierKeyPressed = + (currentPlatform() == Platform.MacOS && keyEvent.metaKey) || + (currentPlatform() != Platform.MacOS && keyEvent.ctrlKey); + + if (keyEvent.key.toUpperCase() == "Z" && modifierKeyPressed) { + this.setText(this._textOnFocusIn); + return; + } + + if (keyEvent.key.toUpperCase() == "V" && modifierKeyPressed) { + if (!this._onAboutPaste(Clipboard.getText())) return; + } + + const selectionInterval = this.getSelection(); + + if (!selectionInterval) return; + + let currentStringProcessorState = { + text: this._currentText, + selection: { + start: this._toStringCaretPosition(this.getLines(), selectionInterval.start), + end: this._toStringCaretPosition(this.getLines(), selectionInterval.end) + } + }; + + let newStringProcessorState = + this._calculateNewEditorStateOnKeyPress(keyEvent, currentStringProcessorState); + + if (newStringProcessorState == null) return; + + if (newStringProcessorState.text != null && newStringProcessorState.text != this._currentText) { + this.setText(newStringProcessorState.text); + } + + if (newStringProcessorState.selection.start != -1) + this.setCaretPosition(this._toTokenCaretPosition(this.getLines(), newStringProcessorState.selection.start)); + } + + private _raiseEventFocusLeaveRequestedIfNecessary(keyEvent: KeyboardEvent): void { + let caretPosition = this.getCaretPosition(); + + if (!caretPosition) return; + + let lines = this.getLines(); + let key = keyEvent.key; + let lastColumnLength: number = lines[lines.length - 1]?.length ?? 0; + + let direction: FocusLeaveDirection = null; + + if (caretPosition.row == 0) { + if (key == "ArrowUp") { + direction = FocusLeaveDirection.Up; + } else if (caretPosition.column == 0) { + if (key == "ArrowLeft") { + direction = FocusLeaveDirection.Left; + } else if (key == "Backspace" && this.getText().length == 0) { + direction = FocusLeaveDirection.Backspace; + } + } + } + + if (caretPosition.row == lines.length - 1 || lines.length == 0) { + if (key == "ArrowDown") { + direction = FocusLeaveDirection.Down; + } else if (caretPosition.column == lastColumnLength && key == "ArrowRight") { + direction = FocusLeaveDirection.Right; + } + } + + if (direction) { + keyEvent.preventDefault(); + this.raise(this.eventFocusLeaveRequested, { direction }); + } + } + + private _onFocusIn(): void { + if (this._placeholderShown) { + this._placeholderShown = false; + this.empty(); + this._textOnFocusIn = ""; + } else { + this._textOnFocusIn = this.getHtmlElement().textContent; + } + } + + private _onFocusOut(): void { + this._showPlaceholderIfNecessary(); + + if (this.getHtmlElement().textContent != this._textOnFocusIn) + this.raise(this.eventChange, {}, true); + } + + private _showPlaceholderIfNecessary(): void { + if (this._currentText.length == 0) { + this.empty(); + this.append([ + this._placeholderSpan.setText(this._placeholder) + ]); + this._placeholderShown = true; + } else { + this._placeholderShown = false; + } + } + + private _populate(lines: Array): this { + this.empty(); + + let offset = 0; + lines.forEach((line, index) => { + line.index = index; + line.offset = offset; + offset += line.length; + }); + + this.append(lines); + return this; + } +} + +TokenTextEditor.style = { + _: { + minWidth: "20px", + display: "inline-block", + color: Colors.workspaceDefault, + ...FontStyles.monoSpace, + "&:focus": { + border: "none", + outline: "none" + } + }, + _placeholderSpan: { + color: Colors.workspacePlaceholder, + pointerEvents: "none" + } +}; diff --git a/src/renderer/ui/TokenTextEditor/index.ts b/src/renderer/ui/TokenTextEditor/index.ts new file mode 100644 index 0000000..5eab5b7 --- /dev/null +++ b/src/renderer/ui/TokenTextEditor/index.ts @@ -0,0 +1,37 @@ +/** + * TokenTextEditor is a custom-written text editor used in httpiness. + * Its most important application is UrlTextEditor used in UrlControl, as it + * supports automatic text wrapping based on text's syntactic structure. It is also + * used as BodyTextEditor for configuration of HTTP body, however it is suboptimal + * for that purpose and should be replaces with an industry-proven web-based code + * editor like Monaco or CodeMirror. + * + * TokenTextEditor is essentially an aflon.Div which is consisted of at least single + * Line. Each Line is consisted of at least single Token (which is a specialization of + * aflon.Span). + * + * TokenTextEditor is customized by overriding its protected methods, most importantly + * '_tokenize', which is is a method which receives new text content of the editor as + * a parameter and returns an array of Lines, each of which contains list of Tokens. + * + * '_toStringCaretPosition' and '_toTokenCaretPosition' methods should + * also be overridden if new '_tokenize' method do not use default mapping of offset of + * a character in input string to its row-column coordinates within lines of the editor + * (non-standard handling of new lines, for example in UrlTextEditor). + * + * Internally, state of TokenTextEditor is stored as an instance of StringProcessorState. + * Each command which is intended to modify content of the editor is encoded as + * StringProcessorDelta. When text modification is initiated, StringProcessorDelta that + * represents the modification and current StringProcessorState are passed to 'process' + * function of StringProcessor. 'process' function is a pure function and returns new + * instance of StringProcessorState. TokenTextEditor reads new StringProcessorState, + * parses and tokenize its 'text' variable, repopulates itself with proper Lines and + * Tokens and configures text selection according to 'selection' variable from + * StringProcessorState. Recycling of existing tokens is not currently implemented, so + * TokenTextEditor has performance issues when large text is used. + */ + +export * from "./TokenTextEditor"; +export * from "./MacroedTextEditor"; +export * from "./Token"; +export * from "./Line"; diff --git a/src/renderer/ui/VResizer.ts b/src/renderer/ui/VResizer.ts new file mode 100644 index 0000000..6af3eb9 --- /dev/null +++ b/src/renderer/ui/VResizer.ts @@ -0,0 +1,65 @@ +import { Div } from "aflon"; + +import { Colors } from "./StyleConstants"; + +export class VResizer extends Div { + + private _resizing: boolean = false; + private _resizingCallback: (e: MouseEvent) => void; + + constructor() { + super(); + this.on(this.eventMouseDown, () => { + if (this._resizing) return; + + this._resizing = true; + document.addEventListener("mousemove", this._handleResizing); + }); + } + + setResizingCallback(callback: (e: MouseEvent) => void): this { + this._resizingCallback = callback; + return this; + } + + getResizingCallback(): (e: MouseEvent) => void { + return this._resizingCallback; + } + + protected _onEnteringDom(): void { + document.addEventListener("mouseup", this._onStoppingResizing); + document.addEventListener("mouseleave", this._onStoppingResizing); + } + + protected _onLeavingDom(): void { + document.removeEventListener("mouseup", this._onStoppingResizing); + document.removeEventListener("mouseleave", this._onStoppingResizing); + document.removeEventListener("mousemove", this._handleResizing); + } + + private _onStoppingResizing = (): void => { + if (!this._resizing) return; + + this._resizing = false; + document.removeEventListener("mousemove", this._handleResizing); + }; + + private _handleResizing = (e: Event): void => { + this._resizingCallback(e); + }; +} + +VResizer.style = { + _: { + width: "4px", + marginLeft: "-2px", + marginRight: "-2px", + background: "transparent", + cursor: "col-resize", + transition: "background-color 0.5s", + "&:hover": { + backgroundColor: Colors.workspaceAccent, + transition: "background-color 0.5s" + } + } +}; diff --git a/src/renderer/ui/WorkspaceControl/Console.ts b/src/renderer/ui/WorkspaceControl/Console.ts new file mode 100644 index 0000000..cc07a45 --- /dev/null +++ b/src/renderer/ui/WorkspaceControl/Console.ts @@ -0,0 +1,303 @@ +import { Div, Animation } from "aflon"; + +import { BoxShadowValues, Colors, FontStyles, ZIndexLayers } from "../StyleConstants"; +import { Icon } from "../Icon"; +import { Tooltip } from "../ContextMenu"; +import { PreferenceStore } from "../PreferenceStore"; +import { RequestExecutionHistoryControl, ResponsePanelOptionsButton } from "../ExecutionHistoryControl"; +import { HttpResponseControl } from "../ExecutionHistoryControl/HttpResponseControl"; + + +export class Console extends Div { + private _background: Div; + private _console: Div; + private _consoleHeader: Div; + private _headerTitle: Div; + private _headerPlaceholder: Div; + private _headerResponseOptionsButton: ResponsePanelOptionsButton; + private _headerSearchButton: Div; + private _headerClearHistoryButton: Div; + private _headerCollapseButton: Div; + private _consoleContent: Div; + private _history: RequestExecutionHistoryControl; + private _responseControl: HttpResponseControl; + + private _showAnimation: Animation = null; + private _hideAnimation: Animation = null; + + private _mouseEntered: boolean = false; + + constructor() { + super(); + + this.append([ + (this._background = new Div()) + .on(this._background.eventClick, () => this._onBackgroundClick()) + .on(this._background.eventMouseEnter, () => this._onConsoleMouseLeave()), + (this._console = new Div()) + .append([ + (this._consoleHeader = new Div()) + .append([ + (this._headerTitle = new Div()) + .setText("Responses & History") + .on(this._consoleHeader.eventClick, () => this._onConsoleHeaderClick()), + (this._headerPlaceholder = new Div()), + (this._headerResponseOptionsButton = new ResponsePanelOptionsButton()), + (this._headerSearchButton = new Div()) + .append([ new Icon("search") ]) + .on(this._headerSearchButton.eventClick, () => this._responseControl.activateSearch()), + (this._headerClearHistoryButton = new Div()) + .append([ new Icon("clear") ]) + .on(this._headerClearHistoryButton.eventClick, () => this._history.clear()), + (this._headerCollapseButton = new Div()) + .append([ new Icon("minimize") ]) + .on(this._headerCollapseButton.eventClick, () => this.hide()) + ]), + (this._consoleContent = new Div()) + ]) + .on(this._background.eventMouseEnter, () => this._onConsoleMouseEnter()) + ]); + + new Tooltip(this._headerSearchButton).setText("Search response"); + new Tooltip(this._headerClearHistoryButton).setText("Clear history"); + new Tooltip(this._headerCollapseButton).setText("Collapse console"); + + window.addEventListener("resize", this._onDocumentResize); + document.addEventListener("beforeunload", this._onDocumentBeforeUnload); + document.addEventListener("keydown", this._onKeyDown); + } + + setContent(historyControl: RequestExecutionHistoryControl, responseControl: HttpResponseControl): this { + this._history = historyControl; + this._responseControl = responseControl; + + this._history.setInlineCss({ + flex: "0 0 300px", + borderRight: `solid 1px ${Colors.workspaceLine}` + }); + + this._responseControl.setInlineCss({ + flex: "1 1 1px", + height: "100%", + minWidth: "0" + }); + + this._consoleContent.append([ this._history, this._responseControl ]); + + return this; + } + + clearContent(): this { + if (this._consoleContent.children().includes(this._history)) + this._consoleContent.removeChild(this._history); + if (this._consoleContent.children().includes(this._responseControl)) + this._consoleContent.removeChild(this._responseControl); + return this; + } + + public show(): this { + if (this._showAnimation) + this._showAnimation.stop(); + + this._showAnimation = new Animation({ + animations: [ + { target: "_console", track: "height", to: `${document.documentElement.clientHeight - 60}px` }, + { target: "_console", track: "width", to: `${document.documentElement.clientWidth - 120}px` }, + { target: "_console", track: "boxShadow", to: BoxShadowValues.consoleExtended }, + { target: "_consoleContent", track: "display", to: "flex", delay: 200 }, + { target: "_background", track: "display", to: "block" }, + { target: "_headerResponseOptionsButton", track: "display", to: "block" }, + { target: "_headerSearchButton", track: "display", to: "block" }, + { target: "_headerClearHistoryButton", track: "display", to: "block" }, + { target: "_headerCollapseButton", track: "display", to: "block" } + ], + ease: "circOut", + duration: 100 + }, this); + + if (this._hideAnimation) { + this._hideAnimation.stop(); + } + + this._showAnimation.start(); + + return this; + } + + public hide(): this { + if (!this._hideAnimation) { + this._hideAnimation = new Animation({ + animations: [ + { target: "_console", track: "height", to: "35px" }, + { target: "_console", track: "width", to: "145px" }, + { target: "_console", track: "boxShadow", to: BoxShadowValues.consoleCollapsed }, + { target: "_consoleContent", track: "display", to: "none" }, + { target: "_background", track: "display", to: "none" }, + { target: "_headerResponseOptionsButton", track: "display", to: "none" }, + { target: "_headerSearchButton", track: "display", to: "none" }, + { target: "_headerClearHistoryButton", track: "display", to: "none" }, + { target: "_headerCollapseButton", track: "display", to: "none" } + ], + ease: "circOut", + duration: 200 + }, this); + } + + if (this._showAnimation) { + this._showAnimation.stop(); + } + + this._hideAnimation.stop(); + this._hideAnimation.start(); + + this._mouseEntered = false; + + return this; + } + + private _onConsoleHeaderClick(): void { + this.show(); + } + + private _onBackgroundClick(): void { + this.hide(); + } + + private _onConsoleMouseEnter(): void { + this._mouseEntered = true; + } + + private _onConsoleMouseLeave(): void { + if (!this._mouseEntered) return; + + let closeOneMouseLeave = PreferenceStore.getCloseConsoleOnMouseLeave(); + + if (!closeOneMouseLeave) return; + this.hide(); + } + + private _onDocumentResize = (): void => { + this.hide(); + }; + + private _onDocumentBeforeUnload = (): void => { + window.removeEventListener("resize", this._onDocumentResize); + document.removeEventListener("beforeunload", this._onDocumentBeforeUnload); + document.removeEventListener("keydown", this._onKeyDown); + }; + + private _onKeyDown = (e:Event): void => { + const keyEvent = e; + + if (keyEvent.key == "Escape") + this.hide(); + }; +} + +Console.style = { + _: { + }, + _background: { + position: "fixed", + opacity: 0, + height: "100vh", + width: "100vw", + top: 0, + left: 0, + zIndex: ZIndexLayers.consoleBackground, + display: "none" + }, + _console: { + position: "fixed", + bottom: 0, + height: "35px", + width: "145px", + left: 0, + right: 0, + margin: "auto", + zIndex: ZIndexLayers.console, + background: Colors.consoleBackground, + border: `solid 1px ${Colors.consoleBorder}`, + borderBottom: "none", + boxShadow: BoxShadowValues.consoleCollapsed, + display: "flex", + flexFlow: "column nowrap", + borderRadius: "8px 8px 0 0", + "&:focus": { + border: `solid 1px ${Colors.consoleBorder}`, + borderBottom: "none", + outline: "none" + } + }, + _consoleHeader: { + flex: "0 0 35px", + color: Colors.consoleDominant, + ...FontStyles.sansSerifBold, + fontSize: "12px", + lineHeight: "35px", + paddingLeft: "10px", + paddingRight: "5px", + borderBottom: `solid 1px ${Colors.workspaceLine}`, + display: "flex", + flexFlow: "row nowrap" + }, + _headerPlaceholder: { + flex: "1 1 1px" + }, + _headerResponseOptionsButton: { + height: "100%", + width: "30px", + display: "none" + }, + _headerSearchButton: { + fontSize: "15px", + display: "none", + alignContent: "center", + alignItems: "center", + height: "100%", + width: "30px", + paddingLeft: "2px", + paddingTop: "2px", + textAlign: "center", + color: Colors.workspaceDefault, + cursor: "pointer", + "&:hover": { + color: Colors.consoleDominant + } + }, + _headerClearHistoryButton: { + display: "none", + alignContent: "center", + alignItems: "center", + height: "100%", + width: "30px", + paddingLeft: "2px", + paddingTop: "2px", + textAlign: "center", + color: Colors.workspaceDefault, + cursor: "pointer", + "&:hover": { + color: Colors.consoleDominant + } + }, + _headerCollapseButton: { + display: "none", + alignContent: "center", + alignItems: "center", + height: "100%", + width: "27px", + paddingTop: "2px", + textAlign: "center", + color: Colors.workspaceDefault, + cursor: "pointer", + "&:hover": { + color: Colors.consoleDominant + } + }, + _consoleContent: { + flex: "1 1 1px", + display: "none", + minHeight: "0", + flexFlow: "row nowrap" + } +}; diff --git a/src/renderer/ui/WorkspaceControl/FeedbackToastContent.ts b/src/renderer/ui/WorkspaceControl/FeedbackToastContent.ts new file mode 100644 index 0000000..9b5b42b --- /dev/null +++ b/src/renderer/ui/WorkspaceControl/FeedbackToastContent.ts @@ -0,0 +1,64 @@ +import { Div } from "aflon"; + +import { Button } from "../BasicControls"; +import { ToastContent } from "../Toast"; + +export class FeedbackToastContent extends Div implements ToastContent { + eventResultReady = "resultReady"; + + private _hand: Div; + private _title: Div; + private _subtitle: Div; + private _yesButton: Button; + private _noButton: Button; + + private _result: boolean = false; + + constructor() { + super(); + + this.append([ + this._hand = new Div().setText("โœ‹"), + this._title = new Div().setText("We kindly ask for your feedback."), + this._subtitle = new Div().setText("Our ambition is to create simple, developer-friendly \ + HTTP client tailored for rapid and responsive testing during development. \ + Reporting issues and giving feedback helps us a lot."), + (this._yesButton = new Button().setText("Give feedback now")) + .on(this._yesButton.eventClick, () => this._onYesClick()), + (this._noButton = new Button().setText("Maybe later")) + .on(this._noButton.eventClick, () => this._onNoClick()) + ]); + } + + public getResult(): boolean { + return this._result; + } + + private _onYesClick(): void { + this._result = true; + this.raise(this.eventResultReady); + } + + private _onNoClick(): void { + this.raise(this.eventResultReady); + } +} + +FeedbackToastContent.style = { + _: { + display: "flex", + flexFlow: "column nowrap", + gap: "10px", + marginTop: "-30px" + }, + _text: { + fontSize: "14px" + }, + _subtitle: { + fontSize: "11px", + marginBottom: "10px" + }, + _hand: { + fontSize: "40px" + } +}; diff --git a/src/renderer/ui/WorkspaceControl/MacrosControl/MacroCollectionControl.ts b/src/renderer/ui/WorkspaceControl/MacrosControl/MacroCollectionControl.ts new file mode 100644 index 0000000..b52b91d --- /dev/null +++ b/src/renderer/ui/WorkspaceControl/MacrosControl/MacroCollectionControl.ts @@ -0,0 +1,162 @@ +import { Div } from "aflon"; + +import { HttpCollection, IMacroSource } from "../../../lib/http"; +import { SimpleEvent } from "../../../lib/SimpleEvent"; + +import { Colors, FontStyles } from "../../StyleConstants"; +import { ContextMenu, ContextMenuItemDefinition, ContextMenuItemType, ContextMenuShowTrigger } from "../../ContextMenu"; +import { IconButton } from "../../IconButton"; +import { MacroPresetsControl } from "../../MarcoPresetsControl"; + +import { MacroRecordControl } from "./MacroRecordControl"; + +export class MacroCollectionControl extends Div implements IMacroSource { + + private _header: Div; + private _headerText: Div; + private _presetsIcon: IconButton; + private _content: Div; + + private _presetContextMenu: ContextMenu; + + private _collection: HttpCollection; + + constructor(collection: HttpCollection) { + super(); + + this._collection = collection; + + this.append([ + (this._header = new Div()) + .append([ + (this._headerText = new Div()) + .setText(`Collection ${this._collection.getName()}`), + (this._presetsIcon = new IconButton("preset")) + ]), + (this._content = new Div()) + ]); + + (this._presetContextMenu = new ContextMenu(this._presetsIcon, [], ContextMenuShowTrigger.OnClickEvent)) + .on(this._presetContextMenu.eventAboutToBeShown, () => this._onPresetContextMenuAboutToBeShown()) + .on(this._presetContextMenu.eventSelected, e => this._onPresetsSelected(e)); + } + + getCollection(): HttpCollection { + return this._collection; + } + + setMacroNames(macros: Array, maxNameWidth: number = 0): this { + this._content.empty(); + this._content.append(macros.map(macro => new MacroRecordControl(this._collection, macro, maxNameWidth))); + return this; + } + + getMacroNames(): Array { + let macros: Array = []; + + this._content.children().forEach(child => { + if (!(child instanceof MacroRecordControl)) return; + let macroRecordControl = (child); + macros.push(macroRecordControl.getMacroName()); + }); + + return macros; + } + + setMacrosInFocus(macrosInFocus: Array): this { + if (macrosInFocus == null) { + this._content.children().forEach(child => { + if (!(child instanceof MacroRecordControl)) return; + child.setInlineCss({ opacity: 1.0 }); + }); + + return this; + } + + if (macrosInFocus.length == 0) { + this._content.children().forEach(child => { + child.setInlineCss({ opacity: 0.5 }); + }); + } + + this._content.children().forEach(child => { + if (!(child instanceof MacroRecordControl)) return; + + if (macrosInFocus.includes(child.getMacroName())) + child.setInlineCss({ opacity: 1.0 }); + else + child.setInlineCss({ opacity: 0.5 }); + }); + + return this; + } + + private async _onPresetsSelected(e: SimpleEvent): Promise { + if (e.detail.id == "configure") { + MacroPresetsControl.showAsModal(this._collection); + return; + } + + this._collection.applyMacroPreset(e.detail.id); + } + + private _onPresetContextMenuAboutToBeShown(): void { + let def: Array = [ + { type: ContextMenuItemType.Title, text: "Parameter presets", id: "title" }, + { + type: ContextMenuItemType.Text, + text: "Presets simultaneously configure multiple parameters to predefined values.", + id: "text" }, + { type: ContextMenuItemType.Divider, text: "Parameter presets", id: "div1" } + ]; + + let presets = this._collection.getMacroPresets().map(preset => + ({ type: ContextMenuItemType.Button, text: preset.name, id: preset.name })); + + if (presets.length != 0) + def = [...def, ...presets]; + else + def.push({ type: ContextMenuItemType.Text, + text: "No defined presets. Click Configure to define new preset.", + id: "text2" }); + + def.push({ type: ContextMenuItemType.Divider, text: "Parameter presets", id: "div2" }); + def.push({ type: ContextMenuItemType.Button, text: "Configure presets", id: "configure", iconName: "settings" }); + + this._presetContextMenu.setDefinition(def); + } +} + +MacroCollectionControl.style = { + _: { + display: "flex", + flexFlow: "column nowrap", + paddingTop: "5px", + paddingBottom: "5px", + borderBottom: `solid 1px ${Colors.workspaceLine}` + }, + _header: { + display: "flex", + flexFlow: "row nowrap", + ...FontStyles.sansSerifNormal, + color: Colors.workspaceDefault, + fontSize: "10px", + paddingLeft: "15px", + paddingRight: "5px", + alignItems: "center" + }, + _headerText: { + flex: "1 1 1px", + paddingBottom: "2px" + }, + _presetsIcon: { + padding: "0", + lineHeight: "normal", + height: "auto", + fontSize: "11px" + }, + _content: { + display: "flex", + flexFlow: "column nowrap" + } +}; diff --git a/src/renderer/ui/WorkspaceControl/MacrosControl/MacroRecordControl.ts b/src/renderer/ui/WorkspaceControl/MacrosControl/MacroRecordControl.ts new file mode 100644 index 0000000..dbed78a --- /dev/null +++ b/src/renderer/ui/WorkspaceControl/MacrosControl/MacroRecordControl.ts @@ -0,0 +1,288 @@ +import { AbstractTextBox, Div, PassBox, Span } from "aflon"; + +import { HttpCollection } from "../../../lib/http"; +import { SimpleEvent } from "../../../lib/SimpleEvent"; + +import { Tooltip } from "../../ContextMenu"; +import { SimpleModals } from "../../Modals"; +import { Icon } from "../../Icon"; +import { Colors, FontStyles } from "../../StyleConstants"; +import { TokenTextEditor } from "../../TokenTextEditor"; + +export class MacroRecordControl extends Div { + private static _defaultSensitiveText = "sensitive"; + + private _collection: HttpCollection; + private _inPreview: boolean = false; + + private _name: Div; + private _nameText: Span; + private _value: AbstractTextBox; + private _sensitiveValue: PassBox; + private _preview: Div; + private _lock: Div; + + private _nameTooltip: Tooltip; + private _lockTooltip: Tooltip; + private _previewTooltip: Tooltip; + + constructor(collection: HttpCollection, name: string, maxNameWidth: number = 0) { + super(); + + this._collection = collection; + this._collection.on(this._collection.eventMacroValueChanged, this._onMacroValueChanged); + + this + .on(this.eventMouseEnter, () => this._onMouseEnter()) + .on(this.eventMouseLeave, () => this._onMouseLeave()) + .append([ + (this._name = new Div()) + .append([ + (this._nameText = new Span()) + .setText(name) + ]), + (this._value = new TokenTextEditor()) + .setPlaceholder("Enter parameter value") + .on(this._value.eventInput, () => this._onValueInput()), + (this._sensitiveValue = new PassBox()) + .setText(MacroRecordControl._defaultSensitiveText) + .on(this._sensitiveValue.eventFocusIn, () => this._onSensitiveValueFocusIn()) + .on(this._sensitiveValue.eventFocusOut, () => this._onSensitiveFocusOut()) + .on(this._value.eventChange, () => this._onSensitiveValueChange()), + (this._preview = new Div()) + .setVisibility(false) + .on(this.eventClick, () => this._onPreviewClick()) + .append([ + new Icon("eye") + ]), + (this._lock = new Div()) + .setVisibility(false) + .on(this.eventClick, () => this._onLockClick()) + .append([ + new Icon("lock") + ]) + ]); + + if (maxNameWidth != 0) + this._name.setInlineCss({ + flex: `0 0 ${maxNameWidth}px` + }); + + if (!this._collection.isMacroSensitive(name)) { + this._value + .setVisibility(true) + .setText(this._collection.getMacroPublicValue(name)); + this._sensitiveValue.setVisibility(false); + } else { + this._value.setVisibility(false); + this._sensitiveValue.setVisibility(true); + this._lock + .setVisibility(true) + .setInlineCss({ borderStyle: "solid" }); + } + + this._lockTooltip = new Tooltip(this._lock) + .setTitle("Toggle parameter lock") + .setText("When parameter is locked it will be persisted in system's default credential \ + store and will not be saved in collection JSON file. Use this option to protect sensitive \ + data like passwords and secret tokens."); + this._nameTooltip = new Tooltip(this._name).setText(name); + this._previewTooltip = new Tooltip(this._preview).setText("Preview locked value"); + } + + getMacroName(): string { + return this._nameText.getText(); + } + + protected _onLeavingDom(): void { + if (this._inPreview) { + this._inPreview = false; + this._lockMacro(); + } + this._collection.off(this._collection.eventMacroValueChanged, this._onMacroValueChanged); + } + + private async _lockMacro(): Promise { + this._sensitiveValue.setVisibility(true); + this._value + .setVisibility(false) + .setText(""); + + await this._collection.setMacro(this._nameText.getText(), null, true); + } + + private async _unlockMacro(): Promise { + let name = this._nameText.getText(); + + this._value + .setText(await this._collection.getMacroValue(name)) + .setVisibility(true); + + this._sensitiveValue.setVisibility(false); + + await this._collection.setMacro(name, null, false); + } + + private async _onValueInput(): Promise { + if (this._sensitiveValue.getVisibility()) return; + await this._collection.setMacro(this._nameText.getText(), this._value.getText(), false); + } + + private async _onSensitiveValueChange(): Promise { + let text = this._sensitiveValue.getText(); + if (text == MacroRecordControl._defaultSensitiveText) return; + + await this._collection.setMacro(this._nameText.getText(), this._sensitiveValue.getText(), true); + } + + private async _onSensitiveValueFocusIn(): Promise { + this._sensitiveValue.setText(await this._collection.getMacroValue(this._nameText.getText())); + } + + private _onSensitiveFocusOut(): void { + this._sensitiveValue.setText(MacroRecordControl._defaultSensitiveText); + } + + private _onMouseEnter(): void { + if (this._collection.isMacroSensitive(this._nameText.getText())) { + this._preview.setVisibility(true); + } + + this._lock.setVisibility(true); + } + + private async _onMouseLeave(): Promise { + this._preview.setVisibility(false); + + if (this._inPreview) { + this._inPreview = false; + + await this._lockMacro(); + } + + if (!this._collection.isMacroSensitive(this._nameText.getText())) { + this._lock.setVisibility(false); + } + } + + private async _onLockClick(): Promise { + const name = this._nameText.getText(); + + if (this._collection.isMacroSensitive(name) || this._inPreview) { + let confirm = await SimpleModals.confirm(`Are you sure you want to unlock parameter ${name}?\n\n` + + "Unlocked parameters are stored as plain text in collection JSON file. " + + "If you share collection with others, they will be able to see value of the parameter."); + + if (!confirm) return; + + await this._unlockMacro(); + this._lock + .setVisibility(false) + .setInlineCss({ borderStyle: "none" }); + } else { + this._lockMacro(); + this._lock.setInlineCss({ borderStyle: "solid" }); + } + } + + private async _onPreviewClick(): Promise { + this._preview.setVisibility(false); + + const name = this._nameText.getText(); + if (!this._collection.isMacroSensitive(name)) return; + + this._inPreview = true; + + await this._unlockMacro(); + } + + private _onMacroValueChanged = async (e: SimpleEvent): Promise => { + let macroName = (e["detail"]["macroName"]); + let macroValue = (e["detail"]["macroValue"]); + + if (macroName != this.getMacroName()) return; + if (macroValue == this._value.getText()) return; + if (this._collection.isMacroSensitive(macroName)) return; + + this._value.setText(macroValue); + }; +} + +MacroRecordControl.style = { + _: { + display: "flex", + flexFlow: "row nowrap", + height: "25px", + alignItems: "center" + }, + _name: { + ...FontStyles.monoSpace, + color: Colors.workspaceParameter, + width: "40%", + minWidth: "50px", + maxWidth: "200px", + whiteSpace: "nowrap", + overflow: "hidden", + textOverflow: "ellipsis", + paddingLeft: "15px", + paddingRight: "10px" + }, + _value: { + ...FontStyles.monoSpace, + color: Colors.workspaceDefault, + height: "20px", + background: "none", + border: "none", + outline: "none", + appearance: "none", + flex: "1 1 1px", + minWidth: "0px", + overflow: "scroll", + "&::-webkit-scrollbar": { + display: "none" + } + }, + _sensitiveValue: { + ...FontStyles.monoSpace, + color: Colors.workspaceDefault, + height: "100%", + background: "none", + border: "none", + outline: "none", + appearance: "none", + flex: "1 1 1px", + minWidth: 0, + "&::placeholder": { + color: Colors.workspaceDefault + } + }, + _preview: { + flex: "0 0 25px", + height: "25px", + fontSize: "14px", + color: Colors.workspaceDefault, + cursor: "pointer", + lineHeight: "25px", + textAlign: "center", + "&:hover": { + color: Colors.workspaceDescriptor + } + }, + _lock: { + flex: "0 0 21px", + height: "21px", + margin: "2px", + marginRight: "5px", + fontSize: "15px", + border: `1px none ${Colors.workspaceDefault}`, + borderRadius: "5px", + color: Colors.workspaceDefault, + cursor: "pointer", + lineHeight: "21px", + textAlign: "center", + "&:hover": { + color: Colors.workspaceDescriptor, + borderColor: Colors.workspaceDescriptor + } + } +}; diff --git a/src/renderer/ui/WorkspaceControl/MacrosControl/MacrosControl.ts b/src/renderer/ui/WorkspaceControl/MacrosControl/MacrosControl.ts new file mode 100644 index 0000000..215026f --- /dev/null +++ b/src/renderer/ui/WorkspaceControl/MacrosControl/MacrosControl.ts @@ -0,0 +1,143 @@ +import { Div } from "aflon"; + +import { HttpCollection } from "../../../lib/http"; +import { Telemetry, TelemetryEvent } from "../../../lib/Telemetry"; +import { openFileInDefaultApp } from "../../../lib/Platform"; + +import { Toast } from "../../Toast"; +import { FontStyles, Colors } from "../../StyleConstants"; + +import { FeedbackToastContent } from "../FeedbackToastContent"; +import { MacroCollectionControl } from "./MacroCollectionControl"; + +export class MacrosFromCollection { + collection: HttpCollection; + macros: Array; +} + +export class MacrosControl extends Div { + private static readonly _maxMacroNameLetterNum = 18; + private static readonly _singleLetterWidth = 7.5; + private static readonly _totalMargin = 25; + + private _header: Div; + private _content: Div; + + private _collectionInFocus: HttpCollection = null; + private _macrosInFocus: Array = null; + + constructor() { + super(); + + this.append([ + (this._header = new Div()) + .setText("Parameters"), + (this._content = new Div()) + ]); + + this.on(this.eventClick, () => this._onClick()); + } + + setMarcos(mcfs: Array): this { + this._content.empty(); + + let maxMacroNameLetterNum: number = 0; + + mcfs.forEach(mcf => { + mcf.macros.forEach(macro => { + if (maxMacroNameLetterNum < macro.length) + maxMacroNameLetterNum = macro.length; + }); + }); + + if (maxMacroNameLetterNum > MacrosControl._maxMacroNameLetterNum) + maxMacroNameLetterNum = MacrosControl._maxMacroNameLetterNum; + + let maxNameWidth = maxMacroNameLetterNum * MacrosControl._singleLetterWidth + MacrosControl._totalMargin; + + mcfs.forEach(mcf => { + if (mcf.macros.length > 0) { + this._content.append([ + new MacroCollectionControl(mcf.collection).setMacroNames(mcf.macros, maxNameWidth) + ]); + } + }); + + this._emphasizeMacrosFromFocusedReqt(); + + if (this._content.getChildrenNumber() > 0) { + this._header.setInlineCss({ borderBottom: `solid 1px ${Colors.workspaceLine}` }); + } else { + this._header.setInlineCss({ borderBottom: "none" }); + } + + return this; + } + + setFocusedMacros(collection: HttpCollection, macros: Array): this { + this._collectionInFocus = collection; + this._macrosInFocus = macros; + + this._emphasizeMacrosFromFocusedReqt(); + + return this; + } + + private _emphasizeMacrosFromFocusedReqt(): void { + if (this._collectionInFocus == null) { + for (let child of this._content.children()) { + if (!(child instanceof MacroCollectionControl)) + continue; + + if (this._collectionInFocus == null) { + child.setMacrosInFocus(null); + } + } + return; + } + + for (let child of this._content.children()) { + if (!(child instanceof MacroCollectionControl)) + continue; + + if (child.getCollection() != this._collectionInFocus) + child.setMacrosInFocus([]); + else + child.setMacrosInFocus(this._macrosInFocus); + } + } + + private async _onClick(): Promise { + let numOfSends = Telemetry.getNumberOfEvents(TelemetryEvent.HttpRequestSent); + let numOfFeedbacks = Telemetry.getNumberOfEvents(TelemetryEvent.FeedbackSent); + let mumOfAsksForFeedback = Telemetry.getNumberOfEvents(TelemetryEvent.AskedForFeedback); + + if (mumOfAsksForFeedback > 0 || numOfFeedbacks > 0 || numOfSends < 200) return; + + Telemetry.reportEvent(TelemetryEvent.AskedForFeedback); + let openFeedback = await Toast.show(new FeedbackToastContent(), 30); + + if (!openFeedback) return; + openFileInDefaultApp("https://forms.gle/CMN15fRycYADAAbT6"); + Telemetry.reportEvent(TelemetryEvent.FeedbackSent); + } +} + +MacrosControl.style = { + _: { + display: "flex", + flexFlow: "column nowrap" + }, + _header: { + padding: "5px 15px", + ...FontStyles.sansSerifBold, + fontSize: "12px", + lineHeight: "25px", + color: Colors.workspaceDescriptor, + flex: "0 0 35px" + }, + _content: { + flex: "1 1 100px", + overflowX: "auto" + } +}; diff --git a/src/renderer/ui/WorkspaceControl/MacrosControl/index.ts b/src/renderer/ui/WorkspaceControl/MacrosControl/index.ts new file mode 100644 index 0000000..1d1b23e --- /dev/null +++ b/src/renderer/ui/WorkspaceControl/MacrosControl/index.ts @@ -0,0 +1 @@ +export { MacrosControl, MacrosFromCollection } from "./MacrosControl"; diff --git a/src/renderer/ui/WorkspaceControl/WItem/AuthDefinitionControl.ts b/src/renderer/ui/WorkspaceControl/WItem/AuthDefinitionControl.ts new file mode 100644 index 0000000..228e966 --- /dev/null +++ b/src/renderer/ui/WorkspaceControl/WItem/AuthDefinitionControl.ts @@ -0,0 +1,528 @@ +import { Div, ILabeled, AbstractInput, AbstractSelectBox, ISelectOption, AbstractTextBox } from "aflon"; + +import { AuthDefinition, AuthLocation, AuthLocationType, AuthType, IMacroSource } from "../../../lib/http"; +import { OAuth2ClientAuthentication, OAuth2Type, PkceCodeChallengeMethod } from "../../../lib/http"; +import { UrlTextEditor } from "../../HttpRequestControl"; +import { UrlTextEditorMode } from "../../HttpRequestControl/UrlTextEditor"; + +import { Colors, FontStyles } from "../../StyleConstants"; +import { MacroedTextEditor } from "../../TokenTextEditor"; +import { SelectBox } from "../../BasicControls"; + +export class LabeledInput extends Div implements ILabeled, AbstractInput { + public eventChange: string = "labeledChanged"; + public eventInput: string = "labeledInput"; + + private _label: Div; + private _input: A; + + constructor(Cons: { new(): A }) { + super(); + + this.append([ + (this._label = new Div()), + (this._input = (new Cons())) + .on(this._input.eventChange, () => this.raise(this.eventChange)) + .on(this._input.eventInput, () => this.raise(this.eventInput)) + ]); + } + + setDisabled(disabled: boolean): this { + this._input.setDisabled(disabled); + return this; + } + + getDisabled(): boolean { + return this._input.getDisabled(); + } + + focus(): void { + this._input.focus(); + } + + blur(): void { + this._input.blur(); + } + + getInput(): A { + return this._input; + } + + setLabel(label: string): this { + this._label.setText(label); + return this; + } + + getLabel(): string { + return this._label.getText(); + } + + setText(text: string): this { + this._input.setText(text); + return this; + } + + getText(): string { + return this._input.getText(); + } +} + +LabeledInput.style = { + _: { + display: "flex", + flexFlow: "row nowrap", + height: "20px", + alignItems: "baseline" + }, + _label: { + ...FontStyles.sansSerifBold, + color: Colors.workspaceDescriptor, + fontSize: "11px", + lineHeight: "20px", + marginRight: "10px", + whiteSpace: "nowrap" + }, + _input: { + flex: "1 0 1px" + } +}; + +export class LabeledTextBox extends LabeledInput implements AbstractTextBox { + setReadOnly(readOnly: boolean): this { + this.getInput().setReadOnly(readOnly); + return this; + } + + getReadOnly(): boolean { + return this.getInput().getReadOnly(); + } + + setPlaceholder(placeholderText: string): this { + this.getInput().setPlaceholder(placeholderText); + return this; + } + + getPlaceholder(): string { + return this.getInput().getPlaceholder(); + } +} + +export class LabeledMacroedTextEditor extends LabeledTextBox { + constructor() { + super(MacroedTextEditor); + + this.getInput().addCssClass({ + overflowX: "scroll", + "&::-webkit-scrollbar": { + display: "none" + } + }); + } +} + +class SingleLineUrlTextEditor extends UrlTextEditor { + constructor() { + super(); + this.setEditorMode(UrlTextEditorMode.SingleLine) + .setPlaceholder("..."); + } +} + +export class LabeledSingleLineUrlTextEditor extends LabeledTextBox { + constructor() { + super(SingleLineUrlTextEditor); + } +} + +export class LabeledSelectBox extends LabeledInput implements AbstractSelectBox { + eventSelected: string = "labeledSelected"; + + constructor() { + super(SelectBox); + this.getInput().on(this.getInput().eventSelected, () => this.raise(this.eventSelected)); + } + + insertOption(option: ISelectOption): this { + this.getInput().insertOption(option); + return this; + } + + removeOption(optionValue: string): this { + this.getInput().removeOption(optionValue); + return this; + } + + insertOptions(options: ISelectOption[]): this { + this.getInput().insertOptions(options); + return this; + } + + setSelectedOption(optionValue: string): this { + this.getInput().setSelectedOption(optionValue); + return this; + } + + getSelectedOption(): ISelectOption { + return this.getInput().getSelectedOption(); + } + + getAllOptions(): ISelectOption[] { + return this.getInput().getAllOptions(); + } +} + +class Buffer extends Div {} + +Buffer.style = { + _: { height: "10px" } +}; + +export class AuthDefinitionControl extends Div implements IMacroSource { + eventAuthDefinitionChanged = "authDefinitionChanged"; + + private _currentlyVisibleParams: Div; + + private _type: LabeledSelectBox; + + private _apiKeyParams: Div; + private _apiKey: LabeledMacroedTextEditor; + private _appendTo: LabeledSelectBox; + private _key: LabeledMacroedTextEditor; + + private _bearerParams: Div; + private _bearerToken: LabeledMacroedTextEditor; + + private _basicParams: Div; + private _username: LabeledMacroedTextEditor; + private _password: LabeledMacroedTextEditor; + + private _oauth2Params: Div; + private _oauth2Type: LabeledSelectBox; + private _pkceChallengeMethod: LabeledSelectBox; + private _callbackURL: LabeledSingleLineUrlTextEditor; + private _authURL: LabeledSingleLineUrlTextEditor; + private _accessTokenURL: LabeledSingleLineUrlTextEditor; + private _clientID: LabeledMacroedTextEditor; + private _clientSecret: LabeledMacroedTextEditor; + private _clientAuthentication: LabeledSelectBox; + private _scope: LabeledMacroedTextEditor; + private _state: LabeledMacroedTextEditor; + private _authorizationHeaderPrefix: LabeledMacroedTextEditor; + + constructor() { + super(); + + this.append([ + (this._type = new LabeledSelectBox) + .setLabel("Type") + .insertOptions([ + { text: "None", value: AuthType.None }, + { text: "Api key", value: AuthType.ApiKey }, + { text: "Bearer", value: AuthType.Bearer }, + { text: "Basic", value: AuthType.Basic }, + { text: "OAuth2", value: AuthType.OAuth2 } + ]) + .on(this._type.eventChange, () => this._onAuthTypeInput()), + this._apiKeyParams = new Div() + .append([ + (this._apiKey = new LabeledMacroedTextEditor()) + .setLabel("API key") + .on(this._apiKey.eventChange, this._onParamChanged), + (this._appendTo = new LabeledSelectBox()) + .setLabel("Add to") + .insertOptions([ + { text: "Headers", value: AuthLocationType.Header }, + { text: "URL query", value: AuthLocationType.UrlQuery } + ]) + .on(this._appendTo.eventChange, this._onAppendToChange) + .on(this._appendTo.eventChange, this._onParamChanged), + (this._key = new LabeledMacroedTextEditor()) + .setLabel("Header key") + .setText("Authorization") + .on(this._key.eventChange, this._onParamChanged) + ]) + .setVisibility(false), + this._bearerParams = new Div() + .append([ + (this._bearerToken = new LabeledMacroedTextEditor) + .setLabel("Token") + .on(this._bearerToken.eventChange, this._onParamChanged) + ]) + .setVisibility(false), + this._basicParams = new Div() + .append([ + (this._username = new LabeledMacroedTextEditor) + .setLabel("Username") + .on(this._username.eventChange, this._onParamChanged), + (this._password = new LabeledMacroedTextEditor) + .setLabel("Password") + .on(this._password.eventChange, this._onParamChanged) + ]) + .setVisibility(false), + this._oauth2Params = new Div() + .append([ + (this._oauth2Type = new LabeledSelectBox) + .setLabel("Flow type") + .insertOptions([ + { text: "Authorization code", value: OAuth2Type.AuthorizationCode }, + { text: "Implicit", value: OAuth2Type.Implicit }, + { text: "Client credentials", value: OAuth2Type.ClientCredentials } + ]) + .on(this._oauth2Type.eventChange, this._onOAuth2TypeChanged), + (this._pkceChallengeMethod = new LabeledSelectBox()) + .setLabel("PKCE challenge") + .insertOptions([ + { text: "None", value: PkceCodeChallengeMethod.None }, + { text: "Plain", value: PkceCodeChallengeMethod.Plain }, + { text: "SHA256", value: PkceCodeChallengeMethod.SHA256 } + ]) + .on(this._pkceChallengeMethod.eventChange, this._onParamChanged), + new Buffer(), + (this._callbackURL = new LabeledSingleLineUrlTextEditor()) + .setLabel("Callback URL") + .on(this._callbackURL.eventChange, this._onParamChanged), + (this._authURL = new LabeledSingleLineUrlTextEditor()) + .setLabel("Auth URL") + .on(this._authURL.eventChange, this._onParamChanged), + (this._accessTokenURL = new LabeledSingleLineUrlTextEditor()) + .setLabel("Token URL") + .on(this._accessTokenURL.eventChange, this._onParamChanged), + new Buffer(), + (this._clientID = new LabeledMacroedTextEditor()) + .setLabel("Client ID") + .on(this._clientID.eventChange, this._onParamChanged), + (this._clientSecret = new LabeledMacroedTextEditor()) + .setLabel("Client secret") + .on(this._clientSecret.eventChange, this._onParamChanged), + (this._clientAuthentication = new LabeledSelectBox()) + .setLabel("Pass client credentials") + .insertOptions([ + { text: "As basic authentication", value: OAuth2ClientAuthentication.BasicAuthentication }, + { text: "In URL-encoded body", value: OAuth2ClientAuthentication.InBody } + ]) + .on(this._clientSecret.eventChange, this._onParamChanged), + new Buffer(), + (this._scope = new LabeledMacroedTextEditor()) + .setLabel("Scope") + .on(this._scope.eventChange, this._onParamChanged), + (this._state = new LabeledMacroedTextEditor()) + .setLabel("State") + .on(this._state.eventChange, this._onParamChanged), + new Buffer(), + (this._authorizationHeaderPrefix = new LabeledMacroedTextEditor()) + .setLabel("Authorization header prefix") + .on(this._state.eventChange, this._onParamChanged) + ]) + .setVisibility(false) + ]); + } + + setAuthDefinition(auth: AuthDefinition, location: AuthLocation = null): this { + if (auth == null) return this; + + this._type.setSelectedOption(auth.type); + this._updateAuthParams(auth, location); + this._updateApiKeyLabel(); + + return this; + } + + getAuthDefinition(): AuthDefinition { + let type: AuthType = (this._type.getSelectedOption().value); + + if (type == AuthType.None) { + return { type }; + } else if (type == AuthType.ApiKey) { + return { type, apiKey: this._apiKey.getText() }; + } else if (type == AuthType.Bearer) { + return { type, bearerToken: this._bearerToken.getText() }; + } else if (type == AuthType.Basic) { + return { + type, + username: this._username.getText(), + password: this._password.getText() + }; + } else if (type == AuthType.OAuth2) { + return { + type, + oauth2Type: this._oauth2Type.getSelectedOption().value, + codeChallengeMethod: this._pkceChallengeMethod.getSelectedOption().value, + callbackURL: this._callbackURL.getText(), + authURL: this._authURL.getText(), + accessTokenURL: this._accessTokenURL.getText(), + clientID: this._clientID.getText(), + clientSecret: this._clientSecret.getText(), + clientAuthentication: this._clientAuthentication.getSelectedOption().value, + scope: this._scope.getText(), + state: this._state.getText() + }; + } else { + throw new Error(`Unsupported auth type ${type}`); + } + } + + getAuthLocation(): AuthLocation { + let type: AuthType = (this._type.getSelectedOption().value); + if (type == AuthType.ApiKey) { + return { + type: this._appendTo.getSelectedOption().value, + key: this._key.getText(), + prefix: "" + }; + } + + if (type == AuthType.OAuth2 && this._authorizationHeaderPrefix.getText().trim() != "Bearer") { + return { + type: AuthLocationType.Header, + key: "Authorization", + prefix: this._authorizationHeaderPrefix.getText() + }; + } + + return null; + } + + getMacroNames(): Array { + if (!this._currentlyVisibleParams) return []; + + let macros: Array = []; + + this._currentlyVisibleParams.children().forEach(child => { + if (child instanceof LabeledMacroedTextEditor) { + child.getInput().getMacroNames().forEach(macro => { + if (macros.indexOf(macro) == -1) + macros.push(macro); + }); + } + }); + + return macros; + } + + private _onAuthTypeInput(): void { + let type: AuthType = (this._type.getSelectedOption().value); + this._updateAuthParams({ type }, this.getAuthLocation()); + this.raise(this.eventAuthDefinitionChanged); + } + + private _updateAuthParams(authDef: AuthDefinition = { type: AuthType.None }, location: AuthLocation = null): void { + if (this._currentlyVisibleParams) + this._currentlyVisibleParams.setVisibility(false); + + if (authDef.type == AuthType.ApiKey) { + this._apiKeyParams.setVisibility(true); + this._apiKey.setText(authDef.apiKey ?? ""); + if (location) { + this._appendTo.setSelectedOption(location.type); + this._key.setText(location.key ?? "Authorization"); + } else { + this._appendTo.setSelectedOption(AuthLocationType.Header); + this._key.setText("Authorization"); + } + this._currentlyVisibleParams = this._apiKeyParams; + } else if (authDef.type == AuthType.Bearer) { + this._bearerParams.setVisibility(true); + this._bearerToken.setText(authDef.bearerToken ?? ""); + this._currentlyVisibleParams = this._bearerParams; + } else if (authDef.type == AuthType.Basic) { + this._basicParams.setVisibility(true); + this._username.setText(authDef.username ?? ""); + this._password.setText(authDef.password ?? ""); + this._currentlyVisibleParams = this._basicParams; + } else if (authDef.type == AuthType.OAuth2) { + this._oauth2Params.setVisibility(true); + this._oauth2Type.setSelectedOption(authDef.oauth2Type ?? OAuth2Type.AuthorizationCode); + this._callbackURL.setText(authDef.callbackURL ?? ""); + this._authURL.setText(authDef.authURL ?? ""); + this._accessTokenURL.setText(authDef.accessTokenURL ?? ""); + this._clientID.setText(authDef.clientID ?? ""); + this._clientSecret.setText(authDef.clientSecret ?? ""); + this._clientAuthentication.setSelectedOption(authDef.clientAuthentication ?? OAuth2ClientAuthentication.InBody); + this._pkceChallengeMethod.setSelectedOption(authDef.codeChallengeMethod ?? PkceCodeChallengeMethod.None); + this._scope.setText(authDef.scope ?? ""); + this._state.setText(authDef.state ?? ""); + + if (location && location.prefix) + this._authorizationHeaderPrefix.setText(location.prefix); + else + this._authorizationHeaderPrefix.setText("Bearer"); + + this._updateOAuth2Params( this._oauth2Type.getSelectedOption().value); + this._currentlyVisibleParams = this._oauth2Params; + } else { + this._currentlyVisibleParams = null; + } + } + + private _updateOAuth2Params(oauth2Type: OAuth2Type): void { + if (oauth2Type == OAuth2Type.ClientCredentials) { + this._pkceChallengeMethod.setVisibility(false); + this._authURL.setVisibility(false); + this._accessTokenURL.setVisibility(true); + this._callbackURL.setVisibility(false); + this._clientSecret.setVisibility(true); + this._state.setVisibility(false); + this._clientAuthentication.setVisibility(true); + } else if (oauth2Type == OAuth2Type.Implicit) { + this._pkceChallengeMethod.setVisibility(false); + this._authURL.setVisibility(true); + this._accessTokenURL.setVisibility(false); + this._callbackURL.setVisibility(true); + this._clientSecret.setVisibility(false); + this._state.setVisibility(true); + this._clientAuthentication.setVisibility(false); + } else { + this._pkceChallengeMethod.setVisibility(true); + this._authURL.setVisibility(true); + this._accessTokenURL.setVisibility(true); + this._callbackURL.setVisibility(true); + this._clientSecret.setVisibility(true); + this._state.setVisibility(true); + this._clientAuthentication.setVisibility(true); + } + } + + private _onParamChanged = (): void => { + this.raise(this.eventAuthDefinitionChanged); + }; + + private _onOAuth2TypeChanged = (): void => { + this._updateOAuth2Params( this._oauth2Type.getSelectedOption().value); + this.raise(this.eventAuthDefinitionChanged); + }; + + private _onAppendToChange = (): void => { + this._updateApiKeyLabel(); + }; + + private _updateApiKeyLabel(): void { + if (this._appendTo.getSelectedOption().value == AuthLocationType.Header) { + this._key.setLabel("Header name"); + } else { + this._key.setLabel("Query key"); + } + } +} + +AuthDefinitionControl.style = { + _: { + display: "flex", + flexFlow: "column nowrap" + }, + _apiKeyParams: { + display: "flex", + flexFlow: "column nowrap" + }, + _bearerParams: { + display: "flex", + flexFlow: "column nowrap" + }, + _basicParams: { + display: "flex", + flexFlow: "column nowrap" + }, + _oauth2Params: { + display: "flex", + flexFlow: "column nowrap" + } +}; diff --git a/src/renderer/ui/WorkspaceControl/WItem/AuthExpandableRow.ts b/src/renderer/ui/WorkspaceControl/WItem/AuthExpandableRow.ts new file mode 100644 index 0000000..5eefcdd --- /dev/null +++ b/src/renderer/ui/WorkspaceControl/WItem/AuthExpandableRow.ts @@ -0,0 +1,222 @@ +import { Div } from "aflon"; + +import { AuthType, HttpAuth, HttpDir, HttpReqt } from "../../../lib/http"; +import { SimpleEvent } from "../../../lib/SimpleEvent"; + +import { ContextMenu, ContextMenuItemDefinition, ContextMenuItemType, ContextMenuShowTrigger } from "../../ContextMenu"; +import { ExpandableRow } from "../../ExpandableTable"; +import { Colors, FontStyles } from "../../StyleConstants"; +import { Button } from "../../BasicControls"; +import { SimpleModals } from "../../Modals"; + +import { AuthDefinitionControl } from "./AuthDefinitionControl"; + +export class AuthExpandableRow extends ExpandableRow { + public eventAuthChanged = "authChanged"; + + private _authSelectBox: Div; + private _authButton: Button; + private _authDefinition: AuthDefinitionControl; + + private _authContextMenu: ContextMenu; + + private _reqt: HttpReqt = null; + + constructor() { + super(); + + this.setTitle("Auth") + .appendContent([ + new Div() + .append([ + (this._authSelectBox = new Div()) + .setText("Inherit authentication ('Default Auth' in ancestor directory)") + .addCssClass({ + ...FontStyles.sansSerifBold, + color: Colors.workspaceDefault, + cursor: "pointer", + textDecoration: "underline", + fontSize: "12px", + display: "inline-block", + marginRight: "10px", + whiteSpace: "nowrap" + }), + (this._authButton = new Button()) + .setText("Execute auth flow") + .addCssClass({ + borderRadius: "3px", + padding: "2px 6px", + fontSize: "11px", + height: "auto" + }) + .on(this._authButton.eventClick, () => this._onAuthButtonClick()) + ]) + .addCssClass({ + display: "flex", + flexFlow: "row nowrap", + marginBottom: "10px" + }), + (this._authDefinition = new AuthDefinitionControl()) + .on(this._authDefinition.eventAuthDefinitionChanged, () => this._onLocalAuthDefChanged()) + .setVisibility(false) + ]); + + (this._authContextMenu = new ContextMenu(this._authSelectBox, [], ContextMenuShowTrigger.OnClickEvent, 400)) + .on(this._authContextMenu.eventAboutToBeShown, () => this._onContextMenuAboutToBeShown()) + .on(this._authContextMenu.eventSelected, e => this._onContextMenuSelected(e)); + } + + setReqt(reqt: HttpReqt): this { + if (this._reqt != null) + this._reqt.off(this._reqt.eventAuthChanged, this._onAuthChanged); + + this._reqt = reqt; + + if (this._reqt == null) return this; + + this._reqt.on(this._reqt.eventAuthChanged, this._onAuthChanged); + + let auth = this._reqt.getAuth(); + + if (!auth) { + this._authSelectBox.setText("Inherit authentication ('Default Auth' in ancestor directory)"); + this._authDefinition.setVisibility(false); + this._authButton.setVisibility(false); + return this; + } + + let authDefVisibility = false; + let authButtonVisibility = false; + + if (auth.getParent() != this._reqt) { + this._authSelectBox.setText(auth.getFullPath().substring(1)); + } else if (auth.getAuthDefinition().type == AuthType.None) { + this._authSelectBox.setText("No authentication"); + } else { + this._authSelectBox.setText("Request-specific authentication"); + this._authDefinition.setAuthDefinition(auth.getAuthDefinition(), auth.getAuthLocation()); + authDefVisibility = true; + } + + if (this._reqt.getAuth().getAuthDefinition().type == AuthType.OAuth2) { + authButtonVisibility = true; + } + + this._authDefinition.setVisibility(authDefVisibility); + this._authButton.setVisibility(authButtonVisibility); + + return this; + } + + getReqt(): HttpReqt { + return this._reqt; + } + + private _onLocalAuthDefChanged(): void { + if (this._reqt.getAuth().getParent() != this._reqt) return; + this._reqt.setAuth(new HttpAuth() + .setParent(this._reqt) + .setAuthDefinition(this._authDefinition.getAuthDefinition()) + .setAuthLocation(this._authDefinition.getAuthLocation())); + } + + private _onContextMenuAboutToBeShown(): void { + let items: Array = [ + { type: ContextMenuItemType.Button, text: "No authentication", id: "none" }, + { type: ContextMenuItemType.Button, text: "Inherit authentication", id: "inherit" }, + { type: ContextMenuItemType.Button, text: "Request-specific authentication", id: "local" }, + { type: ContextMenuItemType.Divider, id: "div" } + ]; + + let auths: Array = []; + + let readAllAuths = (dir: HttpDir): void => { + auths = [ ...auths, ...dir.getAuths() ]; + for (let childDir of dir.getDirs()) + readAllAuths(childDir); + }; + + readAllAuths(this._reqt.getContainingCollection()); + + auths.forEach( + auth => items.push({ type: ContextMenuItemType.Button, text: auth.getFullPath().substring(1), id: auth.getFullPath() }) + ); + + this._authContextMenu.setDefinition(items); + } + + private _onContextMenuSelected(e: SimpleEvent): void { + let fullPath: string = (e["detail"]["id"]); + + if (fullPath == "none") { + this._reqt.setAuth(new HttpAuth() + .setParent(this._reqt) + .setAuthDefinition({ type: AuthType.None })); + return; + } + + if (fullPath == "inherit") { + this._reqt.setAuth(null); + return; + } + + if (fullPath == "local") { + if (this._authDefinition.getVisibility()) + return; + + this._reqt.setAuth(new HttpAuth() + .setParent(this._reqt) + .setAuthDefinition({ type: AuthType.ApiKey })); + return; + } + + let auth = this._reqt.getContainingCollection().findFromAbsolutePath(fullPath); + if (!auth) return; + + this._reqt.setAuth(auth); + } + + private _onAuthChanged = (): void => { + let auth = this._reqt.getAuth(); + + if (!auth) { + this._authDefinition.setVisibility(false); + this._authSelectBox.setText("Inherit authentication ('Default Auth' in ancestor directory)"); + this._authButton.setVisibility(false); + return; + } + + if (auth.getParent() != this._reqt) { + this._authDefinition.setVisibility(false); + this._authSelectBox.setText(auth.getFullPath().substring(1)); + } else { + let definition = auth.getAuthDefinition(); + + if (definition.type == AuthType.None) { + this._authSelectBox.setText("No authentication"); + this._authDefinition.setVisibility(false); + } else { + this._authSelectBox.setText("Request-specific authentication"); + this._authDefinition + .setVisibility(true) + .setAuthDefinition(definition, auth.getAuthLocation()); + } + } + + if (auth.getAuthDefinition().type == AuthType.OAuth2) + this._authButton.setVisibility(true); + else + this._authButton.setVisibility(false); + }; + + private async _onAuthButtonClick(): Promise { + if (this._reqt.getAuth().getAuthDefinition().type != AuthType.OAuth2) return; + + let result = await this._reqt.getAuth().authorize(); + + if (result) + SimpleModals.alert(`There was an error during authentication: ${result}`); + else + SimpleModals.alert("Authentication was successful. Token is saved and will be added to requests which use this authentication method."); + } +} diff --git a/src/renderer/ui/WorkspaceControl/WItem/HttpAuthWitem.ts b/src/renderer/ui/WorkspaceControl/WItem/HttpAuthWitem.ts new file mode 100644 index 0000000..f9aa2d7 --- /dev/null +++ b/src/renderer/ui/WorkspaceControl/WItem/HttpAuthWitem.ts @@ -0,0 +1,274 @@ +import { AbstractTextBox, Div, TextBox } from "aflon"; + +import { AuthType, HttpAuth, IMacroSource } from "../../../lib/http"; +import { Tooltip } from "../../ContextMenu"; + +import { Icon } from "../../Icon"; +import { Colors, FontStyles } from "../../StyleConstants"; +import { SimpleModals } from "../../Modals"; + +import { AuthDefinitionControl } from "./AuthDefinitionControl"; +import { WItem } from "./WItem"; + +export class HttpAuthWitem extends WItem implements IMacroSource { + private _header: Div; + private _expander: Div; + private _methodDescriptor: Div; + private _name: AbstractTextBox; + private _pinButton: Div; + private _closeButton: Div; + private _sendButton: Div; + private _editor: AuthDefinitionControl; + private _authTooltip: Tooltip; + + private _auth: HttpAuth; + private _expanded: boolean = true; + private _pinned: boolean = false; + + constructor() { + super(); + + this.append([ + (this._header = new Div()) + .append([ + (this._expander = new Div()) + .setText("โ€ข") + .on(this._expander.eventClick, () => this._onExpanderClick()) + .on(this._expander.eventDblClick, () => this._onExpanderDblClick()), + (this._methodDescriptor = new Div()) + .setText("Auth") + .on(this._methodDescriptor.eventClick, () => this._onExpanderClick()) + .on(this._methodDescriptor.eventDblClick, () => this._onExpanderDblClick()), + (this._name = new TextBox()) + .on(this._name.eventChange, () => this._onNameChange()) + .setInlineCss({ fontStyle: "italic" }) + .setText("Untitled request"), + (this._pinButton = new Div()) + .append([ new Icon("pin")]) + .on(this._pinButton.eventClick, () => this._onPinButtonClick()), + (this._closeButton = new Div()) + .append([ new Icon("close")]) + .on(this._closeButton.eventClick, () => this._onCloseButtonClick()), + (this._sendButton = new Div()) + .setText("Auth") + .on(this._sendButton.eventClick, () => this._onAuth()) + ]), + (this._editor = new AuthDefinitionControl()) + .on(this._editor.eventAuthDefinitionChanged, () => this._onAuthDefChanged()) + ]); + + (this._authTooltip = new Tooltip(this._sendButton)); + } + + getMacroNames(): Array { + return this._editor.getMacroNames(); + } + + setItem(auth: HttpAuth): this { + if (this._auth == auth) return this; + + this._auth = auth; + this._name.setText(this._auth.getName()); + this._editor.setAuthDefinition(auth.getAuthDefinition(), auth.getAuthLocation()); + this._updateAuthButton(); + this._auth.on(this._auth.eventNameChanged, this._onAuthNameChange); + return this; + } + + getItem(): HttpAuth { + return this._auth; + } + + setExpanded(expanded: boolean): this { + if (!this._pinned) return this; + if (expanded == this._expanded) return this; + + this._expanded = expanded; + + if (this._expanded) { + this._expander.setInlineCss({ transform: "rotate(90deg)" }); + this._editor.setVisibility(true); + } else { + this._expander.setInlineCss({ transform: "none" }); + this._editor.setVisibility(false); + } + + return this; + } + + getExpanded(): boolean { + return this._expanded; + } + + pin(): this { + if (this._pinned) return this; + + this._pinButton.setInlineCss({ display: "none" }); + this._expander.setText("โ–ถ"); + this._expander.setInlineCss({ fontSize: "12px" }); + this._name.setInlineCss({ fontStyle: "normal" }); + this._pinned = true; + + return this; + } + + isPinned(): boolean { + return this._pinned; + } + + focusName(): this { + setTimeout(() => { + this._name.focus(); + ((this._name.getHtmlElement())).select(); + }, 100); + + return this; + } + + private _updateAuthButton(): void { + if (this._auth.getAuthDefinition().type == AuthType.OAuth2) { + this._sendButton.setInlineCss({ + cursor: "pointer", + color: Colors.workspaceDescriptor + }); + this._authTooltip.setText("Execute OAuth flow and obtain authentication data."); + } else { + this._sendButton.setInlineCss({ + cursor: "default", + color: Colors.workspacePlaceholder + }); + this._authTooltip.setText("Selected authentication method does not need to be executed in order to obtain authentication data."); + } + } + + private _onExpanderClick(): void { + if (!this._pinned) return; + + this.setExpanded(!this.getExpanded()); + } + + private _onExpanderDblClick(): void { + if (this._pinned) return; + + this.pin(); + } + + private _onNameChange(): void { + if (this._auth == null) return; + this._auth.setName(this._name.getText().trim()); + } + + private _onPinButtonClick(): void { + this.pin(); + } + + private _onCloseButtonClick(): void { + this.raise(this.eventCloseRequested); + } + + private _onAuthDefChanged(): void { + this._auth + .setAuthDefinition(this._editor.getAuthDefinition()) + .setAuthLocation(this._editor.getAuthLocation()); + this._updateAuthButton(); + this.raise(this.eventCollectionItemChanged); + } + + private async _onAuth(): Promise { + if (this._auth.getAuthDefinition().type != AuthType.OAuth2) return; + + let result = await this._auth.authorize(); + if (result) + SimpleModals.alert(`There was an error during authentication: ${result}`); + else + SimpleModals.alert("Authentication was successful. Token is saved and will be added to requests which use this authentication method."); + } + + private _onAuthNameChange = (): void => { + this._name.setText(this._auth.getName()); + }; +} + +HttpAuthWitem.style = { + _: { + display: "flex", + flexFlow: "column nowrap", + borderBottom: `solid 1px ${Colors.workspaceLine}` + }, + _header: { + display: "flex", + flexFlow: "row nowrap", + height: "40px", + ...FontStyles.sansSerifNormal + }, + _expander: { + flex: "0 0 50px", + height: "100%", + color: Colors.workspaceDescriptor, + lineHeight: "40px", + fontSize: "30px", + textAlign: "center", + transform: "rotate(90deg)", + cursor: "pointer" + }, + _methodDescriptor: { + ...FontStyles.sansSerifBold, + color: Colors.methodOther, + letterSpacing: "-0.5px", + fontSize: "12px", + lineHeight: "40px", + height: "100%", + width: "32px", + cursor: "pointer" + }, + _name: { + flex: "1 1 200px", + height: "100%", + ...FontStyles.sansSerifNormal, + color: Colors.workspaceDescriptor, + border: "none", + outline: "none", + background: "none", + userSelect: "text" + }, + _pinButton: { + flex: "0 0 25px", + height: "25px", + lineHeight: "25px", + marginTop: "8px", + color: Colors.workspaceDescriptor, + fontSize: "14px", + paddingTop: "1px", + textAlign: "center", + cursor: "pointer", + marginRight: "2px" + }, + _closeButton: { + flex: "0 0 25px", + lineHeight: "25px", + marginTop: "8px", + paddingTop: "1px", + color: Colors.workspaceDescriptor, + fontSize: "13px", + height: "25px", + textAlign: "center", + cursor: "pointer" + }, + _sendButton: { + ...FontStyles.sansSerifBold, + color: Colors.workspacePlaceholder, + flex: "0 0 fit-content", + height: "100%", + textAlign: "center", + lineHeight: "40px", + fontSize: "13px", + paddingRight: "15px", + paddingLeft: "8px", + cursor: "pointer" + }, + _editor: { + marginLeft: "52px", + marginTop: "10px", + marginBottom: "20px" + } +}; diff --git a/src/renderer/ui/WorkspaceControl/WItem/HttpReqtWitem.ts b/src/renderer/ui/WorkspaceControl/WItem/HttpReqtWitem.ts new file mode 100644 index 0000000..53a25a8 --- /dev/null +++ b/src/renderer/ui/WorkspaceControl/WItem/HttpReqtWitem.ts @@ -0,0 +1,355 @@ +import { AbstractTextBox, Div, TextBox } from "aflon"; + +import { HttpReqt, HttpRequest, HttpRequestMethod } from "../../../lib/http"; + +import { HttpRequestControl } from "../../HttpRequestControl"; +import { UrlControlMode } from "../../HttpRequestControl/UrlControl"; +import { Icon } from "../../Icon"; +import { PreferenceStore } from "../../PreferenceStore"; +import { Colors, FontStyles, getMethodColor, getShortMethodDesignation } from "../../StyleConstants"; + +import { AuthExpandableRow } from "./AuthExpandableRow"; +import { WItem } from "./WItem"; + +export class HttpReqtWitem extends WItem { + private _header: Div; + private _expander: Div; + private _methodDescriptor: Div; + private _name: AbstractTextBox; + private _pinButton: Div; + private _closeButton: Div; + private _sendButton: Div; + private _editor: HttpRequestControl; + private _authControl: AuthExpandableRow; + + private _reqt: HttpReqt; + private _expanded: boolean = true; + private _pinned: boolean = false; + + constructor() { + super(); + + this.append([ + (this._header = new Div()) + .append([ + (this._expander = new Div()) + .setText("โ€ข") + .on(this._expander.eventClick, () => this._onExpanderClick()) + .on(this._expander.eventDblClick, () => this._onExpanderDblClick()), + (this._methodDescriptor = new Div()) + .setText("OTH") + .on(this._methodDescriptor.eventClick, () => this._onExpanderClick()) + .on(this._methodDescriptor.eventDblClick, () => this._onExpanderDblClick()), + (this._name = new TextBox()) + .on(this._name.eventChange, () => this._onNameChange()) + .setInlineCss({ fontStyle: "italic" }) + .setText("Untitled request"), + (this._pinButton = new Div()) + .append([ new Icon("pin")]) + .on(this._pinButton.eventClick, () => this._onPinButtonClick()), + (this._closeButton = new Div()) + .append([ new Icon("close")]) + .on(this._closeButton.eventClick, () => this._onCloseButtonClick()), + (this._sendButton = new Div()) + .setText("Send") + .on(this._sendButton.eventClick, async () => this.raise(this.eventSendRequested, { + reqt: this._reqt + })) + ]), + (this._editor = new HttpRequestControl()) + .setDescriptorShown(!PreferenceStore.getHideRequestDescriptor()) + .setUrlEditMode(PreferenceStore.getPreferSingleLineUrl() ? + UrlControlMode.SingleLine : UrlControlMode.MultiLineTable) + .on(this._editor.eventMethodChanged, () => this._onMethodChanged()) + .on(this._editor.eventUrlChanged, () => this._onUrlChanged()) + .on(this._editor.eventHeadersChanged, () => this._onHeadersChanges()) + .on(this._editor.eventBodyChanged, () => this._onBodyChanged()) + .on(this._editor.eventSendRequested, async () => this.raise(this.eventSendRequested, { + reqt: this._reqt + })) + .on(this._editor.eventRevertBodyToDefaultRequested, () => this._onRevertBodyToDefaultRequested()) + .on(this._editor.eventSaveCurrentBodyAsDefaultRequested, () => this._onSaveCurrentBodyAsDefaultRequested()), + (this._authControl = new AuthExpandableRow()) + .setDescriptorShown(!PreferenceStore.getHideRequestDescriptor()) + ]); + + PreferenceStore + .on(PreferenceStore.eventHideRequestDescriptorChanged, this._onPreferenceStoreHideRequestLabelsChanged) + .on(PreferenceStore.eventPreferSingleLineUrlChanged, this._onPreferenceStorePreferSingleLineUrlChanged); + } + + getMacroNames(): Array { + let macros = [...this._reqt.getMacroNames()]; + + this._reqt.getAuthMacroNames().forEach(macro => { + if (macros.indexOf(macro) == -1) + macros.push(macro); + }); + + return macros; + } + + setItem(reqt: HttpReqt): this { + if (this._reqt != null) { + this._reqt + .off(this._reqt.eventUrlChanged, this._onRequestChanged) + .off(this._reqt.eventHeadersChanged, this._onRequestChanged) + .off(this._reqt.eventBodyChanged, this._onRequestChanged) + .off(this._reqt.eventNameChanged, this._onRequestNameChanged) + .off(this._reqt.eventAuthChanged, this._onRequestAuthChanged); + } + + this._reqt = reqt; + + if (reqt == null) { + this._methodDescriptor.setText(getShortMethodDesignation(HttpRequestMethod.GET)); + this._methodDescriptor.setInlineCss({ color: getMethodColor(HttpRequestMethod.GET) }); + this._name.setText("Undefined request"); + this._editor.setHttpRequest(new HttpRequest(), null); + return this; + } + + this._methodDescriptor.setText(getShortMethodDesignation(this._reqt.getRawHttpRequest().method)); + this._methodDescriptor.setInlineCss({ color: getMethodColor(this._reqt.getRawHttpRequest().method) }); + this._name.setText(this._reqt.getName()); + this._editor.setHttpRequest(this._reqt.getRawHttpRequest(), this._reqt.getContainingCollection()); + + this._authControl.setReqt(reqt); + + this._reqt + .on(this._reqt.eventUrlChanged, this._onRequestChanged) + .on(this._reqt.eventHeadersChanged, this._onRequestChanged) + .on(this._reqt.eventBodyChanged, this._onRequestChanged) + .on(this._reqt.eventNameChanged, this._onRequestNameChanged) + .on(this._reqt.eventAuthChanged, this._onRequestAuthChanged); + + return this; + } + + getItem(): HttpReqt { + return this._reqt; + } + + setExpanded(expanded: boolean): this { + if (!this._pinned) return this; + if (expanded == this._expanded) return this; + + this._expanded = expanded; + + if (this._expanded) { + this._expander.setInlineCss({ transform: "rotate(90deg)" }); + this._editor.setVisibility(true); + this._authControl.setVisibility(true); + } else { + this._expander.setInlineCss({ transform: "none" }); + this._editor.setVisibility(false); + this._authControl.setVisibility(false); + } + + return this; + } + + getExpanded(): boolean { + return this._expanded; + } + + pin(): this { + if (this._pinned) return this; + + this._pinButton.setInlineCss({ display: "none" }); + this._expander.setText("โ–ถ"); + this._expander.setInlineCss({ fontSize: "12px" }); + this._name.setInlineCss({ fontStyle: "normal" }); + this._pinned = true; + + return this; + } + + isPinned(): boolean { + return this._pinned; + } + + focusName(): this { + setTimeout(() => { + this._name.focus(); + ((this._name.getHtmlElement())).select(); + }, 100); + + return this; + } + + protected _onLeavingDom(): void { + PreferenceStore + .off(PreferenceStore.eventHideRequestDescriptorChanged, this._onPreferenceStoreHideRequestLabelsChanged) + .off(PreferenceStore.eventPreferSingleLineUrlChanged, this._onPreferenceStorePreferSingleLineUrlChanged); + } + + private _onExpanderClick(): void { + if (!this._pinned) return; + + this.setExpanded(!this.getExpanded()); + } + + private _onExpanderDblClick(): void { + if (this._pinned) return; + + this.pin(); + } + + private _onNameChange(): void { + if (this._reqt == null) return; + this._reqt.setName(this._name.getText().trim()); + } + + private _onMethodChanged(): void { + if (this._reqt == null) return; + + this._reqt.setMethod(this._editor.getHttpMethod()); + + this._methodDescriptor.setText(getShortMethodDesignation(this._reqt.getRawHttpRequest().method)); + this._methodDescriptor.setInlineCss({ color: getMethodColor(this._reqt.getRawHttpRequest().method) }); + } + + private _onUrlChanged(): void { + if (this._reqt == null) return; + this._reqt.setUrl(this._editor.getUrl()); + } + + private _onSaveCurrentBodyAsDefaultRequested(): void { + if (this._reqt == null) return; + this._reqt.saveCurrentBodyAsDefault(); + } + + private _onRevertBodyToDefaultRequested(): void { + if (this._reqt == null) return; + this._reqt.revertBodyToDefault(); + this._editor.setHttpRequest(this._reqt.getRawHttpRequest(), this._reqt.getContainingCollection()); + } + + private _onHeadersChanges(): void { + if (this._reqt == null) return; + this._reqt.setHeaders(this._editor.getHeaders()); + } + + private _onBodyChanged(): void { + if (this._reqt == null) return; + this._reqt.setBody(this._editor.getBody()); + } + + private _onPinButtonClick(): void { + if (!this._pinned) + this.pin(); + } + + private _onCloseButtonClick(): void { + this.raise(this.eventCloseRequested); + } + + private _onRequestChanged = (): void => { + this.raise(this.eventCollectionItemChanged); + }; + + private _onRequestNameChanged = (): void => { + this._name.setText(this._reqt.getName()); + }; + + private _onRequestAuthChanged = (): void => { + this.raise(this.eventCollectionItemChanged); + }; + + private _onPreferenceStoreHideRequestLabelsChanged = (): void => { + this._editor.setDescriptorShown(!PreferenceStore.getHideRequestDescriptor()); + this._authControl.setDescriptorShown(!PreferenceStore.getHideRequestDescriptor()); + }; + + private _onPreferenceStorePreferSingleLineUrlChanged = (): void => { + this._editor.setUrlEditMode( + PreferenceStore.getPreferSingleLineUrl() ? + UrlControlMode.SingleLine : UrlControlMode.MultiLineTable + ); + }; +} + +HttpReqtWitem.style = { + _: { + display: "flex", + flexFlow: "column nowrap", + borderBottom: `solid 1px ${Colors.workspaceLine}` + }, + _header: { + display: "flex", + flexFlow: "row nowrap", + height: "40px", + ...FontStyles.sansSerifNormal + }, + _expander: { + flex: "0 0 50px", + height: "100%", + color: Colors.workspaceDescriptor, + lineHeight: "40px", + fontSize: "30px", + textAlign: "center", + transform: "rotate(90deg)", + cursor: "pointer" + }, + _methodDescriptor: { + ...FontStyles.sansSerifExtraBold, + color: Colors.methodOther, + fontSize: "12px", + lineHeight: "40px", + height: "100%", + flex: "0 0 32px", + cursor: "pointer" + }, + _name: { + flex: "1 1 200px", + height: "100%", + ...FontStyles.sansSerifNormal, + color: Colors.workspaceDescriptor, + border: "none", + outline: "none", + background: "none", + userSelect: "text" + }, + _pinButton: { + flex: "0 0 25px", + height: "25px", + lineHeight: "25px", + marginTop: "8px", + color: Colors.workspaceDescriptor, + fontSize: "14px", + paddingTop: "1px", + textAlign: "center", + cursor: "pointer", + marginRight: "2px" + }, + _closeButton: { + flex: "0 0 25px", + lineHeight: "25px", + marginTop: "8px", + paddingTop: "1px", + color: Colors.workspaceDescriptor, + fontSize: "13px", + height: "25px", + textAlign: "center", + cursor: "pointer" + }, + _sendButton: { + ...FontStyles.sansSerifBold, + color: Colors.workspaceDescriptor, + flex: "0 0 fit-content", + height: "100%", + textAlign: "center", + lineHeight: "40px", + cursor: "pointer", + fontSize: "13px", + paddingRight: "15px", + paddingLeft: "8px" + }, + _editor: { + marginTop: "10px" + }, + _authControl: { + marginBottom: "20px" + } +}; diff --git a/src/renderer/ui/WorkspaceControl/WItem/WItem.ts b/src/renderer/ui/WorkspaceControl/WItem/WItem.ts new file mode 100644 index 0000000..6a81ab3 --- /dev/null +++ b/src/renderer/ui/WorkspaceControl/WItem/WItem.ts @@ -0,0 +1,19 @@ +import { Div } from "aflon"; +import { HttpCollectionItem } from "../../../lib/http"; + +// WItem is short for WorkspaceITEM + +export abstract class WItem extends Div { + public eventSendRequested = "sendRequested"; + public eventCloseRequested = "closeRequested"; + public eventCollectionItemChanged = "collectionItemChanged"; + + abstract getMacroNames(): Array; + abstract setExpanded(expanded: boolean): this; + abstract getExpanded(): boolean; + abstract pin(): this; + abstract isPinned(): boolean; + abstract focusName(): this; + abstract setItem(item: HttpCollectionItem): this + abstract getItem(): HttpCollectionItem; +} diff --git a/src/renderer/ui/WorkspaceControl/WItem/index.ts b/src/renderer/ui/WorkspaceControl/WItem/index.ts new file mode 100644 index 0000000..aaf70ab --- /dev/null +++ b/src/renderer/ui/WorkspaceControl/WItem/index.ts @@ -0,0 +1,3 @@ +export * from "./HttpReqtWitem"; +export * from "./HttpAuthWitem"; +export * from "./WItem"; diff --git a/src/renderer/ui/WorkspaceControl/WelcomeControl.ts b/src/renderer/ui/WorkspaceControl/WelcomeControl.ts new file mode 100644 index 0000000..9ebf555 --- /dev/null +++ b/src/renderer/ui/WorkspaceControl/WelcomeControl.ts @@ -0,0 +1,260 @@ +import { Div, Span } from "aflon"; + +import { openFileInDefaultApp } from "../../lib/Platform"; +import { Telemetry, TelemetryEvent } from "../../lib/Telemetry"; + +import { Icon } from "../Icon"; +import { Colors, FontStyles } from "../StyleConstants"; + +class WelcomeActions extends Div { + + private _icon: Icon; + private _title: Div; + + constructor(iconName: string) { + super(); + + this.append([ + (this._icon = new Icon(iconName)), + (this._title = new Div()) + ]); + } + + setIconRightOffset(offset: number): this { + this._icon.setInlineCss({ + paddingRight: `${offset}px` + }); + return this; + } + + setIconTopOffset(offset: number): this { + this._icon.setInlineCss({ + paddingTop: `${offset}px` + }); + return this; + } + + setText(text: string): this { + this._title.setText(text); + return this; + } + + getText(): string { + return this._title.getText(); + } +} + +WelcomeActions.style = { + _: { + display: "flex", + flexFlow: "row nowrap", + height: "30px", + alignItems: "center" + }, + _icon: { + width: "30px", + marginRight: "10px", + textAlign: "right", + fontSize: "16px" + }, + _title: { + fontSize: "12px", + fontWeight: "bold", + cursor: "pointer", + display: "inline-block", + "&:hover": { + textDecoration: "underline" + } + } +}; + +class FeedbackControl extends Div { + + private _hand: Div; + private _title: Div; + private _details: Div; + private _links: Div; + private _reportIssue: Span; + private _giveFeedback: Span; + + constructor() { + super(); + + this.append([ + (this._hand = new Div()) + .setText("โœ‹"), + new Div().append([ + (this._title = new Div()) + .setText("We kindly ask for your feedback."), + (this._details = new Div()) + .setText("Our ambition is to create simple, developer-friendly \ + HTTP client tailored for rapid and responsive testing during development. \ + Reporting issues and giving feedback helps us a lot."), + (this._links = new Div()) + .append([ + (this._reportIssue = new Span()) + .setText("Report an issue") + .on(this._reportIssue.eventClick, () => openFileInDefaultApp("https://forms.gle/nC7XvMZLXXPHyRik9")), + (this._giveFeedback = new Span()) + .setText("Give feedback") + .on(this._reportIssue.eventClick, () => { + Telemetry.reportEvent(TelemetryEvent.FeedbackSent); + openFileInDefaultApp("https://forms.gle/CMN15fRycYADAAbT6"); + }) + ]) + ]) + ]); + } +} + +FeedbackControl.style = { + _: { + fontSize: "12px", + display: "flex", + flexFlow: "row nowrap" + }, + _hand: { + fontSize: "30px", + marginRight: "10px" + }, + _title: { + fontSize: "12px", + fontWeight: "bold", + marginBottom: "7px", + marginTop: "10px" + }, + _details: { + fontSize: "11px", + lineHeight: "15px" + }, + _links: { + marginTop: "10px", + fontSize: "11px", + lineHeight: "15px", + textDecoration: "underline", + cursor: "pointer" + }, + _reportIssue: { + marginRight: "10px", + cursor: "pointer" + } +}; + +export class WelcomeControl extends Div { + public eventActionRequested: "actionRequested"; + + private _logo: Div; + private _options: Div; + private _separator: Div; + private _giveFeedback: FeedbackControl; + + constructor() { + super(); + + this.append([ + (this._logo = new Div()), + (this._options = new Div()) + .append([ + new WelcomeActions("new-file") + .setText("Create new collection") + .on("click", () => this._onCreateNewCollection()), + new WelcomeActions("import") + .setText("Import existing collection") + .on("click", () => this._onImportCollection()), + new WelcomeActions("import-3rd") + .setText("Import 3rd-party collection") + .on("click", () => this._onImportCollection()), + (this._separator = new Div()), + new WelcomeActions("send") + .setText("Send http request") + .setIconTopOffset(2) + .on("click", () => this._onSendHttpRequestRequested()), + new WelcomeActions("docs") + .setText("Read docs") + .setIconRightOffset(1.5) + .on("click", () => this._onReadDocs()) + ]), + (this._giveFeedback = new FeedbackControl()) + ]); + + this._onViewportChange(); + visualViewport.addEventListener("resize", this._onViewportChange); + } + + protected _onLeavingDom(): void { + visualViewport.removeEventListener("resize", this._onViewportChange); + } + + private _onViewportChange = (): void => { + if (visualViewport.height <= 800) { + this._logo.setVisibility(false); + return; + } + + this._logo.setVisibility(true); + }; + + private _onSendHttpRequestRequested(): void { + this.raise(this.eventActionRequested, { action: "send-request" }); + } + + private _onCreateNewCollection(): void { + this.raise(this.eventActionRequested, { action: "create" }); + } + + private _onImportCollection(): void { + this.raise(this.eventActionRequested, { action: "import" }); + } + + private _onReadDocs(): void { + this.raise(this.eventActionRequested, { action: "docs" }); + } +} + +WelcomeControl.style = { + _: { + ...FontStyles.sansSerifNormal, + display: "flex", + flexFlow: "column nowrap", + alignItems: "center", + justifyContent: "space-evenly", + color: Colors.workspacePlaceholder, + paddingBottom: "30px" + }, + _logo: { + width: "250px", + height: "170px", + backgroundImage: "url(./resources/images/WelcomeIcon.svg)", + backgroundPosition: "center", + backgroundSize: "contain", + backgroundRepeat: "no-repeat", + opacity: "0.5" + }, + _options: { + width: "220px", + color: Colors.workspaceDefault, + transition: "opacity 0.5s", + opacity: "0.5", + "&:hover": { + opacity: "1.0", + transition: "opacity 0.5s" + } + }, + _separator: { + height: "1px", + background: Colors.workspacePlaceholder, + marginTop: "10px", + marginBottom: "10px" + }, + _giveFeedback: { + width: "520px", + maxWidth: "80%", + color: Colors.workspaceDefault, + opacity: "0.6", + transition: "opacity 0.5s", + "&:hover": { + opacity: "1.0", + transition: "opacity 0.5s" + } + } +}; diff --git a/src/renderer/ui/WorkspaceControl/WorkspaceControl.ts b/src/renderer/ui/WorkspaceControl/WorkspaceControl.ts new file mode 100644 index 0000000..c7928da --- /dev/null +++ b/src/renderer/ui/WorkspaceControl/WorkspaceControl.ts @@ -0,0 +1,594 @@ +import { Div, getAflonTarget, Animation } from "aflon"; + +import { error } from "../../lib/Logger"; +import { HttpAuth, HttpReqt, HttpCollection, HttpCollectionItem, HttpDir } from "../../lib/http"; + +import { BoxShadowValues, Colors, ZIndexLayers } from "../StyleConstants"; +import { VResizer } from "../VResizer"; +import { AppStatePersister } from "../AppStatePersister"; +import { RequestExecutionHistoryControl, HttpResponseControl } from "../ExecutionHistoryControl"; +import { PreferenceStore, ResponseControlLocation } from "../PreferenceStore"; + +import { HttpReqtWitem, WItem, HttpAuthWitem } from "./WItem"; +import { MacrosControl, MacrosFromCollection } from "./MacrosControl"; +import { WelcomeControl } from "./WelcomeControl"; +import { Console } from "./Console"; + +export class WorkspaceControl extends Div { + public eventActionRequested = "actionRequested"; + + private _innerWorkspace: Div; + private _welcomeControl: WelcomeControl; + private _reqtItems: Div; + private _parametersResizer: VResizer; + private _parameters: MacrosControl; + private _historyHost: Div; + private _history: RequestExecutionHistoryControl; + private _responseControl: HttpResponseControl; + private _console: Console; + + private _responseWithinWorkspace: boolean = false; + private _parametersAnimation: Animation = null; + + constructor() { + super(); + + (this._history = new RequestExecutionHistoryControl()) + .on(this._history.eventExecutionSelected, this._onExecutionSelected); + (this._responseControl = new HttpResponseControl()) + .on(this._responseControl.eventHistoryRequested, () => this._onResponseControlHistoryRequested()) + .setCompactHeader(true); + + this.append([ + (this._innerWorkspace = new Div()) + .append([ + (this._welcomeControl = new WelcomeControl()) + .on(this._welcomeControl.eventActionRequested, e => this.raise(this.eventActionRequested, e["detail"])), + (this._reqtItems = new Div()), + (this._historyHost = new Div()) + .on(this._historyHost.eventMouseLeave, () => this._hideHistoryHost()) + .on(this._historyHost.eventClick, () => this._hideHistoryHost()) + ]), + (this._parametersResizer = new VResizer()) + .setResizingCallback(this._onParametersResize), + (this._parameters = new MacrosControl()) + .setInlineCss({ flex: `0 0 ${AppStatePersister.getParametersWidth()}px` }) + .on(this._parameters.eventMouseEnter, () => this._onParametersMouseEnter()) + .on(this._parameters.eventMouseLeave, () => this._onParametersMouseLeave()), + (this._console = new Console()) + .setContent(this._history, this._responseControl) + ]); + + this._updateResponseLocation(); + this._updateWelcomeControlVisibility(); + } + + setPreviewItem(item: HttpCollectionItem, makeNameEditable: boolean = false, collapseAllItems: boolean = true): this { + if (collapseAllItems) + this._collapseAllItems(); + + let previewControl = this._getPreviewWorkspaceItem(); + let pinnedControl = this._getPinnedWorkspaceItem(item); + + if (pinnedControl) { + if (previewControl) { + this._removeWorkspaceItem(previewControl); + } + pinnedControl.setExpanded(true); + pinnedControl.getHtmlElement().scrollIntoView(); + } else { + let workspaceItemIsOfRightType = + (item instanceof HttpReqt && previewControl instanceof HttpReqtWitem) || + (item instanceof HttpAuth && previewControl instanceof HttpAuthWitem); + + if (previewControl && workspaceItemIsOfRightType) { + previewControl.setItem(item); + previewControl.getHtmlElement().scrollIntoView(); + if (makeNameEditable) + previewControl.focusName(); + } else { + if (previewControl) + this._removeWorkspaceItem(previewControl); + + this._createAndAddWorkspaceItem(item, false, makeNameEditable); + } + } + + this._updateMacrosControl(); + this._updateWelcomeControlVisibility(); + + return this; + } + + addPinnedItem(item: HttpCollectionItem): this { + this._collapseAllItems(); + + let previewControl = this._getPreviewWorkspaceItem(); + + if (previewControl) { + if (previewControl.getItem() == item) { + previewControl.pin(); + return this; + } + + this._removeWorkspaceItem(previewControl); + } + + let pinnedControl = this._getPinnedWorkspaceItem(item); + + if (pinnedControl) { + pinnedControl.setExpanded(true); + pinnedControl.getHtmlElement().scrollIntoView(); + return this; + } + + this._createAndAddWorkspaceItem(item); + + this._updateMacrosControl(); + this._updateWelcomeControlVisibility(); + + return this; + } + + closeAllItemsFromCollection(collection: HttpCollection): this { + this._reqtItems.children() + .filter(child => + child instanceof WItem && + child.getItem().getContainingCollection() == collection) + .forEach(child => this._removeWorkspaceItem(child)); + + this._updateMacrosControl(); + this._updateWelcomeControlVisibility(); + + return this; + } + + closeItem(item: HttpCollectionItem): this { + this._reqtItems.children() + .filter(child => + child instanceof WItem && + child.getItem() == item) + .forEach(child => this._removeWorkspaceItem(child)); + + this._updateMacrosControl(); + this._updateWelcomeControlVisibility(); + + return this; + } + + closeAllDescendantsOf(dir: HttpDir): this { + this._reqtItems.children() + .filter(child => + child instanceof WItem && + dir.isAscendantOf(child.getItem())) + .forEach(child => this._removeWorkspaceItem(child)); + + this._updateMacrosControl(); + this._updateWelcomeControlVisibility(); + + return this; + } + + sendRequest(reqt: HttpReqt): void { + this.setPreviewItem(reqt); + this._history.push(reqt); + if (this._console.getVisibility()) + this._console.show(); + } + + protected _onEnteringDom(): void { + window.addEventListener("beforeunload", this._onBeforeUnload); + PreferenceStore.on(PreferenceStore.eventResponseLocationChanged, this._onResponseLocationChanged); + PreferenceStore.on(PreferenceStore.eventAutoHideParametersChanged, this._updateParametersAutoHide); + visualViewport.addEventListener("resize", this._onViewportResize); + } + + protected _onLeavingDom(): void { + PreferenceStore.off(PreferenceStore.eventResponseLocationChanged, this._onResponseLocationChanged); + PreferenceStore.off(PreferenceStore.eventAutoHideParametersChanged, this._updateParametersAutoHide); + visualViewport.removeEventListener("resize", this._onViewportResize); + } + + private _showHistoryHost(): void { + this.animations("hide").stop(); + this.animations("show").start(); + } + + private _hideHistoryHost(): void { + this.animations("show").stop(); + this.animations("hide").start(); + } + + private _expandParameters(): void { + if (this._parametersAnimation != null) + this._parametersAnimation.stop(); + + this._parametersAnimation = new Animation({ + animations: [{ track: "marginRight", to: 0, ease: "circOut", duration: 150 }] + }, this); + this._parametersAnimation.start(); + } + + private _collapseParameters(): void { + if (this._parametersAnimation != null) + this._parametersAnimation.stop(); + + let rightMargin = this._parameters.getHtmlElement().getBoundingClientRect().width - 50; + + this._parametersAnimation = new Animation({ + animations: [{ track: "marginRight", to: `-${rightMargin}px`, ease: "circOut", duration: 150 }] + }, this); + this._parametersAnimation.start(); + } + + private _onParametersMouseEnter(): void { + if (!PreferenceStore.getAutoHideParameters()) return; + this._expandParameters(); + } + + private _onParametersMouseLeave(): void { + if (!PreferenceStore.getAutoHideParameters()) return; + this._collapseParameters(); + } + + private _moveResponseControl(withinWorkspace: boolean): void { + if (this._responseWithinWorkspace == withinWorkspace) return; + this._responseWithinWorkspace = withinWorkspace; + + if (this._responseWithinWorkspace) { + this._console.clearContent(); + this._reqtItems.insertAfter(this._responseControl); + this._responseControl.setCompactHeader(false); + this._reqtItems.setInlineCss({ + flex: "0 0 50%" + }); + this._responseControl.setInlineCss({ + borderLeft: `1px solid ${Colors.workspaceLine}`, + flex: "0 0 50%" + }); + this._historyHost.append([ this._history ]); + this._history.setInlineCss({ borderRight: "none" }); + this._console.setVisibility(false); + this._updateParametersAutoHide(); + return; + } + + this._responseControl.setVisibility(true); + this._responseControl.setCompactHeader(true); + this._historyHost.removeChild(this._history); + this._history.setInlineCss({ borderRight: "initial" }); + this._reqtItems.setInlineCss({ + flex: "0 0 100%" + }); + this._responseControl.setInlineCss({ + borderLeft: "none", + flex: "auto" + }); + this._innerWorkspace.removeChild(this._responseControl); + this._console.setContent(this._history, this._responseControl); + this._console.setVisibility(true); + this._updateParametersAutoHide(); + } + + private _onParametersResize = (e: MouseEvent): void => { + let x = e.clientX; + let { left, width } = this._parameters.getHtmlElement().getBoundingClientRect(); + + width = left - x + width; + + AppStatePersister.setParametersWidth(width); + + this._parameters.setInlineCss({ + flex: `0 0 ${width}px` + }); + }; + + private _collapseAllItems(): void { + this._reqtItems.children() + .forEach(child => { + if (child instanceof WItem && child.isPinned()) + child.setExpanded(false); + }); + } + + private _updateWelcomeControlVisibility(): void { + if (this._reqtItems.children().length == 0) { + this._welcomeControl.setVisibility(true); + this._reqtItems.setVisibility(false); + this._responseControl.setVisibility(false); + } else { + this._welcomeControl.setVisibility(false); + this._reqtItems.setVisibility(true); + this._responseControl.setVisibility(true); + } + } + + private _updateResponseLocation(): void { + let location = PreferenceStore.getResponseLocation(); + + if (location == ResponseControlLocation.Workspace) { + this._moveResponseControl(true); + } else if (location == ResponseControlLocation.Console) { + this._moveResponseControl(false); + } else if (location == ResponseControlLocation.Automatic) { + if (visualViewport.width > 1700) + this._moveResponseControl(true); + else + this._moveResponseControl(false); + } else { + throw new Error(`ResponseControlLocation ${location} not supported`); + } + } + + private _updateMacrosControl(): void { + let macrosFromCollection: Array = []; + + this._reqtItems.children().forEach(child => { + if (!(child instanceof WItem)) return; + const collection = child.getItem().getContainingCollection(); + const macros = child.getMacroNames(); + + let filterResponse = macrosFromCollection.filter(mfc => mfc.collection == collection); + if (filterResponse.length == 0) { + macrosFromCollection.push({ + collection, macros + }); + } else if (filterResponse.length == 1) { + macros.forEach(macro => { + if (filterResponse[0].macros.indexOf(macro) == -1) + filterResponse[0].macros.push(macro); + }); + } else { + throw new Error("More then one MacrosFromCollection per collection."); + } + }); + + this._parameters.setMarcos(macrosFromCollection); + } + + private _getPreviewWorkspaceItem(): WItem { + if (this._reqtItems.getChildrenNumber() == 0) + return null; + + let firstChild = this._reqtItems.children()[0]; + + if (!(firstChild instanceof WItem)) + return null; + + if (firstChild.isPinned()) + return null; + + return firstChild; + } + + private _getPinnedWorkspaceItem(item: HttpCollectionItem): WItem { + let result = this._reqtItems.children().filter(child => + child instanceof WItem && + child.isPinned() && + child.getItem() == item); + + if (result.length == 0) return null; + else if (result.length == 1) return result[0]; + else { + error("It seems there are several HttpReqtControls with same HttpReqt."); + return null; + } + } + + private _onWorkspaceItemCloseRequested = (e: Event): void => { + let sender = getAflonTarget(e); + if (!(sender instanceof WItem)) return; + + this._removeWorkspaceItem(sender); + this._updateMacrosControl(); + this._updateWelcomeControlVisibility(); + }; + + private _onSendRequested = (e: Event): void => { + this.sendRequest(e["detail"]["reqt"]); + }; + + private _onWorkspaceItemChanged = (): void => this._updateMacrosControl(); + + private _removeWorkspaceItem(item: WItem): void { + item.off(item.eventCloseRequested, this._onWorkspaceItemCloseRequested) + .off(item.eventSendRequested, this._onSendRequested) + .off(item.eventCollectionItemChanged, this._onWorkspaceItemChanged) + .off(item.eventMouseEnter, this._onMouseEnter) + .off(item.eventMouseLeave, this._onMouseLeave); + this._reqtItems.removeChild(item); + } + + private _createAndAddWorkspaceItem(item: HttpCollectionItem, pin: boolean = true, editableName: boolean = false): void { + let control: WItem; + + if (item instanceof HttpReqt) + control = new HttpReqtWitem(); + else if (item instanceof HttpAuth) + control = new HttpAuthWitem(); + else + throw new Error(`Collection item type ${item.constructor.name} not supported in Workspace.`); + + control + .setItem(item) + .on(control.eventCloseRequested, this._onWorkspaceItemCloseRequested) + .on(control.eventSendRequested, this._onSendRequested) + .on(control.eventCollectionItemChanged, this._onWorkspaceItemChanged) + .on(control.eventMouseEnter, this._onMouseEnter) + .on(control.eventMouseLeave, this._onMouseLeave); + + if (pin) { + control + .pin() + .setExpanded(true); + } + + if (editableName) { + control.focusName(); + } + + this._reqtItems.prepend([ control ]); + control.getHtmlElement().scrollIntoView(); + } + + private _onMouseEnter = (e: Event): void => { + let sender = getAflonTarget(e); + if (!(sender instanceof WItem)) return; + this._parameters.setFocusedMacros(sender.getItem().getContainingCollection(), + sender.getMacroNames()); + }; + + private _onMouseLeave = (): void => { + this._parameters.setFocusedMacros(null, null); + }; + + private _onBeforeUnload = (): void => { + window.removeEventListener("beforeunload", this._onBeforeUnload); + this._updateOpenWItemsCache(); + }; + + private _updateOpenWItemsCache(): void { + let items = this._reqtItems.children() + .filter(child => child instanceof WItem) + .map(child => child) + .map(witem => ({ + path: witem.getItem().getFullPath(), + collectionUuid: witem.getItem().getContainingCollection().getUuid(), + pinned: witem.isPinned() + })); + + AppStatePersister.setOpenedWItems(items); + } + + private _onResponseControlHistoryRequested(): void { + if (!this._responseWithinWorkspace) return; + this._showHistoryHost(); + } + + private _onExecutionSelected = (e: Event): void => { + let details = e["detail"]; + + let reqt = details["reqt"]; + let execution = details["execution"]; + + if (reqt == null) + this._responseControl.clear(); + else + this._responseControl.setExecution(reqt, execution); + }; + + private _onResponseLocationChanged = (): void => { + this._updateResponseLocation(); + }; + + private _updateParametersAutoHide = (): void => { + setTimeout(() => { + if (PreferenceStore.getAutoHideParameters()) { + this._collapseParameters(); + return; + } + + this._expandParameters(); + }, 50); + }; + + private _onViewportResize = (): void => { + this._updateResponseLocation(); + }; +} + +WorkspaceControl.style = { + _: { + display: "flex", + flexFlow: "row nowrap", + alignItems: "stretch", + minHeight: "0", + minWidth: "0" + }, + _innerWorkspace: { + position: "relative", + display: "flex", + flexFlow: "row nowrap", + overflowX: "hidden", + overflowY: "hidden", + flex: "1 1 100px", + minHeight: "0" + }, + _welcomeControl: { + width: "100%", + height: "100%" + }, + _reqtItems: { + display: "flex", + flexFlow: "column nowrap", + flex: "1 1 100%", + overflowY: "auto", + overflowX: "hidden", + "&::-webkit-scrollbar": { + borderLeft: `solid 1px ${Colors.workspaceLine}`, + width: "10px" + }, + "&::-webkit-scrollbar-thumb": { + background: Colors.scrollThumb, + opacity: 1.0, + border: `solid 1px ${Colors.workspaceLine}`, + borderRight: "none" + } + }, + _historyHost: { + display: "none", + position: "absolute", + left: "50%", + marginLeft: "2px", + marginTop: "2px", + width: "300px", + height: "70vh", + background: Colors.consoleBackground, + borderRadius: "5px", + boxShadow: BoxShadowValues.consoleCollapsed, + border: `solid 1px ${Colors.consoleBorder}`, + overflow: "hidden", + overflowY: "auto" + }, + _parametersResizer: { + zIndex: ZIndexLayers.base + 1 + }, + _parameters: { + borderLeft: `1px solid ${Colors.workspaceLine}`, + flex: "0 0 350px", + maxWidth: "500px", + minWidth: "350px", + minHeight: "0" + } +}; + +WorkspaceControl.animations = { + show: { + animations: [ + { track: "display", to: "block", target: "_historyHost"}, + { track: "opacity", to: 1, duration: 75, ease: "linear", target: "_historyHost"}, + { track: "boxShadow", target: "_historyHost", duration: 150, to: BoxShadowValues.consoleExtended } + ], + ease: "linear", + duration: 75 + }, + hide: { + animations: [ + { track: "opacity", duration: 40, delay: 40, to: 0, ease: "linear", target: "_historyHost"}, + { track: "boxShadow", target: "_historyHost", duration: 80, to: BoxShadowValues.consoleCollapsed }, + { track: "display", to: "none", target: "_historyHost", delay: 80} + ], + ease: "linear", + duration: 75 + }, + collapseParameters: { + animations: [ + { track: "marginRight", to: "-300px", ease: "circOut", duration: 150 } + ] + }, + expandParameters: { + animations: [ + { track: "marginRight", to: "0px", ease: "circOut", duration: 150 } + ] + } +}; diff --git a/src/renderer/ui/WorkspaceControl/index.ts b/src/renderer/ui/WorkspaceControl/index.ts new file mode 100644 index 0000000..9fc43e4 --- /dev/null +++ b/src/renderer/ui/WorkspaceControl/index.ts @@ -0,0 +1,9 @@ +/** + * Workspace control represents everything on the UI right from the + * RequestBrowser. It contains, among other things: container where workspace + * items are shown (workspace items can be either HttpReqt or HttpAuth instances), + * MacrosControl (which is used for handling parameters), Console (with execution + * details) and WelcomeControl. + */ + +export { WorkspaceControl } from "./WorkspaceControl"; diff --git a/test/manual/PayloadTextFile.txt b/test/manual/PayloadTextFile.txt new file mode 100644 index 0000000..f4b27d2 --- /dev/null +++ b/test/manual/PayloadTextFile.txt @@ -0,0 +1,5 @@ +--- START --- +Hello world! +Line 2. +Line 3. +--- END --- \ No newline at end of file diff --git a/test/manual/TestingPlan.md b/test/manual/TestingPlan.md new file mode 100644 index 0000000..ea9a0be --- /dev/null +++ b/test/manual/TestingPlan.md @@ -0,0 +1,41 @@ +# Testing plan + +1. Collection manipulation + 1. Create + 2. Import + 3. Convert Postman collections + 1. Convert default Postman collection + 2. Convert real-world postman collections (Box, Github, Dropbox, Twitter) + 4. Change collection externally + 5. Reload collections after changed + 6. Collections are automatically saved +2. Directories, Requests and Authentications in Request Browser + 1. Create, Delete, Rename, Cut, Copy, Paste, Duplicate, Drag (between collections as well) + 2. Convert curl commands +3. Request configuration and persistance in the workspace + 1. Preview, pin and unpin + 2. Reconfigure and persist + Name, method, url, headers, body (text, file, form) + 3. Change auth and persist + Inherit, local, custom + 4. Hide request labels / prefer single-line URL + 5. Keyboard navigation +4. Authentication configuration and persistance in the workspace + 1. Preview, pin and unpin + 2. Reconfigure and persist +5. Parameters + 1. Unlocked parameters are persisted + 2. Locked parameters are persisted + 3. Lock-unlock + 4. Preview + 5. Appear and disappear from parameters panel + 6. Multi-collection scenarios + 7. Parameter presets: Create, Delete, Rename, Duplicate entries +6. Request processing + 1. Authentication + 2. Macro substitution + 3. Authentication inheritance + 4. Response payload preview + 5. File path resolution +7. Response and history console + Minimize, resend, select old request, execution, search, preview, response location, parameters auto-hide, console diff --git a/test/manual/httpiness-test.json b/test/manual/httpiness-test.json new file mode 100644 index 0000000..501ba01 --- /dev/null +++ b/test/manual/httpiness-test.json @@ -0,0 +1,1777 @@ +{ + "collectionVersion": "httpiness/JSON/0.11", + "uuid": "5f2ff16c-853c-4d92-8bce-3580eb2d597b", + "authChildren": [], + "dirChildren": [ + { + "name": "Authentication", + "authChildren": [], + "reqtChildren": [ + { + "name": "None", + "rawRequest": { + "method": "GET", + "url": "https://www.httpbin.org/anything", + "headers": [], + "body": null + }, + "auth": { + "name": null, + "authDef": { + "type": "None" + } + } + }, + { + "name": "ApiKey - Header", + "rawRequest": { + "method": "GET", + "url": "https://www.httpbin.org/anything", + "headers": [], + "body": null + }, + "auth": { + "name": null, + "authDef": { + "type": "ApiKey", + "apiKey": "${API_KEY}" + }, + "location": { + "type": "Header", + "key": "HeaderName", + "prefix": "" + } + } + }, + { + "name": "ApiKey - Query", + "rawRequest": { + "method": "GET", + "url": "https://www.httpbin.org/anything", + "headers": [], + "body": null + }, + "auth": { + "name": null, + "authDef": { + "type": "ApiKey", + "apiKey": "${API_KEY}" + }, + "location": { + "type": "UrlQuery", + "key": "queryName", + "prefix": "" + } + } + }, + { + "name": "Bearer", + "rawRequest": { + "method": "GET", + "url": "https://www.httpbin.org/bearer", + "headers": [], + "body": null + }, + "auth": { + "name": null, + "authDef": { + "type": "Bearer", + "bearerToken": "${BEARER_TOKEN}" + } + } + }, + { + "name": "Basic", + "rawRequest": { + "method": "GET", + "url": "https://www.httpbin.org/basic-auth/${BASIC_USERNAME}/${BASIC_PASSWORD}", + "headers": [], + "body": null + }, + "auth": { + "name": null, + "authDef": { + "type": "Basic", + "username": "${BASIC_USERNAME}", + "password": "${BASIC_PASSWORD}" + }, + "location": null + } + } + ], + "dirChildren": [ + { + "name": "OAuth2", + "authChildren": [], + "reqtChildren": [], + "dirChildren": [ + { + "name": "Box", + "authChildren": [ + { + "name": "AuthorizationCode", + "authDef": { + "type": "OAuth2", + "oauth2Type": "AuthorizationCode", + "codeChallengeMethod": "None", + "callbackURL": "https://localhost:8000", + "authURL": "https://account.box.com/api/oauth2/authorize", + "accessTokenURL": "https://api.box.com/oauth2/token", + "clientID": "${BOX_CLIENT_ID}", + "clientSecret": "${BOX_CLIENT_SECRET}", + "clientAuthentication": "InBody", + "scope": "root_readwrite", + "state": "" + }, + "location": null + } + ], + "reqtChildren": [ + { + "name": "Folder", + "rawRequest": { + "method": "GET", + "url": "https://api.box.com/2.0/folders/${BOX_FOLDER_ID}", + "headers": [], + "body": null + }, + "auth": "/Authentication/OAuth2/Box/AuthorizationCode" + }, + { + "name": "Revoke", + "rawRequest": { + "method": "POST", + "url": "https://api.box.com/oauth2/revoke", + "headers": [], + "body": { + "type": "Form", + "encoding": "application/x-www-form-urlencoded", + "records": [ + { + "name": "client_id", + "value": "${BOX_CLIENT_ID}", + "type": "Text" + }, + { + "name": "client_secret", + "value": "${BOX_CLIENT_SECRET}", + "type": "Text" + }, + { + "name": "token", + "value": "${BOX_TOKEN}", + "type": "Text" + } + ] + } + }, + "defaultBody": { + "type": "Form", + "encoding": "application/x-www-form-urlencoded", + "records": [ + { + "name": "client_id", + "value": "${BOX_CLIENT_ID}", + "type": "Text" + }, + { + "name": "client_secret", + "value": "${BOX_CLIENT_SECRET}", + "type": "Text" + }, + { + "name": "token", + "value": "${BOX_TOKEN}", + "type": "Text" + } + ] + } + }, + { + "name": "AuthLoopback", + "rawRequest": { + "method": "GET", + "url": "https://www.httpbin.org/anything?fields=&usemarker=&marker=&offset=&limit=&sort=&direction=", + "headers": [], + "body": null + }, + "auth": "/Authentication/OAuth2/Box/AuthorizationCode" + }, + { + "name": "List items in folder", + "rawRequest": { + "method": "GET", + "url": "https://api.box.com/2.0/folders/${BOX_FOLDER_ID}/items?fields=&usemarker=&marker=&offset=&limit=&sort=&direction=", + "headers": [], + "body": null + }, + "auth": "/Authentication/OAuth2/Box/AuthorizationCode" + }, + { + "name": "Upload file", + "rawRequest": { + "method": "POST", + "url": "https://upload.box.com/api/2.0/files/content?fields=", + "headers": [ + { + "name": "Content-Type", + "value": "multipart/form-data" + } + ], + "body": { + "type": "Form", + "encoding": "multipart/form-data", + "records": [ + { + "name": "attributes", + "value": "{\n \"name\": \"${BOX_FILE_NAME}\",\n \"parent\": {\n \"id\": \"${BOX_FOLDER_ID}\"\n }\n}", + "type": "Text" + }, + { + "name": "file", + "value": "C:\\Users\\bogdan.nikolic\\OneDrive - ENDAVA\\Desktop\\sample2.json", + "type": "File" + } + ] + } + }, + "defaultBody": { + "type": "Form", + "encoding": "multipart/form-data", + "records": [ + { + "name": "attributes", + "value": "{\n \"name\": \"${BOX_FILE_NAME}\",\n \"parent\": {\n \"id\": \"${BOX_FOLDER_ID}\"\n }\n}", + "type": "Text" + }, + { + "name": "file", + "value": "C:\\Users\\Bogdan Nikolic\\Desktop\\dryrun to do.txt", + "type": "File" + } + ] + }, + "auth": "/Authentication/OAuth2/Box/AuthorizationCode" + }, + { + "name": "Get file information", + "rawRequest": { + "method": "GET", + "url": "https://api.box.com/2.0/files/${BOX_FILE_ID}?fields=", + "headers": [ + { + "name": "x-rep-hints", + "value": "[pdf]" + } + ], + "body": null + }, + "auth": "/Authentication/OAuth2/Box/AuthorizationCode" + }, + { + "name": "Get file thumbnail", + "rawRequest": { + "method": "GET", + "url": "https://api.box.com/2.0/files/${BOX_FILE_ID}/thumbnail.:extension?min_height=&min_width=&max_height=&max_width=", + "headers": [], + "body": null + }, + "auth": "/Authentication/OAuth2/Box/AuthorizationCode" + }, + { + "name": "Download file", + "rawRequest": { + "method": "GET", + "url": "https://api.box.com/2.0/files/${BOX_FILE_ID}/content?version=&access_token=", + "headers": [], + "body": null + }, + "auth": "/Authentication/OAuth2/Box/AuthorizationCode" + } + ], + "dirChildren": [] + }, + { + "name": "Dropbox", + "authChildren": [ + { + "name": "AuthorizationCode", + "authDef": { + "type": "OAuth2", + "oauth2Type": "AuthorizationCode", + "codeChallengeMethod": "None", + "callbackURL": "https://localhost:8000", + "authURL": "https://www.dropbox.com/oauth2/authorize", + "accessTokenURL": "https://api.dropboxapi.com/oauth2/token", + "clientID": "${DROPBOX_APP_KEY}", + "clientSecret": "${DROPBOX_APP_SECRET}", + "clientAuthentication": "InBody", + "scope": "", + "state": "" + }, + "location": null + }, + { + "name": "Implicit", + "authDef": { + "type": "OAuth2", + "oauth2Type": "Implicit", + "callbackURL": "https://localhost:8000", + "authURL": "https://www.dropbox.com/oauth2/authorize", + "accessTokenURL": "https://api.dropboxapi.com/oauth2/token", + "clientID": "${DROPBOX_APP_KEY}", + "clientSecret": "${DROPBOX_APP_SECRET}", + "scope": "", + "state": "", + "clientAuthentication": "" + } + }, + { + "name": "PKCE", + "authDef": { + "type": "OAuth2", + "oauth2Type": "AuthorizationCode", + "codeChallengeMethod": "SHA256", + "callbackURL": "https://localhost:8000", + "authURL": "https://www.dropbox.com/oauth2/authorize", + "accessTokenURL": "https://api.dropboxapi.com/oauth2/token", + "clientID": "${DROPBOX_APP_KEY}", + "clientSecret": "${DROPBOX_APP_SECRET}", + "clientAuthentication": "InBody", + "scope": "", + "state": "" + }, + "location": null + } + ], + "reqtChildren": [ + { + "name": "GetCurrentAccount", + "rawRequest": { + "method": "POST", + "url": "https://api.dropboxapi.com/2/users/get_current_account", + "headers": [], + "body": null + }, + "auth": "/Authentication/OAuth2/Dropbox/PKCE" + }, + { + "name": "RevokeToken", + "rawRequest": { + "method": "POST", + "url": "https://api.dropboxapi.com/2/auth/token/revoke", + "headers": [], + "body": null + }, + "auth": "/Authentication/OAuth2/Dropbox/PKCE" + }, + { + "name": "AuthLoopback", + "rawRequest": { + "method": "GET", + "url": "https://www.httpbin.org/anything", + "headers": [], + "body": null + }, + "auth": "/Authentication/OAuth2/Dropbox/PKCE" + }, + { + "name": "ListFolder", + "rawRequest": { + "method": "POST", + "url": "https://api.dropboxapi.com/2/files/list_folder", + "headers": [ + { + "name": "Content-Type", + "value": "application/json" + } + ], + "body": { + "type": "Regular", + "value": "{\n \"path\": \"${DROPBOX_DIR_PATH}\"\n}", + "valueType": "Text" + } + }, + "defaultBody": { + "type": "Regular", + "value": "{\n \"path\": \"\", \n \"recursive\": false, \n \"include_media_info\": true, \n \"include_deleted\": false, \n \"include_has_explicit_shared_members\": false, \n \"include_mounted_folders\": true, \n \"include_non_downloadable_files\": true\n}", + "valueType": "Text" + }, + "auth": "/Authentication/OAuth2/Dropbox/PKCE" + }, + { + "name": "Upload", + "rawRequest": { + "method": "POST", + "url": "https://content.dropboxapi.com/2/files/upload", + "headers": [ + { + "name": "Dropbox-API-Arg", + "value": "{\"autorename\":false,\"mode\":\"add\",\"mute\":false,\"path\":\"${DROPBOX_FILE_PATH}\",\"strict_conflict\":false}" + }, + { + "name": "Content-Type", + "value": "application/octet-stream" + } + ], + "body": { + "type": "Regular", + "value": "C:\\Users\\bogdan.nikolic\\OneDrive - ENDAVA\\Desktop\\versionManagerdesign.png", + "valueType": "File" + } + }, + "defaultBody": { + "type": "Regular", + "value": "C:\\Users\\Bogdan Nikolic\\Desktop\\dryrun to do.txt", + "valueType": "File" + }, + "auth": "/Authentication/OAuth2/Dropbox/PKCE" + }, + { + "name": "GetPreview", + "rawRequest": { + "method": "POST", + "url": "https://content.dropboxapi.com/2/files/get_preview", + "headers": [ + { + "name": "Dropbox-API-Arg", + "value": "{\"path\":\"${DROPBOX_FILE_PATH}\"}" + } + ], + "body": null + }, + "auth": "/Authentication/OAuth2/Dropbox/PKCE" + }, + { + "name": "GetThumbnail", + "rawRequest": { + "method": "POST", + "url": "https://content.dropboxapi.com/2/files/get_thumbnail_v2", + "headers": [ + { + "name": "Dropbox-API-Arg", + "value": "{\"format\":\"jpeg\",\"mode\":\"strict\",\"resource\":{\".tag\":\"path\",\"path\":\"${DROPBOX_FILE_PATH}\"},\"size\":\"${DROPBOX_THUMBNAIL_SIZE}\"}" + } + ], + "body": null + }, + "auth": "/Authentication/OAuth2/Dropbox/PKCE" + }, + { + "name": "Download", + "rawRequest": { + "method": "POST", + "url": "https://content.dropboxapi.com/2/files/download", + "headers": [ + { + "name": "Dropbox-API-Arg", + "value": "{\"path\":\"${DROPBOX_FILE_PATH}\"}" + } + ], + "body": null + }, + "auth": "/Authentication/OAuth2/Dropbox/PKCE" + } + ], + "dirChildren": [] + }, + { + "name": "PetFinder", + "authChildren": [ + { + "name": "ClientCredentials", + "authDef": { + "type": "OAuth2", + "oauth2Type": "ClientCredentials", + "codeChallengeMethod": "None", + "callbackURL": "", + "authURL": "", + "accessTokenURL": "https://api.petfinder.com/v2/oauth2/token", + "clientID": "${PF_CLIENT_ID}", + "clientSecret": "${PF_CLIENT_SECRET}", + "clientAuthentication": "BasicAuthentication", + "scope": "", + "state": "" + }, + "location": null + } + ], + "reqtChildren": [ + { + "name": "Animals", + "rawRequest": { + "method": "GET", + "url": "https://api.petfinder.com/v2/animals", + "headers": [], + "body": null + }, + "auth": "/Authentication/OAuth2/PetFinder/ClientCredentials" + }, + { + "name": "AuthLoopback", + "rawRequest": { + "method": "GET", + "url": "https://www.httpbin.org/anything", + "headers": [], + "body": null + }, + "auth": "/Authentication/OAuth2/PetFinder/ClientCredentials" + } + ], + "dirChildren": [] + }, + { + "name": "Twitter", + "authChildren": [ + { + "name": "PKCE", + "authDef": { + "type": "OAuth2", + "oauth2Type": "AuthorizationCode", + "codeChallengeMethod": "SHA256", + "callbackURL": "https://localhost:8000", + "authURL": "https://twitter.com/i/oauth2/authorize", + "accessTokenURL": "https://api.twitter.com/2/oauth2/token", + "clientID": "${TWITTER_CLIENT_ID}", + "clientSecret": "${TWITTER_CLIENT_SECRET}", + "clientAuthentication": "BasicAuthentication", + "scope": "users.read tweet.read", + "state": "something" + }, + "location": null + } + ], + "reqtChildren": [ + { + "name": "My user", + "rawRequest": { + "method": "GET", + "url": "https://api.twitter.com/2/users/me", + "headers": [], + "body": null + }, + "auth": "/Authentication/OAuth2/Twitter/PKCE" + }, + { + "name": "Revoke", + "rawRequest": { + "method": "POST", + "url": "https://api.twitter.com/2/oauth2/revoke", + "headers": [ + { + "name": "Content-Type", + "value": "application/x-www-form-urlencoded" + } + ], + "body": { + "type": "Form", + "encoding": "application/x-www-form-urlencoded", + "records": [ + { + "type": "Text", + "name": "token", + "value": "${TWITTER_TOKEN}" + }, + { + "type": "Text", + "name": "token_type_hint", + "value": "access_token" + } + ] + } + }, + "defaultBody": { + "type": "Form", + "encoding": "application/x-www-form-urlencoded", + "records": [ + { + "name": "token", + "value": "${TWITTER_TOKEN}", + "type": "Text" + }, + { + "name": "token_type_hint", + "value": "access_token", + "type": "Text" + } + ] + }, + "auth": { + "name": null, + "authDef": { + "type": "Basic", + "username": "${TWITTER_CLIENT_ID}", + "password": "${TWITTER_CLIENT_SECRET}" + }, + "location": null + } + }, + { + "name": "Loopback token", + "rawRequest": { + "method": "GET", + "url": "https://httpbin.org/anything", + "headers": [], + "body": null + }, + "auth": "/Authentication/OAuth2/Twitter/PKCE" + } + ], + "dirChildren": [] + }, + { + "name": "Github", + "authChildren": [ + { + "name": "PKCE", + "authDef": { + "type": "OAuth2", + "oauth2Type": "AuthorizationCode", + "codeChallengeMethod": "SHA256", + "callbackURL": "https://localhost:8000", + "authURL": "https://github.com/login/oauth/authorize", + "accessTokenURL": "https://github.com/login/oauth/access_token", + "clientID": "${GITHUB_CLIENT_ID}", + "clientSecret": "${GITHUB_CLIENT_SECRET}", + "clientAuthentication": "BasicAuthentication", + "scope": "", + "state": "" + }, + "location": { + "type": "Header", + "key": "Authorization", + "prefix": "token" + } + } + ], + "reqtChildren": [ + { + "name": "Authenticated user", + "rawRequest": { + "method": "GET", + "url": "https://api.github.com/user", + "headers": [ + { + "name": "Accept", + "value": "application/vnd.github+json" + } + ], + "body": null + }, + "auth": "/Authentication/OAuth2/Github/PKCE" + }, + { + "name": "Delete app authentication", + "rawRequest": { + "method": "DELETE", + "url": "https://api.github.com/applications/${GITHUB_CLIENT_ID}/token", + "headers": [ + { + "name": "Accept", + "value": "application/vnd.github+json" + }, + { + "name": "Content-Type", + "value": "application/json" + } + ], + "body": { + "type": "Regular", + "value": "{\n \"access_token\": \"${GITHUB_ACCESS_TOKEN}\"\n}", + "valueType": "Text" + } + }, + "defaultBody": { + "type": "Regular", + "value": "{\n \"access_token\": \"${GITHUB_ACCESS_TOKEN}\"\n}", + "valueType": "Text" + }, + "auth": { + "name": null, + "authDef": { + "type": "Basic", + "username": "${GITHUB_CLIENT_ID}", + "password": "${GITHUB_CLIENT_SECRET}" + }, + "location": null + } + }, + { + "name": "Loopback token", + "rawRequest": { + "method": "GET", + "url": "https://httpbin.org/anything", + "headers": [], + "body": null + }, + "auth": "/Authentication/OAuth2/Github/PKCE" + } + ], + "dirChildren": [] + } + ] + } + ] + }, + { + "name": "MacroSubstitution", + "authChildren": [], + "reqtChildren": [ + { + "name": "URL & Headers", + "rawRequest": { + "method": "GET", + "url": "https://www.httpbin.org/anything?not_sent=${NOT_SENT}&${QKEY_1}=&${QKEY_2}=${QVAL_2}&11${QKEY_3a}22${QKEY_3b}33=11${QVAL_3a}22${QVAL_3b}33", + "headers": [ + { + "name": "Key1", + "value": "Value1" + }, + { + "name": "Key2", + "value": "${VALUE_2}" + }, + { + "name": "${KEY_3}", + "value": "Value3" + }, + { + "name": "${KEY_4}", + "value": "${VALUE_4}" + }, + { + "name": "11${KEY_5a}22${KEY_5b}33", + "value": "11${VALUE_5a}22${VALUE_5b}33" + } + ], + "body": null + }, + "auth": { + "name": null, + "authDef": { + "type": "None" + } + } + }, + { + "name": "Body Plain", + "rawRequest": { + "method": "POST", + "url": "https://www.httpbin.org/anything", + "headers": [ + { + "name": "Content-Type", + "value": "application/json" + } + ], + "body": { + "type": "Regular", + "value": "{\n \"Key1\" : \"Value1\",\n \"Key2\" : \"${VALUE_2}\",\n \"${KEY_3}\" : \"Value3\",\n \"${KEY_4}\" : \"${VALUE_4}\",\n \"11${KEY_5a}22${KEY_5b}33\": \"11${VALUE_5a}22${VALUE_5b}33\"\n}", + "valueType": "Text" + } + }, + "defaultBody": { + "type": "Regular", + "value": "{\n \"Key1\" : \"Value1\",\n \"Key2\" : \"${VALUE_2}\",\n \"${KEY_3}\" : \"Value3\",\n \"${KEY_4}\" : \"${VALUE_4}\",\n \"11${KEY_5a}22${KEY_5b}33\": \"11${VALUE_5a}22${VALUE_5b}33\"\n}", + "valueType": "Text" + }, + "auth": { + "name": null, + "authDef": { + "type": "None" + } + } + }, + { + "name": "Body Single File", + "rawRequest": { + "method": "POST", + "url": "https://www.httpbin.org/anything", + "headers": [ + { + "name": "Content-Type", + "value": "text/plain" + } + ], + "body": { + "type": "Regular", + "value": "PayloadTextFile.txt", + "valueType": "File" + } + }, + "defaultBody": { + "type": "Regular", + "value": "PayloadTextFile.txt", + "valueType": "File" + }, + "auth": { + "name": null, + "authDef": { + "type": "None" + } + } + }, + { + "name": "Body URL Encoded - empty values", + "rawRequest": { + "method": "POST", + "url": "https://www.httpbin.org/anything", + "headers": [], + "body": { + "type": "Form", + "encoding": "application/x-www-form-urlencoded", + "records": [ + { + "name": "not_sent", + "value": "${NOT_SENT}", + "type": "Text" + }, + { + "name": "${QKEY_1}", + "value": "", + "type": "Text" + }, + { + "name": "${QKEY_2}", + "value": "${QVAL_2}", + "type": "Text" + }, + { + "name": "11${QKEY_3a}22${QKEY_3b}33", + "value": "11${QVAL_3a}22${QVAL_3b}33", + "type": "Text" + } + ] + } + }, + "defaultBody": { + "type": "Form", + "encoding": "multipart/form-data", + "records": [ + { + "name": "not_sent", + "value": "${NOT_SENT}", + "type": "Text" + }, + { + "name": "${QKEY_1}", + "value": "", + "type": "Text" + }, + { + "name": "${QKEY_2}", + "value": "${QVAL_2}", + "type": "Text" + }, + { + "name": "11${QKEY_3a}22${QKEY_3b}33", + "value": "11${QVAL_3a}22${QVAL_3b}33", + "type": "Text" + } + ] + }, + "auth": { + "name": null, + "authDef": { + "type": "None" + } + } + }, + { + "name": "Body URL Encode - files and lines", + "rawRequest": { + "method": "POST", + "url": "https://www.httpbin.org/anything", + "headers": [], + "body": { + "type": "Form", + "encoding": "application/x-www-form-urlencoded", + "records": [ + { + "name": "Key1", + "value": "Value1", + "type": "Text" + }, + { + "name": "Key2", + "value": "${VALUE_2}", + "type": "Text" + }, + { + "name": "${KEY_3}", + "value": "Value3", + "type": "Text" + }, + { + "name": "${KEY_4}", + "value": "${VALUE_4}", + "type": "Text" + }, + { + "name": "11${KEY_5a}22${KEY_5b}33", + "value": "11${VALUE_5a}22${VALUE_5b}33", + "type": "Text" + }, + { + "name": "Multiline", + "value": "Line 1\nLine 2\nLine 3", + "type": "Text" + }, + { + "name": "Multiline with macros", + "value": "Line 1\n${LINE_2}\nLine 3", + "type": "Text" + }, + { + "name": "File", + "value": "PayloadTextFile.txt", + "type": "File" + } + ] + } + }, + "defaultBody": { + "type": "Form", + "encoding": "multipart/form-data", + "records": [ + { + "name": "Key1", + "value": "Value1", + "type": "Text" + }, + { + "name": "Key2", + "value": "${VALUE_2}", + "type": "Text" + }, + { + "name": "${KEY_3}", + "value": "Value3", + "type": "Text" + }, + { + "name": "${KEY_4}", + "value": "${VALUE_4}", + "type": "Text" + }, + { + "name": "11${KEY_5a}22${KEY_5b}33", + "value": "11${VALUE_5a}22${VALUE_5b}33", + "type": "Text" + }, + { + "name": "Multiline", + "value": "Line 1\nLine 2\nLine 3", + "type": "Text" + }, + { + "name": "Multiline with macros", + "value": "Line 1\n${LINE_2}\nLine 3", + "type": "Text" + }, + { + "name": "File", + "value": "PayloadTextFile.txt", + "type": "File" + } + ] + }, + "auth": { + "name": null, + "authDef": { + "type": "None" + } + } + }, + { + "name": "Body Form - empty values", + "rawRequest": { + "method": "POST", + "url": "https://www.httpbin.org/anything", + "headers": [], + "body": { + "type": "Form", + "encoding": "multipart/form-data", + "records": [ + { + "name": "not_sent", + "value": "${NOT_SENT}", + "type": "Text" + }, + { + "name": "${QKEY_1}", + "value": "", + "type": "Text" + }, + { + "name": "${QKEY_2}", + "value": "${QVAL_2}", + "type": "Text" + }, + { + "name": "11${QKEY_3a}22${QKEY_3b}33", + "value": "11${QVAL_3a}22${QVAL_3b}33", + "type": "Text" + } + ] + } + }, + "defaultBody": { + "type": "Form", + "encoding": "multipart/form-data", + "records": [ + { + "name": "not_sent", + "value": "${NOT_SENT}", + "type": "Text" + }, + { + "name": "${QKEY_1}", + "value": "", + "type": "Text" + }, + { + "name": "${QKEY_2}", + "value": "${QVAL_2}", + "type": "Text" + }, + { + "name": "11${QKEY_3a}22${QKEY_3b}33", + "value": "11${QVAL_3a}22${QVAL_3b}33", + "type": "Text" + } + ] + }, + "auth": { + "name": null, + "authDef": { + "type": "None" + } + } + }, + { + "name": "Body Form - files and lines", + "rawRequest": { + "method": "POST", + "url": "https://www.httpbin.org/anything", + "headers": [], + "body": { + "type": "Form", + "encoding": "multipart/form-data", + "records": [ + { + "name": "Key1", + "value": "Value1", + "type": "Text" + }, + { + "name": "Key2", + "value": "${VALUE_2}", + "type": "Text" + }, + { + "name": "${KEY_3}", + "value": "Value3", + "type": "Text" + }, + { + "name": "${KEY_4}", + "value": "${VALUE_4}", + "type": "Text" + }, + { + "name": "11${KEY_5a}22${KEY_5b}33", + "value": "11${VALUE_5a}22${VALUE_5b}33", + "type": "Text" + }, + { + "name": "Multiline", + "value": "Line 1\nLine 2\nLine 3", + "type": "Text" + }, + { + "name": "Multiline with macros", + "value": "Line 1\n${LINE_2}\nLine 3", + "type": "Text" + }, + { + "name": "File", + "value": "PayloadTextFile.txt", + "type": "File" + } + ] + } + }, + "defaultBody": { + "type": "Form", + "encoding": "multipart/form-data", + "records": [ + { + "name": "Key1", + "value": "Value1", + "type": "Text" + }, + { + "name": "Key2", + "value": "${VALUE_2}", + "type": "Text" + }, + { + "name": "${KEY_3}", + "value": "Value3", + "type": "Text" + }, + { + "name": "${KEY_4}", + "value": "${VALUE_4}", + "type": "Text" + }, + { + "name": "11${KEY_5a}22${KEY_5b}33", + "value": "11${VALUE_5a}22${VALUE_5b}33", + "type": "Text" + }, + { + "name": "Multiline", + "value": "Line 1\nLine 2\nLine 3", + "type": "Text" + }, + { + "name": "Multiline with macros", + "value": "Line 1\n${LINE_2}\nLine 3", + "type": "Text" + }, + { + "name": "File", + "value": "PayloadTextFile.txt", + "type": "File" + } + ] + }, + "auth": { + "name": null, + "authDef": { + "type": "None" + } + } + }, + { + "name": "Presets", + "rawRequest": { + "method": "GET", + "url": "https://www.httpbin.org/anything?key1=${PRESET_VALUE1}&key2=${PRESET_VALUE2}&key3=${PRESET_VALUE3}", + "headers": [], + "body": null + } + } + ], + "dirChildren": [] + }, + { + "name": "ResponsePayloadPreview", + "authChildren": [], + "reqtChildren": [ + { + "name": "PayloadHTML", + "rawRequest": { + "method": "GET", + "url": "https://www.httpbin.org/html", + "headers": [], + "body": null + }, + "auth": { + "name": null, + "authDef": { + "type": "None" + } + } + }, + { + "name": "PayloadJSON", + "rawRequest": { + "method": "GET", + "url": "https://www.httpbin.org/json", + "headers": [], + "body": null + }, + "auth": { + "name": null, + "authDef": { + "type": "None" + } + } + }, + { + "name": "PayloadXML", + "rawRequest": { + "method": "GET", + "url": "https://www.httpbin.org/xml", + "headers": [], + "body": null + }, + "auth": { + "name": null, + "authDef": { + "type": "None" + } + } + }, + { + "name": "PayloadUTF8", + "rawRequest": { + "method": "GET", + "url": "https://www.httpbin.org/encoding/utf8", + "headers": [], + "body": null + }, + "auth": { + "name": null, + "authDef": { + "type": "None" + } + } + }, + { + "name": "PayloadGZIP", + "rawRequest": { + "method": "GET", + "url": "https://www.httpbin.org/gzip", + "headers": [], + "body": null + }, + "auth": { + "name": null, + "authDef": { + "type": "None" + } + } + }, + { + "name": "PayloadBrotli", + "rawRequest": { + "method": "GET", + "url": "https://www.httpbin.org/brotli", + "headers": [], + "body": null + }, + "auth": { + "name": null, + "authDef": { + "type": "None" + } + } + }, + { + "name": "PayloadDeflate", + "rawRequest": { + "method": "GET", + "url": "https://www.httpbin.org/deflate", + "headers": [], + "body": null + }, + "auth": { + "name": null, + "authDef": { + "type": "None" + } + } + }, + { + "name": "PayloadPDF", + "rawRequest": { + "method": "GET", + "url": "http://www.pdf995.com/samples/pdf.pdf", + "headers": [], + "body": null + } + }, + { + "name": "PayloadPNG", + "rawRequest": { + "method": "GET", + "url": "https://www.httpbin.org/image/png", + "headers": [], + "body": null + } + }, + { + "name": "PayloadJPEG", + "rawRequest": { + "method": "GET", + "url": "https://www.httpbin.org/image/jpeg", + "headers": [], + "body": null + } + }, + { + "name": "PayloadSVG", + "rawRequest": { + "method": "GET", + "url": "https://www.httpbin.org/image/svg", + "headers": [], + "body": null + } + }, + { + "name": "PayloadWEBP", + "rawRequest": { + "method": "GET", + "url": "https://www.httpbin.org/image/webp", + "headers": [], + "body": null + } + } + ], + "dirChildren": [] + }, + { + "name": "FilePathResolution", + "authChildren": [], + "reqtChildren": [ + { + "name": "AbsolutePath - File", + "rawRequest": { + "method": "POST", + "url": "https://www.httpbin.org/anything", + "headers": [], + "body": { + "type": "Regular", + "value": "C:\\Users\\bogdan.nikolic\\Repositories\\httpiness\\test\\manual\\PayloadTextFile.txt", + "valueType": "File" + } + }, + "defaultBody": { + "type": "Regular", + "value": "C:\\Repositories\\httpiness\\test\\manual\\PayloadTextFile.txt", + "valueType": "File" + } + }, + { + "name": "AbsolutePath - MultipartForm", + "rawRequest": { + "method": "POST", + "url": "https://www.httpbin.org/anything", + "headers": [], + "body": { + "type": "Form", + "encoding": "multipart/form-data", + "records": [ + { + "name": "File", + "value": "C:\\Users\\bogdan.nikolic\\Repositories\\httpiness\\test\\manual\\PayloadTextFile.txt", + "type": "File" + }, + { + "name": "Text", + "value": "Hello hello hello", + "type": "Text" + } + ] + } + }, + "defaultBody": { + "type": "Form", + "encoding": "multipart/form-data", + "records": [ + { + "name": "File", + "value": "C:\\Repositories\\httpiness\\test\\manual\\PayloadTextFile.txt", + "type": "File" + }, + { + "name": "Text", + "value": "Hello hello hello", + "type": "Text" + } + ] + } + }, + { + "name": "AbsolutePath - UrlEncodedForm", + "rawRequest": { + "method": "POST", + "url": "https://www.httpbin.org/anything", + "headers": [], + "body": { + "type": "Form", + "encoding": "application/x-www-form-urlencoded", + "records": [ + { + "name": "File", + "value": "C:\\Users\\bogdan.nikolic\\Repositories\\httpiness\\test\\manual\\PayloadTextFile.txt", + "type": "File" + }, + { + "name": "Text", + "value": "Hello hello hello", + "type": "Text" + } + ] + } + }, + "defaultBody": { + "type": "Form", + "encoding": "application/x-www-form-urlencoded", + "records": [ + { + "name": "File", + "value": "C:\\Repositories\\httpiness\\test\\manual\\PayloadTextFile.txt", + "type": "File" + }, + { + "name": "Text", + "value": "Hello hello hello", + "type": "Text" + } + ] + } + }, + { + "name": "RelativePath - File", + "rawRequest": { + "method": "POST", + "url": "https://www.httpbin.org/anything", + "headers": [], + "body": { + "type": "Regular", + "value": "PayloadTextFile.txt", + "valueType": "File" + } + }, + "defaultBody": { + "type": "Regular", + "value": "PayloadTextFile.txt", + "valueType": "File" + } + }, + { + "name": "RelativePath - MultipartForm", + "rawRequest": { + "method": "POST", + "url": "https://www.httpbin.org/anything", + "headers": [], + "body": { + "type": "Form", + "encoding": "multipart/form-data", + "records": [ + { + "name": "File", + "value": "PayloadTextFile.txt", + "type": "File" + }, + { + "name": "Text", + "value": "Hello hello hello", + "type": "Text" + } + ] + } + }, + "defaultBody": { + "type": "Form", + "encoding": "multipart/form-data", + "records": [ + { + "name": "File", + "value": "PayloadTextFile.txt", + "type": "File" + }, + { + "name": "Text", + "value": "Hello hello hello", + "type": "Text" + } + ] + } + }, + { + "name": "RelativePath - UrlEncodedForm", + "rawRequest": { + "method": "POST", + "url": "https://www.httpbin.org/anything", + "headers": [], + "body": { + "type": "Form", + "encoding": "application/x-www-form-urlencoded", + "records": [ + { + "name": "File", + "value": "PayloadTextFile.txt", + "type": "File" + }, + { + "name": "Text", + "value": "Hello hello hello", + "type": "Text" + } + ] + } + }, + "defaultBody": { + "type": "Form", + "encoding": "application/x-www-form-urlencoded", + "records": [ + { + "name": "File", + "value": "PayloadTextFile.txt", + "type": "File" + }, + { + "name": "Text", + "value": "Hello hello hello", + "type": "Text" + } + ] + } + } + ], + "dirChildren": [] + }, + { + "name": "AuthenticationInheritance", + "authChildren": [ + { + "name": "Default Auth", + "authDef": { + "type": "Bearer", + "bearerToken": "LEVEL_0" + } + } + ], + "reqtChildren": [ + { + "name": "Request L0", + "rawRequest": { + "method": "GET", + "url": "https://www.httpbin.org/anything?level=0", + "headers": [], + "body": null + } + } + ], + "dirChildren": [ + { + "name": "LEVEL_1", + "authChildren": [ + { + "name": "Default Auth", + "authDef": { + "type": "Bearer", + "bearerToken": "LEVEL_1" + } + } + ], + "reqtChildren": [ + { + "name": "Request L1", + "rawRequest": { + "method": "GET", + "url": "https://www.httpbin.org/anything?level=1", + "headers": [], + "body": null + } + } + ], + "dirChildren": [ + { + "name": "LEVEL_2", + "authChildren": [ + { + "name": "Default Auth", + "authDef": { + "type": "Bearer", + "bearerToken": "LEVEL_2" + } + } + ], + "reqtChildren": [ + { + "name": "Request L2", + "rawRequest": { + "method": "GET", + "url": "https://www.httpbin.org/anything?level=2", + "headers": [], + "body": null + } + } + ], + "dirChildren": [ + { + "name": "LEVEL_3", + "authChildren": [ + { + "name": "Default Auth", + "authDef": { + "type": "Bearer", + "bearerToken": "LEVEL_3" + } + } + ], + "reqtChildren": [ + { + "name": "Request L3", + "rawRequest": { + "method": "GET", + "url": "https://www.httpbin.org/anything?level=3", + "headers": [], + "body": null + } + }, + { + "name": "Request Local", + "rawRequest": { + "method": "GET", + "url": "https://www.httpbin.org/anything?local", + "headers": [], + "body": null + }, + "auth": { + "name": null, + "authDef": { + "type": "Bearer", + "bearerToken": "LOCAL" + }, + "location": null + } + } + ], + "dirChildren": [] + } + ] + }, + { + "name": "LEVEL_1a", + "authChildren": [ + { + "name": "SomeAuth", + "authDef": { + "type": "Bearer", + "bearerToken": "FORBIDDEN" + } + } + ], + "reqtChildren": [], + "dirChildren": [ + { + "name": "LEVEL_1a", + "authChildren": [], + "reqtChildren": [ + { + "name": "Request L1a", + "rawRequest": { + "method": "GET", + "url": "https://www.httpbin.org/anything?level=1a", + "headers": [], + "body": null + } + } + ], + "dirChildren": [] + } + ] + } + ] + } + ] + } + ], + "reqtChildren": [], + "parameters": { + "API_KEY": "****API_KEY****", + "BEARER_TOKEN": "****BEARER_TOKEN****", + "BASIC_USERNAME": "****BASIC_USERNAME****", + "BASIC_PASSWORD": "****BASIC_PASSWORD****", + "BOX_CLIENT_ID": "6f2p2h9uy83famjh558e3dqlqxtoasju", + "BOX_CLIENT_SECRET": "******sensitive******", + "BOX_FOLDER_ID": "0", + "BOX_TOKEN": "******sensitive******", + "BOX_FILE_NAME": "Hello2.txt", + "BOX_FILE_ID": "1108527214135", + "DROPBOX_APP_KEY": "******sensitive******", + "DROPBOX_APP_SECRET": "******sensitive******", + "DROPBOX_DIR_PATH": "", + "DROPBOX_FILE_PATH": "/ble.png", + "DROPBOX_THUMBNAIL_SIZE": "w256h256", + "PF_CLIENT_ID": "******sensitive******", + "PF_CLIENT_SECRET": "******sensitive******", + "TWITTER_CLIENT_ID": "******sensitive******", + "TWITTER_CLIENT_SECRET": "******sensitive******", + "TWITTER_TOKEN": "******sensitive******", + "GITHUB_CLIENT_ID": "******sensitive******", + "GITHUB_CLIENT_SECRET": "******sensitive******", + "GITHUB_ACCESS_TOKEN": "******sensitive******", + "NOT_SENT": "", + "QKEY_1": "******sensitive******", + "QKEY_2": "qkey2", + "QVAL_2": "******sensitive******", + "QKEY_3a": "qkey3a", + "QKEY_3b": "******sensitive******", + "QVAL_3a": "qval3a", + "QVAL_3b": "******sensitive******", + "VALUE_2": "Value2", + "KEY_3": "******sensitive******", + "KEY_4": "Key4", + "VALUE_4": "******sensitive******", + "KEY_5a": "Key5a", + "KEY_5b": "******sensitive******", + "VALUE_5a": "Value5a", + "VALUE_5b": "******sensitive******", + "LINE_2": "Line2", + "PRESET_VALUE1": "ORIGINAL_VALUE", + "PRESET_VALUE2": "******sensitive******", + "PRESET_VALUE3": "ORIGINAL_VALUE" + }, + "parameterPresets": [ + { + "name": "Development", + "macros": [ + { + "name": "PRESET_VALUE1", + "value": "VALUE1_DEV" + }, + { + "name": "PRESET_VALUE2", + "value": "VALUE2_DEV" + }, + { + "name": "PRESET_VALUE3", + "value": "" + } + ] + }, + { + "name": "Production", + "macros": [ + { + "name": "PRESET_VALUE1", + "value": "VALUE1_PROD" + }, + { + "name": "PRESET_VALUE2", + "value": "VALUE2_PROD" + }, + { + "name": "PRESET_VALUE3", + "value": "" + } + ] + }, + { + "name": "Default", + "macros": [ + { + "name": "PRESET_VALUE1", + "value": "ORIGINAL_VALUE" + }, + { + "name": "PRESET_VALUE2", + "value": "ORIGINAL_VALUE" + }, + { + "name": "PRESET_VALUE3", + "value": "ORIGINAL_VALUE" + } + ] + } + ] +} \ No newline at end of file diff --git a/test/manual/postman-test-conversion-collection.postman_collection.json b/test/manual/postman-test-conversion-collection.postman_collection.json new file mode 100644 index 0000000..694f617 --- /dev/null +++ b/test/manual/postman-test-conversion-collection.postman_collection.json @@ -0,0 +1,1309 @@ +{ + "info": { + "_postman_id": "0ab98e51-3d62-4a6a-8f80-2ab16d86ad6f", + "name": "postman-test-conversion-collection", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", + "_exporter_id": "1632771" + }, + "item": [ + { + "name": "Body", + "item": [ + { + "name": "None", + "request": { + "method": "POST", + "header": [], + "url": { + "raw": "https://httpbin.org/anything", + "protocol": "https", + "host": [ + "httpbin", + "org" + ], + "path": [ + "anything" + ] + } + }, + "response": [] + }, + { + "name": "FormData", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "formdata", + "formdata": [ + { + "key": "key1", + "value": "value1", + "type": "text" + }, + { + "key": "key2", + "type": "file", + "src": [ + "/Users/bogdannikolic/Desktop/new_collection.json", + "/Users/bogdannikolic/Desktop/notarize.js" + ] + }, + { + "key": "key3", + "type": "file", + "src": "/Users/bogdannikolic/Desktop/New Collection.postman_collection6.json" + }, + { + "key": "key4", + "value": "{{param4}}", + "type": "text" + } + ] + }, + "url": { + "raw": "https://httpbin.org/anything", + "protocol": "https", + "host": [ + "httpbin", + "org" + ], + "path": [ + "anything" + ] + } + }, + "response": [] + }, + { + "name": "UrlEncoded", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "urlencoded", + "urlencoded": [ + { + "key": "key1", + "value": "value1", + "type": "text" + }, + { + "key": "key2", + "value": "{{param2}}", + "type": "text" + } + ] + }, + "url": { + "raw": "https://httpbin.org/anything", + "protocol": "https", + "host": [ + "httpbin", + "org" + ], + "path": [ + "anything" + ] + } + }, + "response": [] + }, + { + "name": "Raw-Text", + "request": { + "method": "PUT", + "header": [], + "body": { + "mode": "raw", + "raw": "Hello world\nLine1\nLine2\n\nLine3" + }, + "url": { + "raw": "https://httpbin.org/anything", + "protocol": "https", + "host": [ + "httpbin", + "org" + ], + "path": [ + "anything" + ] + } + }, + "response": [] + }, + { + "name": "Raw-JSON", + "request": { + "method": "PUT", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"bla\": \"blu\",\n \"gle\": {\n \"glo\": \"dfdf\"\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "https://httpbin.org/anything", + "protocol": "https", + "host": [ + "httpbin", + "org" + ], + "path": [ + "anything" + ] + } + }, + "response": [] + }, + { + "name": "Raw-XML", + "request": { + "method": "DELETE", + "header": [], + "body": { + "mode": "raw", + "raw": "\n param1\n param2\n param3\n", + "options": { + "raw": { + "language": "xml" + } + } + }, + "url": { + "raw": "https://httpbin.org/anything", + "protocol": "https", + "host": [ + "httpbin", + "org" + ], + "path": [ + "anything" + ] + } + }, + "response": [] + }, + { + "name": "Binary", + "request": { + "method": "DELETE", + "header": [ + { + "key": "", + "value": "", + "type": "text" + } + ], + "body": { + "mode": "file", + "file": { + "src": "/Users/bogdannikolic/Desktop/iMac - 13.png" + } + }, + "url": { + "raw": "https://httpbin.org/anything", + "protocol": "https", + "host": [ + "httpbin", + "org" + ], + "path": [ + "anything" + ] + } + }, + "response": [] + }, + { + "name": "GraphQL", + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "graphql", + "graphql": { + "query": "hello world query", + "variables": "hello world [variables]" + } + }, + "url": { + "raw": "https://httpbin.org/anything", + "protocol": "https", + "host": [ + "httpbin", + "org" + ], + "path": [ + "anything" + ] + } + }, + "response": [] + } + ] + }, + { + "name": "Auth", + "item": [ + { + "name": "Supported", + "item": [ + { + "name": "NoAuth", + "request": { + "auth": { + "type": "noauth" + }, + "method": "GET", + "header": [], + "url": { + "raw": "https://httpbin.org/anything", + "protocol": "https", + "host": [ + "httpbin", + "org" + ], + "path": [ + "anything" + ] + } + }, + "response": [] + }, + { + "name": "ApiKey-Header", + "request": { + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "in", + "value": "header", + "type": "string" + }, + { + "key": "key", + "value": "Authorization", + "type": "string" + }, + { + "key": "value", + "value": "{{AUTH_TOKEN}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "https://httpbin.org/anything", + "protocol": "https", + "host": [ + "httpbin", + "org" + ], + "path": [ + "anything" + ] + } + }, + "response": [] + }, + { + "name": "ApiKey-Query", + "request": { + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "in", + "value": "query", + "type": "string" + }, + { + "key": "key", + "value": "Authorization", + "type": "string" + }, + { + "key": "value", + "value": "{{AUTH_TOKEN}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "https://httpbin.org/anything", + "protocol": "https", + "host": [ + "httpbin", + "org" + ], + "path": [ + "anything" + ] + } + }, + "response": [] + }, + { + "name": "Bearer", + "request": { + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{BEARER_TOKEN}}", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "https://httpbin.org/anything", + "protocol": "https", + "host": [ + "httpbin", + "org" + ], + "path": [ + "anything" + ] + } + }, + "response": [] + }, + { + "name": "Basic", + "request": { + "auth": { + "type": "basic", + "basic": [ + { + "key": "password", + "value": "password", + "type": "string" + }, + { + "key": "username", + "value": "username", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "https://httpbin.org/anything", + "protocol": "https", + "host": [ + "httpbin", + "org" + ], + "path": [ + "anything" + ] + } + }, + "response": [] + }, + { + "name": "OAuth2-AuthCode-BasicClientAuth", + "request": { + "auth": { + "type": "oauth2", + "oauth2": [ + { + "key": "state", + "value": "some very cool state", + "type": "string" + }, + { + "key": "scope", + "value": "scope1, scope2 scope3 {{SCOPE_4}} scope_5", + "type": "string" + }, + { + "key": "clientSecret", + "value": "{{CLIENT_SECRET}}", + "type": "string" + }, + { + "key": "clientId", + "value": "client-id", + "type": "string" + }, + { + "key": "accessTokenUrl", + "value": "{{TOKEN_URL}}", + "type": "string" + }, + { + "key": "authUrl", + "value": "AUTH_URL", + "type": "string" + }, + { + "key": "redirect_uri", + "value": "CALLBACK_URL", + "type": "string" + }, + { + "key": "headerPrefix", + "value": "HeaderPrefix", + "type": "string" + }, + { + "key": "useBrowser", + "value": false, + "type": "boolean" + }, + { + "key": "grant_type", + "value": "authorization_code", + "type": "string" + }, + { + "key": "addTokenTo", + "value": "header", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "https://httpbin.org/anything", + "protocol": "https", + "host": [ + "httpbin", + "org" + ], + "path": [ + "anything" + ] + } + }, + "response": [] + }, + { + "name": "OAuth2-AuthCode-BodyClientAuth", + "request": { + "auth": { + "type": "oauth2", + "oauth2": [ + { + "key": "headerPrefix", + "value": "HeaderPrefix", + "type": "string" + }, + { + "key": "client_authentication", + "value": "body", + "type": "string" + }, + { + "key": "state", + "value": "{{state}}", + "type": "string" + }, + { + "key": "scope", + "value": "scope1, scope2 scope3 {{SCOPE_4}} scope_5", + "type": "string" + }, + { + "key": "clientSecret", + "value": "client secret", + "type": "string" + }, + { + "key": "clientId", + "value": "{{client id}}", + "type": "string" + }, + { + "key": "accessTokenUrl", + "value": "TOKEN_URL", + "type": "string" + }, + { + "key": "authUrl", + "value": "AUTH_URL", + "type": "string" + }, + { + "key": "redirect_uri", + "value": "{{CALLBACK_URL}}", + "type": "string" + }, + { + "key": "useBrowser", + "value": false, + "type": "boolean" + }, + { + "key": "grant_type", + "value": "authorization_code", + "type": "string" + }, + { + "key": "addTokenTo", + "value": "header", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "https://httpbin.org/anything", + "protocol": "https", + "host": [ + "httpbin", + "org" + ], + "path": [ + "anything" + ] + } + }, + "response": [] + }, + { + "name": "OAuth2-AuthCodePKCE-SHA256", + "request": { + "auth": { + "type": "oauth2", + "oauth2": [ + { + "key": "state", + "value": "state hello hello", + "type": "string" + }, + { + "key": "scope", + "value": "scope1, scope2 scope3 {{SCOPE_4}} scope_5", + "type": "string" + }, + { + "key": "clientSecret", + "value": "client secret", + "type": "string" + }, + { + "key": "clientId", + "value": "client id", + "type": "string" + }, + { + "key": "accessTokenUrl", + "value": "access token", + "type": "string" + }, + { + "key": "authUrl", + "value": "{{auth_url}}", + "type": "string" + }, + { + "key": "redirect_uri", + "value": "{{CALLBACK_URL}}", + "type": "string" + }, + { + "key": "grant_type", + "value": "authorization_code_with_pkce", + "type": "string" + }, + { + "key": "headerPrefix", + "value": "HeaderPrefix", + "type": "string" + }, + { + "key": "useBrowser", + "value": false, + "type": "boolean" + }, + { + "key": "addTokenTo", + "value": "header", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "https://httpbin.org/anything", + "protocol": "https", + "host": [ + "httpbin", + "org" + ], + "path": [ + "anything" + ] + } + }, + "response": [] + }, + { + "name": "OAuth2-AuthCodePKCE-Plain", + "request": { + "auth": { + "type": "oauth2", + "oauth2": [ + { + "key": "challengeAlgorithm", + "value": "plain", + "type": "string" + }, + { + "key": "state", + "value": "state hello hello", + "type": "string" + }, + { + "key": "scope", + "value": "scope1, scope2 scope3 {{SCOPE_4}} scope_5", + "type": "string" + }, + { + "key": "clientSecret", + "value": "client secret", + "type": "string" + }, + { + "key": "clientId", + "value": "client id", + "type": "string" + }, + { + "key": "accessTokenUrl", + "value": "access token", + "type": "string" + }, + { + "key": "authUrl", + "value": "{{auth_url}}", + "type": "string" + }, + { + "key": "redirect_uri", + "value": "{{CALLBACK_URL}}", + "type": "string" + }, + { + "key": "grant_type", + "value": "authorization_code_with_pkce", + "type": "string" + }, + { + "key": "headerPrefix", + "value": "HeaderPrefix", + "type": "string" + }, + { + "key": "useBrowser", + "value": false, + "type": "boolean" + }, + { + "key": "addTokenTo", + "value": "header", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "https://httpbin.org/anything", + "protocol": "https", + "host": [ + "httpbin", + "org" + ], + "path": [ + "anything" + ] + } + }, + "response": [] + }, + { + "name": "OAuth2-Implicit", + "request": { + "auth": { + "type": "oauth2", + "oauth2": [ + { + "key": "clientId", + "value": "hell hello", + "type": "string" + }, + { + "key": "authUrl", + "value": "AUTH _ URL", + "type": "string" + }, + { + "key": "redirect_uri", + "value": "CALLBACK URL", + "type": "string" + }, + { + "key": "headerPrefix", + "value": "Header _ Prefix", + "type": "string" + }, + { + "key": "grant_type", + "value": "implicit", + "type": "string" + }, + { + "key": "useBrowser", + "value": false, + "type": "boolean" + }, + { + "key": "addTokenTo", + "value": "header", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "https://httpbin.org/anything", + "protocol": "https", + "host": [ + "httpbin", + "org" + ], + "path": [ + "anything" + ] + } + }, + "response": [] + }, + { + "name": "OAuth2-ClientCredentials", + "request": { + "auth": { + "type": "oauth2", + "oauth2": [ + { + "key": "scope", + "value": "some scope scope scope", + "type": "string" + }, + { + "key": "clientSecret", + "value": "secret", + "type": "string" + }, + { + "key": "clientId", + "value": "id", + "type": "string" + }, + { + "key": "accessTokenUrl", + "value": "{{TOKEN_URL}}", + "type": "string" + }, + { + "key": "grant_type", + "value": "client_credentials", + "type": "string" + }, + { + "key": "headerPrefix", + "value": "header prefix 22", + "type": "string" + }, + { + "key": "useBrowser", + "value": false, + "type": "boolean" + }, + { + "key": "addTokenTo", + "value": "header", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "https://httpbin.org/anything", + "protocol": "https", + "host": [ + "httpbin", + "org" + ], + "path": [ + "anything" + ] + } + }, + "response": [] + }, + { + "name": "OAuth2-DefaultHeaderPrefix", + "request": { + "auth": { + "type": "oauth2", + "oauth2": [ + { + "key": "headerPrefix", + "value": "Bearer", + "type": "string" + }, + { + "key": "scope", + "value": "some scope scope scope", + "type": "string" + }, + { + "key": "clientSecret", + "value": "secret", + "type": "string" + }, + { + "key": "clientId", + "value": "id", + "type": "string" + }, + { + "key": "accessTokenUrl", + "value": "{{TOKEN_URL}}", + "type": "string" + }, + { + "key": "grant_type", + "value": "client_credentials", + "type": "string" + }, + { + "key": "useBrowser", + "value": false, + "type": "boolean" + }, + { + "key": "addTokenTo", + "value": "header", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "https://httpbin.org/anything", + "protocol": "https", + "host": [ + "httpbin", + "org" + ], + "path": [ + "anything" + ] + } + }, + "response": [] + }, + { + "name": "Inherit", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "https://httpbin.org/anything", + "protocol": "https", + "host": [ + "httpbin", + "org" + ], + "path": [ + "anything" + ] + } + }, + "response": [] + } + ] + }, + { + "name": "Unsupported", + "item": [ + { + "name": "Digest", + "request": { + "auth": { + "type": "digest", + "digest": [ + { + "key": "algorithm", + "value": "MD5", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "https://httpbin.org/anything", + "protocol": "https", + "host": [ + "httpbin", + "org" + ], + "path": [ + "anything" + ] + } + }, + "response": [] + }, + { + "name": "OAuth1", + "request": { + "auth": { + "type": "oauth1", + "oauth1": [ + { + "key": "tokenSecret", + "value": "aUXMa7qoAjHA5XJpo4EYb44b1wr8r1a2o8dHoHSfndCt6", + "type": "string" + }, + { + "key": "token", + "value": "1122468199579123713-Zv3n6o1EPGHlnNqORlLFrgxiKBddh6", + "type": "string" + }, + { + "key": "consumerSecret", + "value": "VEe6p7H0j26MlhguPOvUibzL2mqWHrOJkM1GB1cULhFFblqpH2", + "type": "string" + }, + { + "key": "consumerKey", + "value": "qKG16VSZMX9iiEGJReFLl9aEQ", + "type": "string" + }, + { + "key": "disableHeaderEncoding", + "value": false, + "type": "boolean" + }, + { + "key": "addParamsToHeader", + "value": true, + "type": "boolean" + }, + { + "key": "signatureMethod", + "value": "HMAC-SHA1", + "type": "string" + }, + { + "key": "version", + "value": "1.0", + "type": "string" + }, + { + "key": "addEmptyParamsToSign", + "value": false, + "type": "boolean" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "https://httpbin.org/anything", + "protocol": "https", + "host": [ + "httpbin", + "org" + ], + "path": [ + "anything" + ] + } + }, + "response": [] + }, + { + "name": "Hawk", + "request": { + "auth": { + "type": "hawk", + "hawk": [ + { + "key": "algorithm", + "value": "sha256", + "type": "string" + } + ] + }, + "method": "GET", + "header": [], + "url": { + "raw": "https://httpbin.org/anything", + "protocol": "https", + "host": [ + "httpbin", + "org" + ], + "path": [ + "anything" + ] + } + }, + "response": [] + }, + { + "name": "AWS", + "request": { + "auth": { + "type": "awsv4" + }, + "method": "GET", + "header": [], + "url": { + "raw": "https://httpbin.org/anything", + "protocol": "https", + "host": [ + "httpbin", + "org" + ], + "path": [ + "anything" + ] + } + }, + "response": [] + }, + { + "name": "NTLM", + "request": { + "auth": { + "type": "ntlm" + }, + "method": "GET", + "header": [], + "url": { + "raw": "https://httpbin.org/anything", + "protocol": "https", + "host": [ + "httpbin", + "org" + ], + "path": [ + "anything" + ] + } + }, + "response": [] + }, + { + "name": "EdgeGrid", + "request": { + "auth": { + "type": "edgegrid" + }, + "method": "GET", + "header": [], + "url": { + "raw": "https://httpbin.org/anything", + "protocol": "https", + "host": [ + "httpbin", + "org" + ], + "path": [ + "anything" + ] + } + }, + "response": [] + } + ] + } + ], + "auth": { + "type": "apikey", + "apikey": [ + { + "key": "value", + "value": "ThisIsInherited", + "type": "string" + }, + { + "key": "key", + "value": "Inherit", + "type": "string" + } + ] + }, + "event": [ + { + "listen": "prerequest", + "script": { + "type": "text/javascript", + "exec": [ + "" + ] + } + }, + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "" + ] + } + } + ] + }, + { + "name": "Folder with authorization", + "item": [], + "auth": { + "type": "basic", + "basic": [ + { + "key": "password", + "value": "world", + "type": "string" + }, + { + "key": "username", + "value": "hello", + "type": "string" + } + ] + }, + "event": [ + { + "listen": "prerequest", + "script": { + "type": "text/javascript", + "exec": [ + "" + ] + } + }, + { + "listen": "test", + "script": { + "type": "text/javascript", + "exec": [ + "" + ] + } + } + ] + }, + { + "name": "Headers", + "request": { + "method": "GET", + "header": [ + { + "key": "key1", + "value": "value1", + "type": "text" + }, + { + "key": "{{param1}} key2 {{param3}}", + "value": "{{param4}} value2 {{param5}}", + "type": "text" + }, + { + "key": "key3", + "value": "value3", + "type": "text", + "disabled": true + }, + { + "key": "key4", + "value": "value4", + "type": "text" + }, + { + "key": "{{key5}}", + "value": "{{value6}} addendum", + "type": "text" + } + ] + }, + "response": [] + }, + { + "name": "URL", + "request": { + "method": "GET", + "header": [], + "url": { + "raw": "https://www.httpbin.oth/anything?key1=value1&key2={{value2}}&key4=value4#itsahash", + "protocol": "https", + "host": [ + "www", + "httpbin", + "oth" + ], + "path": [ + "anything" + ], + "query": [ + { + "key": "key1", + "value": "value1" + }, + { + "key": "key2", + "value": "{{value2}}" + }, + { + "key": "key3", + "value": "value3", + "disabled": true + }, + { + "key": "key4", + "value": "value4" + } + ], + "hash": "itsahash" + } + }, + "response": [] + } + ], + "variable": [ + { + "key": "param4", + "value": "param4_VALUE" + } + ] +} \ No newline at end of file diff --git a/test/unit/HttpReqt.test.ts b/test/unit/HttpReqt.test.ts new file mode 100644 index 0000000..ceaff4e --- /dev/null +++ b/test/unit/HttpReqt.test.ts @@ -0,0 +1,113 @@ +import { HttpBodyContentType, HttpBodyType, HttpFormBody, HttpReqt, + HttpCollection, HttpRequest, HttpRequestMethod, HttpTextBody } from "../../src/renderer/lib/http"; + +type RequestDef = [HttpRequestMethod, string, Array<[string, string]>, [HttpBodyType, Array<[HttpBodyContentType, string, string]>]]; +type MacroDef = Array<[string, string]>; + +function toHttpRequest(def: RequestDef): HttpRequest { + let request: HttpRequest = new HttpRequest(); + + request.method = def[0]; + request.url = def[1]; + def[2].forEach(header => request.headers.push({ name: header[0], value: header[1] })); + + if (def[3][0] == null) return request; + + if (def[3][0] == HttpBodyType.Regular) { + request.body = new HttpTextBody(); + ((request.body)).valueType = def[3][1][0][0]; + ((request.body)).value = def[3][1][0][2]; + + } else if (def[3][0] == HttpBodyType.Form) { + request.body = new HttpFormBody(); + def[3][1].forEach(record => { + (request.body).records.push({ type: record[0], name: record[1], value: record[2] }); + }); + } + + return request; +} + +const GET = HttpRequestMethod.GET; +const POST = HttpRequestMethod.POST; +const PATCH = HttpRequestMethod.PATCH; + +const Regular = HttpBodyType.Regular; +const Form = HttpBodyType.Form; + +const File = HttpBodyContentType.File; +const Text = HttpBodyContentType.Text; + +function testMacroResolution(testName: string, rawRequest: RequestDef, context: MacroDef, expectedRequest: RequestDef): void { + test(testName, async () => { + const inputRequest = toHttpRequest(rawRequest); + const outputRequest = toHttpRequest(expectedRequest); + + let reqt = new HttpReqt(); + reqt.setMethod(inputRequest.method); + reqt.setUrl(inputRequest.url); + reqt.setHeaders(inputRequest.headers); + reqt.setBody(inputRequest.body); + + let collectionResult = HttpCollection.fromFile("./dummyCollection.json"); + + for (let pair of context) { + await collectionResult.collection.setMacro(pair[0], pair[1]); + } + + collectionResult.collection.addReqt(reqt); + + let actualOutput = await reqt.getHttpRequest(); + + expect(actualOutput).toEqual(outputRequest); + }); +} + + +testMacroResolution("Sanity check", + [ GET, "https://www.someurl.com", [["Content-Length", "222"], ["Content-Type", "application/json"]], + [ Regular, [[Text, "name", "value"]] ] + ], + [], + [ GET, "https://www.someurl.com", [["Content-Length", "222"], ["Content-Type", "application/json"]], + [ Regular, [[Text, "name", "value"]] ] + ] +); + +testMacroResolution("Macros are properly replaces when body type is Text", + [ PATCH, "${PROTOCOL}://${BASE_URL}${SUFFIX}/${SOME_PATH}", + [["Authorization", "Bearer ${AUTH_TOKEN}"], ["Content-Type", "application/json/${PROTOCOL}"]], + [ Regular, [[Text, "name", "This is some ${VALUE} isn't it?! Here's ${VALUE} again!"]]] + ], + [ + ["PROTOCOL", "https"], + ["BASE_URL", "www.someurl"], + ["SUFFIX", ".com"], + ["SOME_PATH", "some_path"], + ["VALUE", "value"], + ["AUTH_TOKEN", "some_token"] + ], + [ PATCH, "https://www.someurl.com/some_path", + [["Authorization", "Bearer some_token"], ["Content-Type", "application/json/https"]], + [ Regular, [[Text, "name", "This is some value isn't it?! Here's value again!"]]] + ] +); + +testMacroResolution("Macros are properly replaces when body type is Form", + [ POST, "${PROTOCOL}://${BASE_URL}${SUFFIX}/${SOME_PATH}", + [["Authorization", "Bearer ${AUTH_TOKEN}"], ["Content-Type", "application/json/${PROTOCOL}"]], + [ Form, [[Text, "name", "This is some ${VALUE} isn't it?! Here's ${VALUE} again!"], [File, "${VALUE}", "This is some ${AUTH_TOKEN} isn't it?! Here's ${VALUE} again!"]] ] + ], + [ + ["PROTOCOL", "https"], + ["BASE_URL", "www.someurl"], + ["SUFFIX", ".com"], + ["SOME_PATH", "some_path"], + ["VALUE", "value"], + ["AUTH_TOKEN", "some_token"] + ], + [ POST, "https://www.someurl.com/some_path", + [["Authorization", "Bearer some_token"], ["Content-Type", "application/json/https"]], + [ Form, [[Text, "name", "This is some value isn't it?! Here's value again!"], [File, "value", "This is some some_token isn't it?! Here's value again!"]] ] + ] +); diff --git a/test/unit/MacroedText.test.ts b/test/unit/MacroedText.test.ts new file mode 100644 index 0000000..4271f3b --- /dev/null +++ b/test/unit/MacroedText.test.ts @@ -0,0 +1,228 @@ +import { MacroedText, MacroedTextPart, MacroedTextPartType } from "../../src/renderer/lib/http/Macro"; +import { hrtime } from "process"; + +function testMttParsing(testName: string, inputString: string, condensedOutput: [ Array, Array ]): void { + test(testName, () => { + let parseOutput: MacroedText = null; + + if (condensedOutput[0].length != condensedOutput[1].length) { + throw new Error("Output not properly specified. There are different number of parts and types."); + } + + let expectedOutput: Array = []; + + condensedOutput[0].forEach((_, i) => { + expectedOutput.push({ + text: condensedOutput[0][i], + type: condensedOutput[1][i] + }); + }); + + let startDT = hrtime.bigint(); + parseOutput = MacroedText.parse(inputString, true); + let endDT = hrtime.bigint(); + const parseDuration = endDT - startDT; + + expect(parseOutput.getParts()).toEqual(expectedOutput); + expect(parseDuration).toBeLessThan(1000000); // 1000000 nanoseconds = 1ms + }); +} + +const PARAM = MacroedTextPartType.Parameter; +const PLAIN = MacroedTextPartType.PlainText; +const EQ = MacroedTextPartType.EqualityChar; + +testMttParsing("1", "some string ${PARAMETER} some other string", [ + [ "some string ", "${PARAMETER}", " some other string" ], + [ PLAIN, PARAM, PLAIN ] +]); + +testMttParsing("2", "some string ${PARAMETER_1} some ${PARAMETER_2} other string", [ + [ "some string ", "${PARAMETER_1}", " some ", "${PARAMETER_2}", " other string" ], + [ PLAIN, PARAM, PLAIN, PARAM, PLAIN ] +]); + +testMttParsing("3", "some string ${PARAMETER_1}${PARAMETER_2} other string", [ + [ "some string ", "${PARAMETER_1}", "${PARAMETER_2}", " other string" ], + [ PLAIN, PARAM, PARAM, PLAIN ] +]); + +testMttParsing("4", "${PARAMETER_1} blabla ${PARAMETER_2}", [ + [ "${PARAMETER_1}", " blabla ", "${PARAMETER_2}" ], + [ PARAM, PLAIN, PARAM ] +]); + +testMttParsing("5", "${PARAMETER_1} blabla ${PARAMETER_2", [ + [ "${PARAMETER_1}", " blabla ", "${PARAMETER_2" ], + [ PARAM, PLAIN, PARAM ] +]); + +testMttParsing("6", "${PARAMETER_1 blabla ${PARAMETER_2", [ + [ "${PARAMETER_1 blabla ${PARAMETER_2"], + [ PARAM ] +]); + +testMttParsing("7", "somthing} ${PARAMETER_1 blabla ${PARAMETER_2} bla } ble", [ + [ "somthing} ", "${PARAMETER_1 blabla ${PARAMETER_2}", " bla } ble"], + [ PLAIN, PARAM, PLAIN ] +]); + +testMttParsing("0e11", "a", [ + [ "a" ], + [ PLAIN ] +]); + +testMttParsing("0e12", "a=", [ + [ "a", "=" ], + [ PLAIN, EQ ] +]); + +testMttParsing("0e13", "=a", [ + [ "=", "a" ], + [ EQ, PLAIN ] +]); + +testMttParsing("0e13", "=", [ + [ "=" ], + [ EQ ] +]); + +testMttParsing("0e21", "am=", [ + [ "am", "=" ], + [ PLAIN, EQ ] +]); + +testMttParsing("0e22", "=am", [ + [ "=", "am" ], + [ EQ, PLAIN ] +]); + +testMttParsing("0e23", "a=m", [ + [ "a", "=", "m" ], + [ PLAIN, EQ, PLAIN ] +]); + +testMttParsing("0e24", "am", [ + [ "am" ], + [ PLAIN ] +]); + +testMttParsing("0e31", "amj", [ + [ "amj" ], + [ PLAIN ] +]); + +testMttParsing("0e32", "=amj", [ + [ "=", "amj" ], + [ EQ, PLAIN ] +]); + +testMttParsing("0e33", "a=mj", [ + [ "a", "=", "mj" ], + [ PLAIN, EQ, PLAIN ] +]); + +testMttParsing("0e34", "am=j", [ + [ "am", "=", "j" ], + [ PLAIN, EQ, PLAIN ] +]); + +testMttParsing("0e35", "amj=", [ + [ "amj", "=" ], + [ PLAIN, EQ ] +]); + +testMttParsing("0e4", "somes=tring", [ + [ "somes", "=", "tring"], + [ PLAIN, EQ, PLAIN ] +]); + +testMttParsing("0e51", "somes=$", [ + [ "somes", "=", "$"], + [ PLAIN, EQ, PLAIN ] +]); + +testMttParsing("0e52", "somes=${", [ + [ "somes", "=", "${"], + [ PLAIN, EQ, PARAM ] +]); + +testMttParsing("1e1", "some str=ing ${PARAMETER} some other string", [ + [ "some str", "=", "ing ", "${PARAMETER}", " some other string" ], + [ PLAIN, EQ, PLAIN, PARAM, PLAIN ] +]); + +testMttParsing("1e2", "some string ${PARA=METER} some other string", [ + [ "some string ", "${PARA=METER}", " some other string" ], + [ PLAIN, PARAM, PLAIN ] +]); + +testMttParsing("1e3", "some string ${=PARAMETER} some other string", [ + [ "some string ", "${=PARAMETER}", " some other string" ], + [ PLAIN, PARAM, PLAIN ] +]); + +testMttParsing("1e4", "some string ${PARAMETER=} some other string", [ + [ "some string ", "${PARAMETER=}", " some other string" ], + [ PLAIN, PARAM, PLAIN ] +]); + +testMttParsing("1e5", "some string =${PARAMETER} some other string", [ + [ "some string ", "=", "${PARAMETER}", " some other string" ], + [ PLAIN, EQ, PARAM, PLAIN ] +]); + +testMttParsing("1e6", "some string ${PARAMETER}= some other string", [ + [ "some string ", "${PARAMETER}", "=", " some other string" ], + [ PLAIN, PARAM, EQ, PLAIN ] +]); + +testMttParsing("1e7", "=some string ${PARAMETER} some other string", [ + [ "=", "some string ", "${PARAMETER}", " some other string" ], + [ EQ, PLAIN, PARAM, PLAIN ] +]); + +testMttParsing("1e8", "some string ${PARAMETER} some other string=", [ + [ "some string ", "${PARAMETER}", " some other string", "=" ], + [ PLAIN, PARAM, PLAIN, EQ ] +]); + +testMttParsing("1e8", "so=me strin=g ${PARAMETER} some other str=ing", [ + [ "so", "=", "me strin=g ", "${PARAMETER}", " some other str=ing"], + [ PLAIN, EQ, PLAIN, PARAM, PLAIN ] +]); + +testMttParsing("2e", "some string ${PARAMETER_1} some =${PARAMETER_2} other string", [ + [ "some string ", "${PARAMETER_1}", " some ", "=", "${PARAMETER_2}", " other string" ], + [ PLAIN, PARAM, PLAIN, EQ, PARAM, PLAIN ] +]); + +testMttParsing("3e1", "some = string ${PARAMETER_1}${PARAMETER_2} other string", [ + [ "some ", "=", " string ", "${PARAMETER_1}", "${PARAMETER_2}", " other string" ], + [ PLAIN, EQ, PLAIN, PARAM, PARAM, PLAIN ] +]); + +testMttParsing("3e2", "some string ${PARAMETER_1}=${PARAMETER_2} other string", [ + [ "some string ", "${PARAMETER_1}", "=", "${PARAMETER_2}", " other string" ], + [ PLAIN, PARAM, EQ, PARAM, PLAIN ] +]); + +testMttParsing("4e", "${PARAMETER_1}= blabla ${PARAMETER_2}", [ + [ "${PARAMETER_1}", "=", " blabla ", "${PARAMETER_2}" ], + [ PARAM, EQ, PLAIN, PARAM ] +]); + +testMttParsing("5e", "${PARAM=ETER_1} bla=bla ${PAR=AMETER_2", [ + [ "${PARAM=ETER_1}", " bla", "=", "bla ", "${PAR=AMETER_2" ], + [ PARAM, PLAIN, EQ, PLAIN, PARAM ] +]); + +testMttParsing("6e", "${PARAMETER_1 bla=bla ${PARAMETER_2", [ + [ "${PARAMETER_1 bla=bla ${PARAMETER_2"], + [ PARAM ] +]); + +testMttParsing("7e", "something=} ${PARAMETER_1 blabla ${PARAMETER_2} bla } ble", [ + [ "something", "=", "} ", "${PARAMETER_1 blabla ${PARAMETER_2}", " bla } ble"], + [ PLAIN, EQ, PLAIN, PARAM, PLAIN ] +]); diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..2522361 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,68 @@ +{ + "compilerOptions": { + /* Visit https://aka.ms/tsconfig.json to read more about this file */ + + /* Basic Options */ + // "incremental": true, /* Enable incremental compilation */ + "target": "es2021", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */ + "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */ + // "lib": [], /* Specify library files to be included in the compilation. */ + // "allowJs": true, /* Allow javascript files to be compiled. */ + // "checkJs": true, /* Report errors in .js files. */ + // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ + // "declaration": false, /* Generates corresponding '.d.ts' file. */ + // "declarationMap": true, ยง /* Generates a sourcemap for each corresponding '.d.ts' file. */ + // "sourceMap": true, /* Generates corresponding '.map' file. */ + // "outFile": "./", /* Concatenate and emit output to single file. */ + // "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ + // "composite": true, /* Enable project compilation */ + // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ + // "removeComments": true, /* Do not emit comments to output. */ + // "noEmit": true, /* Do not emit outputs. */ + // "importHelpers": true, /* Import emit helpers from 'tslib'. */ + // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ + // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ + + /* Strict Type-Checking Options */ + "strict": true, /* Enable all strict type-checking options. */ + "noImplicitAny": false, /* Raise error on expressions and declarations with an implied 'any' type. */ + "strictNullChecks": false, /* Enable strict null checks. */ + // "strictFunctionTypes": true, /* Enable strict checking of function types. */ + // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ + // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ + // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ + // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ + + /* Additional Checks */ + // "noUnusedLocals": true, /* Report errors on unused locals. */ + // "noUnusedParameters": true, /* Report errors on unused parameters. */ + // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ + // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ + + /* Module Resolution Options */ + // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ + // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ + // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ + // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */ + // "typeRoots": [], /* List of folders to include type definitions from. */ + // "types": [], /* Type declaration files to be included in compilation. */ + // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ + "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ + // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ + // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ + + /* Source Map Options */ + // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ + // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ + // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ + // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */ + + /* Experimental Options */ + // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ + // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ + + /* Advanced Options */ + "skipLibCheck": true, /* Skip type checking of declaration files. */ + "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ + } +} diff --git a/webpack.config.js b/webpack.config.js new file mode 100644 index 0000000..e203d11 --- /dev/null +++ b/webpack.config.js @@ -0,0 +1,118 @@ +/* eslint-disable no-undef */ +/* eslint-disable @typescript-eslint/no-var-requires */ + +const path = require("path"); +const webpack = require("webpack"); + +const package = require('./package.json') + +const config = { + mode: "development", + output: { + path: path.resolve(__dirname, "dist/electron-workspace"), + filename: "[name].js" + }, + resolve: { + extensions: [".tsx", ".ts", ".js", ".json"], + }, + module: { + rules: [ + { test: /\.node$/, loader: "native-ext-loader" }, + { test: /\.tsx?$/, use: ["ts-loader"], exclude: /node_modules/ } + ] + } +}; + +const mainConfig = { + ...config, + name: "main-dev", + entry: { + main: "./src/main/index.ts", + }, + plugins: [ + new webpack.DefinePlugin({ + DIST: false, + VERSION: JSON.stringify(package.version), + APP_ID: JSON.stringify(package.appId) + }) + ], + node: { + __dirname: false + }, + target: "electron-main" +}; + +const mainConfigDist = { + ...config, + mode: "production", + name: "main-dist", + entry: { + main: "./src/main/index.ts", + }, + plugins: [ + new webpack.DefinePlugin({ + DIST: true, + VERSION: JSON.stringify(package.version), + APP_ID: JSON.stringify(package.appId) + }) + ], + node: { + __dirname: false + }, + target: "electron-main" +}; + +const rendererConfig = { + ...config, + name: "renderer-dev", + entry: { + renderer: "./src/renderer/index.ts" + }, + plugins: [ + new webpack.DefinePlugin({ + DIST: false, + VERSION: JSON.stringify(package.version), + APP_ID: JSON.stringify(package.appId) + }) + ], + target: "electron-renderer" +}; + +const rendererConfigDist = { + ...config, + mode: "production", + name: "renderer-dist", + entry: { + renderer: "./src/renderer/index.ts" + }, + plugins: [ + new webpack.DefinePlugin({ + DIST: true, + VERSION: JSON.stringify(package.version), + APP_ID: JSON.stringify(package.appId) + }) + ], + target: "electron-renderer" +}; + +const unitTestsConfig = { + ...config, + output: { + path: path.resolve(__dirname, "test/bin"), + filename: "[name].js" + }, + name: "unitTests", + entry: { + unitTests: "./test/unit/index.ts" + }, + plugins: [ + new webpack.DefinePlugin({ + DIST: false, + VERSION: JSON.stringify(package.version), + APP_ID: JSON.stringify(package.appId) + }) + ], + target: "node" +}; + +module.exports = [ mainConfig, mainConfigDist, rendererConfig, rendererConfigDist, unitTestsConfig ]; \ No newline at end of file