diff --git a/.github/workflows/release-current-version.yml b/.github/workflows/release-current-version.yml index 6d6faae..641a3b7 100644 --- a/.github/workflows/release-current-version.yml +++ b/.github/workflows/release-current-version.yml @@ -2,10 +2,8 @@ name: Release Current Version on: push: - branches: [ master, main ] + branches: [ master, main, support/v1 ] jobs: Run: - uses: polarityio/polarity-github-actions/.github/workflows/release-server-versions-for-int-store.yml@master - # with: - # use-integration-development-checklist: false + uses: polarityio/polarity-github-actions/.github/workflows/release-integration.yml@master diff --git a/.github/workflows/run-int-dev-checklist.yml b/.github/workflows/run-int-dev-checklist.yml index ffa86d7..12eba68 100644 --- a/.github/workflows/run-int-dev-checklist.yml +++ b/.github/workflows/run-int-dev-checklist.yml @@ -2,22 +2,23 @@ name: Run Integration Development Checklist on: pull_request: - branches: [ master, main, develop ] + branches: [ master, main, develop, support/v1 ] jobs: run-integration-development-checklist: runs-on: ubuntu-latest - container: 'centos:7' steps: - - uses: actions/checkout@v2 - - uses: actions/setup-node@v1 - - name: Test NPM Install - id: test-npm-install - run: | - npm ci - - name: Polarity Integration Development Checklist - id: int-dev-checklist - uses: polarityio/polarity-integration-development-checklist@main - with: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - uses: actions/checkout@v6 + - uses: actions/setup-node@v4 + with: + node-version: '18' + - name: Test NPM Install + id: test-npm-install + run: | + npm ci + - name: Polarity Integration Development Checklist + id: int-dev-checklist + uses: polarityio/polarity-integration-development-checklist@main + with: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index f7fe565..6b8cdd1 100644 --- a/.gitignore +++ b/.gitignore @@ -33,4 +33,5 @@ test-runner.js .polarity.conf -data/ \ No newline at end of file +data/ +dist/ diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..521a9f7 --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +legacy-peer-deps=true diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..55a10aa --- /dev/null +++ b/.prettierignore @@ -0,0 +1,4 @@ +dist/ +node_modules/ +mocks/ +package-lock.json diff --git a/.prettierrc b/.prettierrc index 8dff95d..e98ecea 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,11 +1,11 @@ -{ - "arrowParens": "always", - "bracketSpacing": true, - "printWidth": 90, - "proseWrap": "preserve", - "semi": true, - "singleQuote": true, - "tabWidth": 2, - "trailingComma": "none", - "useTabs": false -} \ No newline at end of file +{ + "arrowParens": "always", + "bracketSpacing": true, + "printWidth": 100, + "proseWrap": "preserve", + "semi": true, + "singleQuote": true, + "tabWidth": 2, + "trailingComma": "none", + "useTabs": false +} diff --git a/LICENSE b/LICENSE index 6af51a9..5ed68de 100644 --- a/LICENSE +++ b/LICENSE @@ -1,21 +1 @@ -MIT License - -Copyright (c) 2022 Polarity.io, Inc. - -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. \ No newline at end of file +Copyright 2026 Dataminr, Inc. All rights reserved. diff --git a/README.md b/README.md index e257c9b..4fed2df 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ * List of all detections associated with Host
- Host List Detections + Host List Detections
## About Qualys @@ -42,4 +42,4 @@ Installation instructions for integrations are provided on the [PolarityIO GitHu ## Polarity Polarity is a memory-augmentation platform that improves and accelerates analyst decision making. For more information about the Polarity platform please see: -https://polarity.io/ \ No newline at end of file +https://polarity.io/ diff --git a/components/block.js b/components/block.js deleted file mode 100644 index fd19836..0000000 --- a/components/block.js +++ /dev/null @@ -1,74 +0,0 @@ -polarity.export = PolarityComponent.extend({ - details: Ember.computed.alias('block.data.details'), - summary: Ember.computed.alias('block.data.summary'), - tabKeys: Ember.computed.alias('details.tabKeys'), - timezone: Ember.computed('Intl', function () { - return Intl.DateTimeFormat().resolvedOptions().timeZone; - }), - expandableTitleStates: {}, - copiedStates: {}, - isRunningStates: {}, - messageStates: {}, - errorMessageStates: {}, - hostDetectionsFound: false, - activeTab: '', - init() { - const details = this.get('details'); - this.set( - 'activeTab', - this.get('tabKeys').find((tabKey) => details[tabKey] && details[tabKey].length) - ); - - this._super(...arguments); - }, - actions: { - changeTab: function (tabName) { - this.set('activeTab', tabName); - }, - toggleExpandableTitle: function ( - displayFieldIndex, - itemDisplayFieldsIndex, - fieldName - ) { - const modifiedExpandableTitleStates = Object.assign( - {}, - this.get('expandableTitleStates'), - { - [`${displayFieldIndex}${itemDisplayFieldsIndex}${fieldName}`]: !this.get( - 'expandableTitleStates' - )[`${displayFieldIndex}${itemDisplayFieldsIndex}${fieldName}`] - } - ); - - this.set(`expandableTitleStates`, modifiedExpandableTitleStates); - }, - copyField: function ( - displayFieldValue, - displayFieldIndex, - displayFieldLabel, - subIndex, - shouldCopyFieldLabel - ) { - navigator.clipboard.writeText( - `${shouldCopyFieldLabel ? `${displayFieldLabel}: ` : ''}${displayFieldValue}` - ); - - this.set( - `copiedStates`, - Object.assign({}, this.get('copiedStates'), { - [`${displayFieldIndex}${displayFieldLabel}${subIndex}`]: true - }) - ); - this.get('block').notifyPropertyChange('data'); - setTimeout(() => { - this.set( - `copiedStates`, - Object.assign({}, this.get('copiedStates'), { - [`${displayFieldIndex}${displayFieldLabel}${subIndex}`]: false - }) - ); - this.get('block').notifyPropertyChange('data'); - }, 3000); - } - } -}); diff --git a/config/config.js b/config/config.js deleted file mode 100644 index c13ad36..0000000 --- a/config/config.js +++ /dev/null @@ -1,65 +0,0 @@ -module.exports = { - name: 'Qualys', - acronym: 'QLS', - description: - "The Polarity Qualys Integration queries the Qualys Cloud Platform's Host Detection List and KnowledgeBase for IP Addresses, Domains, CVEs and QIDs.", - entityTypes: ['IPv4', 'IPv6'], - customTypes: [ - { - key: 'qid', - regex: /(?:QID|qid):\s*\d{1,8}/ - } - ], - defaultColor: 'light-purple', - styles: ['./styles/styles.less'], - onDemandOnly: true, - block: { - component: { - file: './components/block.js' - }, - template: { - file: './templates/block.hbs' - } - }, - request: { - cert: '', - key: '', - passphrase: '', - ca: '', - proxy: "" - }, - logging: { - level: 'info' //trace, debug, info, warn, error, fatal - }, - - options: [ - { - key: 'url', - name: 'Qualys URL', - description: - 'The URL of the Qualys you would like to connect to (including http:// or https://)', - default: '', - type: 'text', - userCanEdit: false, - adminOnly: true - }, - { - key: 'username', - name: 'Qualys Username', - description: 'The Username for your Qualys Account', - default: '', - type: 'text', - userCanEdit: false, - adminOnly: true - }, - { - key: 'password', - name: 'Qualys Password', - description: 'The Password associated with the Qualys Account', - default: '', - type: 'password', - userCanEdit: false, - adminOnly: true - } - ] -}; diff --git a/config/config.json b/config/config.json index 8806fd1..8bf731e 100644 --- a/config/config.json +++ b/config/config.json @@ -3,39 +3,31 @@ "name": "Qualys", "acronym": "QLS", "description": "The Polarity Qualys Integration queries the Qualys Cloud Platform's Host Detection List and KnowledgeBase for IP Addresses, Domains, CVEs and QIDs.", - "entityTypes": [ + "runtimeVersion": 2, + "defaultColor": "light-purple", + "onDemandOnly": true, + "dataTypes": [ "IPv4", "IPv6" ], - "customTypes": [ - { - "key": "qid", - "regex": "(?:QID|qid):\\s*\\d{1,8}" - } - ], - "defaultColor": "light-purple", - "styles": [ - "./styles/styles.less" - ], - "onDemandOnly": true, - "block": { - "component": { - "file": "./components/block.js" - }, - "template": { - "file": "./templates/block.hbs" - } - }, - "request": { - "cert": "", - "key": "", - "passphrase": "", - "ca": "", - "proxy": "" - }, "logging": { "level": "info" }, + "webComponents": { + "id": "512a1044-4cd2-4184-b120-48de15680389", + "moduleFilename": "web-components.mjs", + "components": [ + { + "type": "details", + "element": "px-int-4szfuj42ymklnvnabm9q4yt1l-qls-details-v3-4-0" + } + ] + }, + "reducer": { + "details": { + "file": "./reducers/details.json" + } + }, "options": [ { "key": "url", @@ -63,11 +55,29 @@ "type": "password", "userCanEdit": false, "adminOnly": true + }, + { + "key": "customTypeValueRegex", + "name": "Custom Type Value Regex", + "description": "A regular expression used to extract the numeric QID value from a matched Custom Type entity. The full regex match is used as the QID (no capture groups). Defaults to '\\d+$' which extracts trailing digits. Only applies when the Custom Type Data Type is enabled.", + "default": "\\d+$", + "type": "text", + "userCanEdit": false, + "adminOnly": true } ], - "reducer": { - "details": { - "file": "./reducers/details.json" + "customTypes": [ + { + "key": "qid", + "regex": "(?:QID|qid)(?:\\s*:\\s*|\\s+)\\d{1,8}" + }, + { + "key": "customType", + "name": "Custom Type", + "description": "A customer-defined entity type. Set the regex to match your organization's QID format in the Polarity admin Data Types settings. The integration will extract a numeric QID from each match using the 'Custom Type Value Regex' option.", + "regex": "CUSTOM-\\d+", + "editable": true, + "enabled": false } - } + ] } \ No newline at end of file diff --git a/assets/Host List Detections.png b/docs/Host List Detections.png similarity index 100% rename from assets/Host List Detections.png rename to docs/Host List Detections.png diff --git a/assets/KnowledgeBase Record.png b/docs/KnowledgeBase Record.png similarity index 100% rename from assets/KnowledgeBase Record.png rename to docs/KnowledgeBase Record.png diff --git a/assets/hostDetectionsFullExample.png b/docs/hostDetectionsFullExample.png similarity index 100% rename from assets/hostDetectionsFullExample.png rename to docs/hostDetectionsFullExample.png diff --git a/assets/knowledgeBaseFullExample.png b/docs/knowledgeBaseFullExample.png similarity index 100% rename from assets/knowledgeBaseFullExample.png rename to docs/knowledgeBaseFullExample.png diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 0000000..8040144 --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,22 @@ +import eslint from '@eslint/js'; +import tseslint from 'typescript-eslint'; +import eslintConfigPrettier from 'eslint-config-prettier'; + +export default tseslint.config( + eslint.configs.recommended, + ...tseslint.configs.recommended, + eslintConfigPrettier, + { + ignores: ['dist/', 'node_modules/', 'mocks/', 'vite.config.js'] + }, + { + files: ['src/**/*.ts', 'web-components/**/*.ts'], + rules: { + '@typescript-eslint/no-explicit-any': 'warn', + '@typescript-eslint/no-unused-vars': [ + 'warn', + { argsIgnorePattern: '^_', varsIgnorePattern: '^_', caughtErrorsIgnorePattern: '^_' } + ] + } + } +); diff --git a/integration.js b/integration.js deleted file mode 100644 index 275ab19..0000000 --- a/integration.js +++ /dev/null @@ -1,62 +0,0 @@ -const createRequestWithDefaults = require('./src/createRequestWithDefaults'); -const { validateStringOptions, validateUrlOption } = require('./src/validateOptions'); -const { parseErrorToReadableJSON } = require('./src/dataTransformations'); -const { getLookupResults } = require('./src/getLookupResults'); - -let Logger; -let requestWithDefaults; - -const startup = async (logger) => { - Logger = logger; - requestWithDefaults = createRequestWithDefaults(Logger); -}; - -const doLookup = async (entities, options, cb) => { - Logger.debug({ entities }, 'Entities'); - - let lookupResults = []; - try { - lookupResults = await getLookupResults( - entities, - options, - requestWithDefaults, - Logger - ); - } catch (error) { - const err = parseErrorToReadableJSON(error); - Logger.error({ error, formattedError: err }, 'Get Lookup Results Failed'); - - return cb({ - detail: error.message ? error.message : 'Searching Failed', - err - }); - } - - Logger.trace({ lookupResults }, 'Lookup Results'); - cb(null, lookupResults); -}; - -const validateOptions = async (options, callback) => { - const stringOptionsErrorMessages = { - url: 'You must provide a valid Qualys URL.', - username: 'You must provide a valid Qualys Username', - password: 'You must provide a valid Qualys Password' - }; - - const stringValidationErrors = validateStringOptions( - stringOptionsErrorMessages, - options - ); - - const urlValidationErrors = validateUrlOption(options.url.value); - - const errors = stringValidationErrors.concat(urlValidationErrors); - - callback(null, errors); -}; - -module.exports = { - startup, - validateOptions, - doLookup -}; diff --git a/package-lock.json b/package-lock.json index c288ba4..f82fe96 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,343 +1,3929 @@ { "name": "qualys", - "version": "3.2.1", - "lockfileVersion": 1, + "version": "4.0.0", + "lockfileVersion": 3, "requires": true, - "dependencies": { - "@postman/form-data": { + "packages": { + "": { + "name": "qualys", + "version": "4.0.0", + "license": "UNLICENSED", + "dependencies": { + "lodash": "^4.17.21", + "polarity-integration-utils": "^4.1.0", + "xml2js": "^0.6.2" + }, + "devDependencies": { + "@eslint/js": "^10.0.1", + "@polarityio/integration-types": "^1.1.0", + "@polarityio/pi-components": "^1.9.3", + "@polarityio/vite-plugin-icl": "^1.0.1", + "@types/lodash": "^4.17.24", + "@types/xml2js": "^0.4.14", + "@vitest/browser": "^4.1.4", + "@vitest/browser-playwright": "^4.1.4", + "eslint": "^10.2.0", + "eslint-config-prettier": "^10.1.8", + "lit": "^3.3.2", + "playwright": "^1.59.1", + "prettier": "^3.8.3", + "typescript": "^5.9.3", + "typescript-eslint": "^8.58.2", + "vite": "^7.3.2", + "vitest": "^4.1.4" + } + }, + "node_modules/@blazediff/core": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@blazediff/core/-/core-1.9.1.tgz", + "integrity": "sha512-ehg3jIkYKulZh+8om/O25vkvSsXXwC+skXmyA87FFx6A/45eqOkZsBltMw/TVteb0mloiGT8oGRTcjRAz66zaA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.7.tgz", + "integrity": "sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.7.tgz", + "integrity": "sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.7.tgz", + "integrity": "sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.7.tgz", + "integrity": "sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.7.tgz", + "integrity": "sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.7.tgz", + "integrity": "sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.7.tgz", + "integrity": "sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.7.tgz", + "integrity": "sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.7.tgz", + "integrity": "sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.7.tgz", + "integrity": "sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.7.tgz", + "integrity": "sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.7.tgz", + "integrity": "sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.7.tgz", + "integrity": "sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.7.tgz", + "integrity": "sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.7.tgz", + "integrity": "sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.7.tgz", + "integrity": "sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.7.tgz", + "integrity": "sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.7.tgz", + "integrity": "sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.7.tgz", + "integrity": "sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.7.tgz", + "integrity": "sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.7.tgz", + "integrity": "sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.7.tgz", + "integrity": "sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.7.tgz", + "integrity": "sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.7.tgz", + "integrity": "sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.7.tgz", + "integrity": "sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.7.tgz", + "integrity": "sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.23.5", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.23.5.tgz", + "integrity": "sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^3.0.5", + "debug": "^4.3.1", + "minimatch": "^10.2.4" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.5.5.tgz", + "integrity": "sha512-eIJYKTCECbP/nsKaaruF6LW967mtbQbsw4JTtSVkUQc9MneSkbrgPJAbKl9nWr0ZeowV8BfsarBmPpBzGelA2w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^1.2.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/core": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.2.1.tgz", + "integrity": "sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/js": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-10.0.1.tgz", + "integrity": "sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "eslint": "^10.0.0" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/@eslint/object-schema": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.5.tgz", + "integrity": "sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.7.1.tgz", + "integrity": "sha512-rZAP3aVgB9ds9KOeUSL+zZ21hPmo8dh6fnIFwRQj5EAZl9gzR7wxYbYXYysAM8CTqGmUGyp2S4kUdV17MnGuWQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^1.2.1", + "levn": "^0.4.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@floating-ui/core": { + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.5.tgz", + "integrity": "sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@floating-ui/utils": "^0.2.11" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.7.6", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.6.tgz", + "integrity": "sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@floating-ui/core": "^1.7.5", + "@floating-ui/utils": "^0.2.11" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.11", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.11.tgz", + "integrity": "sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@fortawesome/fontawesome-common-types": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-7.2.0.tgz", + "integrity": "sha512-IpR0bER9FY25p+e7BmFH25MZKEwFHTfRAfhOyJubgiDnoJNsSvJ7nigLraHtp4VOG/cy8D7uiV0dLkHOne5Fhw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/fontawesome-svg-core": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-7.2.0.tgz", + "integrity": "sha512-6639htZMjEkwskf3J+e6/iar+4cTNM9qhoWuRfj9F3eJD6r7iCzV1SWnQr2Mdv0QT0suuqU8BoJCZUyCtP9R4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@fortawesome/fontawesome-common-types": "7.2.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/free-solid-svg-icons": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-7.2.0.tgz", + "integrity": "sha512-YTVITFGN0/24PxzXrwqCgnyd7njDuzp5ZvaCx5nq/jg55kUYd94Nj8UTchBdBofi/L0nwRfjGOg0E41d2u9T1w==", + "dev": true, + "license": "(CC-BY-4.0 AND MIT)", + "dependencies": { + "@fortawesome/fontawesome-common-types": "7.2.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", + "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/types": "^0.15.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz", + "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.2", + "@humanfs/types": "^0.15.0", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/types": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz", + "integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.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, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@lit-labs/ssr-dom-shim": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@lit-labs/ssr-dom-shim/-/ssr-dom-shim-1.5.1.tgz", + "integrity": "sha512-Aou5UdlSpr5whQe8AA/bZG0jMj96CoJIWbGfZ91qieWu5AWUMKw8VR/pAkQkJYvBNhmCcWnZlyyk5oze8JIqYA==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@lit/reactive-element": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@lit/reactive-element/-/reactive-element-2.1.2.tgz", + "integrity": "sha512-pbCDiVMnne1lYUIaYNN5wrwQXDtHaYtg7YEFPeW+hws6U47WeFvISGUWekPGKWOP1ygrs0ef0o1VJMk1exos5A==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@lit-labs/ssr-dom-shim": "^1.5.0" + } + }, + "node_modules/@polarityio/integration-types": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@polarityio/integration-types/-/integration-types-1.1.0.tgz", + "integrity": "sha512-AEsWN3N5z92Ld8kuLC9sQUJFshlN76vjWYvQS1xoUFsybx55vErPYGOrZl8FZnUkUuxr+phIpv0vp5XtCJ7Gzg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@polarityio/pi-button": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@polarityio/pi-button/-/pi-button-1.0.4.tgz", + "integrity": "sha512-l6kPPQTXDw5pRQ3xQ5CIz4AKiwrvSrXmKYCXP/nR4rYagQJDs/vykzcPRfoeZi7loRclagzqNi3WWh23Np2tzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@fortawesome/fontawesome-svg-core": "^7.0.0", + "@fortawesome/free-solid-svg-icons": "^7.0.0", + "@polarityio/pi-shared": "1.0.3", + "@polarityio/pi-tooltip": "1.0.4" + }, + "peerDependencies": { + "lit": "^3.0.0" + } + }, + "node_modules/@polarityio/pi-button/node_modules/@polarityio/pi-shared": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@polarityio/pi-shared/-/pi-shared-1.0.3.tgz", + "integrity": "sha512-emX5IT4sn1fNJBqABSDeXF+jv/RhTr8b+27jSTnnJ4Jas/APi4rWnZ203dqBI99PhYTV8cvePLabI5KOuKFg/g==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "lit": "^3.0.0" + } + }, + "node_modules/@polarityio/pi-button/node_modules/@polarityio/pi-tooltip": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@polarityio/pi-tooltip/-/pi-tooltip-1.0.4.tgz", + "integrity": "sha512-/q2b3wobIcmmE+EWzZAH995fmbsRZ7k3Usp/k0xadDXO/dU7kwgKdwat+P1rJ4ZNtvV/I9i9mDPsdimuQO6Tow==", + "dev": true, + "license": "MIT", + "dependencies": { + "@floating-ui/dom": "^1.7.6", + "@polarityio/pi-shared": "1.0.3" + }, + "peerDependencies": { + "lit": "^3.0.0" + } + }, + "node_modules/@polarityio/pi-card": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@polarityio/pi-card/-/pi-card-1.2.3.tgz", + "integrity": "sha512-wsuUPNxqvQYSozt1XJ8j9I+Fiu9XfVNEaBSafSLyMn5BISdP9XGWkPomi9GqkzpMMBdXaWPk0q+GjYHEGFu5Dg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@fortawesome/fontawesome-svg-core": "^7.0.0", + "@fortawesome/free-solid-svg-icons": "^7.0.0", + "@polarityio/pi-icon": "1.0.3", + "@polarityio/pi-shared": "1.3.1" + }, + "peerDependencies": { + "lit": "^3.0.0" + } + }, + "node_modules/@polarityio/pi-checkbox": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@polarityio/pi-checkbox/-/pi-checkbox-1.0.4.tgz", + "integrity": "sha512-QhuEoVNOHVG/zSRjG7s6iFNfcYhFqsbhFYSvnv9DD4DO1AmhZ83dmvxLkokKS+399jT/Nv5d2cYr9rmRsHj3rw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@polarityio/pi-shared": "1.2.1" + }, + "peerDependencies": { + "lit": "^3.0.0" + } + }, + "node_modules/@polarityio/pi-checkbox/node_modules/@polarityio/pi-shared": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@polarityio/pi-shared/-/pi-shared-1.2.1.tgz", + "integrity": "sha512-kNemzBeJKcN7QFHdnf1j1L+ImRjIYtRxBDJrlZEvRVKybNlnZwGPjAGyOTRsyAeMJ16HiOECnY/cNMeknEC9/w==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "lit": "^3.0.0" + } + }, + "node_modules/@polarityio/pi-components": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/@polarityio/pi-components/-/pi-components-1.9.3.tgz", + "integrity": "sha512-srGOiNZ7Ql+unJok+pZH2UExhrVg73941ospcFiHvsrCcLuM0tvCDW9LJ1QT+TfZwBaOSnEbEKbSoIeuPm7VOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@polarityio/pi-button": "1.0.4", + "@polarityio/pi-card": "1.2.3", + "@polarityio/pi-checkbox": "1.0.4", + "@polarityio/pi-copy-button": "1.1.0", + "@polarityio/pi-error": "1.1.3", + "@polarityio/pi-external-link": "1.0.4", + "@polarityio/pi-icon": "1.0.3", + "@polarityio/pi-input": "1.0.5", + "@polarityio/pi-key-value": "1.2.3", + "@polarityio/pi-object-table": "1.0.5", + "@polarityio/pi-radio": "1.0.4", + "@polarityio/pi-retry-button": "1.0.1", + "@polarityio/pi-scroller": "1.0.2", + "@polarityio/pi-section-header": "1.0.5", + "@polarityio/pi-select": "1.0.4", + "@polarityio/pi-shared": "1.3.1", + "@polarityio/pi-show-more": "1.1.3", + "@polarityio/pi-spinner": "1.0.4", + "@polarityio/pi-summary-tag": "1.1.0", + "@polarityio/pi-tab": "1.2.2", + "@polarityio/pi-tag": "1.0.6", + "@polarityio/pi-tooltip": "1.0.5" + }, + "peerDependencies": { + "lit": "^3.0.0" + } + }, + "node_modules/@polarityio/pi-copy-button": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@polarityio/pi-copy-button/-/pi-copy-button-1.1.0.tgz", + "integrity": "sha512-EmNUq1DnzrwBqhf9clzuY5cRr0wzuJj8ZWfYaoLrGWGlzhke52XVR3/sRShB4DzH8GkdpYgb7duLM5epyvvQMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@fortawesome/fontawesome-svg-core": "^7.2.0", + "@fortawesome/free-solid-svg-icons": "^7.0.0", + "@polarityio/pi-button": "1.0.4", + "@polarityio/pi-shared": "1.2.1", + "@polarityio/pi-tooltip": "1.0.5" + }, + "peerDependencies": { + "lit": "^3.0.0" + } + }, + "node_modules/@polarityio/pi-copy-button/node_modules/@polarityio/pi-shared": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@polarityio/pi-shared/-/pi-shared-1.2.1.tgz", + "integrity": "sha512-kNemzBeJKcN7QFHdnf1j1L+ImRjIYtRxBDJrlZEvRVKybNlnZwGPjAGyOTRsyAeMJ16HiOECnY/cNMeknEC9/w==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "lit": "^3.0.0" + } + }, + "node_modules/@polarityio/pi-error": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@polarityio/pi-error/-/pi-error-1.1.3.tgz", + "integrity": "sha512-aB7lqiD15axYvGBtUyjhhMduSQG1Myxnb/4K940NfcBS7prfEJbVjXsfJDexGJe1iUGKh5cCfEHeQ2fiQyp/5w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@fortawesome/fontawesome-svg-core": "^7.0.0", + "@fortawesome/free-solid-svg-icons": "^7.0.0", + "@polarityio/pi-button": "1.0.4", + "@polarityio/pi-copy-button": "1.1.0", + "@polarityio/pi-icon": "1.0.3", + "@polarityio/pi-shared": "1.3.1" + }, + "peerDependencies": { + "lit": "^3.0.0" + } + }, + "node_modules/@polarityio/pi-external-link": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@polarityio/pi-external-link/-/pi-external-link-1.0.4.tgz", + "integrity": "sha512-uKlNFq5V0UVnJ5zXI5DqZFvVtjjRMFFa2xaYRRpnOjX5vASlrIEXw/WBc7XM5QmOfEJ4t9yAIveA6d5OGy7I7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@fortawesome/free-solid-svg-icons": "^7.0.0", + "@polarityio/pi-icon": "1.0.3", + "@polarityio/pi-shared": "1.2.1", + "@polarityio/pi-tooltip": "1.0.5" + }, + "peerDependencies": { + "lit": "^3.0.0" + } + }, + "node_modules/@polarityio/pi-external-link/node_modules/@polarityio/pi-shared": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@polarityio/pi-shared/-/pi-shared-1.2.1.tgz", + "integrity": "sha512-kNemzBeJKcN7QFHdnf1j1L+ImRjIYtRxBDJrlZEvRVKybNlnZwGPjAGyOTRsyAeMJ16HiOECnY/cNMeknEC9/w==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "lit": "^3.0.0" + } + }, + "node_modules/@polarityio/pi-icon": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@polarityio/pi-icon/-/pi-icon-1.0.3.tgz", + "integrity": "sha512-NWQurGNSZvOST2lehdaqsc9T5X0mddMNlFPQOgI2YaZJVBwSAahq+j8sL5gUgnLiX/YH4hB/UxuWiWkuOuiPxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@fortawesome/fontawesome-svg-core": "^7.0.0", + "@fortawesome/free-solid-svg-icons": "^7.0.0", + "@polarityio/pi-shared": "1.0.3" + }, + "peerDependencies": { + "lit": "^3.0.0" + } + }, + "node_modules/@polarityio/pi-icon/node_modules/@polarityio/pi-shared": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@polarityio/pi-shared/-/pi-shared-1.0.3.tgz", + "integrity": "sha512-emX5IT4sn1fNJBqABSDeXF+jv/RhTr8b+27jSTnnJ4Jas/APi4rWnZ203dqBI99PhYTV8cvePLabI5KOuKFg/g==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "lit": "^3.0.0" + } + }, + "node_modules/@polarityio/pi-input": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@polarityio/pi-input/-/pi-input-1.0.5.tgz", + "integrity": "sha512-AzKNCwUFuYQz9wfngkIqAFuHUEZF4jWHRLFxCOmRig6I0zx/nI5SQUQorZZPmyDxAnue5ilL/WaWHhXbiAGi9Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@polarityio/pi-shared": "1.2.1" + }, + "peerDependencies": { + "lit": "^3.0.0" + } + }, + "node_modules/@polarityio/pi-input/node_modules/@polarityio/pi-shared": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@polarityio/pi-shared/-/pi-shared-1.2.1.tgz", + "integrity": "sha512-kNemzBeJKcN7QFHdnf1j1L+ImRjIYtRxBDJrlZEvRVKybNlnZwGPjAGyOTRsyAeMJ16HiOECnY/cNMeknEC9/w==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "lit": "^3.0.0" + } + }, + "node_modules/@polarityio/pi-key-value": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@polarityio/pi-key-value/-/pi-key-value-1.2.3.tgz", + "integrity": "sha512-IEIbYMfTKzXcVWDpPJznECfJc1c6kAG5IJeB2HTrrX7X82wQopXX6wzeWJVZ97EYWWbGwO0+wt+IyaHDXAIUIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@fortawesome/free-solid-svg-icons": "^7.0.0", + "@polarityio/pi-icon": "1.0.3", + "@polarityio/pi-shared": "1.2.1", + "@polarityio/pi-tooltip": "1.0.5", + "date-fns": "^4.0.0" + }, + "peerDependencies": { + "lit": "^3.0.0" + } + }, + "node_modules/@polarityio/pi-key-value/node_modules/@polarityio/pi-shared": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@polarityio/pi-shared/-/pi-shared-1.2.1.tgz", + "integrity": "sha512-kNemzBeJKcN7QFHdnf1j1L+ImRjIYtRxBDJrlZEvRVKybNlnZwGPjAGyOTRsyAeMJ16HiOECnY/cNMeknEC9/w==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "lit": "^3.0.0" + } + }, + "node_modules/@polarityio/pi-object-table": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@polarityio/pi-object-table/-/pi-object-table-1.0.5.tgz", + "integrity": "sha512-NzYSXJL/SiGgAdn7SJJU2V/J8l2CbQEMu93pqywGF35MOnZ5bfWvLm4JkIHK7OjjCcFOo6vMLKMtSBrLfw7unw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@polarityio/pi-shared": "1.3.1" + }, + "peerDependencies": { + "lit": "^3.0.0" + } + }, + "node_modules/@polarityio/pi-radio": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@polarityio/pi-radio/-/pi-radio-1.0.4.tgz", + "integrity": "sha512-qhw7s0hJVdwcBH83LnCR02r6mheriI6kCFxVrJHx97/MLSvF70M7gmbDEU3skfGogzHueQ/TUw+o0jWAJRyInA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@polarityio/pi-shared": "1.2.1" + }, + "peerDependencies": { + "lit": "^3.0.0" + } + }, + "node_modules/@polarityio/pi-radio/node_modules/@polarityio/pi-shared": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@polarityio/pi-shared/-/pi-shared-1.2.1.tgz", + "integrity": "sha512-kNemzBeJKcN7QFHdnf1j1L+ImRjIYtRxBDJrlZEvRVKybNlnZwGPjAGyOTRsyAeMJ16HiOECnY/cNMeknEC9/w==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "lit": "^3.0.0" + } + }, + "node_modules/@polarityio/pi-retry-button": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@polarityio/pi-retry-button/-/pi-retry-button-1.0.1.tgz", + "integrity": "sha512-z8DnVCTlmiUyDSFo6jP72qTeZgLPcIPLwKxZTtNZBFYC+BuRP8hXe4GVWXd7kSyymoJze0oHMD8Zl0nAAExmcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@fortawesome/fontawesome-svg-core": "^7.0.0", + "@fortawesome/free-solid-svg-icons": "^7.0.0", + "@polarityio/pi-button": "1.0.4", + "@polarityio/pi-shared": "1.2.0", + "@polarityio/pi-spinner": "1.0.3", + "@polarityio/pi-tooltip": "1.0.4" + }, + "peerDependencies": { + "lit": "^3.0.0" + } + }, + "node_modules/@polarityio/pi-retry-button/node_modules/@polarityio/pi-shared": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@polarityio/pi-shared/-/pi-shared-1.2.0.tgz", + "integrity": "sha512-esRx4+jP9jmKdmXk1ZSyOMbiJ/IxjL+FKUw3iwn3HmX3LaNCBfgIAap/81+tA8Ocgvyr2EjWa1j+FN7ikr+sMg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "lit": "^3.0.0" + } + }, + "node_modules/@polarityio/pi-retry-button/node_modules/@polarityio/pi-spinner": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@polarityio/pi-spinner/-/pi-spinner-1.0.3.tgz", + "integrity": "sha512-TLwb+zzoLiS5sb858RZgcWJXtgZ/AnQ/W5hncPrvuHJLwltGZlkOX+juyQPeqtucWD/p7PexDTKoxHfCOyfbiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@polarityio/pi-shared": "1.0.3" + }, + "peerDependencies": { + "lit": "^3.0.0" + } + }, + "node_modules/@polarityio/pi-retry-button/node_modules/@polarityio/pi-spinner/node_modules/@polarityio/pi-shared": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@polarityio/pi-shared/-/pi-shared-1.0.3.tgz", + "integrity": "sha512-emX5IT4sn1fNJBqABSDeXF+jv/RhTr8b+27jSTnnJ4Jas/APi4rWnZ203dqBI99PhYTV8cvePLabI5KOuKFg/g==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "lit": "^3.0.0" + } + }, + "node_modules/@polarityio/pi-retry-button/node_modules/@polarityio/pi-tooltip": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@polarityio/pi-tooltip/-/pi-tooltip-1.0.4.tgz", + "integrity": "sha512-/q2b3wobIcmmE+EWzZAH995fmbsRZ7k3Usp/k0xadDXO/dU7kwgKdwat+P1rJ4ZNtvV/I9i9mDPsdimuQO6Tow==", + "dev": true, + "license": "MIT", + "dependencies": { + "@floating-ui/dom": "^1.7.6", + "@polarityio/pi-shared": "1.0.3" + }, + "peerDependencies": { + "lit": "^3.0.0" + } + }, + "node_modules/@polarityio/pi-retry-button/node_modules/@polarityio/pi-tooltip/node_modules/@polarityio/pi-shared": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@polarityio/pi-shared/-/pi-shared-1.0.3.tgz", + "integrity": "sha512-emX5IT4sn1fNJBqABSDeXF+jv/RhTr8b+27jSTnnJ4Jas/APi4rWnZ203dqBI99PhYTV8cvePLabI5KOuKFg/g==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "lit": "^3.0.0" + } + }, + "node_modules/@polarityio/pi-scroller": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@polarityio/pi-scroller/-/pi-scroller-1.0.2.tgz", + "integrity": "sha512-d93nkdLxpiTcxMlaqO9VtqLiRsSor3JzYzIj7g8+5tiYWyR60q9TVHYiBrhSkgthp3MrfSqmbaYKtvraDR8HsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@polarityio/pi-shared": "1.3.1" + }, + "peerDependencies": { + "lit": "^3.0.0" + } + }, + "node_modules/@polarityio/pi-section-header": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@polarityio/pi-section-header/-/pi-section-header-1.0.5.tgz", + "integrity": "sha512-pjYHqnISsEchARTfjC72G9WnTHN4mdxW0u6X3dRiX+1EC82G8zFuNc4XoLgxb015hEYSn5xZGlsKkKJ5EfohLA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@fortawesome/fontawesome-svg-core": "^7.0.0", + "@fortawesome/free-solid-svg-icons": "^7.0.0", + "@polarityio/pi-shared": "1.3.1" + }, + "peerDependencies": { + "lit": "^3.0.0" + } + }, + "node_modules/@polarityio/pi-select": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@polarityio/pi-select/-/pi-select-1.0.4.tgz", + "integrity": "sha512-Frl7QLWDgQqUs83LABiEiKzyFjEVtrBRLJjMD1CotC1KcNluIi3yChi1KRZhBLfZr+pa9K0iTQsX6/AGP7/+xg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@floating-ui/dom": "^1.7.6", + "@polarityio/pi-shared": "1.0.3" + }, + "peerDependencies": { + "lit": "^3.0.0" + } + }, + "node_modules/@polarityio/pi-select/node_modules/@polarityio/pi-shared": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@polarityio/pi-shared/-/pi-shared-1.0.3.tgz", + "integrity": "sha512-emX5IT4sn1fNJBqABSDeXF+jv/RhTr8b+27jSTnnJ4Jas/APi4rWnZ203dqBI99PhYTV8cvePLabI5KOuKFg/g==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "lit": "^3.0.0" + } + }, + "node_modules/@polarityio/pi-shared": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@polarityio/pi-shared/-/pi-shared-1.3.1.tgz", + "integrity": "sha512-TXvKZBmFj9rqqZtSMImYLNVFtaYTCVJ69rPxrYBbOSOkoUvPWscCKP9zvT9xJHMaD7tjocI0gv7BvDc3H7grmg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "lit": "^3.0.0" + } + }, + "node_modules/@polarityio/pi-show-more": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@polarityio/pi-show-more/-/pi-show-more-1.1.3.tgz", + "integrity": "sha512-fZhwYRaw03tBEM3u+R/Y7wOcwnaq48ectu2GhE5ygpXNLx5dfyHg5huYKD9KX+JF6cUuOMPE4ogcGvGNaWcIWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@polarityio/pi-button": "1.0.4", + "@polarityio/pi-shared": "1.3.1" + }, + "peerDependencies": { + "lit": "^3.0.0" + } + }, + "node_modules/@polarityio/pi-spinner": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@polarityio/pi-spinner/-/pi-spinner-1.0.4.tgz", + "integrity": "sha512-HsaWqOkzTMe6ZqaBZ3XBkv27BJ5dn5PJrgtOwShmVFQDDx0AY0Ijk8w2rVk6DaaYnBotCeTtw53gUZIR6X0Gow==", + "dev": true, + "license": "MIT", + "dependencies": { + "@polarityio/pi-shared": "1.2.1" + }, + "peerDependencies": { + "lit": "^3.0.0" + } + }, + "node_modules/@polarityio/pi-spinner/node_modules/@polarityio/pi-shared": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@polarityio/pi-shared/-/pi-shared-1.2.1.tgz", + "integrity": "sha512-kNemzBeJKcN7QFHdnf1j1L+ImRjIYtRxBDJrlZEvRVKybNlnZwGPjAGyOTRsyAeMJ16HiOECnY/cNMeknEC9/w==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "lit": "^3.0.0" + } + }, + "node_modules/@polarityio/pi-summary-tag": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@polarityio/pi-summary-tag/-/pi-summary-tag-1.1.0.tgz", + "integrity": "sha512-aR+AgZ9Vcro7u+TQYvM0qwhWxEXSGrkAXEmdZJaZPiAfVd8TXnwjWwZnyF/jF3ttuU0RPXXsgdhk6t8ijy3iKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@fortawesome/fontawesome-svg-core": "^7.0.0", + "@fortawesome/free-solid-svg-icons": "^7.0.0", + "@polarityio/pi-shared": "1.2.1" + }, + "peerDependencies": { + "lit": "^3.0.0" + } + }, + "node_modules/@polarityio/pi-summary-tag/node_modules/@polarityio/pi-shared": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@polarityio/pi-shared/-/pi-shared-1.2.1.tgz", + "integrity": "sha512-kNemzBeJKcN7QFHdnf1j1L+ImRjIYtRxBDJrlZEvRVKybNlnZwGPjAGyOTRsyAeMJ16HiOECnY/cNMeknEC9/w==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "lit": "^3.0.0" + } + }, + "node_modules/@polarityio/pi-tab": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@polarityio/pi-tab/-/pi-tab-1.2.2.tgz", + "integrity": "sha512-tqLT0kXTPmz/e0HWw9S3ogQ5KvN+4NZX5AYY0oW3hI4qtMRTJi/nilHCpgqrrsPPJlYRCWO8UkvBLMgrzDRMcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@polarityio/pi-shared": "1.3.1" + }, + "peerDependencies": { + "lit": "^3.0.0" + } + }, + "node_modules/@polarityio/pi-tag": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@polarityio/pi-tag/-/pi-tag-1.0.6.tgz", + "integrity": "sha512-H/Td6m/n1MOtafmskWgCDhdak2BsuTubpWYzS6OhvxcJcDTQ/b8g2bKG9F/6kFqQ0vYG0rG8mQzHm5E3pFVBrw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@fortawesome/fontawesome-svg-core": "^7.0.0", + "@fortawesome/free-solid-svg-icons": "^7.0.0", + "@polarityio/pi-icon": "1.0.3", + "@polarityio/pi-shared": "1.3.1" + }, + "peerDependencies": { + "lit": "^3.0.0" + } + }, + "node_modules/@polarityio/pi-tooltip": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@polarityio/pi-tooltip/-/pi-tooltip-1.0.5.tgz", + "integrity": "sha512-Ii0Y+j4ji0LGPKBSHZAfdhD5gvrvs++8RStaY591nan9cLDzSC9H3v8PTtUE2pKXsoVyjAk5xmbpLP539VAGKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@floating-ui/dom": "^1.7.6", + "@polarityio/pi-shared": "1.2.1" + }, + "peerDependencies": { + "lit": "^3.0.0" + } + }, + "node_modules/@polarityio/pi-tooltip/node_modules/@polarityio/pi-shared": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@polarityio/pi-shared/-/pi-shared-1.2.1.tgz", + "integrity": "sha512-kNemzBeJKcN7QFHdnf1j1L+ImRjIYtRxBDJrlZEvRVKybNlnZwGPjAGyOTRsyAeMJ16HiOECnY/cNMeknEC9/w==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "lit": "^3.0.0" + } + }, + "node_modules/@polarityio/vite-plugin-icl": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@polarityio/vite-plugin-icl/-/vite-plugin-icl-1.0.1.tgz", + "integrity": "sha512-jKYwWlhENNOj4aQYqMcvnT/txKN/UdLW8F+tudjHTpm83TYE24JnpUVn7y6KwJ5a2w38cuPloGooymFCQs5u/A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=24" + }, + "peerDependencies": { + "vite": ">=5" + } + }, + "node_modules/@polka/url": { + "version": "1.0.0-next.29", + "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.29.tgz", + "integrity": "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==", + "dev": true, + "license": "MIT" + }, + "node_modules/@postman/form-data": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/@postman/form-data/-/form-data-3.1.1.tgz", "integrity": "sha512-vjh8Q2a8S6UCm/KKs31XFJqEEgmbjBmpPNVV2eVav6905wyFAwaUOBGA1NPBI4ERH9MMZc6w0umFgM6WbEPMdg==", - "requires": { + "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" } }, - "@postman/tough-cookie": { + "node_modules/@postman/tough-cookie": { "version": "4.1.3-postman.1", "resolved": "https://registry.npmjs.org/@postman/tough-cookie/-/tough-cookie-4.1.3-postman.1.tgz", "integrity": "sha512-txpgUqZOnWYnUHZpHjkfb0IwVH4qJmyq77pPnJLlfhMtdCLMFTEeQHlzQiK906aaNCe4NEB5fGJHo9uzGbFMeA==", - "requires": { + "dependencies": { "psl": "^1.1.33", "punycode": "^2.1.1", "universalify": "^0.2.0", "url-parse": "^1.5.3" + }, + "engines": { + "node": ">=6" } }, - "@postman/tunnel-agent": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/@postman/tunnel-agent/-/tunnel-agent-0.6.3.tgz", - "integrity": "sha512-k57fzmAZ2PJGxfOA4SGR05ejorHbVAa/84Hxh/2nAztjNXc4ZjOm9NUIk6/Z6LCrBvJZqjRZbN8e/nROVUPVdg==", - "requires": { + "node_modules/@postman/tunnel-agent": { + "version": "0.6.8", + "resolved": "https://registry.npmjs.org/@postman/tunnel-agent/-/tunnel-agent-0.6.8.tgz", + "integrity": "sha512-2U42SmZW5G+suEcS++zB94sBWNO4qD4bvETGFRFDTqSpYl5ksfjcPqzYpgQgXgUmb6dfz+fAGbkcRamounGm0w==", + "license": "Apache-2.0", + "dependencies": { "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.3.tgz", + "integrity": "sha512-x35CNW/ANXG3hE/EZpRU8MXX1JDN86hBb2wMGAtltkz7pc6cxgjpy1OMMfDosOQ+2hWqIkag/fGok1Yady9nGw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.3.tgz", + "integrity": "sha512-xw3xtkDApIOGayehp2+Rz4zimfkaX65r4t47iy+ymQB2G4iJCBBfj0ogVg5jpvjpn8UWn/+q9tprxleYeNp3Hw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.3.tgz", + "integrity": "sha512-vo6Y5Qfpx7/5EaamIwi0WqW2+zfiusVihKatLvtN1VFVy3D13uERk/6gZLU1UiHRL6fDXqj/ELIeVRGnvcTE1g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.3.tgz", + "integrity": "sha512-D+0QGcZhBzTN82weOnsSlY7V7+RMmPuF1CkbxyMAGE8+ZHeUjyb76ZiWmBlCu//AQQONvxcqRbwZTajZKqjuOw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.3.tgz", + "integrity": "sha512-6HnvHCT7fDyj6R0Ph7A6x8dQS/S38MClRWeDLqc0MdfWkxjiu1HSDYrdPhqSILzjTIC/pnXbbJbo+ft+gy/9hQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.3.tgz", + "integrity": "sha512-KHLgC3WKlUYW3ShFKnnosZDOJ0xjg9zp7au3sIm2bs/tGBeC2ipmvRh/N7JKi0t9Ue20C0dpEshi8WUubg+cnA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.3.tgz", + "integrity": "sha512-DV6fJoxEYWJOvaZIsok7KrYl0tPvga5OZ2yvKHNNYyk/2roMLqQAbGhr78EQ5YhHpnhLKJD3S1WFusAkmUuV5g==", + "cpu": [ + "arm" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.3.tgz", + "integrity": "sha512-mQKoJAzvuOs6F+TZybQO4GOTSMUu7v0WdxEk24krQ/uUxXoPTtHjuaUuPmFhtBcM4K0ons8nrE3JyhTuCFtT/w==", + "cpu": [ + "arm" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.3.tgz", + "integrity": "sha512-Whjj2qoiJ6+OOJMGptTYazaJvjOJm+iKHpXQM1P3LzGjt7Ff++Tp7nH4N8J/BUA7R9IHfDyx4DJIflifwnbmIA==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.3.tgz", + "integrity": "sha512-4YTNHKqGng5+yiZt3mg77nmyuCfmNfX4fPmyUapBcIk+BdwSwmCWGXOUxhXbBEkFHtoN5boLj/5NON+u5QC9tg==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.3.tgz", + "integrity": "sha512-SU3kNlhkpI4UqlUc2VXPGK9o886ZsSeGfMAX2ba2b8DKmMXq4AL7KUrkSWVbb7koVqx41Yczx6dx5PNargIrEA==", + "cpu": [ + "loong64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.3.tgz", + "integrity": "sha512-6lDLl5h4TXpB1mTf2rQWnAk/LcXrx9vBfu/DT5TIPhvMhRWaZ5MxkIc8u4lJAmBo6klTe1ywXIUHFjylW505sg==", + "cpu": [ + "loong64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.3.tgz", + "integrity": "sha512-BMo8bOw8evlup/8G+cj5xWtPyp93xPdyoSN16Zy90Q2QZ0ZYRhCt6ZJSwbrRzG9HApFabjwj2p25TUPDWrhzqQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.3.tgz", + "integrity": "sha512-E0L8X1dZN1/Rph+5VPF6Xj2G7JJvMACVXtamTJIDrVI44Y3K+G8gQaMEAavbqCGTa16InptiVrX6eM6pmJ+7qA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.3.tgz", + "integrity": "sha512-oZJ/WHaVfHUiRAtmTAeo3DcevNsVvH8mbvodjZy7D5QKvCefO371SiKRpxoDcCxB3PTRTLayWBkvmDQKTcX/sw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.3.tgz", + "integrity": "sha512-Dhbyh7j9FybM3YaTgaHmVALwA8AkUwTPccyCQ79TG9AJUsMQqgN1DDEZNr4+QUfwiWvLDumW5vdwzoeUF+TNxQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.3.tgz", + "integrity": "sha512-cJd1X5XhHHlltkaypz1UcWLA8AcoIi1aWhsvaWDskD1oz2eKCypnqvTQ8ykMNI0RSmm7NkTdSqSSD7zM0xa6Ig==", + "cpu": [ + "s390x" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.3.tgz", + "integrity": "sha512-DAZDBHQfG2oQuhY7mc6I3/qB4LU2fQCjRvxbDwd/Jdvb9fypP4IJ4qmtu6lNjes6B531AI8cg1aKC2di97bUxA==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.3.tgz", + "integrity": "sha512-cRxsE8c13mZOh3vP+wLDxpQBRrOHDIGOWyDL93Sy0Ga8y515fBcC2pjUfFwUe5T7tqvTvWbCpg1URM/AXdWIXA==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.3.tgz", + "integrity": "sha512-QaWcIgRxqEdQdhJqW4DJctsH6HCmo5vHxY0krHSX4jMtOqfzC+dqDGuHM87bu4H8JBeibWx7jFz+h6/4C8wA5Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.3.tgz", + "integrity": "sha512-AaXwSvUi3QIPtroAUw1t5yHGIyqKEXwH54WUocFolZhpGDruJcs8c+xPNDRn4XiQsS7MEwnYsHW2l0MBLDMkWg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.3.tgz", + "integrity": "sha512-65LAKM/bAWDqKNEelHlcHvm2V+Vfb8C6INFxQXRHCvaVN1rJfwr4NvdP4FyzUaLqWfaCGaadf6UbTm8xJeYfEg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.3.tgz", + "integrity": "sha512-EEM2gyhBF5MFnI6vMKdX1LAosE627RGBzIoGMdLloPZkXrUN0Ckqgr2Qi8+J3zip/8NVVro3/FjB+tjhZUgUHA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.3.tgz", + "integrity": "sha512-E5Eb5H/DpxaoXH++Qkv28RcUJboMopmdDUALBczvHMf7hNIxaDZqwY5lK12UK1BHacSmvupoEWGu+n993Z0y1A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.3.tgz", + "integrity": "sha512-hPt/bgL5cE+Qp+/TPHBqptcAgPzgj46mPcg/16zNUmbQk0j+mOEQV/+Lqu8QRtDV3Ek95Q6FeFITpuhl6OTsAA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/chai": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", + "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/deep-eql": "*", + "assertion-error": "^2.0.1" + } + }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/esrecurse": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz", + "integrity": "sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/lodash": { + "version": "4.17.24", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.24.tgz", + "integrity": "sha512-gIW7lQLZbue7lRSWEFql49QJJWThrTFFeIMJdp3eH4tKoxm1OvEPg02rm4wCCSHS0cL3/Fizimb35b7k8atwsQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "25.7.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.7.0.tgz", + "integrity": "sha512-z+pdZyxE+RTQE9AcboAZCb4otwcrvgHD+GlBpPgn0emDVt0ohrTMhAwlr2Wd9nZ+nihhYFxO2pThz3C5qSu2Eg==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.21.0" + } + }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/xml2js": { + "version": "0.4.14", + "resolved": "https://registry.npmjs.org/@types/xml2js/-/xml2js-0.4.14.tgz", + "integrity": "sha512-4YnrRemBShWRO2QjvUin8ESA41rH+9nQGLUGZV/1IDhi3SL9OhdpNC/MrulTWuptXKwhx/aDxE7toV0f/ypIXQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.59.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.59.3.tgz", + "integrity": "sha512-PwFvSKsXGShKGW6n5bZOhGHEcCZXM8HofLK9fNsEwZXzFRjoY+XT1Vsf1zgyXdwTr0ZYz1/2tkZ0DBTT9jZjhw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.59.3", + "@typescript-eslint/type-utils": "8.59.3", + "@typescript-eslint/utils": "8.59.3", + "@typescript-eslint/visitor-keys": "8.59.3", + "ignore": "^7.0.5", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.59.3", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.59.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.59.3.tgz", + "integrity": "sha512-HPwA+hVkfcriajbNvTmZv4VRauibay+cWArYUYq7u7W7PmGShMxbPxLvrwDme55a6d5alG3nrYfhyJ/G28XlLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.59.3", + "@typescript-eslint/types": "8.59.3", + "@typescript-eslint/typescript-estree": "8.59.3", + "@typescript-eslint/visitor-keys": "8.59.3", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.59.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.59.3.tgz", + "integrity": "sha512-ECiUWa/KYRGDFUqTNehaRgzDshnJfkTABJxVemHk4ko22gcr0ukloKjWvyQ64g8YCV/UI47kN1dbmjf/GaQYng==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.59.3", + "@typescript-eslint/types": "^8.59.3", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.59.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.59.3.tgz", + "integrity": "sha512-t2LvZnoEfzKtnPjgeEu41xw5gxq9mQVfYy4OoZ4Vlt0sk3JwxmhCca/AR7DwOiHrjWgjAj6as4AhRLKSDfvZIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.59.3", + "@typescript-eslint/visitor-keys": "8.59.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.59.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.59.3.tgz", + "integrity": "sha512-PcIJHjmaREXLgIAIzLnSY9VucEzz8FKXsRgFa1DmdGCK/5tJpW03TKJF01Q6VZd1lLdz2sIKPWaDUZN9dp//dw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.59.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.59.3.tgz", + "integrity": "sha512-g71d8QD8UaiHGvrJwyIS1hCX5r63w6Jll+4VEYhEAHXTDIqX1JgxhTAbEHtKntL9kuc4jRo7/GWw5xfCepSccQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.59.3", + "@typescript-eslint/typescript-estree": "8.59.3", + "@typescript-eslint/utils": "8.59.3", + "debug": "^4.4.3", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.59.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.59.3.tgz", + "integrity": "sha512-ePFoH0g4ludssdRFqqDxQePCxU4WQyRa9+XVwjm7yLn0FKhMeoetC+qBEEI1Eyb1pGSDveTIT09Bvw2WhlGayg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.59.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.59.3.tgz", + "integrity": "sha512-CbRjVRAf7Lr9Kr8RopKcbY45p2VfmmHrm0ygOCYFi7oU8q19m0Fs/6iHS7kNOmwpp+ob07ZVcAqlxUod9lYdmg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.59.3", + "@typescript-eslint/tsconfig-utils": "8.59.3", + "@typescript-eslint/types": "8.59.3", + "@typescript-eslint/visitor-keys": "8.59.3", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.59.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.59.3.tgz", + "integrity": "sha512-JAvT14goBzRzzzZyqq3P9BLArIxTtQURUtFgQ/V7FO+eU+Gg6ES+5ymOPP1wRxXcxAYeivCk4uS3jCKWI1K8Zg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.59.3", + "@typescript-eslint/types": "8.59.3", + "@typescript-eslint/typescript-estree": "8.59.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.59.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.59.3.tgz", + "integrity": "sha512-f1UQF7ggd42YiwI5wGrRaPsa+P0CINBlrkLPmGfpq/u/I/oVtecoEIfFR9ag/oa1sLOsRNZ6xehf6qMZhQGBDg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.59.3", + "eslint-visitor-keys": "^5.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@vitest/browser": { + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/@vitest/browser/-/browser-4.1.6.tgz", + "integrity": "sha512-ynsspTubXGSpa58JFJ24xIQt4z4A25epSbugEyaTmmrV1//Wec9EgE/LtoaC6yxUrXi5P7erGHRrkdZIHaVQuA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@blazediff/core": "1.9.1", + "@vitest/mocker": "4.1.6", + "@vitest/utils": "4.1.6", + "magic-string": "^0.30.21", + "pngjs": "^7.0.0", + "sirv": "^3.0.2", + "tinyrainbow": "^3.1.0", + "ws": "^8.19.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "vitest": "4.1.6" + } + }, + "node_modules/@vitest/browser-playwright": { + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/@vitest/browser-playwright/-/browser-playwright-4.1.6.tgz", + "integrity": "sha512-4csoeyl/qwHyxU2zNL0++WaoDr8YJDXOQPwWPNJoTZ+QzcdO3INYKgF5Zfz730Io7zbkuv914aZmfQ+QE+1Hvw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/browser": "4.1.6", + "@vitest/mocker": "4.1.6", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "playwright": "*", + "vitest": "4.1.6" + }, + "peerDependenciesMeta": { + "playwright": { + "optional": false + } + } + }, + "node_modules/@vitest/expect": { + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.6.tgz", + "integrity": "sha512-7EHDquPthALSV0jhhjgEW8FXaviMx7rSqu8W6oqCoAuOhKov814P99QDV1pxMA3QPv21YudvJngIhjrNI4opLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.1.0", + "@types/chai": "^5.2.2", + "@vitest/spy": "4.1.6", + "@vitest/utils": "4.1.6", + "chai": "^6.2.2", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/mocker": { + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.6.tgz", + "integrity": "sha512-MCFc63czMjEInOlcY2cpQCvCN+KgbAn+60xu9cMgP4sKaLC5JNAKw7JH8QdAnoAC88hW1IiSNZ+GgVXlN1UcMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "4.1.6", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.21" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/pretty-format": { + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.6.tgz", + "integrity": "sha512-h5SxD/IzNhZYnrSZRsUZQIC+vD0GY8cUvq0iwsmkFKixRCKLLWqCXa/FIQ4S1R+sI+PGoojkHsdNrbZiM9Qpgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.6.tgz", + "integrity": "sha512-nOPCmn2+yD0ZNmKdsXGv/UxMMWbMuKeD6GyYncNwdkYDxpQvrPSKYj2rWuDjC2Y4b6w6hjip5dBKFzEUuZe3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "4.1.6", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.6.tgz", + "integrity": "sha512-YhsdE6xAVfTDmzjxL2ZDUvjj+ZsgyOKe+TdQzqkD72wIOmHka8NuGQ6NpTNZv9D2Z63fbwWKJPeVpEw4EQgYxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.1.6", + "@vitest/utils": "4.1.6", + "magic-string": "^0.30.21", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.6.tgz", + "integrity": "sha512-JFKxMx6udhwKh/Ldo270e17QX710vgunMkuPAvXjHSvC6oqLWAHhVhjg/I71q0u0CBSErIODV1Kjv0FQNSWjdg==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.6.tgz", + "integrity": "sha512-FxIY+U81R3LGKCxaHHFRQ5+g6/iRgGLmeHWdp2Amj4ljQRrEIWHmZyDfDYBRZlpyqA7qKxtS9DD1dhk8RnRIVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.1.6", + "convert-source-map": "^2.0.0", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/acorn": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "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, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "license": "MIT", + "engines": { + "node": ">= 14" } }, - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "requires": { + "node_modules/ajv": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", + "dev": true, + "license": "MIT", + "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" } }, - "asn1": { + "node_modules/asn1": { "version": "0.2.6", "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", - "requires": { + "license": "MIT", + "dependencies": { "safer-buffer": "~2.1.0" } }, - "assert-plus": { + "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==" + "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", + "license": "MIT", + "engines": { + "node": ">=0.8" + } }, - "asynckit": { + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, - "aws-sign2": { + "node_modules/aws-sign2": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==" + "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==", + "engines": { + "node": "*" + } }, - "aws4": { + "node_modules/aws4": { "version": "1.12.0", "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.12.0.tgz", "integrity": "sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==" }, - "base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" + "node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } }, - "bcrypt-pbkdf": { + "node_modules/bcrypt-pbkdf": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", - "requires": { + "license": "BSD-3-Clause", + "dependencies": { "tweetnacl": "^0.14.3" } }, - "bluebird": { + "node_modules/bluebird": { "version": "2.11.0", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-2.11.0.tgz", "integrity": "sha512-UfFSr22dmHPQqPP9XWHRhq+gWnHCYguQGkXQlbyPtW5qTnhFWA8/iXg765tH0cAjy7l/zPJ1aBTO0g5XgA7kvQ==" }, - "brotli": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/brotli/-/brotli-1.3.3.tgz", - "integrity": "sha512-oTKjJdShmDuGW94SyyaoQvAjf30dZaHnjJ8uAF+u2/vGJkJbJPJAT1gDiOJP5v1Zb6f9KEyW/1HpuaWIXtGHPg==", - "requires": { - "base64-js": "^1.1.2" + "node_modules/brace-expansion": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", + "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "caseless": { + "node_modules/caseless": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==" }, - "combined-stream": { + "node_modules/chai": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", + "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "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==", - "requires": { + "dependencies": { "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" } }, - "core-util-is": { + "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, + "license": "MIT" + }, + "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==" + "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } }, - "dashdash": { + "node_modules/dashdash": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", - "requires": { + "license": "MIT", + "dependencies": { "assert-plus": "^1.0.0" + }, + "engines": { + "node": ">=0.10" } }, - "delayed-stream": { + "node_modules/date-fns": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.1.0.tgz", + "integrity": "sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/kossnocorp" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/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, + "license": "MIT" + }, + "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } }, - "ecc-jsbn": { + "node_modules/ecc-jsbn": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", - "requires": { + "license": "MIT", + "dependencies": { "jsbn": "~0.1.0", "safer-buffer": "^2.1.0" } }, - "extend": { + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-module-lexer": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.1.0.tgz", + "integrity": "sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esbuild": { + "version": "0.27.7", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.7.tgz", + "integrity": "sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.7", + "@esbuild/android-arm": "0.27.7", + "@esbuild/android-arm64": "0.27.7", + "@esbuild/android-x64": "0.27.7", + "@esbuild/darwin-arm64": "0.27.7", + "@esbuild/darwin-x64": "0.27.7", + "@esbuild/freebsd-arm64": "0.27.7", + "@esbuild/freebsd-x64": "0.27.7", + "@esbuild/linux-arm": "0.27.7", + "@esbuild/linux-arm64": "0.27.7", + "@esbuild/linux-ia32": "0.27.7", + "@esbuild/linux-loong64": "0.27.7", + "@esbuild/linux-mips64el": "0.27.7", + "@esbuild/linux-ppc64": "0.27.7", + "@esbuild/linux-riscv64": "0.27.7", + "@esbuild/linux-s390x": "0.27.7", + "@esbuild/linux-x64": "0.27.7", + "@esbuild/netbsd-arm64": "0.27.7", + "@esbuild/netbsd-x64": "0.27.7", + "@esbuild/openbsd-arm64": "0.27.7", + "@esbuild/openbsd-x64": "0.27.7", + "@esbuild/openharmony-arm64": "0.27.7", + "@esbuild/sunos-x64": "0.27.7", + "@esbuild/win32-arm64": "0.27.7", + "@esbuild/win32-ia32": "0.27.7", + "@esbuild/win32-x64": "0.27.7" + } + }, + "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, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.3.0.tgz", + "integrity": "sha512-XbEXaRva5cF0ZQB8w6MluHA0kZZfV2DuCMJ3ozyEOHLwDpZX2Lmm/7Pp0xdJmI0GL1W05VH5VwIFHEm1Vcw2gw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.2", + "@eslint/config-array": "^0.23.5", + "@eslint/config-helpers": "^0.5.5", + "@eslint/core": "^1.2.1", + "@eslint/plugin-kit": "^0.7.1", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.14.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^9.1.2", + "eslint-visitor-keys": "^5.0.1", + "espree": "^11.2.0", + "esquery": "^1.7.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "minimatch": "^10.2.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-config-prettier": { + "version": "10.1.8", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.8.tgz", + "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", + "dev": true, + "license": "MIT", + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "funding": { + "url": "https://opencollective.com/eslint-config-prettier" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-scope": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.2.tgz", + "integrity": "sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@types/esrecurse": "^4.3.1", + "@types/estree": "^1.0.8", + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-11.2.0.tgz", + "integrity": "sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.16.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^5.0.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "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, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "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, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.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, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expect-type": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", + "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" }, - "extsprintf": { + "node_modules/extsprintf": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==" + "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==", + "engines": [ + "node >=0.6.0" + ], + "license": "MIT" }, - "fast-deep-equal": { + "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==" + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true }, - "fast-json-stable-stringify": { + "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==" + "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, + "license": "MIT" + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } }, - "forever-agent": { + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "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, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", + "dev": true, + "license": "ISC" + }, + "node_modules/forever-agent": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==" + "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==", + "engines": { + "node": "*" + } + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } }, - "getpass": { + "node_modules/getpass": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", - "requires": { + "license": "MIT", + "dependencies": { "assert-plus": "^1.0.0" } }, - "har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==" - }, - "har-validator": { - "version": "5.1.5", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", - "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", - "requires": { - "ajv": "^6.12.3", - "har-schema": "^2.0.0" - } - }, - "http-signature": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.3.6.tgz", - "integrity": "sha512-3adrsD6zqo4GsTqtO7FyrejHNv+NgiIfAfv68+jVlFmSr9OGy7zrxONceFRLKvnnZA5jbxQBX1u9PpB6Wi32Gw==", - "requires": { + "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, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.3.tgz", + "integrity": "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/http-signature": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.4.0.tgz", + "integrity": "sha512-G5akfn7eKbpDN+8nPS/cb57YeA1jLTVxjpCj7tmm3QKPdyDy7T+qSC40e9ptydSWvkwjSXw1VbkpyEm39ukeAg==", + "license": "MIT", + "dependencies": { "assert-plus": "^1.0.0", "jsprim": "^2.0.2", - "sshpk": "^1.14.1" + "sshpk": "^1.18.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/ip-address": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.2.0.tgz", + "integrity": "sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA==", + "license": "MIT", + "engines": { + "node": ">= 12" } }, - "is-typedarray": { + "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, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "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, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==" }, - "isstream": { + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/isstream": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==" }, - "jsbn": { + "node_modules/jsbn": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==" + "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", + "license": "MIT" }, - "json-schema": { + "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, + "license": "MIT" + }, + "node_modules/json-schema": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", - "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==" + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", + "license": "(AFL-2.1 OR BSD-3-Clause)" }, - "json-schema-traverse": { + "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==" + "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, + "license": "MIT" }, - "json-stringify-safe": { + "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==" }, - "jsprim": { + "node_modules/jsprim": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-2.0.2.tgz", "integrity": "sha512-gqXddjPqQ6G40VdnI6T6yObEC+pDNvyP95wdQhkWkg7crHH3km5qP1FsOXEkzEQwnz6gz5qGTn1c2Y52wP3OyQ==", - "requires": { + "engines": [ + "node >=0.6.0" + ], + "license": "MIT", + "dependencies": { "assert-plus": "1.0.0", "extsprintf": "1.3.0", "json-schema": "0.4.0", "verror": "1.10.0" } }, - "lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + "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, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "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, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lit": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/lit/-/lit-3.3.2.tgz", + "integrity": "sha512-NF9zbsP79l4ao2SNrH3NkfmFgN/hBYSQo90saIVI1o5GpjAdCPVstVzO1MrLOakHoEhYkrtRjPK6Ob521aoYWQ==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@lit/reactive-element": "^2.1.0", + "lit-element": "^4.2.0", + "lit-html": "^3.3.0" + } + }, + "node_modules/lit-element": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/lit-element/-/lit-element-4.2.2.tgz", + "integrity": "sha512-aFKhNToWxoyhkNDmWZwEva2SlQia+jfG0fjIWV//YeTaWrVnOxD89dPKfigCUspXFmjzOEUQpOkejH5Ly6sG0w==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@lit-labs/ssr-dom-shim": "^1.5.0", + "@lit/reactive-element": "^2.1.0", + "lit-html": "^3.3.0" + } + }, + "node_modules/lit-html": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/lit-html/-/lit-html-3.3.2.tgz", + "integrity": "sha512-Qy9hU88zcmaxBXcc10ZpdK7cOLXvXpRoBxERdtqV9QOrfpMZZ6pSYP91LhpPtap3sFMUiL7Tw2RImbe0Al2/kw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@types/trusted-types": "^2.0.2" + } + }, + "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, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", + "license": "MIT" + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } }, - "mime-db": { + "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==" + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } }, - "mime-types": { + "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==", - "requires": { + "dependencies": { "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mrmime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", + "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.12", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", + "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, - "oauth-sign": { + "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, + "license": "MIT" + }, + "node_modules/oauth-sign": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "engines": { + "node": "*" + } }, - "performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==" - }, - "postman-request": { - "version": "2.88.1-postman.33", - "resolved": "https://registry.npmjs.org/postman-request/-/postman-request-2.88.1-postman.33.tgz", - "integrity": "sha512-uL9sCML4gPH6Z4hreDWbeinKU0p0Ke261nU7OvII95NU22HN6Dk7T/SaVPaj6T4TsQqGKIFw6/woLZnH7ugFNA==", - "requires": { + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/obug": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz", + "integrity": "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==", + "dev": true, + "funding": [ + "https://github.com/sponsors/sxzz", + "https://opencollective.com/debug" + ], + "license": "MIT" + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "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.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "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, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "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, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/playwright": { + "version": "1.60.0", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.60.0.tgz", + "integrity": "sha512-hheHdokM8cdqCb0lcE3s+zT4t4W+vvjpGxsZlDnikarzx8tSzMebh3UiFtgqwFwnTnjYQcsyMF8ei2mCO/tpeA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.60.0" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.60.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.60.0.tgz", + "integrity": "sha512-9bW6zvX/m0lEbgTKJ6YppOKx8H3VOPBMOCFh2irXFOT4BbHgrx5hPjwJYLT40Lu+4qtD36qKc/Hn56StUW57IA==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/pngjs": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-7.0.0.tgz", + "integrity": "sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.19.0" + } + }, + "node_modules/polarity-integration-utils": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/polarity-integration-utils/-/polarity-integration-utils-4.1.0.tgz", + "integrity": "sha512-WcrOJ8CQ/FPcfk5C37QeagOVdzEDE/CumxwVeBoDkCKsOtByDe4u2zcQGwP+o0Ws4xnlfbNVa5lOKgL6KZgpWw==", + "license": "MIT", + "dependencies": { + "lodash": "^4.18.1", + "postman-request": "^2.88.1-postman.48" + }, + "peerDependencies": { + "@polarityio/integration-types": ">=1.1.0" + } + }, + "node_modules/postcss": { + "version": "8.5.14", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.14.tgz", + "integrity": "sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postman-request": { + "version": "2.88.1-postman.48", + "resolved": "https://registry.npmjs.org/postman-request/-/postman-request-2.88.1-postman.48.tgz", + "integrity": "sha512-E32FGh8ig2KDvzo4Byi7Ibr+wK2gNKPSqXoNsvjdCHgDBxSK4sCUwv+aa3zOBUwfiibPImHMy0WdlDSSCTqTuw==", + "license": "Apache-2.0", + "dependencies": { "@postman/form-data": "~3.1.1", "@postman/tough-cookie": "~4.1.3-postman.1", - "@postman/tunnel-agent": "^0.6.3", + "@postman/tunnel-agent": "^0.6.8", "aws-sign2": "~0.7.0", "aws4": "^1.12.0", - "brotli": "^1.3.3", "caseless": "~0.12.0", "combined-stream": "~1.0.6", "extend": "~3.0.2", "forever-agent": "~0.6.1", - "har-validator": "~5.1.3", - "http-signature": "~1.3.1", + "http-signature": "~1.4.0", "is-typedarray": "~1.0.0", "isstream": "~0.1.2", "json-stringify-safe": "~5.0.1", "mime-types": "^2.1.35", "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.3", + "qs": "~6.14.1", "safe-buffer": "^5.1.2", + "socks-proxy-agent": "^8.0.5", "stream-length": "^1.0.2", "uuid": "^8.3.2" + }, + "engines": { + "node": ">= 16" } }, - "psl": { + "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, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.3.tgz", + "integrity": "sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/psl": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==" }, - "punycode": { + "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==" + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "engines": { + "node": ">=6" + } }, - "qs": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", - "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==" + "node_modules/qs": { + "version": "6.14.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz", + "integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "querystringify": { + "node_modules/querystringify": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" }, - "requires-port": { + "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==" }, - "safe-buffer": { + "node_modules/rollup": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.3.tgz", + "integrity": "sha512-pAQK9HalE84QSm4Po3EmWIZPd3FnjkShVkiMlz1iligWYkWQ7wHYd1PF/T7QZ5TVSD6uSTon5gBVMSM4JfBV+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.60.3", + "@rollup/rollup-android-arm64": "4.60.3", + "@rollup/rollup-darwin-arm64": "4.60.3", + "@rollup/rollup-darwin-x64": "4.60.3", + "@rollup/rollup-freebsd-arm64": "4.60.3", + "@rollup/rollup-freebsd-x64": "4.60.3", + "@rollup/rollup-linux-arm-gnueabihf": "4.60.3", + "@rollup/rollup-linux-arm-musleabihf": "4.60.3", + "@rollup/rollup-linux-arm64-gnu": "4.60.3", + "@rollup/rollup-linux-arm64-musl": "4.60.3", + "@rollup/rollup-linux-loong64-gnu": "4.60.3", + "@rollup/rollup-linux-loong64-musl": "4.60.3", + "@rollup/rollup-linux-ppc64-gnu": "4.60.3", + "@rollup/rollup-linux-ppc64-musl": "4.60.3", + "@rollup/rollup-linux-riscv64-gnu": "4.60.3", + "@rollup/rollup-linux-riscv64-musl": "4.60.3", + "@rollup/rollup-linux-s390x-gnu": "4.60.3", + "@rollup/rollup-linux-x64-gnu": "4.60.3", + "@rollup/rollup-linux-x64-musl": "4.60.3", + "@rollup/rollup-openbsd-x64": "4.60.3", + "@rollup/rollup-openharmony-arm64": "4.60.3", + "@rollup/rollup-win32-arm64-msvc": "4.60.3", + "@rollup/rollup-win32-ia32-msvc": "4.60.3", + "@rollup/rollup-win32-x64-gnu": "4.60.3", + "@rollup/rollup-win32-x64-msvc": "4.60.3", + "fsevents": "~2.3.2" + } + }, + "node_modules/rollup/node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" }, - "safer-buffer": { + "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" }, - "sax": { + "node_modules/sax": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/sax/-/sax-1.3.0.tgz", "integrity": "sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA==" }, - "sshpk": { + "node_modules/semver": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.0.tgz", + "integrity": "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", + "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, + "node_modules/sirv": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/sirv/-/sirv-3.0.2.tgz", + "integrity": "sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@polka/url": "^1.0.0-next.24", + "mrmime": "^2.0.0", + "totalist": "^3.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "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==", + "license": "MIT", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.9.tgz", + "integrity": "sha512-LJhUYUvItdQ0LkJTmPeaEObWXAqFyfmP85x0tch/ez9cahmhlBBLbIqDFnvBnUJGagb0JbIQrkBs1wJ+yRYpEw==", + "license": "MIT", + "dependencies": { + "ip-address": "^10.1.1", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks-proxy-agent": { + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz", + "integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "^4.3.4", + "socks": "^2.8.3" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sshpk": { "version": "1.18.0", "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.18.0.tgz", "integrity": "sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==", - "requires": { + "license": "MIT", + "dependencies": { "asn1": "~0.2.3", "assert-plus": "^1.0.0", "bcrypt-pbkdf": "^1.0.0", @@ -347,71 +3933,495 @@ "jsbn": "~0.1.0", "safer-buffer": "^2.0.2", "tweetnacl": "~0.14.0" + }, + "bin": { + "sshpk-conv": "bin/sshpk-conv", + "sshpk-sign": "bin/sshpk-sign", + "sshpk-verify": "bin/sshpk-verify" + }, + "engines": { + "node": ">=0.10.0" } }, - "stream-length": { + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/std-env": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.1.0.tgz", + "integrity": "sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/stream-length": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/stream-length/-/stream-length-1.0.2.tgz", "integrity": "sha512-aI+qKFiwoDV4rsXiS7WRoCt+v2RX1nUj17+KJC5r2gfh5xoSJIfP6Y3Do/HtvesFcTSWthIuJ3l1cvKQY/+nZg==", - "requires": { + "dependencies": { "bluebird": "^2.6.2" } }, - "tweetnacl": { + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.1.2.tgz", + "integrity": "sha512-dAqSqE/RabpBKI8+h26GfLq6Vb3JVXs30XYQjdMjaj/c2tS8IYYMbIzP599KtRj7c57/wYApb3QjgRgXmrCukA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.16", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", + "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyrainbow": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.0.tgz", + "integrity": "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/totalist": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", + "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/ts-api-utils": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", + "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "node_modules/tweetnacl": { "version": "0.14.5", "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==" + "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", + "license": "Unlicense" + }, + "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, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-eslint": { + "version": "8.59.3", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.59.3.tgz", + "integrity": "sha512-KgusgyDgG4LI8Ih/sWaCtZ06tckLAS5CvT5A4D1Q7bYVoAAyzwiZvE4BmwDHkhRVkvhRBepKeASoFzQetha7Fg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.59.3", + "@typescript-eslint/parser": "8.59.3", + "@typescript-eslint/typescript-estree": "8.59.3", + "@typescript-eslint/utils": "8.59.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/undici-types": { + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.21.0.tgz", + "integrity": "sha512-w9IMgQrz4O0YN1LtB7K5P63vhlIOvC7opSmouCJ+ZywlPAlO9gIkJ+otk6LvGpAs2wg4econaCz3TvQ9xPoyuQ==", + "dev": true, + "license": "MIT" }, - "universalify": { + "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==" + "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", + "engines": { + "node": ">= 4.0.0" + } }, - "uri-js": { + "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "requires": { + "dev": true, + "dependencies": { "punycode": "^2.1.0" } }, - "url-parse": { + "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==", - "requires": { + "dependencies": { "querystringify": "^2.1.1", "requires-port": "^1.0.0" } }, - "uuid": { + "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==" + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "deprecated": "uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028).", + "bin": { + "uuid": "dist/bin/uuid" + } }, - "verror": { + "node_modules/verror": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", - "requires": { + "engines": [ + "node >=0.6.0" + ], + "license": "MIT", + "dependencies": { "assert-plus": "^1.0.0", "core-util-is": "1.0.2", "extsprintf": "^1.2.0" } }, - "xml2js": { + "node_modules/vite": { + "version": "7.3.3", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.3.tgz", + "integrity": "sha512-/4XH147Ui7OGTjg3HbdWe5arnZQSbfuRzdr9Ec7TQi5I7R+ir0Rlc9GIvD4v0XZurELqA035KVXJXpR61xhiTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.27.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vite/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, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/vitest": { + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.6.tgz", + "integrity": "sha512-6lvjbS3p9b4CrdCmguzbh2/4uoXhGE2q71R4OX5sqF9R1bo9Xd6fGrMAfvp5wnCzlBnFVdCOp6onuTQVbo8iUQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/expect": "4.1.6", + "@vitest/mocker": "4.1.6", + "@vitest/pretty-format": "4.1.6", + "@vitest/runner": "4.1.6", + "@vitest/snapshot": "4.1.6", + "@vitest/spy": "4.1.6", + "@vitest/utils": "4.1.6", + "es-module-lexer": "^2.0.0", + "expect-type": "^1.3.0", + "magic-string": "^0.30.21", + "obug": "^2.1.1", + "pathe": "^2.0.3", + "picomatch": "^4.0.3", + "std-env": "^4.0.0-rc.1", + "tinybench": "^2.9.0", + "tinyexec": "^1.0.2", + "tinyglobby": "^0.2.15", + "tinyrainbow": "^3.1.0", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@opentelemetry/api": "^1.9.0", + "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", + "@vitest/browser-playwright": "4.1.6", + "@vitest/browser-preview": "4.1.6", + "@vitest/browser-webdriverio": "4.1.6", + "@vitest/coverage-istanbul": "4.1.6", + "@vitest/coverage-v8": "4.1.6", + "@vitest/ui": "4.1.6", + "happy-dom": "*", + "jsdom": "*", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@opentelemetry/api": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser-playwright": { + "optional": true + }, + "@vitest/browser-preview": { + "optional": true + }, + "@vitest/browser-webdriverio": { + "optional": true + }, + "@vitest/coverage-istanbul": { + "optional": true + }, + "@vitest/coverage-v8": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + }, + "vite": { + "optional": false + } + } + }, + "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, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, + "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, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ws": { + "version": "8.20.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.20.1.tgz", + "integrity": "sha512-It4dO0K5v//JtTXuPkfEOaI3uUN87iYPnqo/ZzqCoG3g8uhA66QUMs/SrM0YK7/NAu+r4LMh/9dq2A7k+rHs+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xml2js": { "version": "0.6.2", "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.6.2.tgz", "integrity": "sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==", - "requires": { + "dependencies": { "sax": ">=0.6.0", "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" } }, - "xmlbuilder": { + "node_modules/xmlbuilder": { "version": "11.0.1", "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", - "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==" + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "engines": { + "node": ">=4.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, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } } } } diff --git a/package.json b/package.json index 029b409..f64f275 100644 --- a/package.json +++ b/package.json @@ -1,13 +1,44 @@ { "name": "qualys", - "version": "3.2.1", - "main": "./integration.js", + "version": "3.4.0", + "main": "./dist/integration.js", "private": true, - "license": "MIT", + "license": "UNLICENSED", "author": "Polarity", "dependencies": { "lodash": "^4.17.21", - "postman-request": "^2.88.1-postman.33", + "polarity-integration-utils": "^4.1.0", "xml2js": "^0.6.2" + }, + "devDependencies": { + "@eslint/js": "^10.0.1", + "@polarityio/integration-types": "^1.1.0", + "@polarityio/pi-components": "^1.9.3", + "@polarityio/vite-plugin-icl": "^1.0.1", + "@types/lodash": "^4.17.24", + "@types/xml2js": "^0.4.14", + "@vitest/browser": "^4.1.4", + "@vitest/browser-playwright": "^4.1.4", + "eslint": "^10.2.0", + "eslint-config-prettier": "^10.1.8", + "lit": "^3.3.2", + "playwright": "^1.59.1", + "prettier": "^3.8.3", + "typescript": "^5.9.3", + "typescript-eslint": "^8.58.2", + "vite": "^7.3.2", + "vitest": "^4.1.4" + }, + "scripts": { + "build": "tsc && vite build", + "typecheck": "tsc --noEmit && tsc -p tsconfig.web.json", + "lint": "eslint src/ web-components/", + "lint:fix": "eslint src/ web-components/ --fix", + "format": "prettier --write 'src/**/*.ts' 'web-components/**/*.ts'", + "format:check": "prettier --check 'src/**/*.ts' 'web-components/**/*.ts'", + "test": "vitest run", + "test:server": "vitest run --project server", + "test:browser": "vitest run --project browser", + "test:watch": "vitest" } -} +} \ No newline at end of file diff --git a/src/associateDataWithEntities.js b/src/associateDataWithEntities.js deleted file mode 100644 index 89b6631..0000000 --- a/src/associateDataWithEntities.js +++ /dev/null @@ -1,39 +0,0 @@ -const { map, toLower, filter, flow, replace, includes, get, concat, uniqBy, flatMap } = require('lodash/fp'); - -const associateDataWithEntities = ( - entities, - { allHostDetections, allFoundKnowledgeBaseRecords }, - Logger -) => - map((entity) => { - const knowledgeBaseRecords = getObjectsContainingString( - entity.value, - allFoundKnowledgeBaseRecords - ); - const hostDetections = flow( - flatMap(({ qid }) => getObjectsContainingString(qid, allHostDetections)), - concat(getObjectsContainingString(entity.value, allHostDetections)), - uniqBy('id') - )(knowledgeBaseRecords); - - return { - entity, - results: { - hostDetections, - knowledgeBaseRecords - } - }; - }, entities); - -const getObjectsContainingString = (string, objs) => - filter( - flow( - JSON.stringify, - replace(/[^\w]/g, ''), - toLower, - includes(flow(replace(/[^\w]/g, ''), toLower)(string)) - ), - objs - ); - -module.exports = associateDataWithEntities; diff --git a/src/associateDataWithEntities.ts b/src/associateDataWithEntities.ts new file mode 100644 index 0000000..e695f80 --- /dev/null +++ b/src/associateDataWithEntities.ts @@ -0,0 +1,47 @@ +import { map, toLower, filter, flow, replace, includes, concat, uniqBy, flatMap } from 'lodash/fp'; +import type { Entity } from '@polarityio/integration-types'; + +/* eslint-disable @typescript-eslint/no-explicit-any */ + +export interface EntityData { + allHostDetections: any[]; + allFoundKnowledgeBaseRecords: any[] | undefined; +} + +export interface EntityWithResults { + entity: Entity; + results: { + hostDetections: any[]; + knowledgeBaseRecords: any[]; + }; +} + +const associateDataWithEntities = ( + entities: Entity[], + { allHostDetections, allFoundKnowledgeBaseRecords }: EntityData +): EntityWithResults[] => + map((entity: Entity) => { + const knowledgeBaseRecords = getObjectsContainingString( + entity.value, + allFoundKnowledgeBaseRecords || [] + ); + const hostDetections = flow( + flatMap(({ qid }: any) => getObjectsContainingString(qid, allHostDetections)), + concat(getObjectsContainingString(entity.value, allHostDetections)), + uniqBy('id') + )(knowledgeBaseRecords); + return { entity, results: { hostDetections, knowledgeBaseRecords } }; + }, entities); + +const getObjectsContainingString = (string: string, objs: any[]): any[] => + filter( + flow( + JSON.stringify, + replace(/[^\w]/g, ''), + toLower, + includes(flow(replace(/[^\w]/g, ''), toLower)(string)) + ), + objs + ); + +export default associateDataWithEntities; diff --git a/src/constants.js b/src/constants.ts similarity index 66% rename from src/constants.js rename to src/constants.ts index 8672634..f005970 100644 --- a/src/constants.js +++ b/src/constants.ts @@ -1,12 +1,9 @@ -const IGNORED_IPS = new Set(['127.0.0.1', '255.255.255.255', '0.0.0.0']); +export const IGNORED_IPS = new Set(['127.0.0.1', '255.255.255.255', '0.0.0.0']); -const QUERY_PATHS_BY_TYPE = { +export const QUERY_PATHS_BY_TYPE: Record> = { qid: { assets: ['name'], - vulnerabilities: [ - 'vulnerabilities.vulnerability.qid', - 'vulnerabilities.vulnerability.title' - ] + vulnerabilities: ['vulnerabilities.vulnerability.qid', 'vulnerabilities.vulnerability.title'] }, cve: { assets: ['name'], @@ -14,38 +11,45 @@ const QUERY_PATHS_BY_TYPE = { }, domain: { assets: ['name', 'interfaces.hostname'], - vulnerabilities: [ - 'vulnerabilities.hostAssetName', - 'vulnerabilities.vulnerability.title' - ] + vulnerabilities: ['vulnerabilities.hostAssetName', 'vulnerabilities.vulnerability.title'] }, IPv4: { - assets: [ - 'interfaces.address', - 'name', - 'interfaces.dnsAddress', - 'interfaces.gatewayAddress' - ], + assets: ['interfaces.address', 'name', 'interfaces.dnsAddress', 'interfaces.gatewayAddress'], vulnerabilities: ['vulnerabilities.vulnerability.title'] }, IPv6: { - assets: [ - 'interfaces.address', - 'name', - 'interfaces.dnsAddress', - 'interfaces.gatewayAddress' - ], + assets: ['interfaces.address', 'name', 'interfaces.dnsAddress', 'interfaces.gatewayAddress'], vulnerabilities: ['vulnerabilities.vulnerability.title'] } }; +QUERY_PATHS_BY_TYPE.customType = QUERY_PATHS_BY_TYPE.qid; -const SEARCH_COLUMN_NAMES_BY_TYPE = { +export const SEARCH_COLUMN_NAMES_BY_TYPE: Record = { cve: ['title', 'category', 'diagnosis', 'solution', 'cves', 'vender_references'], qid: ['qid'], default: ['title', 'diagnosis', 'solution', 'vender_references'] }; -const HOST_DETECTION_DISPLAY_FORMAT = { +export interface DisplayFormatEntry { + label?: string; + isTitle?: boolean; + isDate?: boolean; + isList?: boolean; + isListOfLinks?: boolean; + isHtml?: boolean; + isNewSectionLineBreak?: boolean; + indent?: number; + collapsibleListItems?: boolean; + showLabelAndValue?: boolean; + fieldIsCopyable?: boolean; + shouldCopyFieldLabel?: boolean; + itemDisplayFormat?: DisplayFormat; +} + +export type DisplayFormatValue = string | DisplayFormatEntry; +export type DisplayFormat = Record; + +export const HOST_DETECTION_DISPLAY_FORMAT: DisplayFormat = { ip: { label: 'IP Address', isTitle: true, showLabelAndValue: true }, id: 'Host Detection ID', asset_id: 'Asset Id', @@ -82,7 +86,7 @@ const HOST_DETECTION_DISPLAY_FORMAT = { newSectionLineBreak: { isNewSectionLineBreak: true } }; -const KNOWLEDGE_BASE_RECORD_DISPLAY_FORMAT = { +export const KNOWLEDGE_BASE_RECORD_DISPLAY_FORMAT: DisplayFormat = { title: { isTitle: true }, qid: { label: 'QID', fieldIsCopyable: true, shouldCopyFieldLabel: true }, severity: 'Severity', @@ -100,12 +104,4 @@ const KNOWLEDGE_BASE_RECORD_DISPLAY_FORMAT = { diagnosis: { label: 'Diagnosis', isHtml: true }, solution: { label: 'Solution', isHtml: true }, newSectionLineBreak: { isNewSectionLineBreak: true } -}; - -module.exports = { - IGNORED_IPS, - QUERY_PATHS_BY_TYPE, - SEARCH_COLUMN_NAMES_BY_TYPE, - KNOWLEDGE_BASE_RECORD_DISPLAY_FORMAT, - HOST_DETECTION_DISPLAY_FORMAT -}; +}; \ No newline at end of file diff --git a/src/createLookupResults.js b/src/createLookupResults.js deleted file mode 100644 index 8696fb9..0000000 --- a/src/createLookupResults.js +++ /dev/null @@ -1,57 +0,0 @@ -const { - flow, - get, - map, - size, - mapValues, - some, - identity, - keys, - assign, - eq -} = require('lodash/fp'); - -const { HOST_DETECTION_DISPLAY_FORMAT } = require('./constants'); -const getDisplayResults = require('./getDisplayResults'); - -const createLookupResults = (foundEntities, Logger) => - map(({ entity, results }) => { - const formattedQueryResult = formatQueryResult(results); - - const lookupResult = { - entity, - displayValue: entity.type === 'qid' ? `QID: ${entity.value}` : entity.value, - data: !!formattedQueryResult - ? { - summary: createSummary(entity, results, Logger), - details: flow(keys, (keys) => - assign(formattedQueryResult, { - tabKeys: keys - }) - )(formattedQueryResult) - } - : null - }; - - return lookupResult; - }, foundEntities); - -const createSummary = (entity, { hostDetections, knowledgeBaseRecords }, Logger) => - [].concat(size(hostDetections) ? `Host Detections: ${size(hostDetections)}` : []); - -const formatQueryResult = (result) => { - const resultNotEmpty = flow(mapValues(size), some(identity))(result); - - if (resultNotEmpty) { - const hostDetections = getDisplayResults( - HOST_DETECTION_DISPLAY_FORMAT, - result.hostDetections - ); - - return { - hostDetections - }; - } -}; - -module.exports = createLookupResults; diff --git a/src/createLookupResults.ts b/src/createLookupResults.ts new file mode 100644 index 0000000..c8170d5 --- /dev/null +++ b/src/createLookupResults.ts @@ -0,0 +1,46 @@ +import { flow, map, size, mapValues, some, identity, keys, assign } from 'lodash/fp'; +import type { Entity } from '@polarityio/integration-types'; +import type { LookupResult } from '@polarityio/integration-types'; + +import { HOST_DETECTION_DISPLAY_FORMAT } from './constants'; +import getDisplayResults from './getDisplayResults'; +import type { EntityWithResults } from './associateDataWithEntities'; + +/* eslint-disable @typescript-eslint/no-explicit-any */ + +const createLookupResults = (foundEntities: EntityWithResults[]): LookupResult[] => + map(({ entity, results }: EntityWithResults) => { + const formattedQueryResult = formatQueryResult(results); + const lookupResult: LookupResult = { + entity, + displayValue: (entity.type as string) === 'qid' ? `QID: ${entity.value}` : entity.value, + data: formattedQueryResult + ? { + summary: createSummary(entity, results), + details: flow(keys, (k: string[]) => assign(formattedQueryResult, { tabKeys: k }))( + formattedQueryResult + ) + } + : null + }; + return lookupResult; + }, foundEntities); + +const createSummary = ( + _entity: Entity, + { hostDetections }: { hostDetections: any[]; knowledgeBaseRecords: any[] } +): string[] => + ([] as string[]).concat(size(hostDetections) ? `Host Detections: ${size(hostDetections)}` : []); + +const formatQueryResult = (result: { + hostDetections: any[]; + knowledgeBaseRecords: any[]; +}): Record | undefined => { + const resultNotEmpty = flow(mapValues(size), some(identity))(result); + if (resultNotEmpty) { + const hostDetections = getDisplayResults(HOST_DETECTION_DISPLAY_FORMAT, result.hostDetections); + return { hostDetections }; + } +}; + +export default createLookupResults; diff --git a/src/createRequestWithDefaults.js b/src/createRequestWithDefaults.js deleted file mode 100644 index 21aa6dd..0000000 --- a/src/createRequestWithDefaults.js +++ /dev/null @@ -1,115 +0,0 @@ -const fs = require('fs'); -const { identity, omit } = require('lodash/fp'); -const request = require('postman-request'); -const config = require('../config/config'); -const { parseErrorToReadableJSON } = require('./dataTransformations'); - -const SUCCESSFUL_ROUNDED_REQUEST_STATUS_CODES = [200]; - -const _configFieldIsValid = (field) => typeof field === 'string' && field.length > 0; - -const createRequestWithDefaults = (Logger) => { - const { - request: { ca, cert, key, passphrase, rejectUnauthorized, proxy } - } = config; - - const defaults = { - ...(_configFieldIsValid(ca) && { ca: fs.readFileSync(ca) }), - ...(_configFieldIsValid(cert) && { cert: fs.readFileSync(cert) }), - ...(_configFieldIsValid(key) && { key: fs.readFileSync(key) }), - ...(_configFieldIsValid(passphrase) && { passphrase }), - ...(_configFieldIsValid(proxy) && { proxy }), - ...(typeof rejectUnauthorized === 'boolean' && { rejectUnauthorized }), - json: true - }; - - const requestWithDefaults = ( - preRequestFunction = async () => ({}), - postRequestSuccessFunction = async (x) => x, - postRequestFailureFunction = async (e) => { - throw e; - } - ) => { - const defaultsRequest = request.defaults(defaults); - - const _requestWithDefault = (requestOptions) => - new Promise((resolve, reject) => { - defaultsRequest(requestOptions, (err, res, body) => { - if (err) return reject(err); - resolve({ ...res, body }); - }); - }); - - return async (requestOptions) => { - const preRequestFunctionResults = await preRequestFunction(requestOptions); - const _requestOptions = { - ...requestOptions, - ...preRequestFunctionResults - }; - - let postRequestFunctionResults; - try { - const result = await _requestWithDefault(_requestOptions); - checkForStatusError(result, _requestOptions); - - postRequestFunctionResults = await postRequestSuccessFunction( - result, - _requestOptions - ); - } catch (error) { - postRequestFunctionResults = await postRequestFailureFunction( - error, - _requestOptions - ); - } - return postRequestFunctionResults; - }; - }; - - const handleAuth = async ({ options, ...requestOptions }) => ({ - ...requestOptions, - auth: { - username: options.username, - password: options.password, - } - }); - - const checkForStatusError = ({ statusCode, body }, requestOptions) => { - Logger.trace({ - requestOptions: { - ...requestOptions, - auth: '************', - options: '************' - }, - statusCode, - body - }); - - const roundedStatus = Math.round(statusCode / 100) * 100; - if (!SUCCESSFUL_ROUNDED_REQUEST_STATUS_CODES.includes(roundedStatus)) { - const requestError = Error('Request Error'); - requestError.status = statusCode; - requestError.description = JSON.stringify(body); - requestError.requestOptions = JSON.stringify(requestOptions); - throw requestError; - } - }; - - const requestDefaultsWithInterceptors = requestWithDefaults(handleAuth, identity, (error) => { - const err = parseErrorToReadableJSON(error); - if (err.requestOptions && ['{', '['].includes(err.requestOptions[0])) - err.requestOptions = omit(['options', 'auth'], JSON.parse(err.requestOptions)); - - Logger.error({ err }); - let newError = new Error(err.message) - newError.status = err.status; - newError.requestOptions = JSON.stringify(err.requestOptions); - newError.description = err.description; - newError.stack = err.stack; - throw newError; - }); - - return requestDefaultsWithInterceptors; -}; - -module.exports = createRequestWithDefaults; diff --git a/src/dataTransformations.js b/src/dataTransformations.js deleted file mode 100644 index 0937448..0000000 --- a/src/dataTransformations.js +++ /dev/null @@ -1,216 +0,0 @@ -const _ = require('lodash'); -const { - flow, - keys, - values, - zipObject, - map, - first, - omit, - reduce, - size, - negate, - curry, - filter, - eq, - isEmpty, - getOr, - join, - get, - isArray, - identity -} = require('lodash/fp'); - -const { IGNORED_IPS, QUERY_PATHS_BY_TYPE } = require('./constants'); - -const getKeys = (keys, items) => - Array.isArray(items) - ? items.map((item) => _.pickBy(item, (v, key) => keys.includes(key))) - : _.pickBy(items, (v, key) => keys.includes(key)); - -const groupEntities = (entities) => - _.chain(entities) - .groupBy(({ isIP, isDomain, type }) => - isIP - ? 'ip' - : isDomain - ? 'domain' - : type === 'MAC' - ? 'mac' - : type === 'MD5' - ? 'md5' - : type === 'SHA1' - ? 'sha1' - : type === 'SHA256' - ? 'sha256' - : 'unknown' - ) - .omit('unknown') - .value(); - -const splitOutIgnoredIps = (_entitiesPartition) => { - const { ignoredIPs, entitiesPartition } = _.groupBy( - _entitiesPartition, - ({ isIP, value }) => - !isIP || (isIP && !IGNORED_IPS.has(value)) ? 'entitiesPartition' : 'ignoredIPs' - ); - - return { - entitiesPartition, - ignoredIpLookupResults: _.map(ignoredIPs, (entity) => ({ - entity, - data: null - })) - }; -}; - -const objectPromiseAll = async (obj = { fn1: async () => {} }) => { - const labels = keys(obj); - const functions = values(obj); - const executedFunctions = await Promise.all(map((func) => func(), functions)); - - return zipObject(labels, executedFunctions); -}; - -const asyncObjectReduce = async (func, initAgg, obj) => { - const nextKey = flow(keys, first)(obj); - - if (!nextKey) return initAgg; - - const newAgg = await func(initAgg, obj[nextKey], nextKey); - - return await asyncObjectReduce(func, newAgg, omit(nextKey, obj)); -}; - -const transpose2DArray = reduce( - (agg, [key, value]) => [ - [...agg[0], key], - [...agg[1], value] - ], - [[], []] -); - -const or = - (...[func, ...funcs]) => - (x) => - func(x) || (funcs.length && or(...funcs)(x)); - -const and = - (...[func, ...funcs]) => - (x) => - func(x) && (funcs.length ? and(...funcs)(x) : true); - -// func: (value, key) => [newKey, newValue], obj: { key1:value1, key2:value2 } -// return { newKey1: newValue1, newKey2: newValue2 } -const mapObject = curry((func, obj) => - flow( - Object.entries, - map(([key, value]) => func(value, key)), - filter(and(negate(isEmpty), flow(size, eq(2)))), - transpose2DArray, - ([keys, values]) => zipObject(keys, values) - )(obj) -); - -const mapObjectAsync = async (func, obj) => { - // func: (value, key) => [newKey, newValue], obj: { key1:value1, key2:value2 } - // return { newKey1: newValue1, newKey2: newValue2 } - const unzippedResults = await Promise.all( - map(async ([key, value]) => await func(value, key), Object.entries(obj)) - ); - - return flow( - filter(and(negate(isEmpty), flow(size, eq(2)))), - transpose2DArray, - ([keys, values]) => zipObject(keys, values) - )(unzippedResults); -}; - -const parseErrorToReadableJSON = (error) => - JSON.parse(JSON.stringify(error, Object.getOwnPropertyNames(error))); - -const buildQueryByQueryType = (queryType, entities) => - flow( - map((entity) => - flow( - getOr([], [entity.type, queryType]), - map((queryPath) => `${queryPath}: "${entity.value}"`), - join(' OR ') - )(QUERY_PATHS_BY_TYPE) - ), - join(' OR ') - )(entities); - -const millisToHoursMinutesAndSeconds = (millis) => { - let remainingMillis = millis; - - const seconds = Math.floor((remainingMillis / 1000) % 60); - remainingMillis -= seconds * 1000; - - const minutes = Math.floor((remainingMillis / 60000) % 60); - remainingMillis -= minutes * 60000; - - const hours = Math.floor(remainingMillis / 3600000); - - return ( - (hours ? `${hours} hours, ` : '') + - (minutes ? `${minutes} minutes, ` : '') + - (seconds ? `${seconds} seconds` : '') + - (!hours && !minutes && !seconds ? `${millis}ms` : '') - ); -}; - -const processResultWithProcessingFormat = (processingFormat) => (result) => - mapObject( - (value, key) => [ - key, - typeof value === 'string' - ? get(value, result) - : flow(get(value.path), value.process)(result) - ], - processingFormat - ); - -const processPossibleList = - (shouldStringify = true) => - (arrayOrObject) => - arrayOrObject && - (shouldStringify ? JSON.stringify : identity)( - isArray(arrayOrObject) ? arrayOrObject : [arrayOrObject] - ); - -const xml2js = require('xml2js'); - -const xmlToJson = async (xml, Logger) => { - try { - const parser = new xml2js.Parser({ - normalizeTags: true, - explicitArray: false, - charkey: 'value', - attrkey: 'attributes' - }); - return await parser.parseStringPromise(xml); - } catch (e) { - const err = parseErrorToReadableJSON(e); - Logger.error({ MESSAGE: 'Failed to Parse XML', xml, err }); - } -}; - -module.exports = { - getKeys, - groupEntities, - splitOutIgnoredIps, - objectPromiseAll, - asyncObjectReduce, - mapObject, - mapObjectAsync, - transpose2DArray, - parseErrorToReadableJSON, - buildQueryByQueryType, - millisToHoursMinutesAndSeconds, - processResultWithProcessingFormat, - processPossibleList, - xmlToJson, - and, - or -}; diff --git a/src/dataTransformations.ts b/src/dataTransformations.ts new file mode 100644 index 0000000..c022dc7 --- /dev/null +++ b/src/dataTransformations.ts @@ -0,0 +1,211 @@ +import _ from 'lodash'; +import { + flow, + keys, + values, + zipObject, + map, + first, + omit, + reduce, + size, + curry, + filter, + eq, + isEmpty, + getOr, + join, + get, + isArray, + identity +} from 'lodash/fp'; +import * as xml2js from 'xml2js'; +import type { Logger } from '@polarityio/integration-types'; +import type { Entity } from '@polarityio/integration-types'; + +import { IGNORED_IPS, QUERY_PATHS_BY_TYPE } from './constants'; + +/* eslint-disable @typescript-eslint/no-explicit-any */ + +export const getKeys = (keyList: string[], items: any): any => + Array.isArray(items) + ? items.map((item: any) => _.pickBy(item, (_v: any, key: string) => keyList.includes(key))) + : _.pickBy(items, (_v: any, key: string) => keyList.includes(key)); + +export const groupEntities = (entities: Entity[]): Record => + _.chain(entities) + .groupBy(({ isIP, isDomain, type }: any) => + isIP + ? 'ip' + : isDomain + ? 'domain' + : type === 'MAC' + ? 'mac' + : type === 'MD5' + ? 'md5' + : type === 'SHA1' + ? 'sha1' + : type === 'SHA256' + ? 'sha256' + : 'unknown' + ) + .omit('unknown') + .value(); + +export const splitOutIgnoredIps = ( + _entitiesPartition: Entity[] +): { entitiesPartition: Entity[]; ignoredIpLookupResults: any[] } => { + const grouped = _.groupBy(_entitiesPartition, ({ isIP, value }: any) => + !isIP || (isIP && !IGNORED_IPS.has(value)) ? 'entitiesPartition' : 'ignoredIPs' + ); + + return { + entitiesPartition: grouped.entitiesPartition || [], + ignoredIpLookupResults: _.map(grouped.ignoredIPs, (entity: Entity) => ({ + entity, + data: null + })) + }; +}; + +export const objectPromiseAll = async ( + obj: Record Promise> +): Promise> => { + const labels = keys(obj); + const functions = values(obj); + const executedFunctions = await Promise.all(map((func: () => Promise) => func(), functions)); + return zipObject(labels, executedFunctions); +}; + +export const asyncObjectReduce = async ( + func: (agg: any, value: any, key: string) => Promise, + initAgg: any, + obj: Record +): Promise => { + const nextKey = flow(keys, first)(obj) as string | undefined; + if (!nextKey) return initAgg; + const newAgg = await func(initAgg, obj[nextKey], nextKey); + return await asyncObjectReduce(func, newAgg, omit(nextKey, obj) as Record); +}; + +export const transpose2DArray = reduce( + (agg: [any[], any[]], [key, value]: [any, any]) => + [ + [...agg[0], key], + [...agg[1], value] + ] as [any[], any[]], + [[], []] as [any[], any[]] +); + +export const or = + (...[func, ...funcs]: Array<(x: any) => boolean>) => + (x: any): boolean => + func(x) || (funcs.length > 0 && or(...funcs)(x)); + +export const and = + (...[func, ...funcs]: Array<(x: any) => boolean>) => + (x: any): boolean => + func(x) && (funcs.length > 0 ? and(...funcs)(x) : true); + +const negate = + (fn: (x: any) => boolean) => + (x: any): boolean => + !fn(x); + +export const mapObject = curry( + (func: (value: any, key: string) => any, obj: Record): Record => + flow( + Object.entries, + map(([key, value]: [string, any]) => func(value, key)), + filter(and(negate(isEmpty), flow(size, eq(2)))), + transpose2DArray, + ([k, v]: [string[], any[]]) => zipObject(k, v) + )(obj) +); + +export const mapObjectAsync = async ( + func: (value: any, key: string) => Promise, + obj: Record +): Promise> => { + const unzippedResults = await Promise.all( + map(async ([key, value]: [string, any]) => await func(value, key), Object.entries(obj)) + ); + return flow( + filter(and(negate(isEmpty), flow(size, eq(2)))), + transpose2DArray, + ([k, v]: [string[], any[]]) => zipObject(k, v) + )(unzippedResults); +}; + +export const parseErrorToReadableJSON = (error: unknown): Record => { + const serialized = JSON.parse(JSON.stringify(error, Object.getOwnPropertyNames(error as object))); + delete serialized.config; + delete serialized.request; + delete serialized.options; + delete serialized.auth; + return serialized; +}; + +export const buildQueryByQueryType = (queryType: string, entities: Entity[]): string => + flow( + map((entity: Entity) => + flow( + getOr([], [entity.type, queryType]), + map((queryPath: string) => `${queryPath}: "${entity.value}"`), + join(' OR ') + )(QUERY_PATHS_BY_TYPE as any) + ), + join(' OR ') + )(entities); + +export const millisToHoursMinutesAndSeconds = (millis: number): string => { + let remainingMillis = millis; + const seconds = Math.floor((remainingMillis / 1000) % 60); + remainingMillis -= seconds * 1000; + const minutes = Math.floor((remainingMillis / 60000) % 60); + remainingMillis -= minutes * 60000; + const hours = Math.floor(remainingMillis / 3600000); + return ( + (hours ? `${hours} hours, ` : '') + + (minutes ? `${minutes} minutes, ` : '') + + (seconds ? `${seconds} seconds` : '') + + (!hours && !minutes && !seconds ? `${millis}ms` : '') + ); +}; + +export const processResultWithProcessingFormat = + (processingFormat: Record) => + (result: any): Record => + mapObject( + (value: any, key: string) => [ + key, + typeof value === 'string' + ? get(value, result) + : flow(get(value.path), value.process)(result) + ], + processingFormat + ); + +export const processPossibleList = + (shouldStringify = true) => + (arrayOrObject: any): any => + arrayOrObject && + (shouldStringify ? JSON.stringify : identity)( + isArray(arrayOrObject) ? arrayOrObject : [arrayOrObject] + ); + +export const xmlToJson = async (xml: string, Logger: Logger): Promise => { + try { + const parser = new xml2js.Parser({ + normalizeTags: true, + explicitArray: false, + charkey: 'value', + attrkey: 'attributes' + }); + return await parser.parseStringPromise(xml); + } catch (e) { + const err = parseErrorToReadableJSON(e); + Logger.error({ MESSAGE: 'Failed to Parse XML', err }, 'XML Parse Failed'); + Logger.trace({ xml }, 'Raw XML that failed to parse'); + } +}; diff --git a/src/getDisplayResults.js b/src/getDisplayResults.js deleted file mode 100644 index 0fc256c..0000000 --- a/src/getDisplayResults.js +++ /dev/null @@ -1,40 +0,0 @@ -const { flow, get, map, values, assign, flatMap } = require('lodash/fp'); - -const { mapObject } = require('./dataTransformations'); - -const getDisplayResults = (displayFormat, results, dontFlatten) => - (dontFlatten ? map : flatMap)((record) => { - const displayResultsAsObject = mapObject( - (contentInfo, columnName) => - (get(columnName, record) || - get('isTitle', contentInfo) || - get('isNewSectionLineBreak', contentInfo)) && [ - columnName, - buildDisplayValue(contentInfo, columnName, record) - ], - displayFormat - ); - - const displayResults = values(displayResultsAsObject); - - return displayResults; - }, results); - -const buildDisplayValue = (contentInfo, columnName, record) => - typeof contentInfo === 'string' - ? { label: contentInfo, value: get(columnName, record) } - : assign(contentInfo, { - value: flow(get(columnName), (value) => - contentInfo.isListOfLinks - ? JSON.parse(value) - : contentInfo.isList - ? getDisplayResults( - contentInfo.itemDisplayFormat, - get(columnName, record), - true - ) - : value - )(record) - }); - -module.exports = getDisplayResults; diff --git a/src/getDisplayResults.ts b/src/getDisplayResults.ts new file mode 100644 index 0000000..fb17b34 --- /dev/null +++ b/src/getDisplayResults.ts @@ -0,0 +1,48 @@ +import { flow, get, map, values, assign, flatMap } from 'lodash/fp'; +import { mapObject } from './dataTransformations'; +import type { DisplayFormat, DisplayFormatEntry } from './constants'; + +/* eslint-disable @typescript-eslint/no-explicit-any */ + +const getDisplayResults = ( + displayFormat: DisplayFormat, + results: any[], + dontFlatten?: boolean +): any[] => + (dontFlatten ? map : (flatMap as any))((record: any) => { + const displayResultsAsObject = mapObject( + (contentInfo: any, columnName: string) => + (get(columnName, record) || + get('isTitle', contentInfo) || + get('isNewSectionLineBreak', contentInfo)) && [ + columnName, + buildDisplayValue(contentInfo, columnName, record) + ], + displayFormat + ); + const displayResults = values(displayResultsAsObject); + return displayResults; + }, results); + +const buildDisplayValue = ( + contentInfo: string | DisplayFormatEntry, + columnName: string, + record: any +): any => + typeof contentInfo === 'string' + ? { label: contentInfo, value: get(columnName, record) } + : assign(contentInfo, { + value: flow(get(columnName), (value: any) => + (contentInfo as DisplayFormatEntry).isListOfLinks + ? JSON.parse(value) + : (contentInfo as DisplayFormatEntry).isList + ? getDisplayResults( + (contentInfo as DisplayFormatEntry).itemDisplayFormat!, + get(columnName, record), + true + ) + : value + )(record) + }); + +export default getDisplayResults; diff --git a/src/getLookupResults.js b/src/getLookupResults.js deleted file mode 100644 index 190270f..0000000 --- a/src/getLookupResults.js +++ /dev/null @@ -1,67 +0,0 @@ -const { flow, map, split, first, last, trim, concat, get, uniqBy } = require('lodash/fp'); - -const { splitOutIgnoredIps } = require('./dataTransformations'); -const createLookupResults = require('./createLookupResults'); -const queryHostDetectionListForAllEntities = require('./querying/queryHostDetectionListForAllEntities'); -const associateDataWithEntities = require('./associateDataWithEntities'); - -const getLookupResults = async ( - entities, - options, - requestWithDefaults, - Logger -) => { - const entitiesWithCustomTypesSpecified = map(({ type, types, value, ...entity }) => { - type = type === 'custom' ? flow(first, split('.'), last)(types) : type; - - return { - ...entity, - type, - value: type === 'qid' ? flow(split(':'), last, trim)(value) : value - }; - }, entities); - - const { entitiesPartition, ignoredIpLookupResults } = splitOutIgnoredIps( - entitiesWithCustomTypesSpecified - ); - - const data = await getData( - entitiesPartition, - options, - requestWithDefaults, - Logger - ); - - const foundEntities = associateDataWithEntities(entitiesPartition, data, Logger); - const lookupResults = createLookupResults(foundEntities, Logger); - - return lookupResults.concat(ignoredIpLookupResults); -}; - -const getData = async ( - entitiesPartition, - options, - requestWithDefaults, - Logger -) => { - const [initialHostDetections, allFoundKnowledgeBaseRecords] = await Promise.all([ - queryHostDetectionListForAllEntities( - entitiesPartition, - options, - requestWithDefaults, - Logger - ) - ]); - - const allHostDetections = flow( - uniqBy('id') - )(initialHostDetections); - - Logger.trace({ allHostDetections }, 'All Host Detections'); - - return { allHostDetections, allFoundKnowledgeBaseRecords }; -}; - -module.exports = { - getLookupResults -}; diff --git a/src/getLookupResults.ts b/src/getLookupResults.ts new file mode 100644 index 0000000..a6d558a --- /dev/null +++ b/src/getLookupResults.ts @@ -0,0 +1,64 @@ +import { flow, map, split, first, last, uniqBy } from 'lodash/fp'; +import type { Entity, Logger } from '@polarityio/integration-types'; +import type { PolarityRequest } from 'polarity-integration-utils'; + +import { splitOutIgnoredIps } from './dataTransformations'; +import createLookupResults from './createLookupResults'; +import queryHostDetectionListForAllEntities from './querying/queryHostDetectionListForAllEntities'; +import associateDataWithEntities from './associateDataWithEntities'; + +/* eslint-disable @typescript-eslint/no-explicit-any */ + +export const getLookupResults = async ( + entities: Entity[], + options: Record, + request: PolarityRequest, + Logger: Logger +): Promise => { + const entitiesWithCustomTypesSpecified: Entity[] = map((entity: Entity) => { + const type: string = + entity.type === 'custom' + ? (flow(first, split('.'), last)(entity.types) as string) + : entity.type; + + const value = + type === 'qid' + ? (/(?:QID|qid)(?:\s*:\s*|\s+)(\d{1,8})/i.exec(entity.value)?.[1] ?? entity.value) + : entity.value; + + if (type === 'customType') { + const extractRegex = new RegExp(options.customTypeValueRegex || '\\d+$'); + const extracted = extractRegex.exec(entity.value)?.[0]; + if (extracted) { + return { ...entity, type, value: extracted } as Entity; + } + } + + return { ...entity, type, value } as Entity; + }, entities); + + const { entitiesPartition, ignoredIpLookupResults } = splitOutIgnoredIps( + entitiesWithCustomTypesSpecified + ); + + const data = await getData(entitiesPartition, options, request, Logger); + const foundEntities = associateDataWithEntities(entitiesPartition, data); + const lookupResults = createLookupResults(foundEntities); + return lookupResults.concat(ignoredIpLookupResults); +}; + +const getData = async ( + entitiesPartition: Entity[], + options: Record, + request: PolarityRequest, + Logger: Logger +): Promise => { + const [initialHostDetections] = await Promise.all([ + queryHostDetectionListForAllEntities(entitiesPartition, options, request, Logger) + ]); + + const allHostDetections = flow(uniqBy('id'))(initialHostDetections); + Logger.trace({ allHostDetections }, 'All Host Detections'); + + return { allHostDetections, allFoundKnowledgeBaseRecords: undefined }; +}; \ No newline at end of file diff --git a/src/integration.ts b/src/integration.ts new file mode 100644 index 0000000..5cf6db6 --- /dev/null +++ b/src/integration.ts @@ -0,0 +1,80 @@ +import { PolarityRequest, setLogger } from 'polarity-integration-utils'; +import { IntegrationError } from 'polarity-integration-utils'; +import type { + Entity, + DoLookupUserOptions, + DoLookupResult, + Logger, + IntegrationContext, + ValidateOptionsUserOptions, + ValidationError +} from '@polarityio/integration-types'; +import type { BeforeRequestHook } from 'polarity-integration-utils'; + +import { parseErrorToReadableJSON } from './dataTransformations'; +import { getLookupResults } from './getLookupResults'; +import { validateStringOptions, validateUrlOption, validateCustomTypeValueRegex } from './validateOptions'; + +let request: PolarityRequest; + +const addAuth: BeforeRequestHook = async (requestOptions, userOptions) => ({ + ...requestOptions, + auth: { + username: userOptions.username as string, + password: userOptions.password as string + } +}); + +function startup(logger: Logger): void { + setLogger(logger); + request = new PolarityRequest({ + roundedSuccessStatusCodes: [200], + hooks: { beforeRequest: [addAuth] } + }); +} + +async function doLookup( + entities: Entity[], + options: DoLookupUserOptions, + context: IntegrationContext +): Promise { + const Logger = context.logger; + Logger.debug({ entities }, 'Entities'); + + request.userOptions = options; + + try { + const lookupResults = await getLookupResults(entities, options, request, Logger); + Logger.trace({ lookupResults }, 'Lookup Results'); + return lookupResults; + } catch (error) { + const err = parseErrorToReadableJSON(error); + Logger.error({ formattedError: err }, 'Get Lookup Results Failed'); + + throw new IntegrationError( + (error as Error).message ? (error as Error).message : 'Searching Failed', + { cause: error, meta: { formattedError: err } } + ); + } +} + +function validateOptions( + options: ValidateOptionsUserOptions, + _context: IntegrationContext +): ValidationError[] { + const stringOptionsErrorMessages: Record = { + url: 'You must provide a valid Qualys URL.', + username: 'You must provide a valid Qualys Username', + password: 'You must provide a valid Qualys Password' + }; + + const stringValidationErrors = validateStringOptions(stringOptionsErrorMessages, options); + + const urlValidationErrors = validateUrlOption(options.url.value as string); + + const customTypeRegexErrors = validateCustomTypeValueRegex(options); + + return stringValidationErrors.concat(urlValidationErrors).concat(customTypeRegexErrors); +} + +export { startup, doLookup, validateOptions }; \ No newline at end of file diff --git a/src/querying/queryAssetsForAllEntities.js b/src/querying/queryAssetsForAllEntities.js deleted file mode 100644 index 811dcdf..0000000 --- a/src/querying/queryAssetsForAllEntities.js +++ /dev/null @@ -1,72 +0,0 @@ -const { getOr, size, flow, concat, uniqBy } = require('lodash/fp'); -const { - parseErrorToReadableJSON, - buildQueryByQueryType -} = require('../dataTransformations'); - -const ASSET_QUERY_FIELDS = - 'id,name,host,createdAt,tags,sourceInfo,activationModules,activationModulesPending,' + - 'activationModulesNoProfileFound,activationModulesQueuedManifestGen,updatedAt,' + - 'criticalityScore,isDefaultCriticalityScore,criticalityScoreUpdated,lastVmScanDate,' + - 'lastComplianceScanDate'; - -const limit = 200; - -const queryAssetsForAllEntities = async ( - entities, - options, - requestWithDefaults, - Logger, - offset = 0, - allAssets = [] -) => { - try { - const assetQuery = buildQueryByQueryType('assets', entities); - - const assetsData = getOr( - [], - 'body', - await requestWithDefaults({ - uri: `${options.url}/portal-front/rest/assetview/1.0/assets`, - qs: { - havingQuery: assetQuery, - fields: ASSET_QUERY_FIELDS, - order: '-updatedAt', - includeAssets: true, - limit, - offset - }, - options - }) - ); - - const foundAssets = flow(concat(assetsData), uniqBy('id'))(allAssets); - - if (size(assetsData) === 200) { - return await queryAssetsForAllEntities( - entities, - options, - requestWithDefaults, - Logger, - offset + limit, - foundAssets - ); - } - - return foundAssets; - } catch (error) { - const err = parseErrorToReadableJSON(error); - Logger.error( - { - detail: 'Failed to Query Assets', - options, - formattedError: err - }, - 'Query Assets Failed' - ); - - throw error; - } -}; - -module.exports = queryAssetsForAllEntities; diff --git a/src/querying/queryAssetsForAllEntities.ts b/src/querying/queryAssetsForAllEntities.ts new file mode 100644 index 0000000..0adc08d --- /dev/null +++ b/src/querying/queryAssetsForAllEntities.ts @@ -0,0 +1,60 @@ +import { size, flow, concat, uniqBy } from 'lodash/fp'; +import type { Entity, Logger } from '@polarityio/integration-types'; +import type { PolarityRequest } from 'polarity-integration-utils'; + +import { parseErrorToReadableJSON, buildQueryByQueryType } from '../dataTransformations'; + +/* eslint-disable @typescript-eslint/no-explicit-any */ + +const ASSET_QUERY_FIELDS = 'id,name,host,createdAt,tags,sourceInfo,activationModules,...'; +const limit = 200; + +const queryAssetsForAllEntities = async ( + entities: Entity[], + options: Record, + request: PolarityRequest, + Logger: Logger, + offset = 0, + allAssets: any[] = [] +): Promise => { + try { + const assetQuery = buildQueryByQueryType('assets', entities); + + const response = await request.run({ + url: `${options.url}/portal-front/rest/assetview/1.0/assets`, + qs: { + havingQuery: assetQuery, + fields: ASSET_QUERY_FIELDS, + order: '-updatedAt', + includeAssets: true, + limit, + offset + } + }); + + const assetsData = (response!.body as any[]) || []; + const foundAssets = flow(concat(assetsData), uniqBy('id'))(allAssets); + + if (size(assetsData) === 200) { + return await queryAssetsForAllEntities( + entities, + options, + request, + Logger, + offset + limit, + foundAssets + ); + } + + return foundAssets; + } catch (error) { + const err = parseErrorToReadableJSON(error); + Logger.error( + { detail: 'Failed to Query Assets', url: options.url, formattedError: err }, + 'Query Assets Failed' + ); + throw error; + } +}; + +export default queryAssetsForAllEntities; diff --git a/src/querying/queryHostDetectionListForAllEntities.js b/src/querying/queryHostDetectionListForAllEntities.js deleted file mode 100644 index 23e9665..0000000 --- a/src/querying/queryHostDetectionListForAllEntities.js +++ /dev/null @@ -1,140 +0,0 @@ -const { identity } = require('lodash'); -const { - get, - getOr, - map, - flow, - filter, - eq, - uniqBy, - join, - cond, - size -} = require('lodash/fp'); - -const { - parseErrorToReadableJSON, - processResultWithProcessingFormat, - processPossibleList, - xmlToJson, - or -} = require('../dataTransformations'); - -const queryHostDetectionListForAllEntities = async ( - entities, - options, - requestWithDefaults, - Logger -) => { - const allHostDetectionResultForIpAddresses = await flow( - filter(flow(get('type'), or(eq('IPv4'), eq('IPv6')))), - map(get('value')), - cond([[size, queryHostDetectionList('ips', options, requestWithDefaults, Logger)]]) - )(entities); - - const allHostDetectionResultForQids = await flow( - filter(flow(get('type'), eq('qid'))), - map(get('value')), - cond([[size, queryHostDetectionList('qids', options, requestWithDefaults, Logger)]]) - )(entities); - - return uniqBy('id', allHostDetectionResultForIpAddresses).concat( - uniqBy('id', allHostDetectionResultForQids) - ); -}; - -const queryHostDetectionList = - (type, options, requestWithDefaults, Logger) => async (entityValues) => { - try { - const responseXml = getOr( - '', - 'body', - await requestWithDefaults({ - method: 'GET', - url: `${options.url}/api/2.0/fo/asset/host/vm/detection/`, - qs: { - action: 'list', - [type]: join(',', entityValues), - show_asset_id: 1, - show_results: 1 - }, - headers: { 'X-Requested-With': 'Polarity' }, - options - }) - ); - - const hostDetections = flow( - get('host_list_vm_detection_output.response.host_list.host'), - processPossibleList(false) - )(await xmlToJson(responseXml, Logger)); - - const processedJson = map( - processResultWithProcessingFormat(HOST_DETECTIONS_LIST_FORMAT), - hostDetections - ); - - return processedJson; - } catch (error) { - const err = parseErrorToReadableJSON(error); - Logger.error( - { - detail: 'Failed to Query Host Detection List', - formattedError: err - }, - 'Querying Host Detection List Failed' - ); - - throw error; - } - }; - -const HOST_DETECTIONS_LIST_ITEM_FORMAT = { - qid: 'qid', - type: 'type', - severity: 'severity', - port: 'port', - protocol: 'protocol', - ssl: { - path: 'ssl', - process: (ssl) => (ssl == 1 ? 'Yes' : 'No') - }, - results: 'results', - status: 'status', - first_found_datetime: 'first_found_datetime', - last_found_datetime: 'last_found_datetime', - times_found: 'times_found', - last_test_datetime: 'last_test_datetime', - last_update_datetime: 'last_update_datetime', - is_ignored: { - path: 'is_ignored', - process: (is_ignored) => (is_ignored == 1 ? 'Yes' : 'No') - }, - is_disabled: { - path: 'is_disabled', - process: (is_disabled) => (is_disabled == 1 ? 'Yes' : 'No') - }, - last_processed_datetime: 'last_processed_datetime' -}; - -const HOST_DETECTIONS_LIST_FORMAT = { - id: 'id', - asset_id: 'asset_id', - ip: 'ip', - os: 'os', - dns: 'dns', - last_scan_datetime: 'last_scan_datetime', - last_vm_scanned_date: 'last_vm_scanned_date', - last_vm_scanned_duration: 'last_vm_scanned_duration', - category: 'category', - detection_list: { - path: 'detection_list', - process: flow( - get('detection'), - processPossibleList(false), - map(processResultWithProcessingFormat(HOST_DETECTIONS_LIST_ITEM_FORMAT)), - uniqBy('qid') - ) - } -}; - -module.exports = queryHostDetectionListForAllEntities; diff --git a/src/querying/queryHostDetectionListForAllEntities.ts b/src/querying/queryHostDetectionListForAllEntities.ts new file mode 100644 index 0000000..84b3789 --- /dev/null +++ b/src/querying/queryHostDetectionListForAllEntities.ts @@ -0,0 +1,132 @@ +import { get, map, flow, filter, eq, uniqBy, join, size } from 'lodash/fp'; +import type { Entity, Logger } from '@polarityio/integration-types'; +import type { PolarityRequest } from 'polarity-integration-utils'; + +import { + parseErrorToReadableJSON, + processResultWithProcessingFormat, + processPossibleList, + xmlToJson, + or +} from '../dataTransformations'; + +/* eslint-disable @typescript-eslint/no-explicit-any */ + +const queryHostDetectionListForAllEntities = async ( + entities: Entity[], + options: Record, + request: PolarityRequest, + Logger: Logger +): Promise => { + const ipEntities = filter(flow(get('type'), or(eq('IPv4'), eq('IPv6'))), entities); + const ipValues = map(get('value'), ipEntities) as string[]; + + const qidEntities = filter( + flow(get('type'), (t) => t === 'qid' || t === 'customType'), + entities + ); + const qidValues = map(get('value'), qidEntities) as string[]; + + const allHostDetectionResultForIpAddresses = size(ipValues) + ? await queryHostDetectionList('ips', options, request, Logger)(ipValues) + : []; + + const allHostDetectionResultForQids = size(qidValues) + ? await queryHostDetectionList('qids', options, request, Logger)(qidValues) + : []; + + return uniqBy('id', allHostDetectionResultForIpAddresses).concat( + uniqBy('id', allHostDetectionResultForQids) + ); +}; + +const queryHostDetectionList = + (type: string, options: Record, request: PolarityRequest, Logger: Logger) => + async (entityValues: string[]): Promise => { + try { + const response = await request.run({ + method: 'GET', + url: `${options.url}/api/2.0/fo/asset/host/vm/detection/`, + qs: { + action: 'list', + [type]: join(',', entityValues), + show_asset_id: 1, + show_results: 1 + }, + headers: { 'X-Requested-With': 'Polarity' }, + json: false + }); + + const responseXml = (response!.body as string) || ''; + + const hostDetections = flow( + get('host_list_vm_detection_output.response.host_list.host'), + processPossibleList(false) + )(await xmlToJson(responseXml, Logger)); + + const processedJson = map( + processResultWithProcessingFormat(HOST_DETECTIONS_LIST_FORMAT), + hostDetections + ); + + return processedJson; + } catch (error) { + const err = parseErrorToReadableJSON(error); + Logger.error( + { + detail: 'Failed to Query Host Detection List', + formattedError: err + }, + 'Querying Host Detection List Failed' + ); + throw error; + } + }; + +const HOST_DETECTIONS_LIST_ITEM_FORMAT: Record = { + qid: 'qid', + type: 'type', + severity: 'severity', + port: 'port', + protocol: 'protocol', + ssl: { path: 'ssl', process: (ssl: any) => (ssl == 1 ? 'Yes' : 'No') }, + results: 'results', + status: 'status', + first_found_datetime: 'first_found_datetime', + last_found_datetime: 'last_found_datetime', + times_found: 'times_found', + last_test_datetime: 'last_test_datetime', + last_update_datetime: 'last_update_datetime', + is_ignored: { + path: 'is_ignored', + process: (is_ignored: any) => (is_ignored == 1 ? 'Yes' : 'No') + }, + is_disabled: { + path: 'is_disabled', + process: (is_disabled: any) => (is_disabled == 1 ? 'Yes' : 'No') + }, + last_processed_datetime: 'last_processed_datetime' +}; + +const HOST_DETECTIONS_LIST_FORMAT: Record = { + id: 'id', + asset_id: 'asset_id', + ip: 'ip', + os: 'os', + dns: 'dns', + last_scan_datetime: 'last_scan_datetime', + last_vm_scanned_date: 'last_vm_scanned_date', + last_vm_scanned_duration: 'last_vm_scanned_duration', + category: 'category', + detection_list: { + path: 'detection_list', + process: flow( + get('detection'), + processPossibleList(false), + map(processResultWithProcessingFormat(HOST_DETECTIONS_LIST_ITEM_FORMAT)), + uniqBy('qid') + ) + } +}; + +export default queryHostDetectionListForAllEntities; \ No newline at end of file diff --git a/src/querying/queryVulnerabilitiesForAllEntities.js b/src/querying/queryVulnerabilitiesForAllEntities.js deleted file mode 100644 index fd93307..0000000 --- a/src/querying/queryVulnerabilitiesForAllEntities.js +++ /dev/null @@ -1,71 +0,0 @@ -const { getOr, size, flow, concat, uniqBy, orderBy } = require('lodash/fp'); -const { - parseErrorToReadableJSON, - buildQueryByQueryType -} = require('../dataTransformations'); - -const VULNERABILITIES_QUERY_FIELDS = - 'title,id,qid,title,port,age,typeDetected,assetId,assetName,assetUUID,lastDetected,' + - 'firstDetected,fqdn,ssl,severity,assetCount,cveids,vulnStatus,ignored,disabled,' + - 'isQualysPatchable,missingPatchCount,patchLicenseType,patchPlatform'; - -const limit = 200; - -const queryVulnerabilitiesForAllEntities = async ( - entities, - options, - requestWithDefaults, - Logger, - offset = 0, - allVulnerabilities = [] -) => { - try { - const vulnerabilitiesQuery = buildQueryByQueryType('vulnerabilities', entities); - - const vulnerabilitiesData = getOr( - [], - 'body', - await requestWithDefaults({ - uri: `${options.url}/portal-front/rest/assetview/1.0/assets`, - qs: { - havingQuery: vulnerabilitiesQuery, - fields: VULNERABILITIES_QUERY_FIELDS, - limit, - offset - }, - options - }) - ); - - const foundVulnerabilities = flow( - concat(vulnerabilitiesData), - uniqBy('id') - )(allVulnerabilities); - - if (size(vulnerabilitiesData) === 200) { - return await queryVulnerabilitiesForAllEntities( - entities, - options, - requestWithDefaults, - Logger, - offset + limit, - foundVulnerabilities - ); - } - - return orderBy('lastDetected.iMillis', 'desc')(foundVulnerabilities); - } catch (error) { - const err = parseErrorToReadableJSON(error); - Logger.error( - { - detail: 'Failed to Query Vulnerabilities', - formattedError: err - }, - 'Query Vulnerabilities Failed' - ); - - throw error; - } -}; - -module.exports = queryVulnerabilitiesForAllEntities; diff --git a/src/querying/queryVulnerabilitiesForAllEntities.ts b/src/querying/queryVulnerabilitiesForAllEntities.ts new file mode 100644 index 0000000..f19069e --- /dev/null +++ b/src/querying/queryVulnerabilitiesForAllEntities.ts @@ -0,0 +1,61 @@ +import { size, flow, concat, uniqBy, orderBy } from 'lodash/fp'; +import type { Entity, Logger } from '@polarityio/integration-types'; +import type { PolarityRequest } from 'polarity-integration-utils'; + +import { parseErrorToReadableJSON, buildQueryByQueryType } from '../dataTransformations'; + +/* eslint-disable @typescript-eslint/no-explicit-any */ + +const VULNERABILITIES_QUERY_FIELDS = 'title,id,qid,...'; +const limit = 200; + +const queryVulnerabilitiesForAllEntities = async ( + entities: Entity[], + options: Record, + request: PolarityRequest, + Logger: Logger, + offset = 0, + allVulnerabilities: any[] = [] +): Promise => { + try { + const vulnerabilitiesQuery = buildQueryByQueryType('vulnerabilities', entities); + + const response = await request.run({ + url: `${options.url}/portal-front/rest/assetview/1.0/assets`, + qs: { + havingQuery: vulnerabilitiesQuery, + fields: VULNERABILITIES_QUERY_FIELDS, + limit, + offset + } + }); + + const vulnerabilitiesData = (response!.body as any[]) || []; + const foundVulnerabilities = flow( + concat(vulnerabilitiesData), + uniqBy('id') + )(allVulnerabilities); + + if (size(vulnerabilitiesData) === 200) { + return await queryVulnerabilitiesForAllEntities( + entities, + options, + request, + Logger, + offset + limit, + foundVulnerabilities + ); + } + + return orderBy('lastDetected.iMillis', 'desc')(foundVulnerabilities); + } catch (error) { + const err = parseErrorToReadableJSON(error); + Logger.error( + { detail: 'Failed to Query Vulnerabilities', formattedError: err }, + 'Query Vulnerabilities Failed' + ); + throw error; + } +}; + +export default queryVulnerabilitiesForAllEntities; diff --git a/src/validateOptions.js b/src/validateOptions.js deleted file mode 100644 index 9eeb158..0000000 --- a/src/validateOptions.js +++ /dev/null @@ -1,45 +0,0 @@ -const fp = require('lodash/fp'); -const reduce = require('lodash/fp/reduce').convert({ cap: false }); - - -const validateStringOptions = (stringOptionsErrorMessages, options, otherErrors = []) => - reduce((agg, message, optionName) => { - const isString = typeof options[optionName].value === 'string'; - const isEmptyString = isString && fp.isEmpty(options[optionName].value); - - return !isString || isEmptyString - ? agg.concat({ - key: optionName, - message - }) - : agg; - }, otherErrors)(stringOptionsErrorMessages); - -const validateUrlOption = (url, otherErrors = []) => { - const endWithError = - url && url.endsWith('/') - ? otherErrors.concat({ - key: 'url', - message: - 'Your URL must not end with a /' - }) - : otherErrors; - if (endWithError.length) return endWithError; - - if (url) { - try { - new URL(url); - } catch (_) { - return otherErrors.concat({ - key: 'url', - message: - 'This URL is invalid. You must provide a valid URL.' - }); - } - } - - return otherErrors; -}; - - -module.exports = { validateStringOptions, validateUrlOption }; diff --git a/src/validateOptions.ts b/src/validateOptions.ts new file mode 100644 index 0000000..f2052f1 --- /dev/null +++ b/src/validateOptions.ts @@ -0,0 +1,63 @@ +import fp from 'lodash/fp'; +import reduce from 'lodash/fp/reduce'; +import type { ValidationError, ValidateOptionsUserOptions } from '@polarityio/integration-types'; + +const uncappedReduce = (reduce as any).convert({ cap: false }); + +export const validateStringOptions = ( + stringOptionsErrorMessages: Record, + options: ValidateOptionsUserOptions, + otherErrors: ValidationError[] = [] +): ValidationError[] => + uncappedReduce((agg: ValidationError[], message: string, optionName: string) => { + const isString = typeof options[optionName].value === 'string'; + const isEmptyString = isString && fp.isEmpty(options[optionName].value as string); + + return !isString || isEmptyString ? agg.concat({ key: optionName, message }) : agg; + }, otherErrors)(stringOptionsErrorMessages); + +export const validateUrlOption = ( + url: string, + otherErrors: ValidationError[] = [] +): ValidationError[] => { + const endWithError = + url && url.endsWith('/') + ? otherErrors.concat({ + key: 'url', + message: 'Your URL must not end with a /' + }) + : otherErrors; + + if (endWithError.length) return endWithError; + + if (url) { + try { + new URL(url); + } catch { + return otherErrors.concat({ + key: 'url', + message: 'This URL is invalid. You must provide a valid URL.' + }); + } + } + + return otherErrors; +}; + +export const validateCustomTypeValueRegex = ( + options: ValidateOptionsUserOptions, + otherErrors: ValidationError[] = [] +): ValidationError[] => { + const regexValue = options.customTypeValueRegex?.value as string | undefined; + if (regexValue) { + try { + new RegExp(regexValue); + } catch { + return otherErrors.concat({ + key: 'customTypeValueRegex', + message: 'Invalid regular expression syntax.' + }); + } + } + return otherErrors; +}; \ No newline at end of file diff --git a/styles/styles.less b/styles/styles.less deleted file mode 100644 index 38d0270..0000000 --- a/styles/styles.less +++ /dev/null @@ -1,112 +0,0 @@ -.block { - margin-bottom: 5px; -} - -hr { - display: block; - margin-top: 0.5em; - margin-bottom: 0.5em; - margin-left: auto; - margin-right: auto; - border-style: inset; - border-width: 1px; -} - -.expandable-title { - display: block; - color: #5ccd18 !important; - font-weight: 600; - line-height: 16px; - margin-bottom: 5px; - .text { - color: #5ccd18 !important; - border-bottom: 1px solid #5ccd18; - .desc { - color: black !important; - font-weight: 400 !important; - } - } -} - -.p-title { - font-size: 14px !important; - font-weight: 500; - text-decoration: underline; -} - -hr { - margin-top: 13px; -} - -.load-more { - margin-top: 10px; - font-size: 14px; - color: darkgray !important; - &:hover { - color: rgb(119, 119, 119) !important; - } -} - -.loading { - font-size: 14px; - margin-top: 10px; - color: darkgray !important; -} - -.no-style-button { - cursor: pointer; - font-size: 100%; - font-family: inherit; - border: 0; - padding: 0; - background-color: transparent; - - &:hover, - &:focus { - background: inherit; - } - - &:focus { - outline: none; - } - - &:active { - transform: none; - } -} - -.copy-button { - width: 15px; - border-radius: 3px; - &:hover, - &:focus { - background: inherit; - background-color: rgba(153, 153, 153, 0.2); - border-color: rgba(153, 153, 153, 0.2); - cursor: pointer; - } - - &:focus { - outline: none; - } - - &:active { - cursor: pointer; - .header-button-icon { - color: #fff; - } - background-color: rgba(100, 100, 100, 0.4);; - border-color: rgba(100, 100, 100, 0.4);; - transform: none; - } -} - -.copied-label { - color: lightgray; -} - - - -.operation-button-icon { - color: #9e9e9e; -} diff --git a/templates/block.hbs b/templates/block.hbs deleted file mode 100644 index f30ead5..0000000 --- a/templates/block.hbs +++ /dev/null @@ -1,274 +0,0 @@ - -{{#each tabKeys as |tabKey|}} - {{#if (eq activeTab tabKey)}} - {{#each (get details tabKey) as |displayField displayFieldIndex|}} - {{#if displayField.isTitle}} - {{#if (and displayField.displayLink (or displayField.value displayField.label))}} -

