diff --git a/README.md b/README.md index 837bac5..520605c 100644 --- a/README.md +++ b/README.md @@ -1,45 +1,100 @@ # Polarity Qualys Integration - The Polarity Qualys Integration queries the Qualys Cloud Platform's Host Detection List for IP Addresses and QIDs. The Host Detections list will only get queried when searching for IP Addresses and QIDs, the host detection API does not enable other searching at this time. T - -> *NOTE:* QIDs can be searched onDemand by prefixing the QID with `QID: ` - -### How to Review Polarity - Qualys Integration -***Host Detections*** -* **Summary View** -> Number of Host Dections associated with QID or IP Address -* **Detail View** - * *Host Information* - * Asset ID -> Asset ID from Qualys - * Operating System -> Host OS - * DNS -> Host DNS/Domain - * Last Scan Information - * *Detections List* - * List of all detections associated with Host - -
- Host List Detections -
- -## About Qualys -The Qualys Cloud Platform helps businesses simplify security operations and lower the cost of compliance by delivering critical security intelligence on demand and automating the full spectrum of auditing, compliance and protection for IT systems and web applications. + +The Polarity Qualys Integration queries the Qualys Cloud Platform for IP addresses, domains, CVEs, and QIDs. Host detection results are returned for IP addresses and QIDs. CVE lookups query the Qualys KnowledgeBase and return matching vulnerability records along with any associated host detections. + +## About Qualys + +The Qualys Cloud Platform helps businesses simplify security operations and lower the cost of compliance by delivering critical security intelligence on demand and automating the full spectrum of auditing, compliance, and protection for IT systems and web applications. + To learn more about Qualys, visit the [official website](https://www.qualys.com/). +## Supported Entity Types + +| Entity Type | Description | +|---|---| +| IPv4 | Returns host detection list and scan history for the IP address | +| IPv6 | Returns host detection list for the IPv6 address | +| Domain | Returns host detection list for hosts matching the domain name | +| CVE | Returns KnowledgeBase vulnerability records matching the CVE and associated host detections | +| Qualys ID (QID) | Returns KnowledgeBase records and host detections for the matched QID | +| Custom QID Value | User-configured type for matching arbitrary strings and extracting a QID value | + +## QID Entity Type + +The built-in **Qualys ID (QID)** data type automatically recognizes QID strings in the following formats (case-insensitive): + +| Format | Example | +|---|---| +| `QID` (no separator) | `QID12345` | +| `QID:` | `QID:12345` | +| `QID: ` | `QID: 12345` | +| `QID : ` | `QID : 12345` | +| `QID-` | `QID-12345` | +| `QID_` | `QID_12345` | +| `QID ` | `QID 12345` | +| `qid:` (lowercase) | `qid:38623` | + +The numeric QID value is extracted automatically and used for the API lookup. + +## Custom QID Value Data Type + +The **Custom QID Value** data type is disabled by default. It is designed for environments where QID values appear embedded in custom string formats not covered by the built-in QID pattern (e.g., internal ticket references, asset tags, or CMDB identifiers). + +When enabled, Polarity will match text against the regex you configure in the **Custom QID Value Regex** option. The integration then extracts a numeric QID from the matched string and looks it up in Qualys. + +**To enable it:** +1. Go to **Integration Options → Qualys → Data Types** +2. Enable the **Custom QID Value** type and enter a regex pattern that matches your custom format +3. Optionally set the **Custom QID Value Regex** option to extract the QID number (see below) + ## Integration Limitations + ### Host Detection List Lookup Limits -Qualys' Host Detection List API only allows lookups on IP Addresses and QIDs, so only IP Addresses and QIDs will show Host Detection List results. +Qualys' Host Detection List API filters results by the query parameter. When searching by QID, only the detection entry for that specific QID is returned per host — not the host's full detection list. Searching by IP address returns all detections for that host. + +## Integration Options + +All options are admin-only and cannot be edited by regular users. -## Qualys Integration User Options ### Qualys URL -The URL of the Qualys you would like to connect to (including http:// or https://) +*(Required)* The base URL of your Qualys subscription, including the protocol (e.g., `https://qualysapi.qualys.com`). Do not include a trailing slash. ### Qualys Username -The Username for your Qualys Account +*(Required)* The username for your Qualys account. ### Qualys Password -The Password associated with the Qualys Account +*(Required)* The password associated with your Qualys account. + +### Enable Scan Launch +*(Default: disabled)* When enabled, a **Launch Scan** button appears in the Scans tab for IP address entities, allowing analysts to initiate a Qualys VM scan directly from Polarity. Requires the **Scan Option Profile** to be configured. + +### Scan Option Profile +The Qualys option profile title or numeric ID to use when launching scans (e.g., `Initial Options` or `43165`). Required when **Enable Scan Launch** is enabled. You can find the Scan Option Profile by navigating to the "Scans" page and then click on the "Option Profiles" tab. The "Title" column is the name of the scan option profile. Do not include the word `(default)` if selecting the default Scan Option Profile. + +### Scanner Appliance Name +The name of the scanner appliance to target when launching scans (e.g., `scanner1`). Leave blank to use the account's default scanner for the target IP. + +### Custom QID Value Regex +When the **Custom QID Value** data type is enabled, this regex is used to extract the numeric QID from the matched entity string. + +- If the regex contains a **capture group**, the first capture group's value is used as the QID (e.g., `TICKET-(\d+)` would extract `42` from `TICKET-42`). +- If the regex has **no capture group**, the full match is used as the QID. +- If left **blank**, the integration falls back to extracting the last contiguous sequence of digits found in the matched string (e.g., `ASSET-00038623` → `38623`). + +**Examples:** + +| Custom type regex (Data Types) | Custom QID Value Regex (option) | Matched string | Extracted QID | +|---|---|---|---| +| `TICKET-\d+` | `TICKET-(\d+)` | `TICKET-38623` | `38623` | +| `VULN#\d{4,8}` | *(blank)* | `VULN#12345` | `12345` | +| `asset-tag-\d+` | `(\d+)$` | `asset-tag-00091` | `00091` | ## Installation Instructions + Installation instructions for integrations are provided on the [PolarityIO GitHub Page](https://polarityio.github.io/). ## 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 + +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/ diff --git a/config/config.json b/config/config.json index a6d8692..f111add 100644 --- a/config/config.json +++ b/config/config.json @@ -2,13 +2,33 @@ "polarityIntegrationUuid": "fbe4d7e0-cce1-11ed-aeee-075d3490155d", "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.", + "description": "The Polarity Qualys Integration queries the Qualys Cloud Platform's Host Detection List and KnowledgeBase for IP Addresses, IPv6 Addresses, Domains, CVEs, and QIDs.", "runtimeVersion": 2, "defaultColor": "light-purple", "onDemandOnly": true, "dataTypes": [ "IPv4", - "IPv6" + "IPv6", + "domain", + "cve", + { + "type": "custom", + "name": "Qualys ID (QID)", + "description": "Matches QID values prefixed with QID followed by a 1 to 8 digit number (e.g., QID1234, QID-1234, QID:1234, QID 1234)", + "key": "qid", + "regex": "(?:QID|qid)(?:\\s*[:_-]\\s*|\\s*)\\d{1,8}", + "editable": false, + "enabled": true + }, + { + "type": "custom", + "name": "Custom QID Value", + "description": "Match custom QID values and extract the numeric QID via the \"Custom QID Value Regex\" integration option", + "key": "customQid", + "regex": "(?:QID|qid)(?:\\s*[:_-]\\s*|\\s*)\\d{1,8}", + "editable": true, + "enabled": false + } ], "logging": { "level": "info" @@ -19,7 +39,7 @@ "components": [ { "type": "details", - "element": "px-int-4szfuj42ymklnvnabm9q4yt1l-qls-details-v4-0-0" + "element": "px-int-4szfuj42ymklnvnabm9q4yt1l-qls-details-v4-1-0" } ] }, @@ -55,12 +75,42 @@ "type": "password", "userCanEdit": false, "adminOnly": true - } - ], - "customTypes": [ + }, { - "key": "qid", - "regex": "(?:QID|qid):\\s*\\d{1,8}" + "key": "enableScanLaunch", + "name": "Enable Scan Launch", + "description": "When enabled, a 'Launch Scan' button appears in the Scans tab for IP address entities. Requires the Scan Option Profile to be configured.", + "default": false, + "type": "boolean", + "userCanEdit": false, + "adminOnly": true + }, + { + "key": "scanOptionProfile", + "name": "Scan Option Profile", + "description": "The Qualys option profile title or numeric ID to use when launching scans from Polarity (e.g., 'Initial Options' or '43165'). Required when Enable Scan Launch is enabled.", + "default": "", + "type": "text", + "userCanEdit": false, + "adminOnly": true + }, + { + "key": "scannerName", + "name": "Scanner Appliance Name", + "description": "The scanner appliance name to target when launching scans (e.g., 'scanner1'). Leave blank to use the default scanner for the target IP.", + "default": "", + "type": "text", + "userCanEdit": false, + "adminOnly": true + }, + { + "key": "customQidValueRegex", + "name": "Custom QID Value Regex", + "description": "When the Custom QID Value data type is enabled, this regex is used to extract the QID numeric value from the matched entity string. Leave blank to extract the last sequence of digits found in the match.", + "default": "", + "type": "text", + "userCanEdit": false, + "adminOnly": true } ] } diff --git a/docs/Host List Detections.png b/docs/Host List Detections.png deleted file mode 100644 index 36a67e0..0000000 Binary files a/docs/Host List Detections.png and /dev/null differ diff --git a/docs/KnowledgeBase Record.png b/docs/KnowledgeBase Record.png deleted file mode 100644 index 21fc5a1..0000000 Binary files a/docs/KnowledgeBase Record.png and /dev/null differ diff --git a/docs/hostDetectionsFullExample.png b/docs/hostDetectionsFullExample.png deleted file mode 100644 index d93e6be..0000000 Binary files a/docs/hostDetectionsFullExample.png and /dev/null differ diff --git a/docs/knowledgeBaseFullExample.png b/docs/knowledgeBaseFullExample.png deleted file mode 100644 index e015d57..0000000 Binary files a/docs/knowledgeBaseFullExample.png and /dev/null differ diff --git a/package-lock.json b/package-lock.json index f82fe96..13fb6a5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,23 +1,23 @@ { "name": "qualys", - "version": "4.0.0", + "version": "4.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "qualys", - "version": "4.0.0", + "version": "4.1.0", "license": "UNLICENSED", "dependencies": { - "lodash": "^4.17.21", - "polarity-integration-utils": "^4.1.0", + "lodash": "^4.18.1", + "polarity-integration-utils": "^4.3.1", "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", + "@polarityio/integration-types": "^3.1.0", + "@polarityio/pi-components": "^1.14.0", + "@polarityio/vite-plugin-icl": "^1.0.2", "@types/lodash": "^4.17.24", "@types/xml2js": "^0.4.14", "@vitest/browser": "^4.1.4", @@ -540,9 +540,9 @@ } }, "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==", + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.6.0.tgz", + "integrity": "sha512-ii6Bw9jJ2zi2cWA2Z+9/QZ/+3DX6kwaV5Q986D/CdP3Lap3w/pgQZ373FV7byY/i7L4IRH/G43I5dz1ClsCbpA==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -597,9 +597,9 @@ } }, "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==", + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.7.2.tgz", + "integrity": "sha512-+CNAzxglkrpNf/kKywqQfk74QjtceuOE7Qm+AF8miRvPF/wmmK5+OJOgVh3AVTT3RP2mH3+FOaxlE5v72owk0A==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -748,9 +748,9 @@ "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==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@lit-labs/ssr-dom-shim/-/ssr-dom-shim-1.6.0.tgz", + "integrity": "sha512-VHb0ALPMTlgKjM6yIxxoQNnpKyUKLD04VzeQdsiXkMqkvYlAHxq9glGLmgbb889/1GsohSOAjvQYoiBppXFqrQ==", "dev": true, "license": "BSD-3-Clause" }, @@ -765,215 +765,162 @@ } }, "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==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@polarityio/integration-types/-/integration-types-3.1.0.tgz", + "integrity": "sha512-dQlHWtE95eB+X2ChoZ/BS0ia3AShBhFVxppDuTovBixXuvPFtMCYP1dZJx31AKzoKDb8l6oThx1spOl0gTc5Aw==", "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==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@polarityio/pi-button/-/pi-button-1.1.0.tgz", + "integrity": "sha512-vIxYHP2m1Efg1GmtDdhMjYfsPdTXaR14l9JGM2GoRr4XTJO/ixA9zJWFXUdqiC54uyQ8qOHr8j6D054BzLtryg==", "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" + "@polarityio/pi-shared": "1.3.3", + "@polarityio/pi-spinner": "1.0.5", + "@polarityio/pi-tooltip": "1.0.6" }, "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==", + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@polarityio/pi-card/-/pi-card-1.2.6.tgz", + "integrity": "sha512-O9BfVEOdjJxsxnGTTxcnDWwjFqQ42jwfifSFIYVIF+Cs5I9N6fgx75mxvklFQBxHTgSv9dM/HloFhNWtQyGzrQ==", "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" + "@polarityio/pi-icon": "1.0.4", + "@polarityio/pi-shared": "1.3.3" }, "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==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@polarityio/pi-checkbox/-/pi-checkbox-1.0.6.tgz", + "integrity": "sha512-HB5L1qoflEQfWawc8EJfF5S8VdozdmucVfToaN2wp3jf8Hu1r3L9yU8GpxGoAY8U56vt86EpLXlOqBEHQbsO2A==", "dev": true, "license": "MIT", "dependencies": { - "@polarityio/pi-shared": "1.2.1" + "@polarityio/pi-shared": "1.3.3" }, "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" + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/@polarityio/pi-components/-/pi-components-1.14.0.tgz", + "integrity": "sha512-iCqg44z6N8Pnim3D2ZApfx0zSSvyhvrZKsHKZ1Bbcz/f+XcCmuAUigWT6+bDz5qQ7e9V0R/cUZUFvWtZAoJ7IQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@polarityio/pi-button": "1.1.0", + "@polarityio/pi-card": "1.2.6", + "@polarityio/pi-checkbox": "1.0.6", + "@polarityio/pi-copy-button": "1.1.3", + "@polarityio/pi-error": "1.1.7", + "@polarityio/pi-external-link": "1.1.0", + "@polarityio/pi-icon": "1.0.4", + "@polarityio/pi-input": "1.0.6", + "@polarityio/pi-key-value": "1.4.0", + "@polarityio/pi-object-table": "1.0.6", + "@polarityio/pi-radio": "1.0.6", + "@polarityio/pi-retry-button": "1.0.5", + "@polarityio/pi-scroller": "1.0.4", + "@polarityio/pi-section-header": "1.0.8", + "@polarityio/pi-select": "1.2.0", + "@polarityio/pi-shared": "1.3.3", + "@polarityio/pi-show-more": "1.1.6", + "@polarityio/pi-spinner": "1.0.5", + "@polarityio/pi-summary-tag": "1.2.1", + "@polarityio/pi-tab": "1.2.6", + "@polarityio/pi-tag": "1.0.8", + "@polarityio/pi-tooltip": "1.0.6" }, "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==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@polarityio/pi-copy-button/-/pi-copy-button-1.1.3.tgz", + "integrity": "sha512-b5T7NQ34s958zXSVTebCDocuRUydQHeRIh+YnxmcwQmMiWWFcrb/sMlBP10vgPYVEJJjg3B97HyWLHtXAfBHjw==", "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" + "@polarityio/pi-button": "1.1.0", + "@polarityio/pi-shared": "1.3.3", + "@polarityio/pi-tooltip": "1.0.6" }, "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==", + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@polarityio/pi-error/-/pi-error-1.1.7.tgz", + "integrity": "sha512-2UPjU3NNigbQsZvTjfH7BWbmNHBzcNDVY5L+mJBVIkgJVbKzN7ngOwlA+y/7uhEEbNLPFx067muWozZyUjhk2g==", "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" + "@polarityio/pi-button": "1.1.0", + "@polarityio/pi-copy-button": "1.1.3", + "@polarityio/pi-icon": "1.0.4", + "@polarityio/pi-shared": "1.3.3" }, "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==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@polarityio/pi-external-link/-/pi-external-link-1.1.0.tgz", + "integrity": "sha512-v18Szlo/jD+igyjwi5YyXOpJq609A3tvemWve6JGWwBlublSE5lBZgqNgjgbcfx3XElVdlRO67AyURPNcYmFYg==", "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" + "@polarityio/pi-icon": "1.0.4", + "@polarityio/pi-shared": "1.3.3", + "@polarityio/pi-tooltip": "1.0.6" }, "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==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@polarityio/pi-icon/-/pi-icon-1.0.4.tgz", + "integrity": "sha512-570Cyuhjevg8BTqgkfUtGvAjDPiZ5vkuhSWbWhY16TrI0Y8pmnakua+7mknsiX8iDXwaWZSKImMUH8l2Bw/EWg==", "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-shared": "1.3.2" }, "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==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@polarityio/pi-shared/-/pi-shared-1.3.2.tgz", + "integrity": "sha512-+0QS5tITjVr3kUIsxf+K35E9LIvwvP+TWThNRlyyF1aBGroEbtWCvx7MgnlELPYqwYc1mi8zZCxRmseahAxkIw==", "dev": true, "license": "MIT", "peerDependencies": { @@ -981,22 +928,22 @@ } }, "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==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@polarityio/pi-input/-/pi-input-1.0.6.tgz", + "integrity": "sha512-uFxQG0keJ/7XuxibKPkCPCQ3yKJbY1lnOOYqQGZKq6foeY55n7lqMKH/+1lwd4YFZ7Et8DJTfsfOrvosuR2nOA==", "dev": true, "license": "MIT", "dependencies": { - "@polarityio/pi-shared": "1.2.1" + "@polarityio/pi-shared": "1.3.2" }, "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==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@polarityio/pi-shared/-/pi-shared-1.3.2.tgz", + "integrity": "sha512-+0QS5tITjVr3kUIsxf+K35E9LIvwvP+TWThNRlyyF1aBGroEbtWCvx7MgnlELPYqwYc1mi8zZCxRmseahAxkIw==", "dev": true, "license": "MIT", "peerDependencies": { @@ -1004,199 +951,123 @@ } }, "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==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@polarityio/pi-key-value/-/pi-key-value-1.4.0.tgz", + "integrity": "sha512-SL66na1Jqy4NxuTcAlWqO8tU+tVcbmRMJV9TkmiSc7viefxUqV63tHP69akCL4+KXsRZ8N8or9IQ2QQpIC7DMw==", "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", + "@polarityio/pi-external-link": "1.1.0", + "@polarityio/pi-icon": "1.0.4", + "@polarityio/pi-shared": "1.3.3", + "@polarityio/pi-tooltip": "1.0.6", "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==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@polarityio/pi-object-table/-/pi-object-table-1.0.6.tgz", + "integrity": "sha512-mn2uz3/kBmugE18LNMG2W4IfNts/MXAfse48Vf0RG6fjR3SQy8O2TUNu2C5HSR/Gduo2mDpAsV5W9QDQc6XPEg==", "dev": true, "license": "MIT", "dependencies": { - "@polarityio/pi-shared": "1.3.1" + "@polarityio/pi-shared": "1.3.2" }, "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==", + "node_modules/@polarityio/pi-object-table/node_modules/@polarityio/pi-shared": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@polarityio/pi-shared/-/pi-shared-1.3.2.tgz", + "integrity": "sha512-+0QS5tITjVr3kUIsxf+K35E9LIvwvP+TWThNRlyyF1aBGroEbtWCvx7MgnlELPYqwYc1mi8zZCxRmseahAxkIw==", "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==", + "node_modules/@polarityio/pi-radio": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@polarityio/pi-radio/-/pi-radio-1.0.6.tgz", + "integrity": "sha512-RUMcH/XSzLVoFNQpz33eabVmNtdzIoY3DQPgXGUkxA9FeHn2rYLd8q3bjyxwX3Nq/BGmUqpUPiTAPIMn6iphVw==", "dev": true, "license": "MIT", + "dependencies": { + "@polarityio/pi-shared": "1.3.3" + }, "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==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@polarityio/pi-retry-button/-/pi-retry-button-1.0.5.tgz", + "integrity": "sha512-AwiRIasmj05pAjVPhHqJAPD+RriIfE7v57M99GVGqa8TYwP57jznxv0U+LA12cdDh5upazH5eOMuAuTqgs1lTA==", "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" + "@polarityio/pi-button": "1.1.0", + "@polarityio/pi-shared": "1.3.3", + "@polarityio/pi-spinner": "1.0.5", + "@polarityio/pi-tooltip": "1.0.6" }, "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==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@polarityio/pi-scroller/-/pi-scroller-1.0.4.tgz", + "integrity": "sha512-ibwev+vkHGGIYU/ABuMEg+Yl7bnzKpjo3oCkhtPGDC68hk5qgzWcnR0DD2ywehdaPvFBiDaNL+kqzJ9g4asupw==", "dev": true, "license": "MIT", "dependencies": { - "@polarityio/pi-shared": "1.3.1" + "@polarityio/pi-shared": "1.3.3" }, "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==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@polarityio/pi-section-header/-/pi-section-header-1.0.8.tgz", + "integrity": "sha512-A+DrAyp2c8D84qo2nmqr3KhV+CfdHdPhXhDLatu2N/mln9zVZueG9xIFXlR3B+4skJEr8nA7ywIQlBWEtYUpmQ==", "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" + "@polarityio/pi-shared": "1.3.3" }, "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==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@polarityio/pi-select/-/pi-select-1.2.0.tgz", + "integrity": "sha512-EUXuUox+/x2fLfIN4RlgwemeYPjnAMc+wOezlsRR/fXWswMUXw9H4i+5NOHVr288kng5D1P83O8L/Dm5S7jIXg==", "dev": true, "license": "MIT", "dependencies": { "@floating-ui/dom": "^1.7.6", - "@polarityio/pi-shared": "1.0.3" + "@polarityio/pi-shared": "1.3.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==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@polarityio/pi-shared/-/pi-shared-1.3.3.tgz", + "integrity": "sha512-wgg4nULAwlxPKQhAxMTK+M6h5yG0WJUhMYxhgvfN0pje6CzlJi0NvYgKHKCQ/eDK6LsXfMpT+oZILYwYIAKKbw==", "dev": true, "license": "MIT", "peerDependencies": { @@ -1204,36 +1075,36 @@ } }, "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==", + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@polarityio/pi-show-more/-/pi-show-more-1.1.6.tgz", + "integrity": "sha512-7djFZj8n9SaWkFe9Tp9X4aT2hczfCoAxRI6iAOCj2Hg3Qd7s9Bd3NV7sMj0ruFWjtB9trTmTLLIMUs4+YcuCLA==", "dev": true, "license": "MIT", "dependencies": { - "@polarityio/pi-button": "1.0.4", - "@polarityio/pi-shared": "1.3.1" + "@polarityio/pi-button": "1.1.0", + "@polarityio/pi-shared": "1.3.3" }, "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==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@polarityio/pi-spinner/-/pi-spinner-1.0.5.tgz", + "integrity": "sha512-N2QCy52u95ggfjnprKuF0UMHY0E2eUXK4wMMXwb9zMAwppCCS1plzIX7B88v+faozA07I3gthhRbBSanIH4XtA==", "dev": true, "license": "MIT", "dependencies": { - "@polarityio/pi-shared": "1.2.1" + "@polarityio/pi-shared": "1.3.2" }, "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==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@polarityio/pi-shared/-/pi-shared-1.3.2.tgz", + "integrity": "sha512-+0QS5tITjVr3kUIsxf+K35E9LIvwvP+TWThNRlyyF1aBGroEbtWCvx7MgnlELPYqwYc1mi8zZCxRmseahAxkIw==", "dev": true, "license": "MIT", "peerDependencies": { @@ -1241,77 +1112,67 @@ } }, "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==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@polarityio/pi-summary-tag/-/pi-summary-tag-1.2.1.tgz", + "integrity": "sha512-34swOUR/XvPLwgY21400qKR2wiZR7abB26FSh1nCLZ9bFN6hhmKYJ9yZ0n4Yp4PQdoemV/P60VAMhUjDQS4WTg==", "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" + "@polarityio/pi-shared": "1.3.3" }, "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==", + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@polarityio/pi-tab/-/pi-tab-1.2.6.tgz", + "integrity": "sha512-HJMD7WlW3JCrik+GFRdSuEXdi+VhVbj5jaJDglwoiys1xcM42FUDuEj7+EsNG4CJfSFjJkT3jvfM4chNJQvgKw==", "dev": true, "license": "MIT", "dependencies": { - "@polarityio/pi-shared": "1.3.1" + "@polarityio/pi-shared": "1.3.3" }, "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==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@polarityio/pi-tag/-/pi-tag-1.0.8.tgz", + "integrity": "sha512-wm/PmAGjyVp2RaaMj1/ZcvnyZKkrBpPXmSs1hCIHbDb0v+Y5+m1aUvuX0ywUssK+9wlS+95c3NWHglXovwMUsA==", "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" + "@polarityio/pi-icon": "1.0.4", + "@polarityio/pi-shared": "1.3.3" }, "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==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@polarityio/pi-tooltip/-/pi-tooltip-1.0.6.tgz", + "integrity": "sha512-8A56smc9PCiyVTk5913fupoi8yUSnjbdv665RsBUuk0ocznHiwkyGuqjYwtPx962jrbRVZBO0lNL0GhUD7ZYFw==", "dev": true, "license": "MIT", "dependencies": { "@floating-ui/dom": "^1.7.6", - "@polarityio/pi-shared": "1.2.1" + "@polarityio/pi-shared": "1.3.2" }, "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==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@polarityio/pi-shared/-/pi-shared-1.3.2.tgz", + "integrity": "sha512-+0QS5tITjVr3kUIsxf+K35E9LIvwvP+TWThNRlyyF1aBGroEbtWCvx7MgnlELPYqwYc1mi8zZCxRmseahAxkIw==", "dev": true, "license": "MIT", "peerDependencies": { @@ -1319,9 +1180,9 @@ } }, "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==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@polarityio/vite-plugin-icl/-/vite-plugin-icl-1.0.2.tgz", + "integrity": "sha512-eVyPwRtbMWlgEsxyFs19TLYRxOOQSF4Jf+YcrcYYFsi0CH/1+/OAsEZcTm49nDb6dLj/s6Gwjua02doFUTdFJg==", "dev": true, "license": "MIT", "engines": { @@ -1342,6 +1203,7 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/@postman/form-data/-/form-data-3.1.1.tgz", "integrity": "sha512-vjh8Q2a8S6UCm/KKs31XFJqEEgmbjBmpPNVV2eVav6905wyFAwaUOBGA1NPBI4ERH9MMZc6w0umFgM6WbEPMdg==", + "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", @@ -1355,6 +1217,7 @@ "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==", + "license": "BSD-3-Clause", "dependencies": { "psl": "^1.1.33", "punycode": "^2.1.1", @@ -1378,9 +1241,9 @@ } }, "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==", + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.62.0.tgz", + "integrity": "sha512-IPIQ55ythEHkfEd9jMEi32OQ7SxURsGA43JI22lj01OLZNt2NUbJX8YUHxkVWyQ6daHPNn0truF5nSj3DQp6YQ==", "cpu": [ "arm" ], @@ -1392,9 +1255,9 @@ ] }, "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==", + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.62.0.tgz", + "integrity": "sha512-M6s9cr10MibETyo8JsOkq+Lo1+lU6hcvb1MApnUql5qte/5hMEgzlN8/ReIKNfRV8rrqX50W1BX9zoUhC192RA==", "cpu": [ "arm64" ], @@ -1406,9 +1269,9 @@ ] }, "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==", + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.62.0.tgz", + "integrity": "sha512-BqCoMoIbn0keKys+dEAdBa70EtOwV1bEsQCUgU9FdiZmmMge/Zk7LlkYGqbrdHR+Frnt0E1FOanly+rlwvvQzw==", "cpu": [ "arm64" ], @@ -1420,9 +1283,9 @@ ] }, "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==", + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.62.0.tgz", + "integrity": "sha512-SIMzST3VFNXDAbeIWDWiFCNM5qncUBDWaEV7NfE7oZbDt2mgfW4MvbKdbYiGOLoM32gbTv608UMd0XktEYSD7w==", "cpu": [ "x64" ], @@ -1434,9 +1297,9 @@ ] }, "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==", + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.62.0.tgz", + "integrity": "sha512-ezjfSQMP7ArdUsbBwbQIfwAlhE84I2iVnzQNCFSveqV42q+BmKlzVpf7mxv5EchLcoWU4y6/heFzVg1F+hodUQ==", "cpu": [ "arm64" ], @@ -1448,9 +1311,9 @@ ] }, "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==", + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.62.0.tgz", + "integrity": "sha512-9+qTWGW9AZRhnUgwtTwzNwcPlL87ngkeN0LA+q1bADvmY9aNvWaF2TFW8BZgnQPYxpDI7+rMVLivcd4V737TAQ==", "cpu": [ "x64" ], @@ -1462,9 +1325,9 @@ ] }, "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==", + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.62.0.tgz", + "integrity": "sha512-T1dMEQhXA/jkJ/jyMIw9IovK8bSUq7A8kLIlvZTb/6YIVsp2zLavr4F3oyllHWo7eIVJRyE5n3tUjQJEbE1IuQ==", "cpu": [ "arm" ], @@ -1479,9 +1342,9 @@ ] }, "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==", + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.62.0.tgz", + "integrity": "sha512-2as0LgT7qQpyceQq6VUJYnumUMUrgGQCWIiDIN9DE0/tglsk6o66uCB4f3djRawAltvfCNLyZZrsqbPA6inCsA==", "cpu": [ "arm" ], @@ -1496,9 +1359,9 @@ ] }, "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==", + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.62.0.tgz", + "integrity": "sha512-bVURMg+6eNN9C/yc0aVjooZcwTTtYF4YW3xta5pP0//r3o1V8gXEHXWCndj47w/HhwsFroZrFhR+6uQP5T0n0g==", "cpu": [ "arm64" ], @@ -1513,9 +1376,9 @@ ] }, "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==", + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.62.0.tgz", + "integrity": "sha512-Ful8pM/2yYI83PViWdFdpZhdI8HJ5qsXANe5atypbHDf+KIBBDsZsbyy8hbXnULVvW9NsTh5DHwbcBftyLTfiw==", "cpu": [ "arm64" ], @@ -1530,9 +1393,9 @@ ] }, "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==", + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.62.0.tgz", + "integrity": "sha512-9Gp/DgrkzfUBmNPVTyPTvay+4xEP7M/clXpj3efXBcm6uTIVIgDg4rqUpqKXvLEuFRVuEpSAOkhgNeecvaZ4Cg==", "cpu": [ "loong64" ], @@ -1547,9 +1410,9 @@ ] }, "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==", + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.62.0.tgz", + "integrity": "sha512-m9tsJz54LUXkSYM8+8PG81B9IKK5r+2T0clMq4QrS16xFosufU7firBDAZEsDheDs7wTlP7h3++S7lMsU955HA==", "cpu": [ "loong64" ], @@ -1564,9 +1427,9 @@ ] }, "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==", + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.62.0.tgz", + "integrity": "sha512-3UvJ5PNVU16aJf6M3tFI24pWzAl2/ynfbyRN3ICyQajK1lSkrnVYNnLz3v04J32qKa0FczJc22zeToc0lr2A3w==", "cpu": [ "ppc64" ], @@ -1581,9 +1444,9 @@ ] }, "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==", + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.62.0.tgz", + "integrity": "sha512-vRWUAbYLGHBZS6Q8Msb2sfnf1fvJf+47t8l/TwOerM2qArzy+IeNMTHrYLHXh95h8MoatPHI5hhSZNs+mGXKPg==", "cpu": [ "ppc64" ], @@ -1598,9 +1461,9 @@ ] }, "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==", + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.62.0.tgz", + "integrity": "sha512-c00T5SYENHAt86cfW47URaP3Us5vLC/4QO7GYud1G5VNRffCwwCuBspwqYrriuJB+5m0WFzClCn9wed0FBjKvg==", "cpu": [ "riscv64" ], @@ -1615,9 +1478,9 @@ ] }, "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==", + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.62.0.tgz", + "integrity": "sha512-krrCDilhXOwFkSkO3Wm9I/f9H0L92XHHwy2fwxjukxIbh0dem8gZqOW5Y8BsHrpJv5qwlRBV+Wl4ZFyRWhUpwg==", "cpu": [ "riscv64" ], @@ -1632,9 +1495,9 @@ ] }, "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==", + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.62.0.tgz", + "integrity": "sha512-7pfYFSTc4/rUC/FtAI0Qp6QthDBCIi6/AuP1xYqFk5vanI6KnL5dWKP60OM/05LOsbwTmIcvr6eXC4CJuJ75IA==", "cpu": [ "s390x" ], @@ -1649,9 +1512,9 @@ ] }, "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==", + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.62.0.tgz", + "integrity": "sha512-7SDIalKeIpG0Ifogbbdn58HmSotYMlf23K3dCJEmiVd9Fg36Vmni82iPQec27N3wY4Bvbxftkxz6vSx9OcouTg==", "cpu": [ "x64" ], @@ -1666,9 +1529,9 @@ ] }, "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==", + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.62.0.tgz", + "integrity": "sha512-eRZevouTH2i1HeAVLqJuLnt256krQkGY0TN6WsTmsIhuzbh457HuWDMakKwmi0Cjadux983CoSr8Lim2QhUIFw==", "cpu": [ "x64" ], @@ -1683,9 +1546,9 @@ ] }, "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==", + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.62.0.tgz", + "integrity": "sha512-3oVS7FLGa4U1qcvao9ylGxrjXZyUQqR8UwxEcnUEyPX53O/C/mKDZegNXTdHCP+h3e6ta/f1EN38Yif1mmZHYg==", "cpu": [ "x64" ], @@ -1697,9 +1560,9 @@ ] }, "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==", + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.62.0.tgz", + "integrity": "sha512-yTB9TgfWj5wHe5QgktAgXTLLot1gvEjl1NiPPAUiCs4oPrIWFl5V4nC3GrkNdj9LaAU4s94nVrGbGOCqUpyWsg==", "cpu": [ "arm64" ], @@ -1711,9 +1574,9 @@ ] }, "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==", + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.62.0.tgz", + "integrity": "sha512-5LOhoaesY3doG1c+ac/2JtgREpKoJr5bUHH8tKY0V8di7+uSV6BwLs2PlR0/yzefGOkR+wE7ZolZphHCsyG5Rw==", "cpu": [ "arm64" ], @@ -1725,9 +1588,9 @@ ] }, "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==", + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.62.0.tgz", + "integrity": "sha512-yYkWHhmbhRTWTnWos5HC4GcPQfjlzzCNbM9e/+GXrLuaBXYA3qSDR9f0Vgufd5S8yX81U8jPKp7ZnAjZFMtRnw==", "cpu": [ "ia32" ], @@ -1739,9 +1602,9 @@ ] }, "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==", + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.62.0.tgz", + "integrity": "sha512-SoTb6lPg25xZlA2ibwQ++ahCCnH+FP0qmEuafMJ4gznZKOlXioKEAeJLgCrqjM98ACziXM9V1amFjICVL4IFoA==", "cpu": [ "x64" ], @@ -1753,9 +1616,9 @@ ] }, "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==", + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.62.0.tgz", + "integrity": "sha512-5L+T1fMX4RIEBoZzT0+sQ0PhTS36NULFmMXtl1TZo44TMAROIMHbZufSOjVWt/Y622BtxgxtaNOokbTDvfsrZA==", "cpu": [ "x64" ], @@ -1820,13 +1683,13 @@ "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==", + "version": "25.9.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.3.tgz", + "integrity": "sha512-603BddQMv3pUcr4U2dhujk83N2tTDVr/34wII2B6bJy6g+8WD6yUb11jszNs0gdi4PesVWl7ABt8nYMVpnLUcg==", "dev": true, "license": "MIT", "dependencies": { - "undici-types": "~7.21.0" + "undici-types": ">=7.24.0 <7.24.7" } }, "node_modules/@types/trusted-types": { @@ -1847,17 +1710,17 @@ } }, "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==", + "version": "8.61.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.61.1.tgz", + "integrity": "sha512-ZPlVl3PB3et/59Ne0fv/sci6ZXz4T4Hp4nTJ56i/Y0gR89ARb+KphojTq6j+56E5PIezmOIOOWyY+aWQFd+IkQ==", "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", + "@typescript-eslint/scope-manager": "8.61.1", + "@typescript-eslint/type-utils": "8.61.1", + "@typescript-eslint/utils": "8.61.1", + "@typescript-eslint/visitor-keys": "8.61.1", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.5.0" @@ -1870,7 +1733,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.59.3", + "@typescript-eslint/parser": "^8.61.1", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } @@ -1886,16 +1749,16 @@ } }, "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==", + "version": "8.61.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.61.1.tgz", + "integrity": "sha512-PJ5vePq5/ognBbrIcoC5+SHO5dfpeLPzP9FpLkzWrguoYQEeeSjlJpVwOpo1JRSTEi7dRcwNy4h4dzV70PqHcg==", "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", + "@typescript-eslint/scope-manager": "8.61.1", + "@typescript-eslint/types": "8.61.1", + "@typescript-eslint/typescript-estree": "8.61.1", + "@typescript-eslint/visitor-keys": "8.61.1", "debug": "^4.4.3" }, "engines": { @@ -1911,14 +1774,14 @@ } }, "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==", + "version": "8.61.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.61.1.tgz", + "integrity": "sha512-PrC4JYGmR241lYnfhmKGTXkFqv8+ymbTFgSAY0fVXpY82/QkMw5TZPl+vGzuDDU2QYJk9fIDOBTntF+yDv9LEA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.59.3", - "@typescript-eslint/types": "^8.59.3", + "@typescript-eslint/tsconfig-utils": "^8.61.1", + "@typescript-eslint/types": "^8.61.1", "debug": "^4.4.3" }, "engines": { @@ -1933,14 +1796,14 @@ } }, "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==", + "version": "8.61.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.61.1.tgz", + "integrity": "sha512-L2bdIeoQS8FlKAvONAr20w6OcLXeB+qiDKbAooS9A0Ben+iSIkBef0FxqwKWYqt5sa0i4KJtxVyVmhMylKzF5w==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.59.3", - "@typescript-eslint/visitor-keys": "8.59.3" + "@typescript-eslint/types": "8.61.1", + "@typescript-eslint/visitor-keys": "8.61.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1951,9 +1814,9 @@ } }, "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==", + "version": "8.61.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.61.1.tgz", + "integrity": "sha512-UN/H4di+OO7EWx2ovME+8t31YO+KVnK0RRKEHR3kOt21/Ay8BOq3M1OMvWs5vNiqcFCYGYoxK3MXPZzmMUE+yg==", "dev": true, "license": "MIT", "engines": { @@ -1968,15 +1831,15 @@ } }, "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==", + "version": "8.61.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.61.1.tgz", + "integrity": "sha512-GYRicKmVK0C4fsKgaACaknOUAq9Oa2kwsjnpFhFcS/5p4Ht5IP9OVLbgIgcK4SRk92nVHFluurg1lumD9dBcLw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.59.3", - "@typescript-eslint/typescript-estree": "8.59.3", - "@typescript-eslint/utils": "8.59.3", + "@typescript-eslint/types": "8.61.1", + "@typescript-eslint/typescript-estree": "8.61.1", + "@typescript-eslint/utils": "8.61.1", "debug": "^4.4.3", "ts-api-utils": "^2.5.0" }, @@ -1993,9 +1856,9 @@ } }, "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==", + "version": "8.61.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.61.1.tgz", + "integrity": "sha512-G+CRlPqLv7Bz1IZVs03x5K59F1veqL0EJUROAdGhKsEq8qOiRiZbI+HUojPq5l0fEGOKModD9br6lObhB8zkoA==", "dev": true, "license": "MIT", "engines": { @@ -2007,16 +1870,16 @@ } }, "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==", + "version": "8.61.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.61.1.tgz", + "integrity": "sha512-u+oQD3BqYWPc8YV9Zab4vaJElJuwOLPRc10Jm1o/qS+6Qwen14HCWwx0Seo4LnSn2wxea2Ik8DxPt2/FHmuhrg==", "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", + "@typescript-eslint/project-service": "8.61.1", + "@typescript-eslint/tsconfig-utils": "8.61.1", + "@typescript-eslint/types": "8.61.1", + "@typescript-eslint/visitor-keys": "8.61.1", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", @@ -2035,16 +1898,16 @@ } }, "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==", + "version": "8.61.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.61.1.tgz", + "integrity": "sha512-1+P/3Dj6jvtybE1q0HQ6yBt/gq+oKJyLdEv4HdnqasaEXRSYCAsD59mXEVQnM/ULNdQxbX77tdG4jPRjIS6knA==", "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" + "@typescript-eslint/scope-manager": "8.61.1", + "@typescript-eslint/types": "8.61.1", + "@typescript-eslint/typescript-estree": "8.61.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2059,13 +1922,13 @@ } }, "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==", + "version": "8.61.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.61.1.tgz", + "integrity": "sha512-6fJ9MHWtK14C1DSkiMlHUSOmrVebL7150xZJBlJiL62jjhIA4JmOq6flwBgDxIdBKKdoiZRel+dfPD5MLfny3w==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.59.3", + "@typescript-eslint/types": "8.61.1", "eslint-visitor-keys": "^5.0.0" }, "engines": { @@ -2077,15 +1940,15 @@ } }, "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==", + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@vitest/browser/-/browser-4.1.9.tgz", + "integrity": "sha512-j1BKtWmPcqpMhmx/L9EPLgAJpCb0zKfwoWLmqBbxaogCXHjOwHFSEoHCBfnGtx93xKQwilZ26m+UOsHqHMkRNg==", "dev": true, "license": "MIT", "dependencies": { "@blazediff/core": "1.9.1", - "@vitest/mocker": "4.1.6", - "@vitest/utils": "4.1.6", + "@vitest/mocker": "4.1.9", + "@vitest/utils": "4.1.9", "magic-string": "^0.30.21", "pngjs": "^7.0.0", "sirv": "^3.0.2", @@ -2096,18 +1959,18 @@ "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "vitest": "4.1.6" + "vitest": "4.1.9" } }, "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==", + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@vitest/browser-playwright/-/browser-playwright-4.1.9.tgz", + "integrity": "sha512-Bq1rOGf9waevzG3EOkO/dene6bvKTUsZMVg8S1i+WH3JcMjuXEjiahP9rAqZRELUqjBySOJsvvSWqK/B3wjKQw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/browser": "4.1.6", - "@vitest/mocker": "4.1.6", + "@vitest/browser": "4.1.9", + "@vitest/mocker": "4.1.9", "tinyrainbow": "^3.1.0" }, "funding": { @@ -2115,7 +1978,7 @@ }, "peerDependencies": { "playwright": "*", - "vitest": "4.1.6" + "vitest": "4.1.9" }, "peerDependenciesMeta": { "playwright": { @@ -2124,16 +1987,16 @@ } }, "node_modules/@vitest/expect": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.6.tgz", - "integrity": "sha512-7EHDquPthALSV0jhhjgEW8FXaviMx7rSqu8W6oqCoAuOhKov814P99QDV1pxMA3QPv21YudvJngIhjrNI4opLg==", + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.9.tgz", + "integrity": "sha512-vl/rYsUKcBr3SnQn166+XR5ZQcgMx3DQhFWdfli/cWpLnLUmbxZvyrJZotLFUryib+LtArYMSTJ5RbQ57ZqrlA==", "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", + "@vitest/spy": "4.1.9", + "@vitest/utils": "4.1.9", "chai": "^6.2.2", "tinyrainbow": "^3.1.0" }, @@ -2142,13 +2005,13 @@ } }, "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==", + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.9.tgz", + "integrity": "sha512-EVkXzBjrPGM+cK8/ANWgBrkUCfJfb38/EfTSO8h7pWvKkyPkpWxvR7BkD2MyItMF62C97zAEoqdpUixwR/e+Rw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "4.1.6", + "@vitest/spy": "4.1.9", "estree-walker": "^3.0.3", "magic-string": "^0.30.21" }, @@ -2169,9 +2032,9 @@ } }, "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==", + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.9.tgz", + "integrity": "sha512-s0iufns3iIFitdgm+YR7g1whCAaGtXz459VS9/PqyKDEEFgYIhsHOQmXgIgDuYCt7DeQmiZT0Qe2OA2p4ZPu5A==", "dev": true, "license": "MIT", "dependencies": { @@ -2182,13 +2045,13 @@ } }, "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==", + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.9.tgz", + "integrity": "sha512-KXLMDtc7oe70+3mJfGrPUWPesswH+3sTxAMAMl8DG7I8IUQT4XW718dY5ID3vPUcmlu27CcKfY4P3h3I29SLJg==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "4.1.6", + "@vitest/utils": "4.1.9", "pathe": "^2.0.3" }, "funding": { @@ -2196,14 +2059,14 @@ } }, "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==", + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.9.tgz", + "integrity": "sha512-Jc7RKGNBo8Z28WYIm0Niej4xdSPByRf6mU58VpHQkd6Zh05rlnA+twjbK5HyeIGHxrzsc3mJgS43uM0CZKzaIA==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "4.1.6", - "@vitest/utils": "4.1.6", + "@vitest/pretty-format": "4.1.9", + "@vitest/utils": "4.1.9", "magic-string": "^0.30.21", "pathe": "^2.0.3" }, @@ -2212,9 +2075,9 @@ } }, "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==", + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.9.tgz", + "integrity": "sha512-fHpsS6mIi+PiEW+vcRVOMkX1oSaPKne3VOclSFICPcGOmfKgXPU5iAah+wcNcj2xPrCCmfq99IDGf+EojhhvhA==", "dev": true, "license": "MIT", "funding": { @@ -2222,13 +2085,13 @@ } }, "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==", + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.9.tgz", + "integrity": "sha512-A51o8ymO5PpqlWNnBP9ZHPXDIpuMtTLlGSjN7la4US+LJzoUMyhwjA5QXlm39JexgwHKW4Xjs8Z2d3dLCXOeuA==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "4.1.6", + "@vitest/pretty-format": "4.1.9", "convert-source-map": "^2.0.0", "tinyrainbow": "^3.1.0" }, @@ -2237,9 +2100,9 @@ } }, "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==", + "version": "8.17.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.17.0.tgz", + "integrity": "sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==", "dev": true, "license": "MIT", "bin": { @@ -2316,20 +2179,23 @@ "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" }, "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==", + "license": "Apache-2.0", "engines": { "node": "*" } }, "node_modules/aws4": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.12.0.tgz", - "integrity": "sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==" + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.13.2.tgz", + "integrity": "sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw==", + "license": "MIT" }, "node_modules/balanced-match": { "version": "4.0.4", @@ -2353,7 +2219,8 @@ "node_modules/bluebird": { "version": "2.11.0", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-2.11.0.tgz", - "integrity": "sha512-UfFSr22dmHPQqPP9XWHRhq+gWnHCYguQGkXQlbyPtW5qTnhFWA8/iXg765tH0cAjy7l/zPJ1aBTO0g5XgA7kvQ==" + "integrity": "sha512-UfFSr22dmHPQqPP9XWHRhq+gWnHCYguQGkXQlbyPtW5qTnhFWA8/iXg765tH0cAjy7l/zPJ1aBTO0g5XgA7kvQ==", + "license": "MIT" }, "node_modules/brace-expansion": { "version": "5.0.6", @@ -2400,7 +2267,8 @@ "node_modules/caseless": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==" + "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==", + "license": "Apache-2.0" }, "node_modules/chai": { "version": "6.2.2", @@ -2416,6 +2284,7 @@ "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", "dependencies": { "delayed-stream": "~1.0.0" }, @@ -2464,9 +2333,9 @@ } }, "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==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.4.0.tgz", + "integrity": "sha512-+1UMbeh68lH1SegH83CGWwpb6OHHbpSgr3+s5Eww5M4CAgswBpoWS0AjTOfEJ33HiYKz1hdj/KTFprzXHmq/6w==", "dev": true, "license": "MIT", "funding": { @@ -2502,6 +2371,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", "engines": { "node": ">=0.4.0" } @@ -2556,9 +2426,9 @@ "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==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", + "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", "license": "MIT", "dependencies": { "es-errors": "^1.3.0" @@ -2623,18 +2493,21 @@ } }, "node_modules/eslint": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.3.0.tgz", - "integrity": "sha512-XbEXaRva5cF0ZQB8w6MluHA0kZZfV2DuCMJ3ozyEOHLwDpZX2Lmm/7Pp0xdJmI0GL1W05VH5VwIFHEm1Vcw2gw==", + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.5.0.tgz", + "integrity": "sha512-1y+7C+vi12bUK1IpZeaV3gsH9fHLBmPvYmPx42pvT/E9yG0IC8g3PUZZgp0+JLJl7ZDK0flc2gc+Aw9dpCvIsQ==", "dev": true, "license": "MIT", + "workspaces": [ + "packages/*" + ], "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/config-helpers": "^0.6.0", "@eslint/core": "^1.2.1", - "@eslint/plugin-kit": "^0.7.1", + "@eslint/plugin-kit": "^0.7.2", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", @@ -2813,7 +2686,8 @@ "node_modules/extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "license": "MIT" }, "node_modules/extsprintf": { "version": "1.3.0", @@ -2828,13 +2702,15 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/fast-levenshtein": { "version": "2.0.6", @@ -2916,6 +2792,7 @@ "version": "0.6.1", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==", + "license": "Apache-2.0", "engines": { "node": "*" } @@ -3028,9 +2905,9 @@ } }, "node_modules/hasown": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.3.tgz", - "integrity": "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", "license": "MIT", "dependencies": { "function-bind": "^1.1.2" @@ -3108,7 +2985,8 @@ "node_modules/is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==" + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", + "license": "MIT" }, "node_modules/isexe": { "version": "2.0.0", @@ -3120,7 +2998,8 @@ "node_modules/isstream": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==" + "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==", + "license": "MIT" }, "node_modules/jsbn": { "version": "0.1.1", @@ -3145,7 +3024,8 @@ "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", @@ -3157,7 +3037,8 @@ "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==" + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", + "license": "ISC" }, "node_modules/jsprim": { "version": "2.0.2", @@ -3199,9 +3080,9 @@ } }, "node_modules/lit": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/lit/-/lit-3.3.2.tgz", - "integrity": "sha512-NF9zbsP79l4ao2SNrH3NkfmFgN/hBYSQo90saIVI1o5GpjAdCPVstVzO1MrLOakHoEhYkrtRjPK6Ob521aoYWQ==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/lit/-/lit-3.3.3.tgz", + "integrity": "sha512-fycuvZg/hkpozL00lm1pEJH5nN/lr9ZXd6mJI2HSN4+Bzc+LDNdEApJ6HFbPkdFNHLvOplIIuJvxkS4XUxqirw==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -3223,9 +3104,9 @@ } }, "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==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/lit-html/-/lit-html-3.3.3.tgz", + "integrity": "sha512-el8M6jK2o3RXBnrSHX3ZKrsN8zEV63pSExTO1wYJz7QndGYZ8353e2a5PPX+qHe2aGayfnchQmkAojaWAREOIA==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -3277,6 +3158,7 @@ "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -3285,6 +3167,7 @@ "version": "2.1.35", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", "dependencies": { "mime-db": "1.52.0" }, @@ -3325,9 +3208,9 @@ "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==", + "version": "3.3.13", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.13.tgz", + "integrity": "sha512-sPdqC6ByMVVGvF1ynvvMo0/o+oD1VX7DaHhijt1bFgjvBkHBib4t49GoNDhf2NDta4oeUNlaGbSt5K7qjZ955Q==", "dev": true, "funding": [ { @@ -3354,6 +3237,7 @@ "version": "0.9.0", "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "license": "Apache-2.0", "engines": { "node": "*" } @@ -3371,15 +3255,18 @@ } }, "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==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.3.tgz", + "integrity": "sha512-9miFgM2OFba7hB+pRgvtV84pYTBaoTHohvmIgiRt6dRIzbwEOIaNaP+dIlGs2fNFoB0SeISs0Jz5WFVRid6Xyg==", "dev": true, "funding": [ "https://github.com/sponsors/sxzz", "https://opencollective.com/debug" ], - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">=12.20.0" + } }, "node_modules/optionator": { "version": "0.9.4", @@ -3479,13 +3366,13 @@ } }, "node_modules/playwright": { - "version": "1.60.0", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.60.0.tgz", - "integrity": "sha512-hheHdokM8cdqCb0lcE3s+zT4t4W+vvjpGxsZlDnikarzx8tSzMebh3UiFtgqwFwnTnjYQcsyMF8ei2mCO/tpeA==", + "version": "1.61.0", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.61.0.tgz", + "integrity": "sha512-Z+7BeeqQPRRzklHsVFP4KTGIyMxKUmfeRA4WisM6G3/XW6nwGeX6fX9qYaDa+CiUqpOkb2f6X3nar05R3kSuJQ==", "dev": true, "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.60.0" + "playwright-core": "1.61.0" }, "bin": { "playwright": "cli.js" @@ -3498,9 +3385,9 @@ } }, "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==", + "version": "1.61.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.61.0.tgz", + "integrity": "sha512-caX7TrY3Ml6egyDX0WUcTHDxodl/b51y5wJOdCEA36QviK/s2g081hvmGs8eaE3DWb6NYZQ6BjO/QkNRPenoPA==", "dev": true, "license": "Apache-2.0", "bin": { @@ -3521,22 +3408,22 @@ } }, "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==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/polarity-integration-utils/-/polarity-integration-utils-4.3.2.tgz", + "integrity": "sha512-+HwlnkWE52ci78myHW83cfICrS+Zb7LcgRVvg+cstpihqRY2ZDyHWdaXkRRDkJTS4K7SPzgGUMKI2nTaePlG8A==", "license": "MIT", "dependencies": { "lodash": "^4.18.1", - "postman-request": "^2.88.1-postman.48" + "postman-request": "2.88.1-postman.48" }, "peerDependencies": { - "@polarityio/integration-types": ">=1.1.0" + "@polarityio/integration-types": "^3.0.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==", + "version": "8.5.15", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz", + "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==", "dev": true, "funding": [ { @@ -3554,7 +3441,7 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.11", + "nanoid": "^3.3.12", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, @@ -3604,9 +3491,9 @@ } }, "node_modules/prettier": { - "version": "3.8.3", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.3.tgz", - "integrity": "sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw==", + "version": "3.8.4", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.4.tgz", + "integrity": "sha512-N2MylSdi48+5N/6S5j+maeHbUSIzzZ5uOcX5Hm4QpV8Dkb1HFjfAKTKX6yNPJQD9AhcT3ifHNB66tWTTJDi11Q==", "dev": true, "license": "MIT", "bin": { @@ -3620,22 +3507,30 @@ } }, "node_modules/psl": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", - "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==" + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz", + "integrity": "sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==", + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "funding": { + "url": "https://github.com/sponsors/lupomontero" + } }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/qs": { - "version": "6.14.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz", - "integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==", + "version": "6.15.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.2.tgz", + "integrity": "sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw==", "license": "BSD-3-Clause", "dependencies": { "side-channel": "^1.1.0" @@ -3650,21 +3545,23 @@ "node_modules/querystringify": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", - "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==" + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", + "license": "MIT" }, "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==" + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "license": "MIT" }, "node_modules/rollup": { - "version": "4.60.3", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.3.tgz", - "integrity": "sha512-pAQK9HalE84QSm4Po3EmWIZPd3FnjkShVkiMlz1iligWYkWQ7wHYd1PF/T7QZ5TVSD6uSTon5gBVMSM4JfBV+A==", + "version": "4.62.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.62.0.tgz", + "integrity": "sha512-nc72Wgq62I7rtDV4izT5/aaS0zxy3kttkinf9586ApknY3jZO9NYsmtc24fUckA0X7Q2v+ML4a15pdUlV5V/jA==", "dev": true, "license": "MIT", "dependencies": { - "@types/estree": "1.0.8" + "@types/estree": "1.0.9" }, "bin": { "rollup": "dist/bin/rollup" @@ -3674,41 +3571,34 @@ "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", + "@rollup/rollup-android-arm-eabi": "4.62.0", + "@rollup/rollup-android-arm64": "4.62.0", + "@rollup/rollup-darwin-arm64": "4.62.0", + "@rollup/rollup-darwin-x64": "4.62.0", + "@rollup/rollup-freebsd-arm64": "4.62.0", + "@rollup/rollup-freebsd-x64": "4.62.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.62.0", + "@rollup/rollup-linux-arm-musleabihf": "4.62.0", + "@rollup/rollup-linux-arm64-gnu": "4.62.0", + "@rollup/rollup-linux-arm64-musl": "4.62.0", + "@rollup/rollup-linux-loong64-gnu": "4.62.0", + "@rollup/rollup-linux-loong64-musl": "4.62.0", + "@rollup/rollup-linux-ppc64-gnu": "4.62.0", + "@rollup/rollup-linux-ppc64-musl": "4.62.0", + "@rollup/rollup-linux-riscv64-gnu": "4.62.0", + "@rollup/rollup-linux-riscv64-musl": "4.62.0", + "@rollup/rollup-linux-s390x-gnu": "4.62.0", + "@rollup/rollup-linux-x64-gnu": "4.62.0", + "@rollup/rollup-linux-x64-musl": "4.62.0", + "@rollup/rollup-openbsd-x64": "4.62.0", + "@rollup/rollup-openharmony-arm64": "4.62.0", + "@rollup/rollup-win32-arm64-msvc": "4.62.0", + "@rollup/rollup-win32-ia32-msvc": "4.62.0", + "@rollup/rollup-win32-x64-gnu": "4.62.0", + "@rollup/rollup-win32-x64-msvc": "4.62.0", "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", @@ -3736,14 +3626,18 @@ "license": "MIT" }, "node_modules/sax": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.3.0.tgz", - "integrity": "sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA==" + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.0.tgz", + "integrity": "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=11.0.0" + } }, "node_modules/semver": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.0.tgz", - "integrity": "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==", + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.4.tgz", + "integrity": "sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==", "dev": true, "license": "ISC", "bin": { @@ -3777,14 +3671,14 @@ } }, "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==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz", + "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==", "license": "MIT", "dependencies": { "es-errors": "^1.3.0", - "object-inspect": "^1.13.3", - "side-channel-list": "^1.0.0", + "object-inspect": "^1.13.4", + "side-channel-list": "^1.0.1", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" }, @@ -3961,6 +3855,7 @@ "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==", + "license": "WTFPL", "dependencies": { "bluebird": "^2.6.2" } @@ -3973,9 +3868,9 @@ "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==", + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.2.4.tgz", + "integrity": "sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==", "dev": true, "license": "MIT", "engines": { @@ -3983,9 +3878,9 @@ } }, "node_modules/tinyglobby": { - "version": "0.2.16", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", - "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", "dev": true, "license": "MIT", "dependencies": { @@ -4066,16 +3961,16 @@ } }, "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==", + "version": "8.61.1", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.61.1.tgz", + "integrity": "sha512-V7PayAfJokV3pEHgN7/v03D1SpujhRfQtYLbLIiBfDDncdg4PAiRBfoS4cnCANK4jmAPncczi59QO3afiXUlNw==", "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" + "@typescript-eslint/eslint-plugin": "8.61.1", + "@typescript-eslint/parser": "8.61.1", + "@typescript-eslint/typescript-estree": "8.61.1", + "@typescript-eslint/utils": "8.61.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4090,9 +3985,9 @@ } }, "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==", + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz", + "integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==", "dev": true, "license": "MIT" }, @@ -4100,6 +3995,7 @@ "version": "0.2.0", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", + "license": "MIT", "engines": { "node": ">= 4.0.0" } @@ -4109,6 +4005,7 @@ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "punycode": "^2.1.0" } @@ -4117,6 +4014,7 @@ "version": "1.5.10", "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "license": "MIT", "dependencies": { "querystringify": "^2.1.1", "requires-port": "^1.0.0" @@ -4127,6 +4025,7 @@ "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", "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).", + "license": "MIT", "bin": { "uuid": "dist/bin/uuid" } @@ -4146,9 +4045,9 @@ } }, "node_modules/vite": { - "version": "7.3.3", - "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.3.tgz", - "integrity": "sha512-/4XH147Ui7OGTjg3HbdWe5arnZQSbfuRzdr9Ec7TQi5I7R+ir0Rlc9GIvD4v0XZurELqA035KVXJXpR61xhiTA==", + "version": "7.3.5", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.5.tgz", + "integrity": "sha512-KuOaNhcnGFN2zIPGA7wRmzF+lJA1sea7rHq17aiJ++9lzY1WWG6Jpwqwe1KNbRVPIqHmr8GLYx7jbrQcN/7/ww==", "dev": true, "license": "MIT", "dependencies": { @@ -4236,19 +4135,19 @@ } }, "node_modules/vitest": { - "version": "4.1.6", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.6.tgz", - "integrity": "sha512-6lvjbS3p9b4CrdCmguzbh2/4uoXhGE2q71R4OX5sqF9R1bo9Xd6fGrMAfvp5wnCzlBnFVdCOp6onuTQVbo8iUQ==", + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.9.tgz", + "integrity": "sha512-nE3/LEyc0z87uHYLZebqCUOaJr2hdtuPp7BQ4BosVFnfltxgAvMG08NyrSGlPpOUWvR27c5flSmYFTNr78L9GQ==", "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", + "@vitest/expect": "4.1.9", + "@vitest/mocker": "4.1.9", + "@vitest/pretty-format": "4.1.9", + "@vitest/runner": "4.1.9", + "@vitest/snapshot": "4.1.9", + "@vitest/spy": "4.1.9", + "@vitest/utils": "4.1.9", "es-module-lexer": "^2.0.0", "expect-type": "^1.3.0", "magic-string": "^0.30.21", @@ -4276,12 +4175,12 @@ "@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", + "@vitest/browser-playwright": "4.1.9", + "@vitest/browser-preview": "4.1.9", + "@vitest/browser-webdriverio": "4.1.9", + "@vitest/coverage-istanbul": "4.1.9", + "@vitest/coverage-v8": "4.1.9", + "@vitest/ui": "4.1.9", "happy-dom": "*", "jsdom": "*", "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" @@ -4369,9 +4268,9 @@ } }, "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==", + "version": "8.21.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.0.tgz", + "integrity": "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==", "dev": true, "license": "MIT", "engines": { @@ -4394,6 +4293,7 @@ "version": "0.6.2", "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.6.2.tgz", "integrity": "sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==", + "license": "MIT", "dependencies": { "sax": ">=0.6.0", "xmlbuilder": "~11.0.0" @@ -4406,6 +4306,7 @@ "version": "11.0.1", "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "license": "MIT", "engines": { "node": ">=4.0" } diff --git a/package.json b/package.json index 4a1f6c5..eea26dd 100644 --- a/package.json +++ b/package.json @@ -1,20 +1,20 @@ { "name": "qualys", - "version": "4.0.0", + "version": "4.1.0", "main": "./dist/integration.js", "private": true, "license": "UNLICENSED", "author": "Polarity", "dependencies": { - "lodash": "^4.17.21", - "polarity-integration-utils": "^4.1.0", + "lodash": "^4.18.1", + "polarity-integration-utils": "^4.3.1", "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", + "@polarityio/integration-types": "^3.1.0", + "@polarityio/pi-components": "^1.14.0", + "@polarityio/vite-plugin-icl": "^1.0.2", "@types/lodash": "^4.17.24", "@types/xml2js": "^0.4.14", "@vitest/browser": "^4.1.4", @@ -40,5 +40,11 @@ "test:server": "vitest run --project server", "test:browser": "vitest run --project browser", "test:watch": "vitest" + }, + "overrides": { + "postman-request": { + ".": "2.88.1-postman.48", + "qs": "^6.15.2" + } } } diff --git a/src/associateDataWithEntities.ts b/src/associateDataWithEntities.ts index e695f80..42e25e8 100644 --- a/src/associateDataWithEntities.ts +++ b/src/associateDataWithEntities.ts @@ -1,11 +1,14 @@ import { map, toLower, filter, flow, replace, includes, concat, uniqBy, flatMap } from 'lodash/fp'; import type { Entity } from '@polarityio/integration-types'; +import type { ScanResult } from './querying/queryScanListForEntities'; + /* eslint-disable @typescript-eslint/no-explicit-any */ export interface EntityData { allHostDetections: any[]; - allFoundKnowledgeBaseRecords: any[] | undefined; + allFoundKnowledgeBaseRecords: any[]; + allScanResults: ScanResult[]; } export interface EntityWithResults { @@ -13,26 +16,73 @@ export interface EntityWithResults { results: { hostDetections: any[]; knowledgeBaseRecords: any[]; + scans: any[]; }; } const associateDataWithEntities = ( entities: Entity[], - { allHostDetections, allFoundKnowledgeBaseRecords }: EntityData + { allHostDetections, allFoundKnowledgeBaseRecords, allScanResults }: 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 } }; + const entityType = (entity as any).type; + const entityScans = findEntityScans(entity, allScanResults || []); + + // CVE entities: match KB records by _sourceCve tag + if (entityType === 'cve') { + const knowledgeBaseRecords = (allFoundKnowledgeBaseRecords || []).filter( + (kb: any) => kb._sourceCve && kb._sourceCve.toLowerCase() === entity.value.toLowerCase() + ); + + const hostDetections = flow( + flatMap(({ qid }: any) => getObjectsContainingString(qid, allHostDetections)), + uniqBy('id') + )(knowledgeBaseRecords) as any[]; + + return { entity, results: { hostDetections, knowledgeBaseRecords, scans: entityScans } }; + } + + // QID / customQid entities: match KB records by qid field + if (entityType === 'qid' || entityType === 'customQid') { + const knowledgeBaseRecords = (allFoundKnowledgeBaseRecords || []).filter( + (kb: any) => String(kb.qid) === String(entity.value) + ); + + const hostDetections = flow( + flatMap(({ qid }: any) => getObjectsContainingString(qid, allHostDetections)), + concat(getObjectsContainingString(entity.value, allHostDetections)), + uniqBy('id') + )(knowledgeBaseRecords) as any[]; + + return { entity, results: { hostDetections, knowledgeBaseRecords, scans: entityScans } }; + } + + // IP / Domain / IPv6 entities: host detections by text search; no KB records + const hostDetections = getObjectsContainingString(entity.value, allHostDetections); + + return { + entity, + results: { hostDetections, knowledgeBaseRecords: [], scans: entityScans } + }; }, entities); +/** + * Find scan results for a given entity. + * IPs: match by entityValue + * QIDs: use the shared '__RECENT__' bucket + */ +const findEntityScans = (entity: Entity, allScanResults: ScanResult[]): any[] => { + if ((entity as any).isIP || (entity as any).type === 'IPv4' || (entity as any).type === 'IPv6') { + const entry = allScanResults.find((r) => r.entityValue === entity.value); + return entry ? entry.scans : []; + } + if ((entity as any).type === 'qid' || (entity as any).type === 'customQid') { + const entry = allScanResults.find((r) => r.entityValue === '__RECENT__'); + return entry ? entry.scans : []; + } + return []; +}; + const getObjectsContainingString = (string: string, objs: any[]): any[] => filter( flow( @@ -41,7 +91,7 @@ const getObjectsContainingString = (string: string, objs: any[]): any[] => toLower, includes(flow(replace(/[^\w]/g, ''), toLower)(string)) ), - objs + objs || [] ); export default associateDataWithEntities; diff --git a/src/checkScanStatus.ts b/src/checkScanStatus.ts new file mode 100644 index 0000000..23b956d --- /dev/null +++ b/src/checkScanStatus.ts @@ -0,0 +1,99 @@ +import { get, flow } from 'lodash/fp'; +import type { Logger } from '@polarityio/integration-types'; +import type { PolarityRequest } from 'polarity-integration-utils'; +import { IntegrationError, ApiRequestError } from 'polarity-integration-utils'; + +import { xmlToJson, processPossibleList } from './dataTransformations'; + +/* eslint-disable @typescript-eslint/no-explicit-any */ + +export interface ScanStatus { + ref: string; + state: string; + subState: string; + title: string; + duration: string; + launchDatetime: string; +} + +/** + * Query the current status of a specific Qualys VM scan by scan reference. + * GET /api/3.0/fo/scan/?action=list&scan_ref=&show_status=1 + */ +const checkScanStatus = async ( + rawScanRef: string, + options: Record, + request: PolarityRequest, + Logger: Logger +): Promise => { + // Sanitize: extract just the valid scan/XXXXX.XXXXX portion in case the value + // was persisted with leading corruption (e.g. from xml2js charkey collision). + const scanRefMatch = (rawScanRef || '').match(/scan\/\d+\.\d+/); + const scanRef = scanRefMatch ? scanRefMatch[0] : rawScanRef.trim(); + + if (!scanRef || !/^scan\/\d+\.\d+$/.test(scanRef)) { + throw new IntegrationError(`Invalid scan reference: "${rawScanRef}"`, { + title: 'Invalid Scan Reference', + help: 'The scan reference must match the format scan/XXXXXXXXXX.XXXXX.' + }); + } + + Logger.debug({ scanRef }, 'Checking scan status'); + + let responseBody: string; + try { + const response = await request.run({ + method: 'GET', + url: `${options.url}/api/3.0/fo/scan/`, + qs: { action: 'list', scan_ref: scanRef, show_status: 1 }, + headers: { 'X-Requested-With': 'Polarity' }, + json: false + }); + responseBody = (response!.body as string) || ''; + } catch (err) { + if (err instanceof ApiRequestError && typeof (err as any).meta?.body === 'string') { + const errorXml = (err as any).meta.body as string; + const errorJson = await xmlToJson(errorXml, Logger); + const errorText = (get('simple_return.response.text', errorJson) as string) || ''; + if (errorText) { + throw new IntegrationError(errorText, { + title: 'Check Scan Status Failed', + cause: err + }); + } + } + throw err; + } + + const responseXml = responseBody; + const json = await xmlToJson(responseXml, Logger); + + const rawScans = flow( + get('scan_list_output.response.scan_list.scan'), + processPossibleList(false) + )(json) as any[] | null; + + if (!rawScans || !rawScans.length) { + return { + ref: scanRef, + state: 'Queued', + subState: '', + title: '', + duration: '', + launchDatetime: '' + }; + } + + const scan = Array.isArray(rawScans) ? rawScans[0] : rawScans; + + return { + ref: scan.ref || scanRef, + state: (scan.status && scan.status.state) || 'Unknown', + subState: (scan.status && scan.status.sub_state) || '', + title: (scan.title && (scan.title.value || scan.title)) || '', + duration: scan.duration || '', + launchDatetime: scan.launch_datetime || '' + }; +}; + +export default checkScanStatus; diff --git a/src/constants.ts b/src/constants.ts index 2c130ca..2a4723c 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -35,6 +35,7 @@ export interface DisplayFormatEntry { isDate?: boolean; isList?: boolean; isListOfLinks?: boolean; + isKeyValueObject?: boolean; isHtml?: boolean; isNewSectionLineBreak?: boolean; indent?: number; @@ -64,14 +65,16 @@ export const HOST_DETECTION_DISPLAY_FORMAT: DisplayFormat = { indent: 1, collapsibleListItems: true, itemDisplayFormat: { - results: 'Detection Result', qid: { label: 'QID', fieldIsCopyable: true, shouldCopyFieldLabel: true }, type: 'Detection Type', severity: 'Severity', + qds: 'QDS Score', + qds_severity: 'QDS Severity', + qds_factors: { label: 'QDS Factors', isKeyValueObject: true }, + status: 'Status', port: 'Port', protocol: 'Protocol', ssl: 'SSL', - status: 'Status', is_ignored: 'Is Ignored', is_disabled: 'Is Disabled', times_found: 'Times Found', @@ -79,7 +82,8 @@ export const HOST_DETECTION_DISPLAY_FORMAT: DisplayFormat = { last_found_datetime: { label: 'Last Found', isDate: true }, last_test_datetime: { label: 'Last Tested', isDate: true }, last_update_datetime: { label: 'Last Updated', isDate: true }, - last_processed_datetime: { label: 'Last Processed', isDate: true } + last_processed_datetime: { label: 'Last Processed', isDate: true }, + results: 'Detection Result' } }, newSectionLineBreak: { isNewSectionLineBreak: true } @@ -104,3 +108,38 @@ export const KNOWLEDGE_BASE_RECORD_DISPLAY_FORMAT: DisplayFormat = { solution: { label: 'Solution', isHtml: true }, newSectionLineBreak: { isNewSectionLineBreak: true } }; + +export const CVE_DISPLAY_FORMAT: DisplayFormat = { + title: { isTitle: true }, + qid: { label: 'QID', fieldIsCopyable: true, shouldCopyFieldLabel: true }, + vuln_type: 'Vuln Type', + severity: 'Severity Level', + category: 'Category', + patchable: 'Patch Available', + published: { label: 'Published On', isDate: true }, + modified: { label: 'Last Modified', isDate: true }, + cvss_base: 'CVSS Base', + cvss_temporal: 'CVSS Temporal', + cvss_v3_base: 'CVSS v3 Base', + cvss_v3_temporal: 'CVSS v3 Temporal', + threat_intelligence: 'Threat Intelligence', + cves: { label: 'CVE IDs', isListOfLinks: true }, + diagnosis: { label: 'Diagnosis', isHtml: true }, + consequence: { label: 'Consequence', isHtml: true }, + solution: { label: 'Solution', isHtml: true }, + newSectionLineBreak: { isNewSectionLineBreak: true } +}; + +export const SCAN_DISPLAY_FORMAT: DisplayFormat = { + title: { isTitle: true }, + state: 'Status', + sub_state: 'Sub-Status', + launch_datetime: { label: 'Launched', isDate: true }, + duration: 'Duration', + type: 'Scan Type', + user_login: 'Launched By', + target: 'Target', + option_profile: 'Option Profile', + ref: { label: 'Reference', fieldIsCopyable: true, shouldCopyFieldLabel: false }, + newSectionLineBreak: { isNewSectionLineBreak: true } +}; diff --git a/src/createLookupResults.ts b/src/createLookupResults.ts index c8170d5..fcc8d93 100644 --- a/src/createLookupResults.ts +++ b/src/createLookupResults.ts @@ -1,25 +1,38 @@ -import { flow, map, size, mapValues, some, identity, keys, assign } from 'lodash/fp'; +import { flow, map, size, 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 { + HOST_DETECTION_DISPLAY_FORMAT, + KNOWLEDGE_BASE_RECORD_DISPLAY_FORMAT, + CVE_DISPLAY_FORMAT, + SCAN_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[] => +const createLookupResults = ( + foundEntities: EntityWithResults[], + options: Record = {} +): LookupResult[] => map(({ entity, results }: EntityWithResults) => { - const formattedQueryResult = formatQueryResult(results); + const formattedQueryResult = formatQueryResult(results, entity, options); const lookupResult: LookupResult = { entity, - displayValue: (entity.type as string) === 'qid' ? `QID: ${entity.value}` : entity.value, + displayValue: + (entity.type as string) === 'qid' || (entity.type as string) === 'customQid' + ? `QID: ${entity.value}` + : entity.value, data: formattedQueryResult ? { summary: createSummary(entity, results), - details: flow(keys, (k: string[]) => assign(formattedQueryResult, { tabKeys: k }))( - formattedQueryResult - ) + details: flow( + keys, + (k: string[]) => k.filter((key) => !key.startsWith('_')), + (k: string[]) => assign(formattedQueryResult, { tabKeys: k }) + )(formattedQueryResult) } : null }; @@ -27,20 +40,58 @@ const createLookupResults = (foundEntities: EntityWithResults[]): 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 }; + entity: Entity, + { hostDetections, knowledgeBaseRecords, scans }: EntityWithResults['results'] +): string[] => { + const summary: string[] = []; + if (size(hostDetections)) summary.push(`Host Detections: ${size(hostDetections)}`); + if (size(knowledgeBaseRecords)) summary.push(`KB Records: ${size(knowledgeBaseRecords)}`); + if (size(scans)) summary.push(`Scans: ${size(scans)}`); + return summary; +}; + +const formatQueryResult = ( + result: EntityWithResults['results'], + entity: Entity, + options: Record +): Record | undefined => { + const hasData = + size(result.hostDetections) > 0 || + size(result.knowledgeBaseRecords) > 0 || + size(result.scans) > 0; + + if (!hasData) return undefined; + + const formatted: Record = {}; + + if (size(result.hostDetections)) { + formatted.hostDetections = getDisplayResults( + HOST_DETECTION_DISPLAY_FORMAT, + result.hostDetections + ); } + + if (size(result.knowledgeBaseRecords)) { + const isCve = (entity as any).type === 'cve'; + const displayFormat = isCve ? CVE_DISPLAY_FORMAT : KNOWLEDGE_BASE_RECORD_DISPLAY_FORMAT; + formatted.knowledgeBaseRecords = getDisplayResults( + displayFormat, + result.knowledgeBaseRecords, + isCve + ); + } + + if (size(result.scans)) { + formatted.scans = getDisplayResults(SCAN_DISPLAY_FORMAT, result.scans); + // Pass scan launch capability info for the web component + formatted._scanMeta = { + enableScanLaunch: !!options.enableScanLaunch, + entityValue: entity.value, + entityType: (entity as any).type + }; + } + + return formatted; }; export default createLookupResults; diff --git a/src/getLookupResults.ts b/src/getLookupResults.ts index 9ca66eb..adde4e1 100644 --- a/src/getLookupResults.ts +++ b/src/getLookupResults.ts @@ -1,14 +1,37 @@ -import { flow, map, split, first, last, trim, uniqBy } from 'lodash/fp'; +import { flow, map, first, last, split, 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 queryKnowledgeBaseForAllEntities from './querying/queryKnowledgeBaseForAllEntities'; +import queryScanListForAllEntities from './querying/queryScanListForEntities'; import associateDataWithEntities from './associateDataWithEntities'; /* eslint-disable @typescript-eslint/no-explicit-any */ +export const extractQidValue = (value: string): string => { + const match = value.match(/(?:QID|qid)(?:\s*[:\-_]\s*|\s*)(\d{1,8})/i); + return match ? match[1] : value.trim(); +}; + +export const extractCustomQidValue = (value: string, customQidValueRegex?: string): string => { + if (customQidValueRegex && customQidValueRegex.trim() !== '') { + try { + const re = new RegExp(customQidValueRegex); + const match = value.match(re); + if (match) { + return match[1] !== undefined ? match[1] : match[0]; + } + } catch (_) { + // fall through to default digit extraction + } + } + const match = value.match(/\d+$/); + return match ? match[0] : value.trim(); +}; + export const getLookupResults = async ( entities: Entity[], options: Record, @@ -20,8 +43,16 @@ export const getLookupResults = async ( entity.type === 'custom' ? (flow(first, split('.'), last)(entity.types) as string) : entity.type; - const value = - type === 'qid' ? (flow(split(':'), last, trim)(entity.value) as string) : entity.value; + + let value = entity.value; + if (type === 'qid') { + value = extractQidValue(entity.value); + } else if (type === 'customQid') { + value = extractCustomQidValue( + entity.value, + options.customQidValueRegex?.value as string | undefined + ); + } return { ...entity, type, value } as Entity; }, entities); @@ -32,7 +63,7 @@ export const getLookupResults = async ( const data = await getData(entitiesPartition, options, request, Logger); const foundEntities = associateDataWithEntities(entitiesPartition, data); - const lookupResults = createLookupResults(foundEntities); + const lookupResults = createLookupResults(foundEntities, options); return lookupResults.concat(ignoredIpLookupResults); }; @@ -42,12 +73,31 @@ const getData = async ( request: PolarityRequest, Logger: Logger ): Promise => { - const [initialHostDetections] = await Promise.all([ - queryHostDetectionListForAllEntities(entitiesPartition, options, request, Logger) - ]); + // Sequential queries — Qualys enforces concurrent call limits per subscription tier + const allHostDetections = uniqBy( + 'id', + await queryHostDetectionListForAllEntities(entitiesPartition, options, request, Logger) + ); + + const allFoundKnowledgeBaseRecords = await queryKnowledgeBaseForAllEntities( + entitiesPartition, + options, + request, + Logger + ); - const allHostDetections = flow(uniqBy('id'))(initialHostDetections); - Logger.trace({ allHostDetections }, 'All Host Detections'); + // Scan list: fetch for IP and QID entities (gracefully skipped on failure) + let allScanResults: any[] = []; + try { + allScanResults = await queryScanListForAllEntities(entitiesPartition, options, request, Logger); + } catch (error) { + Logger.warn({ error }, 'Scan list query failed — scans tab will be empty'); + } + + Logger.trace( + { allHostDetections, allFoundKnowledgeBaseRecords, allScanResults }, + 'getData results' + ); - return { allHostDetections, allFoundKnowledgeBaseRecords: undefined }; + return { allHostDetections, allFoundKnowledgeBaseRecords, allScanResults }; }; diff --git a/src/integration.ts b/src/integration.ts index 696a04d..1999765 100644 --- a/src/integration.ts +++ b/src/integration.ts @@ -13,10 +13,19 @@ import type { BeforeRequestHook } from 'polarity-integration-utils'; import { parseErrorToReadableJSON } from './dataTransformations'; import { getLookupResults } from './getLookupResults'; -import { validateStringOptions, validateUrlOption } from './validateOptions'; +import { + validateStringOptions, + validateUrlOption, + validateScanOptions, + validateCustomQidValueRegex +} from './validateOptions'; +import launchScan from './launchScan'; +import checkScanStatus from './checkScanStatus'; let request: PolarityRequest; +const VALID_ACTIONS = new Set(['LAUNCH_SCAN', 'CHECK_SCAN_STATUS']); + const addAuth: BeforeRequestHook = async (requestOptions, userOptions) => ({ ...requestOptions, auth: { @@ -42,6 +51,7 @@ async function doLookup( Logger.debug({ entities }, 'Entities'); request.userOptions = options; + request.network = context.network; try { const lookupResults = await getLookupResults(entities, options, request, Logger); @@ -58,6 +68,41 @@ async function doLookup( } } +async function onMessage( + payload: unknown, + options: DoLookupUserOptions, + context: IntegrationContext +): Promise { + const Logger = context.logger; + request.userOptions = options; + request.network = context.network; + + const msg = payload as Record; + const action = msg?.action as string; + + Logger.debug({ action }, 'onMessage received'); + + if (!action || !VALID_ACTIONS.has(action)) { + throw new IntegrationError(`Unknown onMessage action: ${action}`, { + title: 'Invalid Action' + }); + } + + try { + if (action === 'LAUNCH_SCAN') { + return await launchScan(msg.entityValue as string, options as any, request, Logger); + } + + if (action === 'CHECK_SCAN_STATUS') { + return await checkScanStatus(msg.scanRef as string, options as any, request, Logger); + } + } catch (error) { + const err = parseErrorToReadableJSON(error); + Logger.error({ error, formattedError: err }, 'onMessage failed'); + throw error; + } +} + function validateOptions( options: ValidateOptionsUserOptions, _context: IntegrationContext @@ -69,10 +114,14 @@ function validateOptions( }; const stringValidationErrors = validateStringOptions(stringOptionsErrorMessages, options); - const urlValidationErrors = validateUrlOption(options.url.value as string); + const scanValidationErrors = validateScanOptions(options); + const customTypeRegexErrors = validateCustomQidValueRegex(options); - return stringValidationErrors.concat(urlValidationErrors); + return stringValidationErrors + .concat(urlValidationErrors) + .concat(scanValidationErrors) + .concat(customTypeRegexErrors); } -export { startup, doLookup, validateOptions }; +export { startup, doLookup, onMessage, validateOptions }; diff --git a/src/launchScan.ts b/src/launchScan.ts new file mode 100644 index 0000000..4ce03b8 --- /dev/null +++ b/src/launchScan.ts @@ -0,0 +1,121 @@ +import { get } from 'lodash/fp'; +import type { Logger } from '@polarityio/integration-types'; +import type { PolarityRequest } from 'polarity-integration-utils'; +import { IntegrationError, ApiRequestError } from 'polarity-integration-utils'; + +import { xmlToJson } from './dataTransformations'; + +/* eslint-disable @typescript-eslint/no-explicit-any */ + +export interface LaunchScanResult { + scanRef: string; + scanId: string; + message: string; + scanTitle: string; +} + +/** + * Launch a Qualys VM scan for a target IP address. + * POST /api/3.0/fo/scan/?action=launch + */ +const launchScan = async ( + entityValue: string, + options: Record, + request: PolarityRequest, + Logger: Logger +): Promise => { + const today = new Date().toISOString().split('T')[0]; + const scanTitle = `Polarity Scan - ${entityValue} - ${today}`; + + const formData: Record = { + action: 'launch', + scan_title: scanTitle, + ip: entityValue + }; + + const optionProfile = getOptionValue(options.scanOptionProfile); + if (optionProfile && optionProfile.trim()) { + if (/^\d+$/.test(optionProfile.trim())) { + formData.option_id = optionProfile.trim(); + } else { + formData.option_title = optionProfile.trim(); + } + } + + const scannerName = getOptionValue(options.scannerName); + if (scannerName && scannerName.trim()) { + formData.iscanner_name = scannerName.trim(); + } else { + formData.default_scanner = 1; + } + + Logger.debug({ scanTitle, formData }, 'Launching Qualys scan'); + + let responseBody: string; + try { + const response = await request.run({ + method: 'POST', + url: `${options.url}/api/3.0/fo/scan/`, + form: formData, + headers: { 'X-Requested-With': 'Polarity' }, + json: false + }); + responseBody = (response!.body as string) || ''; + } catch (err) { + if (err instanceof ApiRequestError && typeof (err as any).meta?.body === 'string') { + const errorXml = (err as any).meta.body as string; + const errorJson = await xmlToJson(errorXml, Logger); + const errorText = (get('simple_return.response.text', errorJson) as string) || ''; + if (errorText) { + throw new IntegrationError(errorText, { + title: 'Scan Launch Failed', + help: 'Check that the scan option profile ID or name is valid in your Qualys account.', + cause: err + }); + } + } + throw err; + } + + const responseXml = responseBody; + const json = await xmlToJson(responseXml, Logger); + const responseText = (get('simple_return.response.text', json) as string) || ''; + + const rawItems = get('simple_return.response.item_list.item', json); + const items: any[] = Array.isArray(rawItems) ? rawItems : rawItems ? [rawItems] : []; + + const scanRef = extractItemValue(items, 'REFERENCE'); + const scanId = extractItemValue(items, 'ID'); + + if (!responseText.toLowerCase().includes('launched')) { + const errorText = responseText || 'No confirmation received from Qualys.'; + Logger.error({ responseText, json }, 'Scan launch failed'); + throw new IntegrationError(errorText, { + title: 'Scan Launch Failed', + help: 'Check that the scan option profile and scanner name are valid.' + }); + } + + return { scanRef, scanId, message: responseText, scanTitle }; +}; + +const getOptionValue = (opt: any): string => { + if (opt === null || opt === undefined) return ''; + if (typeof opt === 'object' && 'value' in opt) return (opt.value as string) || ''; + return String(opt); +}; + +/** + * xml2js with charkey:'value' conflicts with Qualys element names, + * producing arrays that mix whitespace text nodes with the real value. + * Flatten to a string and extract clean content. + */ +const extractItemValue = (items: any[], keyName: string): string => { + const item = items.find((i: any) => ((i.key as string) || '').toUpperCase() === keyName); + if (!item) return ''; + const raw = item.value; + const str = Array.isArray(raw) ? raw.join('') : String(raw ?? ''); + return str.trim(); +}; + +export default launchScan; diff --git a/src/querying/queryHostDetectionListForAllEntities.ts b/src/querying/queryHostDetectionListForAllEntities.ts index 9c35db6..c8b922b 100644 --- a/src/querying/queryHostDetectionListForAllEntities.ts +++ b/src/querying/queryHostDetectionListForAllEntities.ts @@ -21,9 +21,11 @@ const queryHostDetectionListForAllEntities = async ( 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'), eq('qid')), entities); + const qidEntities = filter(flow(get('type'), or(eq('qid'), eq('customQid'))), entities); const qidValues = map(get('value'), qidEntities) as string[]; + const domainEntities = filter(flow(get('type'), eq('domain')), entities) as Entity[]; + const allHostDetectionResultForIpAddresses = size(ipValues) ? await queryHostDetectionList('ips', options, request, Logger)(ipValues) : []; @@ -32,9 +34,13 @@ const queryHostDetectionListForAllEntities = async ( ? await queryHostDetectionList('qids', options, request, Logger)(qidValues) : []; - return uniqBy('id', allHostDetectionResultForIpAddresses).concat( - uniqBy('id', allHostDetectionResultForQids) - ); + const allHostDetectionResultForDomains = size(domainEntities) + ? await queryHostDetectionListForDomains(domainEntities, options, request, Logger) + : []; + + return uniqBy('id', allHostDetectionResultForIpAddresses) + .concat(uniqBy('id', allHostDetectionResultForQids)) + .concat(uniqBy('id', allHostDetectionResultForDomains)); }; const queryHostDetectionList = @@ -43,12 +49,14 @@ const queryHostDetectionList = try { const response = await request.run({ method: 'GET', - url: `${options.url}/api/2.0/fo/asset/host/vm/detection/`, + url: `${options.url}/api/5.0/fo/asset/host/vm/detection/`, qs: { action: 'list', [type]: join(',', entityValues), show_asset_id: 1, - show_results: 1 + show_results: 1, + show_qds: 1, + show_qds_factors: 1 }, headers: { 'X-Requested-With': 'Polarity' }, json: false @@ -80,6 +88,68 @@ const queryHostDetectionList = } }; +/** + * Two-step domain resolution: + * 1. Query /api/5.0/fo/asset/host/ with tracking_method=DNS to find hosts by DNS name + * 2. Run detection query against the resolved IPs + */ +const queryHostDetectionListForDomains = async ( + domainEntities: Entity[], + options: Record, + request: PolarityRequest, + Logger: Logger +): Promise => { + const results: any[] = []; + + for (const entity of domainEntities) { + try { + const hostListResponse = await request.run({ + method: 'GET', + url: `${options.url}/api/5.0/fo/asset/host/`, + qs: { + action: 'list', + details: 'Basic', + tracking_method: 'DNS', + show_asset_id: 1 + }, + headers: { 'X-Requested-With': 'Polarity' }, + json: false + }); + + const hostListXml = (hostListResponse!.body as string) || ''; + const hostListJson = await xmlToJson(hostListXml, Logger); + + const hosts = flow( + get('host_list_output.response.host_list.host'), + processPossibleList(false) + )(hostListJson) as any[] | null; + + if (!hosts || !hosts.length) continue; + + const matchingIps = hosts + .filter((h: any) => { + const dns = ((h.dns as string) || '').toLowerCase(); + return dns.includes(entity.value.toLowerCase()); + }) + .map((h: any) => h.ip as string) + .filter(Boolean); + + if (!matchingIps.length) continue; + + const detections = await queryHostDetectionList('ips', options, request, Logger)(matchingIps); + results.push(...(detections || [])); + } catch (error) { + const err = parseErrorToReadableJSON(error); + Logger.error( + { detail: 'Failed Domain Two-Step Lookup', domain: entity.value, formattedError: err }, + 'Domain Host Detection Failed' + ); + } + } + + return uniqBy('id', results) as any[]; +}; + const HOST_DETECTIONS_LIST_ITEM_FORMAT: Record = { qid: 'qid', type: 'type', @@ -87,6 +157,28 @@ const HOST_DETECTIONS_LIST_ITEM_FORMAT: Record = { port: 'port', protocol: 'protocol', ssl: { path: 'ssl', process: (ssl: any) => (ssl == 1 ? 'Yes' : 'No') }, + qds: { + path: 'qds', + process: (qds: any) => (qds && qds.value != null ? qds.value : qds) + }, + qds_severity: { + path: 'qds', + process: (qds: any) => (qds && qds.attributes ? qds.attributes.severity : null) + }, + qds_factors: { + path: 'qds_factors', + process: (qdsFactors: any) => { + if (!qdsFactors) return null; + const factors = processPossibleList(false)(qdsFactors.qds_factor || qdsFactors) as any[]; + if (!factors) return null; + return factors.reduce((acc: Record, f: any) => { + if (f && f.attributes && f.attributes.name) { + acc[f.attributes.name] = f.value || f; + } + return acc; + }, {}); + } + }, results: 'results', status: 'status', first_found_datetime: 'first_found_datetime', diff --git a/src/querying/queryKnowledgeBaseForAllEntities.ts b/src/querying/queryKnowledgeBaseForAllEntities.ts new file mode 100644 index 0000000..bf4d8af --- /dev/null +++ b/src/querying/queryKnowledgeBaseForAllEntities.ts @@ -0,0 +1,150 @@ +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 */ + +/** + * Query the Qualys KnowledgeBase API for CVE and QID entities. + * + * CVE → POST /api/4.0/fo/knowledge_base/vuln/index.php?action=list&cve={id}&details=All + * QID → POST /api/4.0/fo/knowledge_base/vuln/index.php?action=list&ids={qid}&details=All + */ +const queryKnowledgeBaseForAllEntities = async ( + entities: Entity[], + options: Record, + request: PolarityRequest, + Logger: Logger +): Promise => { + const cveEntities = filter(flow(get('type'), eq('cve')), entities) as Entity[]; + const qidEntities = filter( + flow(get('type'), or(eq('qid'), eq('customQid'))), + entities + ) as Entity[]; + const qidValues = map(get('value'), qidEntities) as string[]; + + const kbResultsForCves = size(cveEntities) + ? await queryKnowledgeBaseByCve(cveEntities, options, request, Logger) + : []; + + const kbResultsForQids = size(qidValues) + ? await queryKnowledgeBaseByQid(qidValues, options, request, Logger) + : []; + + return uniqBy('qid', kbResultsForCves.concat(kbResultsForQids)); +}; + +const queryKnowledgeBaseByCve = async ( + cveEntities: Entity[], + options: Record, + request: PolarityRequest, + Logger: Logger +): Promise => { + const results: any[] = []; + + for (const entity of cveEntities) { + const records = await queryKnowledgeBase({ cve: entity.value }, options, request, Logger); + records.forEach((r: any) => { + r._sourceCve = entity.value; + }); + results.push(...records); + } + + return results; +}; + +const queryKnowledgeBaseByQid = async ( + qidValues: string[], + options: Record, + request: PolarityRequest, + Logger: Logger +): Promise => { + return await queryKnowledgeBase({ ids: join(',', qidValues) }, options, request, Logger); +}; + +const queryKnowledgeBase = async ( + filterParams: Record, + options: Record, + request: PolarityRequest, + Logger: Logger +): Promise => { + try { + const response = await request.run({ + method: 'POST', + url: `${options.url}/api/4.0/fo/knowledge_base/vuln/index.php`, + qs: { + action: 'list', + details: 'All', + ...filterParams + }, + headers: { 'X-Requested-With': 'Polarity' }, + json: false + }); + + const responseXml = (response!.body as string) || ''; + const json = await xmlToJson(responseXml, Logger); + + const vulns = flow( + get('knowledge_base_vuln_list_output.response.vuln_list.vuln'), + processPossibleList(false) + )(json) as any[] | null; + + if (!vulns || !vulns.length) return []; + + return map(processResultWithProcessingFormat(KB_RECORD_FORMAT), vulns); + } catch (error) { + const err = parseErrorToReadableJSON(error); + Logger.error( + { detail: 'Failed to Query KnowledgeBase', filterParams, formattedError: err }, + 'KnowledgeBase Query Failed' + ); + throw error; + } +}; + +const KB_RECORD_FORMAT: Record = { + qid: 'qid', + vuln_type: 'vuln_type', + severity: 'severity_level', + title: 'title', + category: 'category', + patchable: { + path: 'patchable', + process: (v: any) => (v == 1 ? 'Yes' : 'No') + }, + published: 'published_datetime', + modified: 'last_service_modification_datetime', + cves: { + path: 'cve_list', + process: (cveList: any) => { + if (!cveList) return null; + const items = processPossibleList(false)(cveList.cve || cveList) as any[]; + return JSON.stringify((items || []).map((c: any) => ({ id: c.id || c, url: c.url || null }))); + } + }, + cvss_base: 'cvss.base', + cvss_temporal: 'cvss.temporal', + cvss_v3_base: 'cvss_v3.cvss3_base', + cvss_v3_temporal: 'cvss_v3.cvss3_temporal', + threat_intelligence: { + path: 'threat_intelligence', + process: (ti: any) => { + if (!ti) return null; + const items = processPossibleList(false)(ti.threat_intel || ti) as any[]; + return (items || []).map((t: any) => t.value || t).join(', ') || null; + } + }, + diagnosis: 'diagnosis', + consequence: 'consequence', + solution: 'solution' +}; + +export default queryKnowledgeBaseForAllEntities; diff --git a/src/querying/queryScanListForEntities.ts b/src/querying/queryScanListForEntities.ts new file mode 100644 index 0000000..17c350c --- /dev/null +++ b/src/querying/queryScanListForEntities.ts @@ -0,0 +1,124 @@ +import { get, flow } from 'lodash/fp'; +import type { Entity, Logger } from '@polarityio/integration-types'; +import type { PolarityRequest } from 'polarity-integration-utils'; + +import { xmlToJson, processPossibleList } from '../dataTransformations'; + +/* eslint-disable @typescript-eslint/no-explicit-any */ + +export interface ScanResult { + entityValue: string; + scans: NormalizedScan[]; +} + +export interface NormalizedScan { + title: string; + ref: string; + type: string; + user_login: string; + launch_datetime: string; + duration: string; + state: string; + sub_state: string; + target: string; + option_profile: string; +} + +/** + * Query the Qualys VM Scan List API for recent scans. + * + * IP/IPv6 entities: GET /api/3.0/fo/scan/?action=list&target= + * QID entities: GET /api/3.0/fo/scan/?action=list (recent account scans) + */ +const queryScanListForAllEntities = async ( + entities: Entity[], + options: Record, + request: PolarityRequest, + Logger: Logger +): Promise => { + if (!entities || !entities.length) return []; + + const results: ScanResult[] = []; + + const ipEntities = entities.filter((e: any) => e.isIP || e.type === 'IPv4' || e.type === 'IPv6'); + + const qidEntities = entities.filter((e: any) => e.type === 'qid' || e.type === 'customQid'); + + for (const entity of ipEntities) { + try { + const scans = await queryScanList( + { action: 'list', show_op: 1, show_status: 1, target: entity.value }, + options, + request, + Logger + ); + results.push({ entityValue: entity.value, scans }); + } catch (error) { + Logger.warn({ error, ip: entity.value }, 'queryScanList failed for IP — defaulting to empty'); + results.push({ entityValue: entity.value, scans: [] }); + } + } + + if (qidEntities.length > 0) { + try { + const scans = await queryScanList( + { action: 'list', show_op: 1, show_status: 1 }, + options, + request, + Logger + ); + results.push({ entityValue: '__RECENT__', scans }); + } catch (error) { + Logger.warn({ error }, 'queryScanList failed for recent scans — defaulting to empty'); + results.push({ entityValue: '__RECENT__', scans: [] }); + } + } + + return results; +}; + +const queryScanList = async ( + qs: Record, + options: Record, + request: PolarityRequest, + Logger: Logger +): Promise => { + const response = await request.run({ + method: 'GET', + url: `${options.url}/api/3.0/fo/scan/`, + qs, + headers: { 'X-Requested-With': 'Polarity' }, + json: false + }); + + const responseXml = (response!.body as string) || ''; + const json = await xmlToJson(responseXml, Logger); + + const rawScans = flow( + get('scan_list_output.response.scan_list.scan'), + processPossibleList(false) + )(json) as any[] | null; + + if (!rawScans || !rawScans.length) return []; + + return rawScans.slice(0, 10).map(normalizeScan); +}; + +const normalizeScan = (scan: any): NormalizedScan => ({ + title: (scan.title && (scan.title.value || scan.title)) || 'Unnamed Scan', + ref: scan.ref || '', + type: scan.type || '', + user_login: scan.user_login || '', + launch_datetime: scan.launch_datetime || '', + duration: scan.duration || '', + state: (scan.status && scan.status.state) || '', + sub_state: (scan.status && scan.status.sub_state) || '', + target: (scan.target && (scan.target.value || scan.target)) || '', + option_profile: + (scan.option_profile && + scan.option_profile.title && + (scan.option_profile.title.value || scan.option_profile.title)) || + '' +}); + +export default queryScanListForAllEntities; diff --git a/src/validateOptions.ts b/src/validateOptions.ts index 422d72b..340844c 100644 --- a/src/validateOptions.ts +++ b/src/validateOptions.ts @@ -43,3 +43,40 @@ export const validateUrlOption = ( return otherErrors; }; + +export const validateCustomQidValueRegex = ( + options: ValidateOptionsUserOptions, + otherErrors: ValidationError[] = [] +): ValidationError[] => { + const regexValue = options.customQidValueRegex?.value as string | undefined; + if (!regexValue || regexValue.trim() === '') return otherErrors; + + try { + new RegExp(regexValue); + } catch (_) { + return otherErrors.concat({ + key: 'customQidValueRegex', + message: 'The Custom Type Regex is not a valid regular expression.' + }); + } + + return otherErrors; +}; + +export const validateScanOptions = ( + options: ValidateOptionsUserOptions, + otherErrors: ValidationError[] = [] +): ValidationError[] => { + const enableScanLaunch = options.enableScanLaunch?.value; + if (!enableScanLaunch) return otherErrors; + + const scanOptionProfile = options.scanOptionProfile?.value as string | undefined; + if (!scanOptionProfile || scanOptionProfile.trim() === '') { + return otherErrors.concat({ + key: 'scanOptionProfile', + message: 'Scan Option Profile is required when Enable Scan Launch is enabled.' + }); + } + + return otherErrors; +}; diff --git a/test/integration.test.ts b/test/integration.test.ts index 0c813ef..34bf8dc 100644 --- a/test/integration.test.ts +++ b/test/integration.test.ts @@ -288,12 +288,14 @@ describe('Qualys Integration', () => { expect(mockRun).toHaveBeenCalledWith( expect.objectContaining({ method: 'GET', - url: 'https://qualysapi.example.com/api/2.0/fo/asset/host/vm/detection/', + url: 'https://qualysapi.example.com/api/5.0/fo/asset/host/vm/detection/', qs: expect.objectContaining({ action: 'list', ips: '10.20.30.40', show_asset_id: 1, - show_results: 1 + show_results: 1, + show_qds: 1, + show_qds_factors: 1 }), headers: { 'X-Requested-With': 'Polarity' }, json: false @@ -449,7 +451,7 @@ describe('Qualys Integration', () => { expect(errors.some((e) => e.key === 'url' && e.message.includes('invalid'))).toBe(true); }); - it('should return multiple errors when multiple fields are invalid', () => { + it('should return error when multiple fields are invalid', () => { const options = { url: { value: '' }, username: { value: '' }, @@ -461,5 +463,121 @@ describe('Qualys Integration', () => { expect(errors.length).toBeGreaterThanOrEqual(3); }); + + it('should return no errors when customQidValueRegex is empty', () => { + const options = { + url: { value: 'https://qualysapi.qualys.com' }, + username: { value: 'testuser' }, + password: { value: 'testpass' }, + customQidValueRegex: { value: '' } + }; + const context = createMockContext(); + + const errors = validateOptions(options as any, context as unknown as IntegrationContext); + + expect(errors).toHaveLength(0); + }); + + it('should return no errors when customQidValueRegex is a valid regex', () => { + const options = { + url: { value: 'https://qualysapi.qualys.com' }, + username: { value: 'testuser' }, + password: { value: 'testpass' }, + customQidValueRegex: { value: 'TICKET-(\\d+)' } + }; + const context = createMockContext(); + + const errors = validateOptions(options as any, context as unknown as IntegrationContext); + + expect(errors).toHaveLength(0); + }); + + it('should return error when customQidValueRegex is an invalid regex', () => { + const options = { + url: { value: 'https://qualysapi.qualys.com' }, + username: { value: 'testuser' }, + password: { value: 'testpass' }, + customQidValueRegex: { value: '[invalid(' } + }; + const context = createMockContext(); + + const errors = validateOptions(options as any, context as unknown as IntegrationContext); + + expect(errors.some((e) => e.key === 'customQidValueRegex')).toBe(true); + }); + }); + + describe('QID value extraction', () => { + it('should extract QID value with colon format "QID:12345"', async () => { + const { extractQidValue } = await import('../src/getLookupResults'); + expect(extractQidValue('QID:12345')).toBe('12345'); + }); + + it('should extract QID value with space-colon format "QID : 12345"', async () => { + const { extractQidValue } = await import('../src/getLookupResults'); + expect(extractQidValue('QID : 12345')).toBe('12345'); + }); + + it('should extract QID value with space format "QID 12345"', async () => { + const { extractQidValue } = await import('../src/getLookupResults'); + expect(extractQidValue('QID 12345')).toBe('12345'); + }); + + it('should extract QID value with spaced colon "QID: 12345"', async () => { + const { extractQidValue } = await import('../src/getLookupResults'); + expect(extractQidValue('QID: 12345')).toBe('12345'); + }); + + it('should handle lowercase qid prefix', async () => { + const { extractQidValue } = await import('../src/getLookupResults'); + expect(extractQidValue('qid:98765')).toBe('98765'); + }); + + it('should extract QID value with hyphen separator "QID-12345"', async () => { + const { extractQidValue } = await import('../src/getLookupResults'); + expect(extractQidValue('QID-12345')).toBe('12345'); + }); + + it('should extract QID value with underscore separator "QID_12345"', async () => { + const { extractQidValue } = await import('../src/getLookupResults'); + expect(extractQidValue('QID_12345')).toBe('12345'); + }); + + it('should extract QID value with spaced hyphen "QID - 12345"', async () => { + const { extractQidValue } = await import('../src/getLookupResults'); + expect(extractQidValue('QID - 12345')).toBe('12345'); + }); + + it('should extract QID value with no separator "QID12345"', async () => { + const { extractQidValue } = await import('../src/getLookupResults'); + expect(extractQidValue('QID12345')).toBe('12345'); + }); + }); + + describe('customType value extraction', () => { + it('should use trailing digits as default when no regex provided', async () => { + const { extractCustomQidValue } = await import('../src/getLookupResults'); + expect(extractCustomQidValue('TICKET-42')).toBe('42'); + }); + + it('should use custom regex capture group 1 when provided', async () => { + const { extractCustomQidValue } = await import('../src/getLookupResults'); + expect(extractCustomQidValue('TICKET-99', 'TICKET-(\\d+)')).toBe('99'); + }); + + it('should use full match when custom regex has no capture group', async () => { + const { extractCustomQidValue } = await import('../src/getLookupResults'); + expect(extractCustomQidValue('12345', '\\d+')).toBe('12345'); + }); + + it('should fall back to trailing digits when custom regex does not match', async () => { + const { extractCustomQidValue } = await import('../src/getLookupResults'); + expect(extractCustomQidValue('TICKET-55', 'NOMATCH-(\\d+)')).toBe('55'); + }); + + it('should fall back to trailing digits when custom regex is invalid', async () => { + const { extractCustomQidValue } = await import('../src/getLookupResults'); + expect(extractCustomQidValue('TICKET-77', '[invalid(')).toBe('77'); + }); }); }); diff --git a/test/web-components/details.test.ts b/test/web-components/details.test.ts index 7600b8b..ddb134b 100644 --- a/test/web-components/details.test.ts +++ b/test/web-components/details.test.ts @@ -116,7 +116,7 @@ describe('Qualys DetailsComponent', () => { const html = getShadowHTML(el); expect(html).toContain('IP Address'); expect(html).toContain('10.0.0.1'); - expect(html).toContain(' { @@ -177,9 +177,9 @@ describe('Qualys DetailsComponent', () => { ); const html = getShadowHTML(el); + expect(html).toContain('pi-section-header'); expect(html).toContain('Diagnosis'); - // HTML content is collapsed by default - expect(html).not.toContain('This is a vulnerability description'); + expect(html).toContain('This is a vulnerability description'); }); it('should render the copy button', async () => { @@ -194,7 +194,7 @@ describe('Qualys DetailsComponent', () => { expect(html).toContain('pi-copy-button'); }); - it('should render section breaks for isNewSectionLineBreak fields', async () => { + it('should render hostDetection records as pi-cards grouped by host', async () => { el = await renderComponent( createBlock({ tabKeys: ['hostDetections'], @@ -207,7 +207,10 @@ describe('Qualys DetailsComponent', () => { ); const html = getShadowHTML(el); - expect(html).toContain('section-break'); + expect(html).toContain('pi-card'); + expect(html).toContain('Field1'); + expect(html).toContain('Field2'); + expect(html).not.toContain('section-break'); }); it('should not render tabs when there is only one visible tab', async () => { @@ -283,6 +286,7 @@ describe('Qualys DetailsComponent', () => { ); const html = getShadowHTML(el); + expect(html).toContain('pi-card'); expect(html).toContain('pi-external-link'); expect(html).toContain('https://example.com/host/1'); }); @@ -316,4 +320,95 @@ describe('Qualys DetailsComponent', () => { expect(html).toContain('CVE-2024-0002'); expect(html).toContain('pi-external-link'); }); + + it('should render CVE knowledge base records as pi-cards with QID title', async () => { + el = await renderComponent( + createBlock({ + tabKeys: ['knowledgeBaseRecords'], + knowledgeBaseRecords: [ + [ + { value: 'SQL Injection Vulnerability', isTitle: true }, + { label: 'QID', value: '12345', fieldIsCopyable: true, shouldCopyFieldLabel: true }, + { label: 'Severity Level', value: '5' } + ], + [ + { value: 'Buffer Overflow Exploit', isTitle: true }, + { label: 'QID', value: '67890', fieldIsCopyable: true, shouldCopyFieldLabel: true }, + { label: 'Severity Level', value: '3' } + ] + ] + }) + ); + + const html = getShadowHTML(el); + expect(html).toContain('pi-card'); + expect(html).toContain('SQL Injection Vulnerability'); + expect(html).toContain('Buffer Overflow Exploit'); + expect(html).toContain('12345'); + expect(html).toContain('67890'); + }); + + it('should render scan results as bordered items without hr separators', async () => { + el = await renderComponent( + createBlock({ + tabKeys: ['scans'], + scans: [ + { label: 'Scan One', value: 'Scan One', isTitle: true }, + { label: 'Status', value: 'Finished' }, + { isNewSectionLineBreak: true }, + { label: 'Scan Two', value: 'Scan Two', isTitle: true }, + { label: 'Status', value: 'Running' }, + { isNewSectionLineBreak: true } + ] + }) + ); + + const html = getShadowHTML(el); + expect(html).toContain('scan-item'); + expect(html).toContain('Scan One'); + expect(html).toContain('Scan Two'); + expect(html).not.toContain('section-break'); + }); + + it('should show a count message when there are more than 10 scans', async () => { + const scanFields: Record[] = []; + for (let i = 1; i <= 12; i++) { + scanFields.push({ label: `Scan ${i}`, value: `Scan ${i}`, isTitle: true }); + scanFields.push({ label: 'Status', value: 'Finished' }); + scanFields.push({ isNewSectionLineBreak: true }); + } + + el = await renderComponent( + createBlock({ + tabKeys: ['scans'], + scans: scanFields + }) + ); + + const html = getShadowHTML(el); + expect(html).toContain('scan-count-message'); + expect(html).toContain('10 of'); + expect(html).toContain('Scan 1'); + expect(html).toContain('Scan 10'); + expect(html).not.toContain('Scan 11'); + expect(html).not.toContain('Scan 12'); + }); + + it('should not show a count message when there are 10 or fewer scans', async () => { + const scanFields: Record[] = []; + for (let i = 1; i <= 5; i++) { + scanFields.push({ label: `Scan ${i}`, value: `Scan ${i}`, isTitle: true }); + scanFields.push({ isNewSectionLineBreak: true }); + } + + el = await renderComponent( + createBlock({ + tabKeys: ['scans'], + scans: scanFields + }) + ); + + const html = getShadowHTML(el); + expect(html).not.toContain('Showing first'); + }); }); diff --git a/web-components/details.ts b/web-components/details.ts index 63938c5..93a9d81 100644 --- a/web-components/details.ts +++ b/web-components/details.ts @@ -2,7 +2,7 @@ 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'; +import { faAngleUp, faAngleDown, faCopy, faPlay, faRotate } from '@polarityio/pi-icon'; interface DisplayField { label?: string; @@ -11,6 +11,7 @@ interface DisplayField { isDate?: boolean; isList?: boolean; isListOfLinks?: boolean; + isKeyValueObject?: boolean; isHtml?: boolean; isTextBlock?: boolean; isNewSectionLineBreak?: boolean; @@ -78,6 +79,16 @@ export class DetailsComponent extends IntegrationComponentBase { @state() private _activeTab = ''; @state() private _expandedStates: Record = {}; @state() private _copiedStates: Record = {}; + @state() private _scanLaunching = false; + @state() private _scanCheckingStatus = false; + @state() private _scanError: string | null = null; + @state() private _scanResult: { scanRef: string; scanTitle: string; message: string } | null = + null; + @state() private _scanStatus: { + ref: string; + state: string; + subState: string; + } | null = null; private _copyContentId = `copy-content-${Array.from( crypto.getRandomValues(new Uint8Array(16)), @@ -93,6 +104,7 @@ export class DetailsComponent extends IntegrationComponentBase { 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); + --pi-card-background: var(--pi-color-background-application-3, #222837); } .copy-btn-container { @@ -102,10 +114,6 @@ export class DetailsComponent extends IntegrationComponentBase { z-index: 1; } - .content-wrapper { - position: relative; - } - .tab-content { display: flex; flex-direction: column; @@ -113,6 +121,10 @@ export class DetailsComponent extends IntegrationComponentBase { padding-top: var(--pi-size-spacing-xs, 4px); } + .list-of-links { + margin-bottom: var(--pi-size-spacing-sm, 8px); + } + .field-row { display: flex; align-items: baseline; @@ -230,6 +242,49 @@ export class DetailsComponent extends IntegrationComponentBase { .indent-3 { padding-left: var(--pi-size-spacing-xl, 24px); } + + .scan-actions { + display: flex; + flex-direction: column; + gap: var(--pi-size-spacing-xs, 4px); + padding-bottom: var(--pi-size-spacing-sm, 8px); + border-bottom: 1px solid var(--pi-color-border-container, #606470); + margin-bottom: var(--pi-size-spacing-sm, 8px); + } + + .scan-list { + display: flex; + flex-direction: column; + gap: var(--pi-size-spacing-xs, 4px); + } + + .scan-item { + border: 1px solid var(--pi-color-border-container, #606470); + border-radius: var(--pi-size-radius-base, 4px); + padding: var(--pi-size-spacing-sm, 8px); + display: flex; + flex-direction: column; + gap: var(--pi-size-spacing-xxs, 2px); + } + + .scan-item h2 { + margin-top: 0; + } + + .kv-object-section h2 { + margin-top: 0; + margin-bottom: var(--pi-size-spacing-xxs, 2px); + } + + .kv-object-section-values { + margin-left: var(--pi-size-spacing-sm, 8px); + } + + .scan-count-message { + font-size: var(--pi-size-font-sm, 0.875rem); + color: var(--pi-color-font-secondary, #cdced6); + margin-bottom: var(--pi-size-spacing-xs, 4px); + } ` ]; @@ -307,6 +362,69 @@ export class DetailsComponent extends IntegrationComponentBase { // No-op: cards remain expanded after copy }; + private async _launchScan() { + const d = this.block.data?.details as any; + const meta = d?._scanMeta; + if (!meta?.entityValue) return; + + this._scanLaunching = true; + this._scanError = null; + this._scanResult = null; + this._scanStatus = null; + + try { + const result = (await this.sendIntegrationMessage({ + action: 'LAUNCH_SCAN', + entityValue: meta.entityValue + })) as any; + // Sanitize scanRef: extract just the scan/XXXXX.XXXXX portion to handle + // xml2js charkey collision that can prefix the value with garbage characters. + const rawScanRef = (result.scanRef as string) || ''; + const scanRefMatch = rawScanRef.match(/scan\/\d+\.\d+/); + const scanRef = scanRefMatch ? scanRefMatch[0] : rawScanRef.trim(); + this._scanResult = { + scanRef, + scanTitle: result.scanTitle, + message: result.message + }; + // Persist to block data so it survives component destruction + this.block.data!.details = { + ...(this.block.data!.details as object), + _scanResult: { ...result, scanRef } + }; + } catch (error: any) { + this._scanError = error?.message || 'Failed to launch scan'; + } finally { + this._scanLaunching = false; + } + } + + private async _checkScanStatus() { + const scanRef = + this._scanResult?.scanRef || (this.block.data?.details as any)?._scanResult?.scanRef; + if (!scanRef) return; + + this._scanCheckingStatus = true; + this._scanError = null; + + try { + const result = (await this.sendIntegrationMessage({ + action: 'CHECK_SCAN_STATUS', + scanRef + })) as any; + this._scanStatus = { ref: result.ref, state: result.state, subState: result.subState }; + // Persist + this.block.data!.details = { + ...(this.block.data!.details as object), + _scanStatus: result + }; + } catch (error: any) { + this._scanError = error?.message || 'Failed to check scan status'; + } finally { + this._scanCheckingStatus = false; + } + } + render() { const d = this.block.data?.details as Details | undefined; if (!d?.tabKeys?.length) return nothing; @@ -331,7 +449,7 @@ export class DetailsComponent extends IntegrationComponentBase {
${visibleTabs.length === 1 - ? this._renderTabContent(d[visibleTabs[0]] as DisplayField[]) + ? this._renderTabContent(d[visibleTabs[0]] as DisplayField[], visibleTabs[0]) : html` ${visibleTabs.map( @@ -340,7 +458,9 @@ export class DetailsComponent extends IntegrationComponentBase { slot="tab" panel=${tabKey} ?active=${idx === activeIndex} - .count=${(d[tabKey] as DisplayField[]).length} + .count=${this._countTabRecords( + d[tabKey] as DisplayField[] | DisplayField[][] + )} > ${humanizeTabKey(tabKey)} @@ -349,7 +469,7 @@ export class DetailsComponent extends IntegrationComponentBase { ${visibleTabs.map( (tabKey, idx) => html` - ${this._renderTabContent(d[tabKey] as DisplayField[])} + ${this._renderTabContent(d[tabKey] as DisplayField[], tabKey)} ` )} @@ -360,15 +480,196 @@ export class DetailsComponent extends IntegrationComponentBase { `; } - private _renderTabContent(fields: DisplayField[]) { + private _renderTabContent(fields: DisplayField[] | DisplayField[][], tabKey?: string) { if (!fields?.length) return nothing; + + if (Array.isArray(fields[0])) { + return html` +
+ ${(fields as DisplayField[][]).map((group) => this._renderKnowledgeBaseCard(group))} +
+ `; + } + + if (tabKey === 'scans') { + return html` +
+ ${this._renderScanActions()} ${this._renderScansContent(fields as DisplayField[])} +
+ `; + } + + if (tabKey === 'hostDetections') { + return html` +
+ ${this._renderHostDetectionsContent(fields as DisplayField[])} +
+ `; + } + return html`
- ${fields.map((field, idx) => this._renderDisplayField(field, idx))} + ${(fields as DisplayField[]).map((field, idx) => this._renderDisplayField(field, idx))}
`; } + private _renderKnowledgeBaseCard(group: DisplayField[]) { + const titleField = group.find((f) => f.isTitle); + const title = titleField?.value || titleField?.label || ''; + const bodyFields = group.filter((f) => !f.isTitle && !f.isNewSectionLineBreak); + return html` + + ${bodyFields.map((field, idx) => this._renderDisplayField(field, idx))} + + `; + } + + private _renderHostDetectionsContent(fields: DisplayField[]) { + // Group flat fields into per-host arrays (split on isNewSectionLineBreak) + const groups: DisplayField[][] = []; + let current: DisplayField[] = []; + for (const field of fields) { + if (field.isNewSectionLineBreak) { + if (current.length) { + groups.push(current); + current = []; + } + } else { + current.push(field); + } + } + if (current.length) groups.push(current); + + return html` + ${groups.map((group) => { + const titleField = group.find((f) => f.isTitle); + const title = titleField?.showLabelAndValue + ? `${titleField.label}: ${titleField.value}` + : titleField?.value || titleField?.label || 'Host'; + // If the title has a displayLink, keep it in the body so it renders as pi-external-link + const bodyFields = group.filter((f) => !f.isTitle || f.displayLink); + return html` + + ${bodyFields.map((field, idx) => this._renderDisplayField(field, idx))} + + `; + })} + `; + } + + private _renderScanActions() { + const d = this.block.data?.details as any; + const meta = d?._scanMeta; + const isIp = meta?.entityType === 'IPv4' || meta?.entityType === 'IPv6'; + + if (!meta?.enableScanLaunch || !isIp) return nothing; + + // Restore persisted results + const scanResult = this._scanResult || d?._scanResult; + const scanStatus = this._scanStatus || d?._scanStatus; + + return html` +
+ ${!scanResult + ? html` + this._launchScan()} + > + Launch Scan + + ` + : html` + + + ${scanStatus + ? html` + + ` + : nothing} + this._checkScanStatus()} + > + Check Status + + `} + ${this._scanError + ? html`` + : nothing} +
+ `; + } + + private _countTabRecords(fields: DisplayField[] | DisplayField[][]): number { + if (!fields?.length) return 0; + // Grouped format (CVE knowledgeBaseRecords) — each inner array is one record + if (Array.isArray(fields[0])) return (fields as DisplayField[][]).length; + // Flat format — count isNewSectionLineBreak markers (one per record) + return (fields as DisplayField[]).filter((f) => f.isNewSectionLineBreak).length; + } + + private _renderScansContent(fields: DisplayField[]) { + const MAX_SCANS = 10; + + // Group flat fields into per-scan arrays (split on isNewSectionLineBreak) + const groups: DisplayField[][] = []; + let current: DisplayField[] = []; + for (const field of fields) { + if (field.isNewSectionLineBreak) { + if (current.length) { + groups.push(current); + current = []; + } + } else { + current.push(field); + } + } + if (current.length) groups.push(current); + + const totalScans = groups.length; + const visibleGroups = groups.slice(0, MAX_SCANS); + + return html` + ${totalScans > MAX_SCANS + ? html`
+ Showing first ${MAX_SCANS} of ${totalScans} scans +
` + : nothing} + +
+ ${visibleGroups.map( + (group) => html` +
+ ${group.map((field, idx) => this._renderDisplayField(field, idx))} +
+ ` + )} +
+
+ `; + } + private _renderDisplayField(field: DisplayField, fieldIndex: number) { if (field.isTitle) { return this._renderTitle(field); @@ -379,8 +680,11 @@ export class DetailsComponent extends IntegrationComponentBase { if (field.isListOfLinks) { return this._renderListOfLinks(field); } + if (field.isKeyValueObject) { + return this._renderKeyValueObject(field); + } if (field.isHtml) { - return this._renderHtmlField(field, fieldIndex); + return this._renderHtmlField(field); } if (field.isList) { return this._renderList(field, fieldIndex); @@ -395,7 +699,7 @@ export class DetailsComponent extends IntegrationComponentBase { } private _renderTitle(field: DisplayField) { - const indentClass = `indent-${field.indent || 0}`; + const indentClass = field.indent ? `indent-${field.indent}` : ''; const labelText = field.capitalize ? capitalizeStr(field.label || field.value || '') : field.label || field.value || ''; @@ -435,7 +739,7 @@ export class DetailsComponent extends IntegrationComponentBase { const indentClass = `indent-${field.indent || 0}`; const links = field.value as Array<{ url: string; id: string; capitalize?: boolean }>; return html` -
+