- {{#if displayField.icon}} - {{fa-icon displayField.icon fixedWidth=true}} - {{/if}} - - {{#if displayField.capitalize}} - {{capitalize (or displayField.label displayField.value)}} - {{else}} - {{or displayField.label displayField.value}} - {{/if}} - - {{fa-icon "external-link" class="p-link ml-1"}} -

- {{else if displayField.showLabelAndValue}} -

- {{#if displayField.displayLink}} - - {{displayField.label}}: {{displayField.value}} - - {{else}} - {{displayField.label}}: {{displayField.value}} - {{/if}} - {{#if - (and - displayField.pathIsLinkToMoreData (not displayField.moreDataIsPopulated) - ) - }} - {{fa-icon "spinner-third" fixedWidth=true spin=true}} - {{/if}} -

- {{else}} -

- {{#if displayField.displayLink}} - - {{or displayField.label displayField.value}} - - {{else}} - {{or displayField.label displayField.value}} - {{/if}} - {{#if - (and - displayField.pathIsLinkToMoreData (not displayField.moreDataIsPopulated) - ) - }} - {{fa-icon "spinner-third" fixedWidth=true spin=true}} - {{/if}} -

- {{/if}} - {{else if displayField.isTextBlock}} -
- {{#if displayField.capitalize}} - {{capitalize displayField.value}} - {{else}} - {{displayField.value}} - {{/if}} -
- {{else if displayField.isListOfLinks}} -
- {{displayField.label}}: - - {{#each displayField.value as |content contentIndex|}} - {{#if content.capitalize}}{{capitalize content.id}}{{else}}{{content.id}}{{/if}}{{#if (not (eq contentIndex (sub displayField.value.length 1)))}}, {{/if}} - {{/each}} - -
- {{else if displayField.isHtml}} -
- {{#if (get expandableTitleStates (concat displayFieldIndex 0 displayField.label))}} - - {{displayField.label}} {{fa-icon "angle-up"}} - -
- {{html-safe displayField.value}} -
- {{else}} - - {{displayField.label}} {{fa-icon "angle-down"}} - - {{/if}} -
- {{else if displayField.isList}} -

- {{displayField.label}} -

-
- {{#each displayField.value as |itemDisplayFields itemDisplayFieldsIndex|}} - {{#if displayField.collapsibleListItems}} - {{#if (get expandableTitleStates (concat displayFieldIndex itemDisplayFieldsIndex displayField.label))}} - - {{itemDisplayFields.0.value}} {{fa-icon "angle-up"}} - - {{#each itemDisplayFields as |itemDisplayField itemDisplayFieldIndex|}} - {{#if (not (eq itemDisplayFieldIndex 0))}} -
- {{itemDisplayField.label}}: - - {{#if itemDisplayField.isDate}} - {{moment-format - itemDisplayField.value - "MM/DD/YYYY HH:mm:ss" - timeZone=timezone - }} - {{else}} - {{itemDisplayField.value}} - {{/if}} - - {{#if itemDisplayField.fieldIsCopyable}} - - {{#if (get copiedStates (concat displayFieldIndex itemDisplayField.label itemDisplayFieldIndex))}} - Copied - {{/if}} - {{/if}} -
- {{/if}} - {{/each}} - {{else}} - - {{itemDisplayFields.0.value}} {{fa-icon "angle-down"}} - - {{/if}} - {{else}} - {{#each itemDisplayFields as |itemDisplayField itemDisplayFieldIndex|}} -
- {{itemDisplayField.label}}: - - {{#if itemDisplayField.isDate}} - {{moment-format - itemDisplayField.value - "MM/DD/YYYY HH:mm:ss" - timeZone=timezone - }} - {{else}} - {{itemDisplayField.value}} - {{/if}} - - {{#if itemDisplayField.fieldIsCopyable}} - - {{#if (get copiedStates (concat displayFieldIndex itemDisplayField.label itemDisplayFieldIndex))}} - Copied - {{/if}} - {{/if}} -
- {{/each}} - {{/if}} - {{/each}} -
- {{else if displayField.isNewSectionLineBreak}} -
- {{else if displayField.fieldIsCopyable}} -
- {{displayField.label}}: - - {{#if displayField.isDate}} - {{moment-format - displayField.value - "MM/DD/YYYY HH:mm:ss" - timeZone=timezone - }} - {{else if displayField.capitalize}} - {{capitalize displayField.value}} - {{else}} - {{displayField.value}} - {{/if}} - - - {{#if (get copiedStates (concat displayFieldIndex displayField.label 0))}} - Copied - {{/if}} -
- {{else}} -
- {{displayField.label}}: - - {{#if displayField.pathToOnePropertyFromMoreDataToDisplay}} - {{fa-icon "spinner-third" fixedWidth=true spin=true}} - {{else}} - {{#if displayField.isDate}} - {{moment-format - displayField.value - "MM/DD/YYYY HH:mm:ss" - timeZone=timezone - }} - {{else if displayField.displayLink}} - - {{#if displayField.capitalize}} - {{capitalize displayField.value}} - {{else}} - {{displayField.value}} - {{/if}} - - {{else if displayField.capitalize}} - {{capitalize displayField.value}} - {{else}} - {{displayField.value}} - {{/if}} - {{/if}} - -
- {{/if}} - {{/each}} - {{/if}} -{{/each}} \ No newline at end of file diff --git a/test/integration.test.ts b/test/integration.test.ts new file mode 100644 index 0000000..0c813ef --- /dev/null +++ b/test/integration.test.ts @@ -0,0 +1,465 @@ +import { vi, describe, it, expect, beforeEach } from 'vitest'; +import type { + Entity, + DoLookupUserOptions, + IntegrationContext +} from '@polarityio/integration-types'; +import type { HttpRequestResponse } from 'polarity-integration-utils'; + +function createEntity(type: string, value: string): Entity { + const isDomain = /^[a-zA-Z0-9][a-zA-Z0-9-]{1,61}[a-zA-Z0-9](?:\.[a-zA-Z]{2,})+$/.test(value); + const isIPv4 = /^(?:[0-9]{1,3}\.){3}[0-9]{1,3}$/.test(value); + const isIPv6 = /^[\da-fA-F:]+$/.test(value) && value.includes(':'); + const typeId = type === 'IP' ? 'IPv4' : type.startsWith('custom.') ? 'custom' : type; + return { + value, + rawValue: value, + displayValue: value, + type: typeId, + types: [type], + isDomain, + isIPv4, + isIP: isIPv4 || isIPv6, + isIPv6, + isEmail: false, + isHash: false, + isHex: false, + isHTMLTag: false, + isMD5: false, + isPrivateIP: false, + isSHA1: false, + isSHA256: false, + isSHA512: false, + isURL: false, + channels: [], + requestContext: { requestType: 'OnDemand', isUserInitiated: true } + } as unknown as Entity; +} + +function createMockIntegrationContext() { + const createCacheScope = () => ({ get: vi.fn(), set: vi.fn(), delete: vi.fn() }); + const logger = { + child: vi.fn().mockReturnThis(), + info: vi.fn(), + debug: vi.fn(), + trace: vi.fn(), + warn: vi.fn(), + error: vi.fn(), + fatal: vi.fn() + }; + (logger.child as any).mockReturnValue(logger); + return { + cache: { + global: createCacheScope(), + integration: createCacheScope(), + user: createCacheScope() + }, + integrationId: 'test-integration', + userId: 1, + logger, + startPolling: vi.fn(), + stopPolling: vi.fn() + }; +} + +const { mockRun, mockSetLogger } = vi.hoisted(() => ({ + mockRun: vi.fn(), + mockSetLogger: vi.fn() +})); + +vi.mock('polarity-integration-utils', () => { + class MockIntegrationError extends Error { + cause: unknown; + meta: unknown; + constructor(message: string, props?: any) { + super(message); + this.name = 'IntegrationError'; + this.cause = props?.cause; + this.meta = props?.meta; + } + } + + class MockPolarityRequest { + run = mockRun; + runInParallel = vi.fn(); + userOptions: any = null; + network: any = null; + limiter: any = null; + hooks = { beforeRequest: [], afterResponse: [], onApiError: [], onNetworkError: [] }; + constructor(_opts?: any) {} + } + + return { + PolarityRequest: MockPolarityRequest, + setLogger: mockSetLogger, + IntegrationError: MockIntegrationError, + ApiRequestError: class extends Error { + constructor(m: string) { + super(m); + this.name = 'ApiRequestError'; + } + }, + NetworkError: class extends Error { + constructor(m: string) { + super(m); + this.name = 'NetworkError'; + } + } + }; +}); + +import { doLookup, startup, validateOptions } from '../src/integration'; + +const createMockContext = () => createMockIntegrationContext(); + +const createOptions = (overrides: Partial> = {}): DoLookupUserOptions => + ({ + url: 'https://qualysapi.qualys.com', + username: 'testuser', + password: 'testpass', + ...overrides + }) as unknown as DoLookupUserOptions; + +const HOST_DETECTION_XML = ` + + + + + 12345 + 67890 + 192.168.1.1 + Linux 4.15 + host.example.com + 2024-01-15T10:30:00Z + 2024-01-15T10:30:00Z + 120 + Confirmed + + + 38623 + Confirmed + 3 + 443 + tcp + 1 + SSL Certificate - Subject + Active + 2024-01-10T08:00:00Z + 2024-01-15T10:30:00Z + 5 + 2024-01-15T10:30:00Z + 2024-01-15T10:30:00Z + 0 + 0 + 2024-01-15T10:30:00Z + + + + + +`; + +const EMPTY_HOST_DETECTION_XML = ` + + + +`; + +describe('Qualys Integration', () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + describe('startup', () => { + it('should initialize without error', () => { + const mockLogger = { + info: vi.fn(), + debug: vi.fn(), + trace: vi.fn(), + warn: vi.fn(), + error: vi.fn(), + fatal: vi.fn(), + child: vi.fn() + }; + expect(() => startup(mockLogger as any)).not.toThrow(); + }); + }); + + describe('doLookup', () => { + beforeEach(() => { + const mockLogger = { + info: vi.fn(), + debug: vi.fn(), + trace: vi.fn(), + warn: vi.fn(), + error: vi.fn(), + fatal: vi.fn(), + child: vi.fn() + }; + startup(mockLogger as any); + }); + + it('should return lookup results with data for an IPv4 entity with host detections', async () => { + mockRun.mockResolvedValue({ + statusCode: 200, + body: HOST_DETECTION_XML, + headers: {} + } as HttpRequestResponse); + + const entities: Entity[] = [createEntity('IPv4', '192.168.1.1')]; + const options = createOptions(); + const context = createMockContext(); + + const results = await doLookup(entities, options, context as unknown as IntegrationContext); + + expect(results).toHaveLength(1); + expect(results![0].entity.value).toBe('192.168.1.1'); + expect(results![0].data).not.toBeNull(); + expect(results![0].data!.summary).toContain('Host Detections: 1'); + expect(results![0].data!.details).toHaveProperty('tabKeys'); + }); + + it('should return data: null for an entity with no results', async () => { + mockRun.mockResolvedValue({ + statusCode: 200, + body: EMPTY_HOST_DETECTION_XML, + headers: {} + } as HttpRequestResponse); + + const entities: Entity[] = [createEntity('IPv4', '10.0.0.1')]; + const options = createOptions(); + const context = createMockContext(); + + const results = await doLookup(entities, options, context as unknown as IntegrationContext); + + expect(results).toHaveLength(1); + expect(results![0].data).toBeNull(); + }); + + it('should handle multiple entities', async () => { + mockRun.mockResolvedValue({ + statusCode: 200, + body: EMPTY_HOST_DETECTION_XML, + headers: {} + } as HttpRequestResponse); + + const entities: Entity[] = [ + createEntity('IPv4', '192.168.1.1'), + createEntity('IPv4', '192.168.1.2') + ]; + const options = createOptions(); + const context = createMockContext(); + + const results = await doLookup(entities, options, context as unknown as IntegrationContext); + + expect(results).toHaveLength(2); + }); + + it('should return data: null for ignored IPs (127.0.0.1)', async () => { + mockRun.mockResolvedValue({ + statusCode: 200, + body: EMPTY_HOST_DETECTION_XML, + headers: {} + } as HttpRequestResponse); + + const entities: Entity[] = [createEntity('IPv4', '127.0.0.1')]; + const options = createOptions(); + const context = createMockContext(); + + const results = await doLookup(entities, options, context as unknown as IntegrationContext); + + expect(results).toHaveLength(1); + expect(results![0].data).toBeNull(); + }); + + it('should pass correct request options to the API', async () => { + mockRun.mockResolvedValue({ + statusCode: 200, + body: EMPTY_HOST_DETECTION_XML, + headers: {} + } as HttpRequestResponse); + + const entities: Entity[] = [createEntity('IPv4', '10.20.30.40')]; + const options = createOptions({ url: 'https://qualysapi.example.com' }); + const context = createMockContext(); + + await doLookup(entities, options, context as unknown as IntegrationContext); + + expect(mockRun).toHaveBeenCalledWith( + expect.objectContaining({ + method: 'GET', + url: 'https://qualysapi.example.com/api/2.0/fo/asset/host/vm/detection/', + qs: expect.objectContaining({ + action: 'list', + ips: '10.20.30.40', + show_asset_id: 1, + show_results: 1 + }), + headers: { 'X-Requested-With': 'Polarity' }, + json: false + }) + ); + }); + + it('should throw IntegrationError when API request fails', async () => { + const apiError = new Error('API request failed'); + mockRun.mockRejectedValue(apiError); + + const entities: Entity[] = [createEntity('IPv4', '192.168.1.1')]; + const options = createOptions(); + const context = createMockContext(); + + await expect( + doLookup(entities, options, context as unknown as IntegrationContext) + ).rejects.toThrow('API request failed'); + }); + + it('should throw IntegrationError with default message for non-Error objects', async () => { + mockRun.mockRejectedValue('string error'); + + const entities: Entity[] = [createEntity('IPv4', '192.168.1.1')]; + const options = createOptions(); + const context = createMockContext(); + + await expect( + doLookup(entities, options, context as unknown as IntegrationContext) + ).rejects.toThrow('Searching Failed'); + }); + + it('should not make API calls when only ignored IPs are provided', async () => { + const entities: Entity[] = [ + createEntity('IPv4', '127.0.0.1'), + createEntity('IPv4', '255.255.255.255'), + createEntity('IPv4', '0.0.0.0') + ]; + const options = createOptions(); + const context = createMockContext(); + + const results = await doLookup(entities, options, context as unknown as IntegrationContext); + + expect(mockRun).not.toHaveBeenCalled(); + expect(results).toHaveLength(3); + results!.forEach((r) => expect(r.data).toBeNull()); + }); + + it('should handle QID custom type entities', async () => { + mockRun.mockResolvedValue({ + statusCode: 200, + body: HOST_DETECTION_XML, + headers: {} + } as HttpRequestResponse); + + const entity = createEntity('custom', 'QID: 38623') as any; + entity.type = 'custom'; + entity.types = ['custom.qid']; + const entities: Entity[] = [entity]; + const options = createOptions(); + const context = createMockContext(); + + const results = await doLookup(entities, options, context as unknown as IntegrationContext); + + expect(results).toHaveLength(1); + expect(mockRun).toHaveBeenCalledWith( + expect.objectContaining({ + qs: expect.objectContaining({ + qids: '38623' + }) + }) + ); + }); + }); + + describe('validateOptions', () => { + it('should return no errors when all options are valid', () => { + const options = { + url: { value: 'https://qualysapi.qualys.com' }, + username: { value: 'testuser' }, + password: { value: 'testpass' } + }; + const context = createMockContext(); + + const errors = validateOptions(options as any, context as unknown as IntegrationContext); + + expect(errors).toHaveLength(0); + }); + + it('should return error when url is empty', () => { + const options = { + url: { value: '' }, + username: { value: 'testuser' }, + password: { value: 'testpass' } + }; + const context = createMockContext(); + + const errors = validateOptions(options as any, context as unknown as IntegrationContext); + + expect(errors.length).toBeGreaterThan(0); + expect(errors.some((e) => e.key === 'url')).toBe(true); + }); + + it('should return error when username is empty', () => { + const options = { + url: { value: 'https://qualysapi.qualys.com' }, + username: { value: '' }, + password: { value: 'testpass' } + }; + const context = createMockContext(); + + const errors = validateOptions(options as any, context as unknown as IntegrationContext); + + expect(errors.some((e) => e.key === 'username')).toBe(true); + }); + + it('should return error when password is empty', () => { + const options = { + url: { value: 'https://qualysapi.qualys.com' }, + username: { value: 'testuser' }, + password: { value: '' } + }; + const context = createMockContext(); + + const errors = validateOptions(options as any, context as unknown as IntegrationContext); + + expect(errors.some((e) => e.key === 'password')).toBe(true); + }); + + it('should return error when URL ends with a slash', () => { + const options = { + url: { value: 'https://qualysapi.qualys.com/' }, + username: { value: 'testuser' }, + password: { value: 'testpass' } + }; + const context = createMockContext(); + + const errors = validateOptions(options as any, context as unknown as IntegrationContext); + + expect(errors.some((e) => e.key === 'url' && e.message.includes('/'))).toBe(true); + }); + + it('should return error when URL is invalid', () => { + const options = { + url: { value: 'not-a-valid-url' }, + username: { value: 'testuser' }, + password: { value: 'testpass' } + }; + const context = createMockContext(); + + const errors = validateOptions(options as any, context as unknown as IntegrationContext); + + expect(errors.some((e) => e.key === 'url' && e.message.includes('invalid'))).toBe(true); + }); + + it('should return multiple errors when multiple fields are invalid', () => { + const options = { + url: { value: '' }, + username: { value: '' }, + password: { value: '' } + }; + const context = createMockContext(); + + const errors = validateOptions(options as any, context as unknown as IntegrationContext); + + expect(errors.length).toBeGreaterThanOrEqual(3); + }); + }); +}); diff --git a/test/web-components/details.test.ts b/test/web-components/details.test.ts new file mode 100644 index 0000000..7600b8b --- /dev/null +++ b/test/web-components/details.test.ts @@ -0,0 +1,319 @@ +import { describe, it, expect, beforeEach, afterEach } from 'vitest'; +import { DetailsComponent } from '../../web-components/details'; +import type { IBlock } from '@polarityio/pi-components'; + +customElements.define('test-qualys-details', DetailsComponent); + +function createBlock(details: Record): IBlock { + return { + integrationId: 'qualys-integration', + acronym: 'QLS', + data: { details, summary: ['Host Detections: 1'] } + }; +} + +async function renderComponent(block: IBlock): Promise { + const el = document.createElement('test-qualys-details') as DetailsComponent; + (el as any).services = { + currentServer: {}, + currentUser: {}, + flashMessages: {}, + moment: {}, + notificationsData: {}, + polarityx: {}, + searchData: {}, + session: {}, + store: {}, + windowService: {}, + log: {}, + integrationMessenger: {}, + lookupService: () => undefined + }; + el.block = block; + document.body.appendChild(el); + await el.updateComplete; + return el; +} + +function getShadowHTML(el: DetailsComponent): string { + return el.shadowRoot?.innerHTML || ''; +} + +describe('Qualys DetailsComponent', () => { + let el: DetailsComponent | null = null; + + afterEach(() => { + if (el && el.parentNode) { + el.parentNode.removeChild(el); + } + el = null; + }); + + it('should render without errors when given valid host detection data', async () => { + el = await renderComponent( + createBlock({ + tabKeys: ['hostDetections'], + hostDetections: [ + { label: 'IP Address', value: '192.168.1.1', isTitle: true, showLabelAndValue: true }, + { label: 'Host Detection ID', value: '12345' }, + { label: 'Asset Id', value: '67890' }, + { label: 'Operating System', value: 'Linux 4.15' } + ] + }) + ); + + const html = getShadowHTML(el); + expect(html).toContain('192.168.1.1'); + expect(html).toContain('IP Address'); + }); + + it('should render nothing when tabKeys is empty', async () => { + el = await renderComponent(createBlock({ tabKeys: [] })); + const html = getShadowHTML(el); + expect(html).not.toContain('pi-tab'); + expect(html).not.toContain('pi-key-value'); + }); + + it('should render nothing when details is undefined', async () => { + const block: IBlock = { + integrationId: 'qualys-integration', + acronym: 'QLS', + data: { details: undefined as any, summary: [] } + }; + el = await renderComponent(block); + const html = getShadowHTML(el); + expect(html).not.toContain('pi-key-value'); + }); + + it('should render key-value pairs for standard fields', async () => { + el = await renderComponent( + createBlock({ + tabKeys: ['hostDetections'], + hostDetections: [ + { label: 'Operating System', value: 'Linux 4.15' }, + { label: 'DNS', value: 'host.example.com' }, + { label: 'Category', value: 'Confirmed' } + ] + }) + ); + + const html = getShadowHTML(el); + expect(html).toContain('Linux 4.15'); + expect(html).toContain('host.example.com'); + expect(html).toContain('Confirmed'); + }); + + it('should render title fields with showLabelAndValue', async () => { + el = await renderComponent( + createBlock({ + tabKeys: ['hostDetections'], + hostDetections: [ + { label: 'IP Address', value: '10.0.0.1', isTitle: true, showLabelAndValue: true } + ] + }) + ); + + const html = getShadowHTML(el); + expect(html).toContain('IP Address'); + expect(html).toContain('10.0.0.1'); + expect(html).toContain(' { + el = await renderComponent( + createBlock({ + tabKeys: ['hostDetections'], + hostDetections: [{ label: 'Last Scanned', value: '2024-01-15T10:30:00Z', isDate: true }] + }) + ); + + const html = getShadowHTML(el); + expect(html).toContain('Last Scanned'); + // Should contain a formatted date (not the raw ISO string) + expect(html).toContain('01/15/2024'); + }); + + it('should render collapsible list items for detection lists', async () => { + el = await renderComponent( + createBlock({ + tabKeys: ['hostDetections'], + hostDetections: [ + { + label: 'Detections List', + isList: true, + collapsibleListItems: true, + indent: 1, + value: [ + [ + { label: 'Detection Result', value: 'SSL Certificate' }, + { label: 'QID', value: '38623', fieldIsCopyable: true, shouldCopyFieldLabel: true }, + { label: 'Severity', value: '3' } + ] + ] + } + ] + }) + ); + + const html = getShadowHTML(el); + expect(html).toContain('Detections List'); + expect(html).toContain('SSL Certificate'); + // Collapsed by default — inner fields should not be visible + expect(html).not.toContain('Severity'); + }); + + it('should render HTML content in expandable sections', async () => { + el = await renderComponent( + createBlock({ + tabKeys: ['hostDetections'], + hostDetections: [ + { + label: 'Diagnosis', + value: '

This is a vulnerability description

', + isHtml: true + } + ] + }) + ); + + const html = getShadowHTML(el); + expect(html).toContain('Diagnosis'); + // HTML content is collapsed by default + expect(html).not.toContain('This is a vulnerability description'); + }); + + it('should render the copy button', async () => { + el = await renderComponent( + createBlock({ + tabKeys: ['hostDetections'], + hostDetections: [{ label: 'IP', value: '192.168.1.1' }] + }) + ); + + const html = getShadowHTML(el); + expect(html).toContain('pi-copy-button'); + }); + + it('should render section breaks for isNewSectionLineBreak fields', async () => { + el = await renderComponent( + createBlock({ + tabKeys: ['hostDetections'], + hostDetections: [ + { label: 'Field1', value: 'value1' }, + { isNewSectionLineBreak: true }, + { label: 'Field2', value: 'value2' } + ] + }) + ); + + const html = getShadowHTML(el); + expect(html).toContain('section-break'); + }); + + it('should not render tabs when there is only one visible tab', async () => { + el = await renderComponent( + createBlock({ + tabKeys: ['hostDetections'], + hostDetections: [{ label: 'Field', value: 'value' }] + }) + ); + + const html = getShadowHTML(el); + expect(html).not.toContain('pi-tab-group'); + }); + + it('should render tabs when there are multiple visible tabs', async () => { + el = await renderComponent( + createBlock({ + tabKeys: ['hostDetections', 'knowledgeBase'], + hostDetections: [{ label: 'IP', value: '192.168.1.1' }], + knowledgeBase: [{ label: 'Title', value: 'Vuln Title', isTitle: true }] + }) + ); + + const html = getShadowHTML(el); + expect(html).toContain('pi-tab-group'); + expect(html).toContain('Host Detections'); + expect(html).toContain('Knowledge Base'); + }); + + it('should skip tabs with empty data arrays', async () => { + el = await renderComponent( + createBlock({ + tabKeys: ['hostDetections', 'emptyTab'], + hostDetections: [{ label: 'IP', value: '192.168.1.1' }], + emptyTab: [] + }) + ); + + const html = getShadowHTML(el); + // Should NOT show a tab group since only one tab has data + expect(html).not.toContain('pi-tab-group'); + }); + + it('should render copyable fields with copy button', async () => { + el = await renderComponent( + createBlock({ + tabKeys: ['hostDetections'], + hostDetections: [ + { label: 'QID', value: '38623', fieldIsCopyable: true, shouldCopyFieldLabel: true } + ] + }) + ); + + const html = getShadowHTML(el); + expect(html).toContain('copyable-field'); + expect(html).toContain('38623'); + expect(html).toContain('QID'); + }); + + it('should render links with pi-external-link for title fields with displayLink', async () => { + el = await renderComponent( + createBlock({ + tabKeys: ['hostDetections'], + hostDetections: [ + { + label: 'Host', + value: 'server1', + isTitle: true, + displayLink: 'https://example.com/host/1' + } + ] + }) + ); + + const html = getShadowHTML(el); + expect(html).toContain('pi-external-link'); + expect(html).toContain('https://example.com/host/1'); + }); + + it('should render list of links', async () => { + el = await renderComponent( + createBlock({ + tabKeys: ['hostDetections'], + hostDetections: [ + { + label: 'CVE IDs', + isListOfLinks: true, + value: [ + { + url: 'https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2024-0001', + id: 'CVE-2024-0001' + }, + { + url: 'https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2024-0002', + id: 'CVE-2024-0002' + } + ] + } + ] + }) + ); + + const html = getShadowHTML(el); + expect(html).toContain('CVE IDs'); + expect(html).toContain('CVE-2024-0001'); + expect(html).toContain('CVE-2024-0002'); + expect(html).toContain('pi-external-link'); + }); +}); diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..7bee2dc --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "CommonJS", + "moduleResolution": "node", + "outDir": "./dist", + "rootDir": "./src", + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "strict": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "declaration": true, + "sourceMap": true, + "experimentalDecorators": true, + "useDefineForClassFields": false + }, + "include": ["src/**/*.ts"], + "exclude": ["node_modules", "dist", "web-components"] +} diff --git a/tsconfig.web.json b/tsconfig.web.json new file mode 100644 index 0000000..ce44f5f --- /dev/null +++ b/tsconfig.web.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "ESNext", + "moduleResolution": "bundler", + "experimentalDecorators": true, + "useDefineForClassFields": false, + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "noEmit": true + }, + "include": ["web-components/**/*.ts"] +} diff --git a/vite.config.js b/vite.config.js new file mode 100644 index 0000000..1d275b4 --- /dev/null +++ b/vite.config.js @@ -0,0 +1,36 @@ +import config from './config/config.json' with { type: 'json' }; +import { defineConfig } from 'vite'; +import { transformComponentNames, VIRTUAL_COMPONENTS_ID } from '@polarityio/vite-plugin-icl'; +import { resolve } from 'node:path'; + +export default defineConfig({ + plugins: [ + transformComponentNames({ + componentsDir: resolve(__dirname, 'web-components'), + componentRegistries: [ + '@polarityio/pi-components/component-registry.json', + ], + }), + ], + build: { + outDir: 'dist', + emptyOutDir: false, + lib: { + entry: VIRTUAL_COMPONENTS_ID, + name: 'qualys-v2_build', + fileName: () => { + const webComponentsConfig = config.webComponents; + return webComponentsConfig.moduleFilename; + }, + formats: ['es'], + }, + minify: false, + rollupOptions: { + output: { + globals: { + lit: 'lit', + }, + }, + }, + }, +}); diff --git a/vitest.config.ts b/vitest.config.ts new file mode 100644 index 0000000..e6cf8f6 --- /dev/null +++ b/vitest.config.ts @@ -0,0 +1,54 @@ +import { defineConfig } from 'vitest/config'; +import { playwright } from '@vitest/browser-playwright'; +import path from 'path'; +import { fileURLToPath } from 'url'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); + +const resolveConfig = { + alias: [ + { + find: /^polarity-integration-utils\/requests$/, + replacement: path.resolve( + __dirname, + 'node_modules/polarity-integration-utils/dist/lib/requests/index.js' + ) + }, + { + find: /^polarity-integration-utils$/, + replacement: path.resolve( + __dirname, + 'node_modules/polarity-integration-utils/dist/lib/index.js' + ) + } + ] +}; + +export default defineConfig({ + resolve: resolveConfig, + test: { + projects: [ + { + resolve: resolveConfig, + test: { + name: 'server', + environment: 'node', + include: ['test/**/*.test.ts'], + exclude: ['test/web-components/**'] + } + }, + { + resolve: resolveConfig, + test: { + name: 'browser', + include: ['test/web-components/**/*.test.ts'], + browser: { + enabled: true, + provider: playwright(), + instances: [{ browser: 'chromium' }] + } + } + } + ] + } +}); diff --git a/web-components/details.ts b/web-components/details.ts new file mode 100644 index 0000000..63938c5 --- /dev/null +++ b/web-components/details.ts @@ -0,0 +1,628 @@ +import { css, html, nothing, unsafeCSS } from 'lit'; +import { property, state } from 'lit/decorators.js'; +import { unsafeHTML } from 'lit/directives/unsafe-html.js'; +import { IntegrationComponentBase, IBlock, typographyCSS } from '@polarityio/pi-components'; +import { faAngleUp, faAngleDown, faCopy } from '@polarityio/pi-icon'; + +interface DisplayField { + label?: string; + value?: any; + isTitle?: boolean; + isDate?: boolean; + isList?: boolean; + isListOfLinks?: boolean; + isHtml?: boolean; + isTextBlock?: boolean; + isNewSectionLineBreak?: boolean; + indent?: number; + collapsibleListItems?: boolean; + showLabelAndValue?: boolean; + fieldIsCopyable?: boolean; + shouldCopyFieldLabel?: boolean; + displayLink?: string; + icon?: string; + capitalize?: boolean; +} + +interface Details { + tabKeys: string[]; + [key: string]: DisplayField[] | string[] | any; +} + +/** + * Converts a camelCase or PascalCase key like "hostDetections" + * to a title-cased, spaced label like "Host Detections". + */ +function humanizeTabKey(key: string): string { + return key + .replace(/([a-z])([A-Z])/g, '$1 $2') + .replace(/([A-Z]+)([A-Z][a-z])/g, '$1 $2') + .replace(/^./, (c) => c.toUpperCase()); +} + +/** + * Formats an ISO 8601 date string using Intl.DateTimeFormat (Chrome 90 safe). + */ +function formatDate(value: string): string { + try { + const date = new Date(value); + if (isNaN(date.getTime())) return value; + const tz = Intl.DateTimeFormat().resolvedOptions().timeZone; + return new Intl.DateTimeFormat('en-US', { + month: '2-digit', + day: '2-digit', + year: 'numeric', + hour: '2-digit', + minute: '2-digit', + second: '2-digit', + hour12: false, + timeZone: tz + }).format(date); + } catch { + return String(value); + } +} + +function capitalizeStr(str: string): string { + if (!str) return ''; + return str.charAt(0).toUpperCase() + str.slice(1); +} + +export class DetailsComponent extends IntegrationComponentBase { + @property({ type: Object }) block: IBlock = { + integrationId: '', + acronym: '', + data: { details: {} as any, summary: [] } + }; + + @state() private _activeTab = ''; + @state() private _expandedStates: Record = {}; + @state() private _copiedStates: Record = {}; + + private _copyContentId = `copy-content-${Array.from( + crypto.getRandomValues(new Uint8Array(16)), + (b) => b.toString(16).padStart(2, '0') + ).join('')}`; + + static styles = [ + unsafeCSS(typographyCSS), + css` + :host { + display: block; + font-family: var(--pi-font-family-base, 'NotoSans', sans-serif); + font-size: var(--pi-size-font-base, 0.8125rem); + color: var(--pi-color-font-primary, #fafaff); + line-height: var(--pi-line-height-base, 1.4); + } + + .copy-btn-container { + position: absolute; + right: 10px; + top: 7px; + z-index: 1; + } + + .content-wrapper { + position: relative; + } + + .tab-content { + display: flex; + flex-direction: column; + gap: var(--pi-size-spacing-xxs, 2px); + padding-top: var(--pi-size-spacing-xs, 4px); + } + + .field-row { + display: flex; + align-items: baseline; + gap: var(--pi-size-spacing-xs, 4px); + flex-wrap: wrap; + } + + .text-block { + margin-bottom: var(--pi-size-spacing-xs, 4px); + color: var(--pi-color-font-secondary, #cdced6); + } + + .section-break { + border: none; + border-top: 1px solid var(--pi-color-border-container, #606470); + margin: var(--pi-size-spacing-sm, 8px) 0; + } + + .expandable-title { + display: flex; + align-items: center; + gap: var(--pi-size-spacing-xs, 4px); + cursor: pointer; + background: none; + border: none; + padding: var(--pi-size-spacing-xxs, 2px) 0; + font-family: inherit; + font-size: inherit; + color: var(--pi-color-font-info, #33b2ff); + font-weight: var(--pi-font-weight-semibold, 600); + line-height: 1.2; + margin-bottom: var(--pi-size-spacing-xs, 4px); + } + + .expandable-title:hover { + opacity: 0.8; + } + + .expandable-title-text { + border-bottom: 1px solid var(--pi-color-font-info, #33b2ff); + } + + .list-section { + margin-bottom: var(--pi-size-spacing-xs, 4px); + } + + .list-items { + padding-left: var(--pi-size-spacing-sm, 8px); + display: flex; + flex-direction: column; + gap: var(--pi-size-spacing-xxs, 2px); + } + + .list-item-fields { + padding-left: var(--pi-size-spacing-md, 12px); + display: flex; + flex-direction: column; + gap: var(--pi-size-spacing-xxs, 2px); + } + + .copyable-field { + display: inline-flex; + align-items: center; + gap: var(--pi-size-spacing-xs, 4px); + } + + .copy-inline-btn { + cursor: pointer; + background: none; + border: none; + padding: 0; + display: inline-flex; + align-items: center; + color: var(--pi-color-font-secondary, #cdced6); + } + + .copy-inline-btn:hover { + color: var(--pi-color-font-primary, #fafaff); + } + + .copied-label { + font-size: var(--pi-size-font-xs, 0.75rem); + color: var(--pi-color-font-secondary, #cdced6); + } + + .html-content { + padding-left: var(--pi-size-spacing-md, 12px); + margin-bottom: var(--pi-size-spacing-xs, 4px); + color: var(--pi-color-font-primary, #fafaff); + } + + .link-list { + display: inline; + } + + .kv-key { + color: var(--pi-color-font-secondary, #cdced6); + font-weight: var(--pi-font-weight-semibold, 600); + white-space: nowrap; + } + + .link-separator { + color: var(--pi-color-font-secondary, #cdced6); + } + + .indent-0 { + padding-left: 0; + } + .indent-1 { + padding-left: var(--pi-size-spacing-sm, 8px); + } + .indent-2 { + padding-left: var(--pi-size-spacing-lg, 16px); + } + .indent-3 { + padding-left: var(--pi-size-spacing-xl, 24px); + } + ` + ]; + + connectedCallback() { + super.connectedCallback(); + this._initActiveTab(); + } + + updated(changedProperties: Map) { + super.updated(changedProperties); + if (changedProperties.has('block')) { + this._initActiveTab(); + } + } + + private _initActiveTab() { + const d = this.block.data?.details as Details | undefined; + if (!d?.tabKeys) return; + + // Restore persisted tab from componentState, or pick first non-empty + const persisted = (this.block as any).componentState?.activeTab; + if (persisted && d.tabKeys.includes(persisted) && d[persisted]?.length) { + this._activeTab = persisted; + } else if (!this._activeTab || !d.tabKeys.includes(this._activeTab)) { + this._activeTab = + d.tabKeys.find((key) => d[key] && Array.isArray(d[key]) && d[key].length) || + d.tabKeys[0] || + ''; + } + } + + private _onTabChange(e: CustomEvent) { + const d = this.block.data?.details as Details; + if (!d?.tabKeys) return; + const tabIndex = e.detail?.tabIndex ?? 0; + const tabKey = d.tabKeys.filter((k) => d[k] && Array.isArray(d[k]) && d[k].length)[tabIndex]; + if (tabKey) { + this._activeTab = tabKey; + // Persist active tab in componentState + if (this.block) { + (this.block as any).componentState = { + ...((this.block as any).componentState || {}), + activeTab: tabKey + }; + } + } + } + + private _toggleExpanded(key: string) { + this._expandedStates = { + ...this._expandedStates, + [key]: !this._expandedStates[key] + }; + } + + private _copyField(value: string, label: string, shouldCopyLabel: boolean, stateKey: string) { + const text = shouldCopyLabel ? `${label}: ${value}` : value; + navigator.clipboard.writeText(text); + this._copiedStates = { ...this._copiedStates, [stateKey]: true }; + setTimeout(() => { + this._copiedStates = { ...this._copiedStates, [stateKey]: false }; + }, 3000); + } + + private _beforeCopy = async () => { + this.shadowRoot?.querySelectorAll('[data-pi-card]').forEach((card: any) => { + if (typeof card.expanded !== 'undefined') { + card.expanded = true; + } + }); + await this.updateComplete; + }; + + private _afterCopy = async () => { + // No-op: cards remain expanded after copy + }; + + render() { + const d = this.block.data?.details as Details | undefined; + if (!d?.tabKeys?.length) return nothing; + + const visibleTabs = d.tabKeys.filter((key) => d[key] && Array.isArray(d[key]) && d[key].length); + + if (!visibleTabs.length) return nothing; + + const activeIndex = Math.max(0, visibleTabs.indexOf(this._activeTab)); + + return html` +
+
+ +
+
+ ${visibleTabs.length === 1 + ? this._renderTabContent(d[visibleTabs[0]] as DisplayField[]) + : html` + + ${visibleTabs.map( + (tabKey, idx) => html` + + ${humanizeTabKey(tabKey)} + + ` + )} + ${visibleTabs.map( + (tabKey, idx) => html` + + ${this._renderTabContent(d[tabKey] as DisplayField[])} + + ` + )} + + `} +
+
+ `; + } + + private _renderTabContent(fields: DisplayField[]) { + if (!fields?.length) return nothing; + return html` +
+ ${fields.map((field, idx) => this._renderDisplayField(field, idx))} +
+ `; + } + + private _renderDisplayField(field: DisplayField, fieldIndex: number) { + if (field.isTitle) { + return this._renderTitle(field); + } + if (field.isTextBlock) { + return this._renderTextBlock(field); + } + if (field.isListOfLinks) { + return this._renderListOfLinks(field); + } + if (field.isHtml) { + return this._renderHtmlField(field, fieldIndex); + } + if (field.isList) { + return this._renderList(field, fieldIndex); + } + if (field.isNewSectionLineBreak) { + return html`
`; + } + if (field.fieldIsCopyable) { + return this._renderCopyableField(field, fieldIndex, 0); + } + return this._renderKeyValue(field); + } + + private _renderTitle(field: DisplayField) { + const indentClass = `indent-${field.indent || 0}`; + const labelText = field.capitalize + ? capitalizeStr(field.label || field.value || '') + : field.label || field.value || ''; + + if (field.displayLink && (field.value || field.label)) { + if (field.showLabelAndValue) { + return html` +

+ +

+ `; + } + return html` +

+ +

+ `; + } + + if (field.showLabelAndValue) { + return html`

${field.label}: ${field.value}

`; + } + + return html`

${labelText}

`; + } + + private _renderTextBlock(field: DisplayField) { + const indentClass = `indent-${field.indent || 0}`; + const text = field.capitalize ? capitalizeStr(field.value) : field.value; + return html`
${text}
`; + } + + private _renderListOfLinks(field: DisplayField) { + const indentClass = `indent-${field.indent || 0}`; + const links = field.value as Array<{ url: string; id: string; capitalize?: boolean }>; + return html` +
+ ${field.label}: + + ${links.map( + // prettier-ignore + (link, i) => html`${i < links.length - 1 + ? html`, ` + : nothing}` + )} + +
+ `; + } + + private _renderHtmlField(field: DisplayField, fieldIndex: number) { + const stateKey = `${fieldIndex}-0-${field.label}`; + const isExpanded = !!this._expandedStates[stateKey]; + return html` +
+ + ${isExpanded ? html`
${unsafeHTML(field.value)}
` : nothing} +
+ `; + } + + private _renderList(field: DisplayField, fieldIndex: number) { + const items = field.value as DisplayField[][]; + if (!items?.length) return nothing; + + return html` +
+

${field.label}

+
+ ${items.map((itemFields, itemIdx) => + field.collapsibleListItems + ? this._renderCollapsibleListItem(field, fieldIndex, itemFields, itemIdx) + : this._renderFlatListItem(itemFields, fieldIndex, itemIdx) + )} +
+
+ `; + } + + private _renderCollapsibleListItem( + parentField: DisplayField, + fieldIndex: number, + itemFields: DisplayField[], + itemIdx: number + ) { + const stateKey = `${fieldIndex}-${itemIdx}-${parentField.label}`; + const isExpanded = !!this._expandedStates[stateKey]; + const headerValue = itemFields[0]?.value ?? ''; + + return html` +
+ + ${isExpanded + ? html` +
+ ${itemFields + .slice(1) + .map((itemField, subIdx) => + this._renderListItemField(itemField, fieldIndex, subIdx + 1) + )} +
+ ` + : nothing} +
+ `; + } + + private _renderFlatListItem(itemFields: DisplayField[], fieldIndex: number, _itemIdx: number) { + return html` +
+ ${itemFields.map((itemField, subIdx) => + this._renderListItemField(itemField, fieldIndex, subIdx) + )} +
+ `; + } + + private _renderListItemField(field: DisplayField, fieldIndex: number, subIndex: number) { + const value = field.isDate ? formatDate(field.value) : field.value; + const copyStateKey = `${fieldIndex}-${field.label}-${subIndex}`; + + if (field.fieldIsCopyable) { + return html` +
+ + + ${this._copiedStates[copyStateKey] + ? html`Copied` + : nothing} +
+ `; + } + + return html` `; + } + + private _renderCopyableField(field: DisplayField, fieldIndex: number, subIndex: number) { + const indentClass = `indent-${field.indent || 0}`; + const value = field.isDate + ? formatDate(field.value) + : field.capitalize + ? capitalizeStr(field.value) + : field.value; + const copyStateKey = `${fieldIndex}-${field.label}-${subIndex}`; + + return html` +
+ + + ${this._copiedStates[copyStateKey] + ? html`Copied` + : nothing} +
+ `; + } + + private _renderKeyValue(field: DisplayField) { + const indentClass = `indent-${field.indent || 0}`; + + if (field.isDate) { + return html` +
+ +
+ `; + } + + if (field.displayLink) { + const displayText = field.capitalize ? capitalizeStr(field.value) : field.value; + return html` +
+ ${field.label}: + +
+ `; + } + + const value = field.capitalize ? capitalizeStr(field.value) : field.value; + return html` +
+ +
+ `; + } +}