diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..22ea559 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,61 @@ +name: CI + +on: + pull_request: + push: + branches: + - main + +jobs: + extension: + name: Extension client + runs-on: ubuntu-latest + + steps: + - name: Check out repository + uses: actions/checkout@v6 + + - name: Set up Node + uses: actions/setup-node@v6 + with: + node-version: "24" + cache: npm + cache-dependency-path: client/package-lock.json + + - name: Install dependencies + run: npm ci + working-directory: client + + - name: Audit dependencies + run: npm audit --audit-level=moderate + working-directory: client + + - name: Run unit tests + run: npm test + working-directory: client + + - name: Run TypeScript checks + run: npm run check + working-directory: client + + - name: Build extension + run: npm run build + working-directory: client + + - name: Package extension + run: npm run package + working-directory: client + + - name: Upload extension package + uses: actions/upload-artifact@v7 + with: + name: conspiracy-alert-plugin + path: client/release/*.zip + + - name: Install Playwright browser + run: npx playwright install --with-deps chromium + working-directory: client + + - name: Run extension E2E tests + run: xvfb-run -a npm run test:e2e + working-directory: client diff --git a/ARTIFACT.md b/ARTIFACT.md new file mode 100644 index 0000000..07028fc --- /dev/null +++ b/ARTIFACT.md @@ -0,0 +1,153 @@ +# Paper Artifact Guide + +This guide is the shortest path for an external reviewer to run the browser extension client for +the paper artifact. + +## Scope + +This repository contains the Chrome/Chromium extension client. It does not embed the public +datasets and does not reimplement the backend. The extension calls the FastAPI service from +`channel-checker-bot` and displays: + +- a toolbar popup for the active tab; +- a manual `Check now` action; +- an optional in-page banner when the current page URL matches a dataset or monetization signal; +- a per-tab badge for matched pages or backend errors. + +## Paper Alignment + +The paper describes ConspiracyAlert as a Chrome TypeScript browser plug-in that observes web +navigation, sends the loaded page URL to a remote server, checks it against the Conspiracy Resource +Dataset and URL dataset, and displays an informative warning when a match exists. + +This artifact implements that workflow as a Manifest V3 extension: + +- the content script evaluates the loaded page URL through the background worker; +- the background worker calls the shared Channel Checker backend; +- matched pages show an in-page warning banner and toolbar badge; +- the popup lets users inspect the current tab status and run a manual `Check now`; +- automatic in-page checks can be disabled from the options page; +- extension storage is limited to configuration and the latest tab evaluation state. + +The extension intentionally does not ship the datasets or a separate Flask server. Matching stays +in the backend so the browser artifact remains small and privacy boundaries are easier to audit. + +Related repositories: + +- `channel-checker-bot`: backend API and optional Telegram bot; +- `conspiracy-dataset-telegram`: public CSV datasets consumed by the backend. + +## Prerequisites + +- Chrome or Chromium with extension developer mode available; +- Node.js 24 with npm; +- the backend running at `http://127.0.0.1:8000`; +- the expected sibling checkout layout: + +```text +SystemsLab-Sapienza/ + channel-checker-bot/ + conspiracy-dataset-telegram/ + conspiracy-alert-plugin/ +``` + +## Start The Backend + +From `channel-checker-bot`: + +```bash +docker build -t channel-checker-bot:local . +docker run --rm \ + -p 8000:8000 \ + -e CHANNEL_CHECKER_DATASET_DIR=/data \ + -v "$(pwd)/../conspiracy-dataset-telegram:/data:ro" \ + channel-checker-bot:local +``` + +Verify it: + +```bash +curl -s http://127.0.0.1:8000/health +curl -s -X POST http://127.0.0.1:8000/v1/evaluate-url \ + -H "Content-Type: application/json" \ + -d '{"url":"https://8kun.top/qresearch/catalog.html"}' +``` + +The first command should return `{"status":"ok","version":"0.1.0"}`. The second should return a +matched result. + +## Build And Load The Extension + +From `conspiracy-alert-plugin/client`: + +```bash +npm ci +npm run build +``` + +Then: + +1. Open `chrome://extensions/`. +2. Enable Developer mode. +3. Click `Load unpacked`. +4. Select `conspiracy-alert-plugin/client/dist`. +5. Open the extension options page and confirm the backend URL is `http://127.0.0.1:8000`. + +## Manual Smoke Test + +With the backend running and the extension loaded: + +1. Open `https://www.amazon.com/?tag=example-20`. +2. Wait for the page to finish loading. +3. The extension should show a non-blocking in-page banner for monetization signals. +4. The toolbar popup should show the current tab status. +5. Disable automatic page checks in the extension options page. +6. Reload the page and confirm the banner does not appear automatically. +7. Use `Check now` in the popup to run a manual check. + +This smoke test uses a monetization signal because it does not depend on any third-party page from +the public dataset being reachable during review. + +## Package Artifact + +```bash +npm run package +``` + +The ZIP is written to `client/release/conspiracy-alert-plugin-1.0.0.zip` and contains the built +extension files from `client/dist`. + +## Verification Commands + +```bash +npm run check +npm test +npm run build +npm run package +npm run check:api-types +npm run test:e2e +``` + +CI installs dependencies with `npm ci`, audits dependencies at moderate severity, runs TypeScript +checks, unit tests, production build, package creation, and Playwright extension E2E tests. + +`npm run check:api-types` regenerates the backend OpenAPI schema from the sibling +`channel-checker-bot` checkout and verifies that generated frontend types are current. + +## Privacy And Storage + +The extension stores only configuration and the latest evaluation state needed for the active tab. +It does not store browsing history, raw page content, Telegram data, or dataset contents. The +backend performs all matching. + +## Troubleshooting + +- `?` badge: backend unavailable or returned an error. Check `http://127.0.0.1:8000/health`. +- No in-page banner: automatic checks may be disabled, the page URL may have no signal, or the + backend may not be running. +- Custom backend URL: set it from the options page. The extension may request additional host + permission for non-default origins. + +## License + +This software artifact is released under the MIT License. See [LICENSE](LICENSE). diff --git a/CITATION.cff b/CITATION.cff new file mode 100644 index 0000000..f88bee8 --- /dev/null +++ b/CITATION.cff @@ -0,0 +1,34 @@ +cff-version: 1.2.0 +message: "If you use this artifact, please cite the associated paper." +title: "Conspiracy Alert Plugin" +type: software +authors: + - family-names: "Imperati" + given-names: "Vincenzo" + - family-names: "La Morgia" + given-names: "Massimo" + - family-names: "Mei" + given-names: "Alessandro" + - family-names: "Mongardini" + given-names: "Alberto Maria" + - family-names: "Sassi" + given-names: "Francesco" +version: "1.0.0" +repository-code: "https://github.com/SystemsLab-Sapienza/conspiracy-alert-plugin" +preferred-citation: + type: conference-paper + title: "The Conspiracy Money Machine: Uncovering Telegram's Conspiracy Channels and their Profit Model" + authors: + - family-names: "Imperati" + given-names: "Vincenzo" + - family-names: "La Morgia" + given-names: "Massimo" + - family-names: "Mei" + given-names: "Alessandro" + - family-names: "Mongardini" + given-names: "Alberto Maria" + - family-names: "Sassi" + given-names: "Francesco" + conference: "34th USENIX Security Symposium (USENIX Security 25)" + year: 2025 + url: "https://arxiv.org/abs/2310.15977" diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..67ba722 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 SystemsLab Sapienza + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index 6299a70..c7a798e 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ If you use this plugin, or the findings from the paper, please cite: - [Overview](#overview) - [Prerequisites](#prerequisites) - [Installation](#installation) - - [Server Setup](#server-setup) + - [Backend Setup](#backend-setup) - [Client Setup](#client-setup) - [Building the Client](#building-the-client) - [Installing the Browser Plugin](#installing-the-browser-plugin) @@ -28,69 +28,58 @@ If you use this plugin, or the findings from the paper, please cite: ## Overview -This repository contains the code for both the server and client sides of conspiracy alert plugin. The server handles backend operations, while the client is responsible for the frontend interface that users interact with in their browsers. +This repository contains the browser extension client for the Channel Checker backend. The rebuilt +client calls the FastAPI backend from `channel-checker-bot`, stores only the latest tab evaluation +in Chrome local extension storage, and renders a compact popup for the current tab. The backend URL +defaults to `http://127.0.0.1:8000` and can be changed from the extension options page. +When a page URL matches a dataset or monetization signal, the extension also shows a non-blocking +in-page banner and marks the extension icon with a per-tab badge. + +For a reviewer-friendly setup path, start with [ARTIFACT.md](ARTIFACT.md). ## Prerequisites Before you begin, ensure you have the following installed: -- Python 3.x -- Node.js and npm -- Git +- Node.js 24 with npm, matching CI. +- Git. +- The sibling `channel-checker-bot` and `conspiracy-dataset-telegram` repositories when running + the extension against the paper artifact backend. ## Installation -### Server Setup - -1. Clone the repository: - ```bash - git clone https://github.com/SystemsLab-Sapienza/conspiracy-alert-plugin.git - cd conspiracy-alert-plugin/server - ``` - -2. Create a virtual environment: - ```bash - python3 -m venv venv - ``` - -3. Activate the virtual environment: +### Backend Setup - - On macOS/Linux: +Run the backend from the sibling `channel-checker-bot` repository: - ```bash - source venv/bin/activate - ``` - - - On Windows: - - ```bash - venv\Scripts\activate - ``` - -4. Install the required Python packages: +```bash +cd ../channel-checker-bot +CHANNEL_CHECKER_DATASET_DIR=../conspiracy-dataset-telegram \ +PYTHONPATH=src uvicorn --factory channel_checker.api.server:create_app_from_environment \ + --host 127.0.0.1 \ + --port 8000 +``` - ```bash - pip3 install -r requirements.txt - ``` +### Client Setup -5. Start the server: +1. Navigate to the client directory: ```bash - python3 server.py + cd client ``` -### Client Setup - -1. Navigate to the client directory: +2. Install the required Node.js packages: ```bash - cd ../client + npm ci ``` -2. Install the required Node.js packages: +3. Run client checks: ```bash - npm install + npm test + npm run check + npm run build ``` ## Building the Client @@ -103,6 +92,14 @@ To build the client for production, run the following command in the client dire This will create a production-ready build of the client in the `dist` folder. +To create the installable ZIP artifact used by CI: + +```bash + npm run package +``` + +The archive is written to `client/release/` and contains the built extension files from `dist`. + ## Installing the Browser Plugin To install the custom browser plugin, follow these steps: @@ -115,4 +112,19 @@ To install the custom browser plugin, follow these steps: ## Usage -Once the plugin is installed, you can access its features directly from your browser's toolbar. \ No newline at end of file +Once the plugin is installed, you can access its features directly from your browser's toolbar. +Use the popup Settings button, or the browser extension details page, to configure a non-default +backend URL. + +On matching pages, the content script asks the background worker to evaluate the current URL and +shows a dismissible banner inside the page. The browser toolbar popup does not open automatically. +Dataset/resource matches are labeled as questionable resources; affiliate, donation, crowdfunding, +marketplace, and commerce-path matches are labeled as monetization signals. If the backend is +unavailable, the page is not modified and the extension icon shows a `?` badge. + +Automatic in-page checks are enabled by default and can be disabled from the extension options +page. The popup still supports manual checks through `Check now`. + +## License + +This software artifact is released under the MIT License. See [LICENSE](LICENSE). diff --git a/client/.gitignore b/client/.gitignore index 54f07af..eb4ce91 100644 --- a/client/.gitignore +++ b/client/.gitignore @@ -10,6 +10,9 @@ lerna-debug.log* node_modules dist dist-ssr +release +playwright-report +test-results *.local # Editor directories and files @@ -21,4 +24,4 @@ dist-ssr *.ntvs* *.njsproj *.sln -*.sw? \ No newline at end of file +*.sw? diff --git a/client/e2e/extension.spec.ts b/client/e2e/extension.spec.ts new file mode 100644 index 0000000..475ca4a --- /dev/null +++ b/client/e2e/extension.spec.ts @@ -0,0 +1,161 @@ +import { expect, test, chromium, type BrowserContext } from "@playwright/test"; +import { createServer, type Server } from "node:http"; +import { mkdtempSync, rmSync } from "node:fs"; +import { tmpdir } from "node:os"; +import path from "node:path"; + +const BACKEND_PORT = 8000; +const PAGE_PORT = 8765; + +test.describe("extension in-page alert", () => { + let backend: Server; + let pageServer: Server; + let context: BrowserContext; + let userDataDir: string; + let backendRequests = 0; + + test.beforeAll(async () => { + backend = await listen( + createBackendServer(() => { + backendRequests += 1; + }), + BACKEND_PORT, + ); + pageServer = await listen(createPageServer(), PAGE_PORT); + + userDataDir = mkdtempSync(path.join(tmpdir(), "conspiracy-alert-e2e-")); + const extensionPath = path.resolve("dist"); + context = await chromium.launchPersistentContext(userDataDir, { + headless: false, + args: [ + `--disable-extensions-except=${extensionPath}`, + `--load-extension=${extensionPath}`, + ], + }); + await waitForExtensionServiceWorker(context); + }); + + test.beforeEach(async () => { + backendRequests = 0; + await setExtensionSettings(context, { + apiBaseUrl: "http://127.0.0.1:8000", + automaticChecksEnabled: true, + }); + }); + + test.afterAll(async () => { + await context?.close(); + await closeServer(pageServer); + await closeServer(backend); + if (userDataDir) { + rmSync(userDataDir, { recursive: true, force: true }); + } + }); + + test("shows a page banner for matched URLs", async () => { + const page = await context.newPage(); + + await page.goto(`http://127.0.0.1:${PAGE_PORT}/trigger`); + + await expect.poll(() => backendRequests).toBeGreaterThan(0); + await expect(page.getByText("Questionable resource detected")).toBeVisible(); + await expect(page.getByText("Questionable content.")).toBeVisible(); + }); + + test("does not request backend when automatic page checks are disabled", async () => { + await setExtensionSettings(context, { + apiBaseUrl: "http://127.0.0.1:8000", + automaticChecksEnabled: false, + }); + const page = await context.newPage(); + + await page.goto(`http://127.0.0.1:${PAGE_PORT}/disabled`); + + await page.waitForTimeout(300); + expect(backendRequests).toBe(0); + await expect(page.getByText("Questionable resource detected")).toHaveCount(0); + }); +}); + +async function waitForExtensionServiceWorker(context: BrowserContext): Promise { + const existing = context.serviceWorkers()[0]; + const serviceWorker = existing ?? (await context.waitForEvent("serviceworker")); + expect(serviceWorker.url()).toContain("service-worker-loader.js"); +} + +async function setExtensionSettings( + context: BrowserContext, + settings: { apiBaseUrl: string; automaticChecksEnabled: boolean }, +): Promise { + const serviceWorker = context.serviceWorkers()[0] ?? (await context.waitForEvent("serviceworker")); + await serviceWorker.evaluate((nextSettings) => { + return chrome.storage.sync.set({ settings: nextSettings }); + }, settings); +} + +function createBackendServer(onEvaluateUrl: () => void): Server { + return createServer((request, response) => { + if (request.method === "POST" && request.url === "/v1/evaluate-url") { + onEvaluateUrl(); + let body = ""; + request.on("data", (chunk: Buffer) => { + body += chunk.toString("utf8"); + }); + request.on("end", () => { + const payload = JSON.parse(body) as { url: string }; + const normalizedUrl = new URL(payload.url).host; + response.writeHead(200, { "Content-Type": "application/json" }); + response.end( + JSON.stringify({ + status: "matched", + normalized_url: normalizedUrl, + matches: [], + signals: [ + { + type: "resource_dataset", + severity: "high", + count: 1, + message: "Questionable content.", + }, + ], + }), + ); + }); + return; + } + + response.writeHead(404); + response.end(); + }); +} + +function createPageServer(): Server { + return createServer((_request, response) => { + response.writeHead(200, { "Content-Type": "text/html" }); + response.end("Trigger
Trigger page
"); + }); +} + +function listen(server: Server, port: number): Promise { + return new Promise((resolve, reject) => { + server.once("error", reject); + server.listen(port, "127.0.0.1", () => resolve(server)); + }); +} + +function closeServer(server: Server | undefined): Promise { + return new Promise((resolve, reject) => { + if (!server?.listening) { + resolve(); + return; + } + + server.close((error) => { + if (error) { + reject(error); + return; + } + resolve(); + }); + }); +} diff --git a/client/index.html b/client/index.html index 4a80af5..816eb99 100644 --- a/client/index.html +++ b/client/index.html @@ -2,9 +2,8 @@ - + - Conspiracy Alert diff --git a/client/manifest.json b/client/manifest.json index fa6c86d..f501d99 100644 --- a/client/manifest.json +++ b/client/manifest.json @@ -1,28 +1,43 @@ { - "manifest_version": 3, - "name": "Conspiracy Alert", - "version": "1.0.0", - "action": { - "default_popup": "index.html", - "default_icon": "src/logo.png" - }, - "icons": { - "16": "src/logo.png", - "48": "src/logo.png", - "128": "src/logo.png" - }, - "content_scripts": [{ - "js": ["src/main.tsx"], - "matches": [""] - }], - "background": { - "service_worker": "src/background.ts", - "persistent": true - }, - "permissions": [ - "tabs", - "activeTab", - "scripting", - "storage" - ] -} \ No newline at end of file + "manifest_version": 3, + "name": "Conspiracy Alert", + "version": "1.0.0", + "options_page": "options.html", + "action": { + "default_popup": "index.html", + "default_icon": "src/logo.png" + }, + "icons": { + "16": "src/logo.png", + "48": "src/logo.png", + "128": "src/logo.png" + }, + "background": { + "service_worker": "src/background.ts" + }, + "content_scripts": [ + { + "matches": [ + "http://*/*", + "https://*/*" + ], + "js": [ + "src/contentScript.ts" + ], + "run_at": "document_idle" + } + ], + "permissions": [ + "tabs", + "activeTab", + "storage" + ], + "host_permissions": [ + "http://127.0.0.1:8000/*", + "http://localhost:8000/*" + ], + "optional_host_permissions": [ + "http://*/*", + "https://*/*" + ] +} diff --git a/client/openapi/channel-checker.openapi.json b/client/openapi/channel-checker.openapi.json new file mode 100644 index 0000000..d718a9c --- /dev/null +++ b/client/openapi/channel-checker.openapi.json @@ -0,0 +1 @@ +{"components": {"schemas": {"ChannelAnalysisResponse": {"properties": {"analyzed_at": {"format": "date-time", "title": "Analyzed At", "type": "string"}, "channel_id": {"anyOf": [{"type": "integer"}, {"type": "null"}], "title": "Channel Id"}, "evidence_urls": {"items": {"type": "string"}, "title": "Evidence Urls", "type": "array"}, "handle": {"title": "Handle", "type": "string"}, "label": {"title": "Label", "type": "string"}, "signal_counts": {"additionalProperties": {"type": "integer"}, "title": "Signal Counts", "type": "object"}, "title": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Title"}, "url_count": {"title": "Url Count", "type": "integer"}}, "required": ["handle", "label", "analyzed_at", "url_count", "signal_counts", "evidence_urls"], "title": "ChannelAnalysisResponse", "type": "object"}, "EvaluateChannelRequest": {"properties": {"force_refresh": {"default": false, "title": "Force Refresh", "type": "boolean"}, "handle": {"minLength": 1, "title": "Handle", "type": "string"}, "max_messages": {"default": 200, "maximum": 5000.0, "minimum": 1.0, "title": "Max Messages", "type": "integer"}}, "required": ["handle"], "title": "EvaluateChannelRequest", "type": "object"}, "EvaluateChannelResponse": {"properties": {"analysis": {"anyOf": [{"$ref": "#/components/schemas/ChannelAnalysisResponse"}, {"type": "null"}]}, "handle": {"title": "Handle", "type": "string"}, "message": {"default": "", "title": "Message", "type": "string"}, "status": {"enum": ["cached", "analyzed", "unavailable", "not_found"], "title": "Status", "type": "string"}}, "required": ["status", "handle"], "title": "EvaluateChannelResponse", "type": "object"}, "EvaluateTextRequest": {"properties": {"text": {"minLength": 1, "title": "Text", "type": "string"}}, "required": ["text"], "title": "EvaluateTextRequest", "type": "object"}, "EvaluateTextResponse": {"properties": {"evaluations": {"items": {"$ref": "#/components/schemas/EvaluationResponse"}, "title": "Evaluations", "type": "array"}, "signals": {"items": {"$ref": "#/components/schemas/SignalResponse"}, "title": "Signals", "type": "array"}, "status": {"enum": ["matched", "no_match"], "title": "Status", "type": "string"}, "urls": {"items": {"type": "string"}, "title": "Urls", "type": "array"}}, "required": ["status", "urls", "evaluations", "signals"], "title": "EvaluateTextResponse", "type": "object"}, "EvaluateUrlRequest": {"properties": {"url": {"minLength": 1, "title": "Url", "type": "string"}}, "required": ["url"], "title": "EvaluateUrlRequest", "type": "object"}, "EvaluateUrlResponse": {"properties": {"evaluation": {"$ref": "#/components/schemas/EvaluationResponse"}, "matches": {"items": {"$ref": "#/components/schemas/MatchResponse"}, "title": "Matches", "type": "array"}, "normalized_url": {"title": "Normalized Url", "type": "string"}, "signals": {"items": {"$ref": "#/components/schemas/SignalResponse"}, "title": "Signals", "type": "array"}, "status": {"enum": ["matched", "no_match"], "title": "Status", "type": "string"}}, "required": ["status", "evaluation", "normalized_url", "matches", "signals"], "title": "EvaluateUrlResponse", "type": "object"}, "EvaluationResponse": {"properties": {"matches": {"items": {"$ref": "#/components/schemas/MatchResponse"}, "title": "Matches", "type": "array"}, "normalized_url": {"title": "Normalized Url", "type": "string"}, "raw_url": {"title": "Raw Url", "type": "string"}}, "required": ["raw_url", "normalized_url", "matches"], "title": "EvaluationResponse", "type": "object"}, "HTTPValidationError": {"properties": {"detail": {"items": {"$ref": "#/components/schemas/ValidationError"}, "title": "Detail", "type": "array"}}, "title": "HTTPValidationError", "type": "object"}, "MatchResponse": {"properties": {"count": {"title": "Count", "type": "integer"}, "message": {"title": "Message", "type": "string"}, "platform": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Platform"}, "resource": {"anyOf": [{"type": "string"}, {"type": "null"}], "title": "Resource"}, "severity": {"enum": ["high", "warning", "info"], "title": "Severity", "type": "string"}, "type": {"enum": ["known_conspiracy_url", "resource_dataset", "affiliate_url", "donation_url", "crowdfunding_url", "marketplace_url", "commerce_path_url"], "title": "Type", "type": "string"}}, "required": ["type", "severity", "message", "count"], "title": "MatchResponse", "type": "object"}, "SignalResponse": {"properties": {"count": {"title": "Count", "type": "integer"}, "message": {"title": "Message", "type": "string"}, "severity": {"enum": ["high", "warning", "info"], "title": "Severity", "type": "string"}, "type": {"enum": ["known_conspiracy_url", "resource_dataset", "affiliate_url", "donation_url", "crowdfunding_url", "marketplace_url", "commerce_path_url"], "title": "Type", "type": "string"}}, "required": ["type", "severity", "count", "message"], "title": "SignalResponse", "type": "object"}, "ValidationError": {"properties": {"ctx": {"title": "Context", "type": "object"}, "input": {"title": "Input"}, "loc": {"items": {"anyOf": [{"type": "string"}, {"type": "integer"}]}, "title": "Location", "type": "array"}, "msg": {"title": "Message", "type": "string"}, "type": {"title": "Error Type", "type": "string"}}, "required": ["loc", "msg", "type"], "title": "ValidationError", "type": "object"}}}, "info": {"title": "Channel Checker API", "version": "0.1.0"}, "openapi": "3.1.0", "paths": {"/health": {"get": {"operationId": "health_health_get", "responses": {"200": {"content": {"application/json": {"schema": {"additionalProperties": {"type": "string"}, "title": "Response Health Health Get", "type": "object"}}}, "description": "Successful Response"}}, "summary": "Health"}}, "/v1/evaluate-channel": {"post": {"operationId": "evaluate_channel_route_v1_evaluate_channel_post", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/EvaluateChannelRequest"}}}, "required": true}, "responses": {"200": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/EvaluateChannelResponse"}}}, "description": "Successful Response"}, "422": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}, "description": "Validation Error"}}, "summary": "Evaluate Channel Route"}}, "/v1/evaluate-text": {"post": {"operationId": "evaluate_text_route_v1_evaluate_text_post", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/EvaluateTextRequest"}}}, "required": true}, "responses": {"200": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/EvaluateTextResponse"}}}, "description": "Successful Response"}, "422": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}, "description": "Validation Error"}}, "summary": "Evaluate Text Route"}}, "/v1/evaluate-url": {"post": {"operationId": "evaluate_url_route_v1_evaluate_url_post", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/EvaluateUrlRequest"}}}, "required": true}, "responses": {"200": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/EvaluateUrlResponse"}}}, "description": "Successful Response"}, "422": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}, "description": "Validation Error"}}, "summary": "Evaluate Url Route"}}}} \ No newline at end of file diff --git a/client/options.html b/client/options.html new file mode 100644 index 0000000..1b7bb3d --- /dev/null +++ b/client/options.html @@ -0,0 +1,13 @@ + + + + + + + Conspiracy Alert Settings + + +
+ + + diff --git a/client/package-lock.json b/client/package-lock.json index 3d6aa08..4370bb9 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -1,27 +1,32 @@ { "name": "conspiracy-alert-plugin", - "version": "0.0.0", + "version": "1.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "conspiracy-alert-plugin", - "version": "0.0.0", + "version": "1.0.0", + "license": "MIT", "dependencies": { "react": "^18.0.0", "react-dom": "^18.0.0" }, "devDependencies": { - "@crxjs/vite-plugin": "^1.0.14", - "@types/chrome": "^0.0.287", - "@types/react": "^18.0.0", - "@types/react-dom": "^18.0.0", - "@vitejs/plugin-react": "^1.3.0", - "autoprefixer": "^10.4.20", + "@crxjs/vite-plugin": "^2.4.0", + "@playwright/test": "^1.59.1", + "@types/chrome": "^0.1.42", + "@types/react": "^18.3.28", + "@types/react-dom": "^18.3.7", + "@vitejs/plugin-react": "^6.0.1", + "autoprefixer": "^10.5.0", + "openapi-typescript": "^7.13.0", "postcss": "^8.4.49", - "tailwindcss": "^3.4.16", - "typescript": "^4.6.3", - "vite": "^2.9.15" + "tailwindcss": "^3.4.19", + "typescript": "^5.9.3", + "vite": "^8.0.11", + "vitest": "^4.1.5", + "yazl": "^3.3.1" } }, "node_modules/@alloc/quick-lru": { @@ -36,379 +41,107 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@ampproject/remapping": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", - "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", - "dev": true, - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/@babel/code-frame": { - "version": "7.26.2", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", - "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.25.9", + "@babel/helper-validator-identifier": "^7.28.5", "js-tokens": "^4.0.0", - "picocolors": "^1.0.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.3.tgz", - "integrity": "sha512-nHIxvKPniQXpmQLb0vhY3VaFb3S0YrTAwpOWJZh1wn3oJPjJk9Asva204PsBdmAE8vpzfHudT8DB0scYvy9q0g==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.0.tgz", - "integrity": "sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==", - "dev": true, - "dependencies": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.26.0", - "@babel/generator": "^7.26.0", - "@babel/helper-compilation-targets": "^7.25.9", - "@babel/helper-module-transforms": "^7.26.0", - "@babel/helpers": "^7.26.0", - "@babel/parser": "^7.26.0", - "@babel/template": "^7.25.9", - "@babel/traverse": "^7.25.9", - "@babel/types": "^7.26.0", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@babel/generator": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.3.tgz", - "integrity": "sha512-6FF/urZvD0sTeO7k6/B15pMLC4CHUv1426lzr3N01aHJTl046uCAh9LXW/fzeXXjPNCJ6iABW5XaWOsIZB93aQ==", - "dev": true, - "dependencies": { - "@babel/parser": "^7.26.3", - "@babel/types": "^7.26.3", - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25", - "jsesc": "^3.0.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-annotate-as-pure": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz", - "integrity": "sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==", - "dev": true, - "dependencies": { - "@babel/types": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.9.tgz", - "integrity": "sha512-j9Db8Suy6yV/VHa4qzrj9yZfZxhLWQdVnRlXxmKLYlhWUVB1sB2G5sxuWYXk/whHD9iW76PmNzxZ4UCnTQTVEQ==", - "dev": true, - "dependencies": { - "@babel/compat-data": "^7.25.9", - "@babel/helper-validator-option": "^7.25.9", - "browserslist": "^4.24.0", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", - "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==", - "dev": true, - "dependencies": { - "@babel/traverse": "^7.25.9", - "@babel/types": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz", - "integrity": "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==", - "dev": true, - "dependencies": { - "@babel/helper-module-imports": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9", - "@babel/traverse": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" + "picocolors": "^1.1.1" }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.25.9.tgz", - "integrity": "sha512-kSMlyUVdWe25rEsRGviIgOWnoT/nfABVWlqt9N19/dIPWViAOW2s9wznP5tURbs/IDuNk4gPy3YdYRgH3uxhBw==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", - "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", - "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", - "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-option": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz", - "integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.0.tgz", - "integrity": "sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==", - "dev": true, - "dependencies": { - "@babel/template": "^7.25.9", - "@babel/types": "^7.26.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/parser": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.3.tgz", - "integrity": "sha512-WJ/CvmY8Mea8iDXo6a7RK2wbmJITT5fN3BEkRuFlxVyNx8jOKIIhmC4fSkTcPcf8JyavbBwIe6OpiCOBXt/IcA==", - "dev": true, - "dependencies": { - "@babel/types": "^7.26.3" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.25.9.tgz", - "integrity": "sha512-ld6oezHQMZsZfp6pWtbjaNDF2tiiCYYDqQszHt5VV437lewP9aSi2Of99CK0D0XB21k7FLgnLcmQKyKzynfeAA==", - "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-jsx": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.25.9.tgz", - "integrity": "sha512-s5XwpQYCqGerXl+Pu6VDL3x0j2d82eiV77UJ8a2mDHAW7j9SWRqQ2y1fNo1Z74CdcYipl5Z41zvjj4Nfzq36rw==", - "dev": true, - "dependencies": { - "@babel/helper-annotate-as-pure": "^7.25.9", - "@babel/helper-module-imports": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/plugin-syntax-jsx": "^7.25.9", - "@babel/types": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-jsx-development": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-development/-/plugin-transform-react-jsx-development-7.25.9.tgz", - "integrity": "sha512-9mj6rm7XVYs4mdLIpbZnHOYdpW42uoiBCTVowg7sP1thUOiANgMb4UtpRivR0pp5iL+ocvUv7X4mZgFRpJEzGw==", - "dev": true, - "dependencies": { - "@babel/plugin-transform-react-jsx": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-jsx-self": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.25.9.tgz", - "integrity": "sha512-y8quW6p0WHkEhmErnfe58r7x0A70uKphQm8Sp8cV7tjNQwK56sNVK0M73LK3WuYmsuyrftut4xAkjjgU0twaMg==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, + "license": "MIT", "engines": { "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/plugin-transform-react-jsx-source": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.25.9.tgz", - "integrity": "sha512-+iqjT8xmXhhYv4/uiYd8FNQsraMFZIfxVSqxxVSZP0WbbSAWvBXAul0m/zu+7Vv4O/3WtApy9pmaTMiumEZgfg==", + "node_modules/@crxjs/vite-plugin": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@crxjs/vite-plugin/-/vite-plugin-2.4.0.tgz", + "integrity": "sha512-bDLdq0W2V1SkMQDJjrcYyjK9/uKtdl4joT7GRImcootCjZdKRiRYt+cv9z8tJoU/tK3o1lX48LTqN7JMsk5AQg==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" + "@rollup/pluginutils": "^4.1.2", + "@webcomponents/custom-elements": "^1.5.0", + "acorn-walk": "^8.2.0", + "convert-source-map": "^1.7.0", + "debug": "^4.3.3", + "es-module-lexer": "^0.10.0", + "fast-glob": "^3.2.11", + "fs-extra": "^10.0.1", + "jsesc": "^3.0.2", + "magic-string": "^0.30.12", + "node-html-parser": "^7.0.2", + "pathe": "^2.0.1", + "picocolors": "^1.1.1", + "react-refresh": "^0.13.0", + "rollup": "2.79.2", + "rxjs": "7.5.7" }, "peerDependencies": { - "@babel/core": "^7.0.0-0" + "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" } }, - "node_modules/@babel/template": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz", - "integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==", + "node_modules/@crxjs/vite-plugin/node_modules/rollup": { + "version": "2.80.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.80.0.tgz", + "integrity": "sha512-cIFJOD1DESzpjOBl763Kp1AH7UE/0fcdHe6rZXUdQ9c50uvgigvW97u3IcSeBwOkgqL/PXPBktBCh0KEu5L8XQ==", "dev": true, - "dependencies": { - "@babel/code-frame": "^7.25.9", - "@babel/parser": "^7.25.9", - "@babel/types": "^7.25.9" + "license": "MIT", + "bin": { + "rollup": "dist/bin/rollup" }, "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.26.4", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.4.tgz", - "integrity": "sha512-fH+b7Y4p3yqvApJALCPJcwb0/XaOSgtK4pzV6WVjPR5GLFQBRI7pfoX2V2iM48NXvX07NUxxm1Vw98YjqTcU5w==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.26.2", - "@babel/generator": "^7.26.3", - "@babel/parser": "^7.26.3", - "@babel/template": "^7.25.9", - "@babel/types": "^7.26.3", - "debug": "^4.3.1", - "globals": "^11.1.0" + "node": ">=10.0.0" }, - "engines": { - "node": ">=6.9.0" + "optionalDependencies": { + "fsevents": "~2.3.2" } }, - "node_modules/@babel/types": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.3.tgz", - "integrity": "sha512-vN5p+1kl59GVKMvTHt55NzzmYVxprfJD+ql7U9NFIfKCBkYE55LYtS+WtPlaYOyzydrKI8Nezd+aZextrd+FMA==", + "node_modules/@emnapi/core": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", + "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", "dev": true, + "license": "MIT", + "optional": true, "dependencies": { - "@babel/helper-string-parser": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9" - }, - "engines": { - "node": ">=6.9.0" + "@emnapi/wasi-threads": "1.2.1", + "tslib": "^2.4.0" } }, - "node_modules/@crxjs/vite-plugin": { - "version": "1.0.14", - "resolved": "https://registry.npmjs.org/@crxjs/vite-plugin/-/vite-plugin-1.0.14.tgz", - "integrity": "sha512-emOueVCqFRFmpcfT80Xsm4mfuFw9VSp5GY4eh5qeLDeiP81g0hddlobVQCo0pE2ZvNnWbyhLrXEYAaMAXjNL6A==", + "node_modules/@emnapi/runtime": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", + "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", "dev": true, + "license": "MIT", + "optional": true, "dependencies": { - "@rollup/pluginutils": "^4.1.2", - "@webcomponents/custom-elements": "^1.5.0", - "acorn-walk": "^8.2.0", - "cheerio": "^1.0.0-rc.10", - "connect-injector": "^0.4.4", - "debug": "^4.3.3", - "es-module-lexer": "^0.10.0", - "fast-glob": "^3.2.11", - "fs-extra": "^10.0.1", - "jsesc": "^3.0.2", - "magic-string": "^0.26.0", - "picocolors": "^1.0.0", - "react-refresh": "^0.13.0", - "rollup": "^2.70.2" - }, - "engines": { - "node": ">=14" - }, - "optionalDependencies": { - "@vitejs/plugin-react": ">=1.2.0" - }, - "peerDependencies": { - "vite": "^2.9.0" + "tslib": "^2.4.0" } }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.14.54", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.14.54.tgz", - "integrity": "sha512-bZBrLAIX1kpWelV0XemxBZllyRmM6vgFQQG2GdNb+r3Fkp0FOh1NJSvekXDs7jq70k4euu1cryLMfU+mTXlEpw==", - "cpu": [ - "loong64" - ], + "node_modules/@emnapi/wasi-threads": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", + "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", "dev": true, + "license": "MIT", "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" + "dependencies": { + "tslib": "^2.4.0" } }, "node_modules/@isaacs/cliui": { @@ -429,17 +162,14 @@ } }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", - "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", "dev": true, + "license": "MIT", "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" } }, "node_modules/@jridgewell/resolve-uri": { @@ -451,31 +181,43 @@ "node": ">=6.0.0" } }, - "node_modules/@jridgewell/set-array": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "dev": true, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", - "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", - "dev": true + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", "dev": true, + "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.4.tgz", + "integrity": "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@tybys/wasm-util": "^0.10.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -511,6 +253,16 @@ "node": ">= 8" } }, + "node_modules/@oxc-project/types": { + "version": "0.128.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.128.0.tgz", + "integrity": "sha512-huv1Y/LzBJkBVHt3OlC7u0zHBW9qXf1FdD7sGmc1rXc2P1mTwHssYv7jyGx5KAACSCH+9B3Bhn6Z9luHRvf7pQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -521,999 +273,1210 @@ "node": ">=14" } }, - "node_modules/@rollup/pluginutils": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-4.2.1.tgz", - "integrity": "sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==", + "node_modules/@playwright/test": { + "version": "1.59.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.59.1.tgz", + "integrity": "sha512-PG6q63nQg5c9rIi4/Z5lR5IVF7yU5MqmKaPOe0HSc0O2cX1fPi96sUQu5j7eo4gKCkB2AnNGoWt7y4/Xx3Kcqg==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "estree-walker": "^2.0.1", - "picomatch": "^2.2.2" + "playwright": "1.59.1" + }, + "bin": { + "playwright": "cli.js" }, "engines": { - "node": ">= 8.0.0" + "node": ">=18" } }, - "node_modules/@types/chrome": { - "version": "0.0.287", - "resolved": "https://registry.npmjs.org/@types/chrome/-/chrome-0.0.287.tgz", - "integrity": "sha512-wWhBNPNXZHwycHKNYnexUcpSbrihVZu++0rdp6GEk5ZgAglenLx+RwdEouh6FrHS0XQiOxSd62yaujM1OoQlZQ==", + "node_modules/@redocly/ajv": { + "version": "8.11.2", + "resolved": "https://registry.npmjs.org/@redocly/ajv/-/ajv-8.11.2.tgz", + "integrity": "sha512-io1JpnwtIcvojV7QKDUSIuMN/ikdOUd1ReEnUnMKGfDVridQZ31J0MmIuqwuRjWDZfmvr+Q0MqCcfHM2gTivOg==", "dev": true, + "license": "MIT", "dependencies": { - "@types/filesystem": "*", - "@types/har-format": "*" + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js-replace": "^1.0.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/@types/filesystem": { - "version": "0.0.36", - "resolved": "https://registry.npmjs.org/@types/filesystem/-/filesystem-0.0.36.tgz", - "integrity": "sha512-vPDXOZuannb9FZdxgHnqSwAG/jvdGM8Wq+6N4D/d80z+D4HWH+bItqsZaVRQykAn6WEVeEkLm2oQigyHtgb0RA==", + "node_modules/@redocly/config": { + "version": "0.22.0", + "resolved": "https://registry.npmjs.org/@redocly/config/-/config-0.22.0.tgz", + "integrity": "sha512-gAy93Ddo01Z3bHuVdPWfCwzgfaYgMdaZPcfL7JZ7hWJoK9V0lXDbigTWkhiPFAaLWzbOJ+kbUQG1+XwIm0KRGQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@redocly/openapi-core": { + "version": "1.34.14", + "resolved": "https://registry.npmjs.org/@redocly/openapi-core/-/openapi-core-1.34.14.tgz", + "integrity": "sha512-y+xFx+Zz54Xhr8jUdnLENYnt7Y7GEDL6Q03ga7rTtX8DVwefX9H+hQEPgJp1nda7vdH+wJ9/HBVvyfBuW9x6rA==", "dev": true, + "license": "MIT", "dependencies": { - "@types/filewriter": "*" + "@redocly/ajv": "8.11.2", + "@redocly/config": "0.22.0", + "colorette": "1.4.0", + "https-proxy-agent": "7.0.6", + "js-levenshtein": "1.1.6", + "js-yaml": "4.1.1", + "minimatch": "5.1.9", + "pluralize": "8.0.0", + "yaml-ast-parser": "0.0.43" + }, + "engines": { + "node": ">=18.17.0", + "npm": ">=9.5.0" } }, - "node_modules/@types/filewriter": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/@types/filewriter/-/filewriter-0.0.33.tgz", - "integrity": "sha512-xFU8ZXTw4gd358lb2jw25nxY9QAgqn2+bKKjKOYfNCzN4DKCFetK7sPtrlpg66Ywe3vWY9FNxprZawAh9wfJ3g==", - "dev": true - }, - "node_modules/@types/har-format": { - "version": "1.2.16", - "resolved": "https://registry.npmjs.org/@types/har-format/-/har-format-1.2.16.tgz", - "integrity": "sha512-fluxdy7ryD3MV6h8pTfTYpy/xQzCFC7m89nOH9y94cNqJ1mDIDPut7MnRHI3F6qRmh/cT2fUjG1MLdCNb4hE9A==", - "dev": true + "node_modules/@redocly/openapi-core/node_modules/minimatch": { + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz", + "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } }, - "node_modules/@types/prop-types": { - "version": "15.7.14", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz", - "integrity": "sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==", - "dev": true + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.0.0-rc.18", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.18.tgz", + "integrity": "sha512-lIDyUAfD7U3+BWKzdxMbJcsYHuqXqmGz40aeRqvuAm3y5TkJSYTBW2RDrn65DJFPQqVjUAUqq5uz8urzQ8aBdQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/@types/react": { - "version": "18.3.16", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.16.tgz", - "integrity": "sha512-oh8AMIC4Y2ciKufU8hnKgs+ufgbA/dhPTACaZPM86AbwX9QwnFtSoPWEeRUj8fge+v6kFt78BXcDhAU1SrrAsw==", + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.0.0-rc.18", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.18.tgz", + "integrity": "sha512-apJq2ktnGp27nSInMR5Vcj8kY6xJzDAvfdIFlpDcAK/w4cDO58qVoi1YQsES/SKiFNge/6e4CUzgjfHduYqWpQ==", + "cpu": [ + "arm64" + ], "dev": true, - "dependencies": { - "@types/prop-types": "*", - "csstype": "^3.0.2" + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@types/react-dom": { - "version": "18.3.5", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.5.tgz", - "integrity": "sha512-P4t6saawp+b/dFrUr2cvkVsfvPguwsxtH6dNIYRllMsefqFzkZk5UIjzyDOv5g1dXIPdG4Sp1yCR4Z6RCUsG/Q==", + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.0.0-rc.18", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.18.tgz", + "integrity": "sha512-5Ofot8xbs+pxRHJqm9/9N/4sTQOvdrwEsmPE9pdLEEoAbdZtG6F2LMDfO1sp6ZAtXJuJV/21ew2srq3W8NXB5g==", + "cpu": [ + "x64" + ], "dev": true, - "peerDependencies": { - "@types/react": "^18.0.0" + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@vitejs/plugin-react": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-1.3.2.tgz", - "integrity": "sha512-aurBNmMo0kz1O4qRoY+FM4epSA39y3ShWGuqfLRA/3z0oEJAdtoSfgA3aO98/PCCHAqMaduLxIxErWrVKIFzXA==", + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.0.0-rc.18", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.18.tgz", + "integrity": "sha512-7h8eeOTT1eyqJyx64BFCnWZpNm486hGWt2sqeLLgDxA0xI1oGZ9H7gK1S85uNGmBhkdPwa/6reTxfFFKvIsebw==", + "cpu": [ + "x64" + ], "dev": true, - "dependencies": { - "@babel/core": "^7.17.10", - "@babel/plugin-transform-react-jsx": "^7.17.3", - "@babel/plugin-transform-react-jsx-development": "^7.16.7", - "@babel/plugin-transform-react-jsx-self": "^7.16.7", - "@babel/plugin-transform-react-jsx-source": "^7.16.7", - "@rollup/pluginutils": "^4.2.1", - "react-refresh": "^0.13.0", - "resolve": "^1.22.0" - }, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], "engines": { - "node": ">=12.0.0" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@webcomponents/custom-elements": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@webcomponents/custom-elements/-/custom-elements-1.6.0.tgz", - "integrity": "sha512-CqTpxOlUCPWRNUPZDxT5v2NnHXA4oox612iUGnmTUGQFhZ1Gkj8kirtl/2wcF6MqX7+PqqicZzOCBKKfIn0dww==", - "dev": true + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.0.0-rc.18", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.18.tgz", + "integrity": "sha512-eRcm/HVt9U/JFu5RKAEKwGQYtDCKWLiaH6wOnsSEp6NMBb/3Os8LgHZlNyzMpFVNmiiMFlfb2zEnebfzJrHFmg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/acorn": { - "version": "8.14.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", - "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.0.0-rc.18", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.18.tgz", + "integrity": "sha512-SOrT/cT4ukTmgnrEz/Hg3m7LBnuCLW9psDeMKrimRWY4I8DmnO7Lco8W2vtqPmMkbVu8iJ+g4GFLVLLOVjJ9DQ==", + "cpu": [ + "arm64" + ], "dev": true, - "bin": { - "acorn": "bin/acorn" - }, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=0.4.0" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/acorn-walk": { - "version": "8.3.4", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", - "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.0.0-rc.18", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.18.tgz", + "integrity": "sha512-QWjdxN1HJCpBTAcZ5N5F7wju3gVPzRzSpmGzx7na0c/1qpN9CFil+xt+l9lV/1M6/gqHSNXCiqPfwhVJPeLnug==", + "cpu": [ + "arm64" + ], "dev": true, - "dependencies": { - "acorn": "^8.11.0" - }, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=0.4.0" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/ansi-regex": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", - "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.0.0-rc.18", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.18.tgz", + "integrity": "sha512-ugCOyj7a4d9h3q9B+wXmf6g3a68UsjGh6dob5DHevHGMwDUbhsYNbSPxJsENcIttJZ9jv7qGM2UesLw5jqIhdg==", + "cpu": [ + "ppc64" + ], "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.0.0-rc.18", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.18.tgz", + "integrity": "sha512-kKWRhbsotpXkGbcd5dllUWg5gEXcDAa8u5YnP9AV5DYNbvJHGzzuwv7dpmhc8NqKMJldl0a+x76IHbspEpEmdA==", + "cpu": [ + "s390x" + ], "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/any-promise": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", - "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", - "dev": true + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.0.0-rc.18", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.18.tgz", + "integrity": "sha512-uCo8ElcCIAMyYAZyuIZ81oFkhTSIllNvUCHCAlbhlN4ji3uC28h7IIdlXyIvGO7HsuqnV9p3rD/bpH7XhIyhRw==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.0.0-rc.18", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.18.tgz", + "integrity": "sha512-XNOQZtuE6yUIvx4rwGemwh8kpL1xvU41FXy/s9K7T/3JVcqGzo3NfKM2HrbrGgfPYGFW42f07Wk++aOC6B9NWA==", + "cpu": [ + "x64" + ], "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.0.0-rc.18", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.18.tgz", + "integrity": "sha512-tSn/kzrfa7tNOXr7sEacDBN4YsIqTyLqh45IO0nHDwtpKIDNDJr+VFojt+4klSpChxB29JLyduSsE0MKEwa65A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-wasm32-wasi": { + "version": "1.0.0-rc.18", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.18.tgz", + "integrity": "sha512-+J9YGmc+czgqlhYmwun3S3O0FIZhsH8ep2456xwjAdIOmuJxM7xz4P4PtrxU+Bz17a/5bqPA8o3HAAoX0teUdg==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" + "@emnapi/core": "1.10.0", + "@emnapi/runtime": "1.10.0", + "@napi-rs/wasm-runtime": "^1.1.4" }, "engines": { - "node": ">= 8" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/arg": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", - "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", - "dev": true + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.0.0-rc.18", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.18.tgz", + "integrity": "sha512-zsu47DgU0FQzSwi6sU9dZoEdUv7pc1AptSEz/Z8HBg54sV0Pbs3N0+CrIbTsgiu6EyoaNN9CHboqbLaz9lhOyQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } }, - "node_modules/autoprefixer": { - "version": "10.4.20", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.20.tgz", - "integrity": "sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==", + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.0.0-rc.18", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.18.tgz", + "integrity": "sha512-7H+3yqGgmnlDTRRhw/xpYY9J1kf4GC681nVc4GqKhExZTDrVVrV2tsOR9kso0fvgBdcTCcQShx4SLLoHgaLwhg==", + "cpu": [ + "x64" + ], "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/autoprefixer" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } + "license": "MIT", + "optional": true, + "os": [ + "win32" ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-rc.7", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.7.tgz", + "integrity": "sha512-qujRfC8sFVInYSPPMLQByRh7zhwkGFS4+tyMQ83srV1qrxL4g8E2tyxVVyxd0+8QeBM1mIk9KbWxkegRr76XzA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/pluginutils": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-4.2.1.tgz", + "integrity": "sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==", + "dev": true, + "license": "MIT", "dependencies": { - "browserslist": "^4.23.3", - "caniuse-lite": "^1.0.30001646", - "fraction.js": "^4.3.7", - "normalize-range": "^0.1.2", - "picocolors": "^1.0.1", - "postcss-value-parser": "^4.2.0" - }, - "bin": { - "autoprefixer": "bin/autoprefixer" + "estree-walker": "^2.0.1", + "picomatch": "^2.2.2" }, "engines": { - "node": "^10 || ^12 || >=14" - }, - "peerDependencies": { - "postcss": "^8.1.0" + "node": ">= 8.0.0" } }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "dev": true, + "license": "MIT" }, - "node_modules/binary-extensions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", - "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "node_modules/@tybys/wasm-util": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.2.tgz", + "integrity": "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==", "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" } }, - "node_modules/boolbase": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", - "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "node_modules/@types/chai": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", + "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/deep-eql": "*", + "assertion-error": "^2.0.1" + } + }, + "node_modules/@types/chrome": { + "version": "0.1.42", + "resolved": "https://registry.npmjs.org/@types/chrome/-/chrome-0.1.42.tgz", + "integrity": "sha512-tdT2roFqGecZZDjA9fUEAINb2STxSPifHMDvY6EfRjNRCjdrs/0FwKt5RCIA9MKMd1arAYZZL3nwEkp6ZLZu2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/filesystem": "*", + "@types/har-format": "*" + } + }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/filesystem": { + "version": "0.0.36", + "resolved": "https://registry.npmjs.org/@types/filesystem/-/filesystem-0.0.36.tgz", + "integrity": "sha512-vPDXOZuannb9FZdxgHnqSwAG/jvdGM8Wq+6N4D/d80z+D4HWH+bItqsZaVRQykAn6WEVeEkLm2oQigyHtgb0RA==", + "dev": true, + "dependencies": { + "@types/filewriter": "*" + } + }, + "node_modules/@types/filewriter": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/@types/filewriter/-/filewriter-0.0.33.tgz", + "integrity": "sha512-xFU8ZXTw4gd358lb2jw25nxY9QAgqn2+bKKjKOYfNCzN4DKCFetK7sPtrlpg66Ywe3vWY9FNxprZawAh9wfJ3g==", + "dev": true + }, + "node_modules/@types/har-format": { + "version": "1.2.16", + "resolved": "https://registry.npmjs.org/@types/har-format/-/har-format-1.2.16.tgz", + "integrity": "sha512-fluxdy7ryD3MV6h8pTfTYpy/xQzCFC7m89nOH9y94cNqJ1mDIDPut7MnRHI3F6qRmh/cT2fUjG1MLdCNb4hE9A==", + "dev": true + }, + "node_modules/@types/prop-types": { + "version": "15.7.14", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz", + "integrity": "sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==", "dev": true }, - "node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "node_modules/@types/react": { + "version": "18.3.28", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.28.tgz", + "integrity": "sha512-z9VXpC7MWrhfWipitjNdgCauoMLRdIILQsAEV+ZesIzBq/oUlxk0m3ApZuMFCXdnS4U7KrI+l3WRUEGQ8K1QKw==", "dev": true, + "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0" + "@types/prop-types": "*", + "csstype": "^3.2.2" } }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "node_modules/@types/react-dom": { + "version": "18.3.7", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.7.tgz", + "integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==", "dev": true, - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" + "license": "MIT", + "peerDependencies": { + "@types/react": "^18.0.0" } }, - "node_modules/browserslist": { - "version": "4.24.3", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.3.tgz", - "integrity": "sha512-1CPmv8iobE2fyRMV97dAcMVegvvWKxmq94hkLiAkUGwKVTyDLw33K+ZxiFrREKmmps4rIw6grcCFCnTMSZ/YiA==", + "node_modules/@vitejs/plugin-react": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-6.0.1.tgz", + "integrity": "sha512-l9X/E3cDb+xY3SWzlG1MOGt2usfEHGMNIaegaUGFsLkb3RCn/k8/TOXBcab+OndDI4TBtktT8/9BwwW8Vi9KUQ==", "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], + "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001688", - "electron-to-chromium": "^1.5.73", - "node-releases": "^2.0.19", - "update-browserslist-db": "^1.1.1" - }, - "bin": { - "browserslist": "cli.js" + "@rolldown/pluginutils": "1.0.0-rc.7" }, "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "@rolldown/plugin-babel": "^0.1.7 || ^0.2.0", + "babel-plugin-react-compiler": "^1.0.0", + "vite": "^8.0.0" + }, + "peerDependenciesMeta": { + "@rolldown/plugin-babel": { + "optional": true + }, + "babel-plugin-react-compiler": { + "optional": true + } } }, - "node_modules/camelcase-css": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", - "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "node_modules/@vitest/expect": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.5.tgz", + "integrity": "sha512-PWBaRY5JoKuRnHlUHfpV/KohFylaDZTupcXN1H9vYryNLOnitSw60Mw9IAE2r67NbwwzBw/Cc/8q9BK3kIX8Kw==", "dev": true, - "engines": { - "node": ">= 6" + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.1.0", + "@types/chai": "^5.2.2", + "@vitest/spy": "4.1.5", + "@vitest/utils": "4.1.5", + "chai": "^6.2.2", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" } }, - "node_modules/caniuse-lite": { - "version": "1.0.30001688", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001688.tgz", - "integrity": "sha512-Nmqpru91cuABu/DTCXbM2NSRHzM2uVHfPnhJ/1zEAJx/ILBRVmz3pzH4N7DZqbdG0gWClsCC05Oj0mJ/1AWMbA==", + "node_modules/@vitest/mocker": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.5.tgz", + "integrity": "sha512-/x2EmFC4mT4NNzqvC3fmesuV97w5FC903KPmey4gsnJiMQ3Be1IlDKVaDaG8iqaLFHqJ2FVEkxZk5VmeLjIItw==", "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + "license": "MIT", + "dependencies": { + "@vitest/spy": "4.1.5", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.21" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" + "vite": { + "optional": true } - ] + } }, - "node_modules/cheerio": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0.tgz", - "integrity": "sha512-quS9HgjQpdaXOvsZz82Oz7uxtXiy6UIsIQcpBj7HRw2M63Skasm9qlDocAM7jNuaxdhpPU7c4kJN+gA5MCu4ww==", + "node_modules/@vitest/mocker/node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", "dev": true, + "license": "MIT", "dependencies": { - "cheerio-select": "^2.1.0", - "dom-serializer": "^2.0.0", - "domhandler": "^5.0.3", - "domutils": "^3.1.0", - "encoding-sniffer": "^0.2.0", - "htmlparser2": "^9.1.0", - "parse5": "^7.1.2", - "parse5-htmlparser2-tree-adapter": "^7.0.0", - "parse5-parser-stream": "^7.1.2", - "undici": "^6.19.5", - "whatwg-mimetype": "^4.0.0" - }, - "engines": { - "node": ">=18.17" - }, - "funding": { - "url": "https://github.com/cheeriojs/cheerio?sponsor=1" + "@types/estree": "^1.0.0" } }, - "node_modules/cheerio-select": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz", - "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==", + "node_modules/@vitest/pretty-format": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.5.tgz", + "integrity": "sha512-7I3q6l5qr03dVfMX2wCo9FxwSJbPdwKjy2uu/YPpU3wfHvIL4QHwVRp57OfGrDFeUJ8/8QdfBKIV12FTtLn00g==", "dev": true, + "license": "MIT", "dependencies": { - "boolbase": "^1.0.0", - "css-select": "^5.1.0", - "css-what": "^6.1.0", - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3", - "domutils": "^3.0.1" + "tinyrainbow": "^3.1.0" }, "funding": { - "url": "https://github.com/sponsors/fb55" + "url": "https://opencollective.com/vitest" } }, - "node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "node_modules/@vitest/runner": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.5.tgz", + "integrity": "sha512-2D+o7Pr82IEO46YPpoA/YU0neeyr6FTerQb5Ro7BUnBuv6NQtT/kmVnczngiMEBhzgqz2UZYl5gArejsyERDSQ==", "dev": true, + "license": "MIT", "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" + "@vitest/utils": "4.1.5", + "pathe": "^2.0.3" }, "funding": { - "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" + "url": "https://opencollective.com/vitest" } }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/@vitest/snapshot": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.5.tgz", + "integrity": "sha512-zypXEt4KH/XgKGPUz4eC2AvErYx0My5hfL8oDb1HzGFpEk1P62bxSohdyOmvz+d9UJwanI68MKwr2EquOaOgMQ==", "dev": true, + "license": "MIT", "dependencies": { - "color-name": "~1.1.4" + "@vitest/pretty-format": "4.1.5", + "@vitest/utils": "4.1.5", + "magic-string": "^0.30.21", + "pathe": "^2.0.3" }, - "engines": { - "node": ">=7.0.0" + "funding": { + "url": "https://opencollective.com/vitest" } }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/commander": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", - "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "node_modules/@vitest/spy": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.5.tgz", + "integrity": "sha512-2lNOsh6+R2Idnf1TCZqSwYlKN2E/iDlD8sgU59kYVl+OMDmvldO1VDk39smRfpUNwYpNRVn3w4YfuC7KfbBnkQ==", "dev": true, - "engines": { - "node": ">= 6" + "license": "MIT", + "funding": { + "url": "https://opencollective.com/vitest" } }, - "node_modules/connect-injector": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/connect-injector/-/connect-injector-0.4.4.tgz", - "integrity": "sha512-hdBG8nXop42y2gWCqOV8y1O3uVk4cIU+SoxLCPyCUKRImyPiScoNiSulpHjoktRU1BdI0UzoUdxUa87thrcmHw==", + "node_modules/@vitest/utils": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.5.tgz", + "integrity": "sha512-76wdkrmfXfqGjueGgnb45ITPyUi1ycZ4IHgC2bhPDUfWHklY/q3MdLOAB+TF1e6xfl8NxNY0ZYaPCFNWSsw3Ug==", "dev": true, + "license": "MIT", "dependencies": { - "debug": "^2.0.0", - "q": "^1.0.1", - "stream-buffers": "^0.2.3", - "uberproto": "^1.1.0" + "@vitest/pretty-format": "4.1.5", + "convert-source-map": "^2.0.0", + "tinyrainbow": "^3.1.0" }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/connect-injector/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "dependencies": { - "ms": "2.0.0" + "funding": { + "url": "https://opencollective.com/vitest" } }, - "node_modules/connect-injector/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - }, - "node_modules/convert-source-map": { + "node_modules/@vitest/utils/node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@webcomponents/custom-elements": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@webcomponents/custom-elements/-/custom-elements-1.6.0.tgz", + "integrity": "sha512-CqTpxOlUCPWRNUPZDxT5v2NnHXA4oox612iUGnmTUGQFhZ1Gkj8kirtl/2wcF6MqX7+PqqicZzOCBKKfIn0dww==", "dev": true }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "node_modules/acorn": { + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", + "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", "dev": true, - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" + "bin": { + "acorn": "bin/acorn" }, "engines": { - "node": ">= 8" + "node": ">=0.4.0" } }, - "node_modules/css-select": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", - "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==", + "node_modules/acorn-walk": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", "dev": true, "dependencies": { - "boolbase": "^1.0.0", - "css-what": "^6.1.0", - "domhandler": "^5.0.2", - "domutils": "^3.0.1", - "nth-check": "^2.0.1" + "acorn": "^8.11.0" }, - "funding": { - "url": "https://github.com/sponsors/fb55" + "engines": { + "node": ">=0.4.0" } }, - "node_modules/css-what": { + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-regex": { "version": "6.1.0", - "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", - "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", "dev": true, "engines": { - "node": ">= 6" + "node": ">=12" }, "funding": { - "url": "https://github.com/sponsors/fb55" + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "node_modules/cssesc": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", "dev": true, - "bin": { - "cssesc": "bin/cssesc" - }, "engines": { - "node": ">=4" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/csstype": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", - "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", "dev": true }, - "node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", "dev": true, "dependencies": { - "ms": "^2.1.3" + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" }, "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "node": ">= 8" } }, - "node_modules/didyoumean": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", - "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", - "dev": true - }, - "node_modules/dlv": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", - "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", "dev": true }, - "node_modules/dom-serializer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", - "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true, - "dependencies": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.2", - "entities": "^4.2.0" - }, - "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + "license": "Python-2.0" + }, + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" } }, - "node_modules/domelementtype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", - "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "node_modules/autoprefixer": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.5.0.tgz", + "integrity": "sha512-FMhOoZV4+qR6aTUALKX2rEqGG+oyATvwBt9IIzVR5rMa2HRWPkxf+P+PAJLD1I/H5/II+HuZcBJYEFBpq39ong==", "dev": true, "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, { "type": "github", - "url": "https://github.com/sponsors/fb55" + "url": "https://github.com/sponsors/ai" } - ] - }, - "node_modules/domhandler": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", - "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", - "dev": true, + ], + "license": "MIT", "dependencies": { - "domelementtype": "^2.3.0" + "browserslist": "^4.28.2", + "caniuse-lite": "^1.0.30001787", + "fraction.js": "^5.3.4", + "picocolors": "^1.1.1", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" }, "engines": { - "node": ">= 4" + "node": "^10 || ^12 || >=14" }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" + "peerDependencies": { + "postcss": "^8.1.0" } }, - "node_modules/domutils": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", - "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true, - "dependencies": { - "dom-serializer": "^2.0.0", - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3" + "license": "MIT" + }, + "node_modules/baseline-browser-mapping": { + "version": "2.10.29", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.29.tgz", + "integrity": "sha512-Asa2krT+XTPZINCS+2QcyS8WTkObE77RwkydwF7h6DmnKqbvlalz93m/dnphUyCa6SWSP51VgtEUf2FN+gelFQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" }, - "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" + "engines": { + "node": ">=6.0.0" } }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true - }, - "node_modules/electron-to-chromium": { - "version": "1.5.73", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.73.tgz", - "integrity": "sha512-8wGNxG9tAG5KhGd3eeA0o6ixhiNdgr0DcHWm85XPCphwZgD1lIEoi6t3VERayWao7SF7AAZTw6oARGJeVjH8Kg==", - "dev": true + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "dev": true, + "license": "ISC" }, - "node_modules/encoding-sniffer": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/encoding-sniffer/-/encoding-sniffer-0.2.0.tgz", - "integrity": "sha512-ju7Wq1kg04I3HtiYIOrUrdfdDvkyO9s5XM8QAj/bN61Yo/Vb4vgJxy5vi4Yxk01gWHbrofpPtpxM8bKger9jhg==", + "node_modules/brace-expansion": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.0.tgz", + "integrity": "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==", "dev": true, + "license": "MIT", "dependencies": { - "iconv-lite": "^0.6.3", - "whatwg-encoding": "^3.1.1" - }, - "funding": { - "url": "https://github.com/fb55/encoding-sniffer?sponsor=1" + "balanced-match": "^1.0.0" } }, - "node_modules/entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, - "engines": { - "node": ">=0.12" + "dependencies": { + "fill-range": "^7.1.1" }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" + "engines": { + "node": ">=8" } }, - "node_modules/es-module-lexer": { - "version": "0.10.5", - "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.10.5.tgz", - "integrity": "sha512-+7IwY/kiGAacQfY+YBhKMvEmyAJnw5grTUgjG85Pe7vcUI/6b7pZjZG8nQ7+48YhzEAEqrEgD2dCz/JIK+AYvw==", - "dev": true - }, - "node_modules/esbuild": { - "version": "0.14.54", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.14.54.tgz", - "integrity": "sha512-Cy9llcy8DvET5uznocPyqL3BFRrFXSVqbgpMJ9Wz8oVjZlh/zUSNbPRbov0VX7VxN2JH1Oa0uNxZ7eLRb62pJA==", + "node_modules/browserslist": { + "version": "4.28.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz", + "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==", "dev": true, - "hasInstallScript": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.10.12", + "caniuse-lite": "^1.0.30001782", + "electron-to-chromium": "^1.5.328", + "node-releases": "^2.0.36", + "update-browserslist-db": "^1.2.3" + }, "bin": { - "esbuild": "bin/esbuild" + "browserslist": "cli.js" }, "engines": { - "node": ">=12" - }, - "optionalDependencies": { - "@esbuild/linux-loong64": "0.14.54", - "esbuild-android-64": "0.14.54", - "esbuild-android-arm64": "0.14.54", - "esbuild-darwin-64": "0.14.54", - "esbuild-darwin-arm64": "0.14.54", - "esbuild-freebsd-64": "0.14.54", - "esbuild-freebsd-arm64": "0.14.54", - "esbuild-linux-32": "0.14.54", - "esbuild-linux-64": "0.14.54", - "esbuild-linux-arm": "0.14.54", - "esbuild-linux-arm64": "0.14.54", - "esbuild-linux-mips64le": "0.14.54", - "esbuild-linux-ppc64le": "0.14.54", - "esbuild-linux-riscv64": "0.14.54", - "esbuild-linux-s390x": "0.14.54", - "esbuild-netbsd-64": "0.14.54", - "esbuild-openbsd-64": "0.14.54", - "esbuild-sunos-64": "0.14.54", - "esbuild-windows-32": "0.14.54", - "esbuild-windows-64": "0.14.54", - "esbuild-windows-arm64": "0.14.54" - } - }, - "node_modules/esbuild-android-64": { - "version": "0.14.54", - "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.14.54.tgz", - "integrity": "sha512-Tz2++Aqqz0rJ7kYBfz+iqyE3QMycD4vk7LBRyWaAVFgFtQ/O8EJOnVmTOiDWYZ/uYzB4kvP+bqejYdVKzE5lAQ==", - "cpu": [ - "x64" - ], + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/buffer-crc32": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-1.0.0.tgz", + "integrity": "sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==", "dev": true, - "optional": true, - "os": [ - "android" - ], + "license": "MIT", "engines": { - "node": ">=12" + "node": ">=8.0.0" } }, - "node_modules/esbuild-android-arm64": { - "version": "0.14.54", - "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.14.54.tgz", - "integrity": "sha512-F9E+/QDi9sSkLaClO8SOV6etqPd+5DgJje1F9lOWoNncDdOBL2YF59IhsWATSt0TLZbYCf3pNlTHvVV5VfHdvg==", - "cpu": [ - "arm64" - ], + "node_modules/camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", "dev": true, - "optional": true, - "os": [ - "android" - ], "engines": { - "node": ">=12" + "node": ">= 6" } }, - "node_modules/esbuild-darwin-64": { - "version": "0.14.54", - "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.14.54.tgz", - "integrity": "sha512-jtdKWV3nBviOd5v4hOpkVmpxsBy90CGzebpbO9beiqUYVMBtSc0AL9zGftFuBon7PNDcdvNCEuQqw2x0wP9yug==", - "cpu": [ - "x64" - ], + "node_modules/caniuse-lite": { + "version": "1.0.30001792", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001792.tgz", + "integrity": "sha512-hVLMUZFgR4JJ6ACt1uEESvQN1/dBVqPAKY0hgrV70eN3391K6juAfTjKZLKvOMsx8PxA7gsY1/tLMMTcfFLLpw==", "dev": true, - "optional": true, - "os": [ - "darwin" + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } ], + "license": "CC-BY-4.0" + }, + "node_modules/chai": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", + "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=12" + "node": ">=18" } }, - "node_modules/esbuild-darwin-arm64": { - "version": "0.14.54", - "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.54.tgz", - "integrity": "sha512-OPafJHD2oUPyvJMrsCvDGkRrVCar5aVyHfWGQzY1dWnzErjrDuSETxwA2HSsyg2jORLY8yBfzc1MIpUkXlctmw==", - "cpu": [ - "arm64" - ], + "node_modules/change-case": { + "version": "5.4.4", + "resolved": "https://registry.npmjs.org/change-case/-/change-case-5.4.4.tgz", + "integrity": "sha512-HRQyTk2/YPEkt9TnUPbOpr64Uw3KOicFWPVBb+xiHvd6eBx/qPr9xqfBFDT8P2vWsvvz4jbEkfDe71W3VyNu2w==", "dev": true, - "optional": true, - "os": [ - "darwin" - ], + "license": "MIT" + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, "engines": { - "node": ">=12" + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" } }, - "node_modules/esbuild-freebsd-64": { - "version": "0.14.54", - "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.54.tgz", - "integrity": "sha512-OKwd4gmwHqOTp4mOGZKe/XUlbDJ4Q9TjX0hMPIDBUWWu/kwhBAudJdBoxnjNf9ocIB6GN6CPowYpR/hRCbSYAg==", - "cpu": [ - "x64" - ], + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, - "optional": true, - "os": [ - "freebsd" - ], + "dependencies": { + "color-name": "~1.1.4" + }, "engines": { - "node": ">=12" + "node": ">=7.0.0" } }, - "node_modules/esbuild-freebsd-arm64": { - "version": "0.14.54", - "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.54.tgz", - "integrity": "sha512-sFwueGr7OvIFiQT6WeG0jRLjkjdqWWSrfbVwZp8iMP+8UHEHRBvlaxL6IuKNDwAozNUmbb8nIMXa7oAOARGs1Q==", - "cpu": [ - "arm64" - ], + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/colorette": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.4.0.tgz", + "integrity": "sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==", + "dev": true, + "license": "MIT" + }, + "node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", "dev": true, - "optional": true, - "os": [ - "freebsd" - ], "engines": { - "node": ">=12" + "node": ">= 6" } }, - "node_modules/esbuild-linux-32": { - "version": "0.14.54", - "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.14.54.tgz", - "integrity": "sha512-1ZuY+JDI//WmklKlBgJnglpUL1owm2OX+8E1syCD6UAxcMM/XoWd76OHSjl/0MR0LisSAXDqgjT3uJqT67O3qw==", - "cpu": [ - "ia32" - ], + "node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } + "license": "MIT" }, - "node_modules/esbuild-linux-64": { - "version": "0.14.54", - "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.14.54.tgz", - "integrity": "sha512-EgjAgH5HwTbtNsTqQOXWApBaPVdDn7XcK+/PtJwZLT1UmpLoznPd8c5CxqsH2dQK3j05YsB3L17T8vE7cp4cCg==", - "cpu": [ - "x64" - ], + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, - "optional": true, - "os": [ - "linux" - ], + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, "engines": { - "node": ">=12" + "node": ">= 8" } }, - "node_modules/esbuild-linux-arm": { - "version": "0.14.54", - "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.14.54.tgz", - "integrity": "sha512-qqz/SjemQhVMTnvcLGoLOdFpCYbz4v4fUo+TfsWG+1aOu70/80RV6bgNpR2JCrppV2moUQkww+6bWxXRL9YMGw==", - "cpu": [ - "arm" - ], + "node_modules/css-select": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.2.2.tgz", + "integrity": "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==", "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" } }, - "node_modules/esbuild-linux-arm64": { - "version": "0.14.54", - "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.54.tgz", - "integrity": "sha512-WL71L+0Rwv+Gv/HTmxTEmpv0UgmxYa5ftZILVi2QmZBgX3q7+tDeOQNqGtdXSdsL8TQi1vIaVFHUPDe0O0kdig==", - "cpu": [ - "arm64" - ], + "node_modules/css-what": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz", + "integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==", "dev": true, - "optional": true, - "os": [ - "linux" - ], + "license": "BSD-2-Clause", "engines": { - "node": ">=12" + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" } }, - "node_modules/esbuild-linux-mips64le": { - "version": "0.14.54", - "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.54.tgz", - "integrity": "sha512-qTHGQB8D1etd0u1+sB6p0ikLKRVuCWhYQhAHRPkO+OF3I/iSlTKNNS0Lh2Oc0g0UFGguaFZZiPJdJey3AGpAlw==", - "cpu": [ - "mips64el" - ], + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", "dev": true, - "optional": true, - "os": [ - "linux" - ], + "bin": { + "cssesc": "bin/cssesc" + }, "engines": { - "node": ">=12" + "node": ">=4" } }, - "node_modules/esbuild-linux-ppc64le": { - "version": "0.14.54", - "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.54.tgz", - "integrity": "sha512-j3OMlzHiqwZBDPRCDFKcx595XVfOfOnv68Ax3U4UKZ3MTYQB5Yz3X1mn5GnodEVYzhtZgxEBidLWeIs8FDSfrQ==", - "cpu": [ - "ppc64" - ], + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", "dev": true, - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=12" - } + "license": "MIT" }, - "node_modules/esbuild-linux-riscv64": { - "version": "0.14.54", - "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.54.tgz", - "integrity": "sha512-y7Vt7Wl9dkOGZjxQZnDAqqn+XOqFD7IMWiewY5SPlNlzMX39ocPQlOaoxvT4FllA5viyV26/QzHtvTjVNOxHZg==", - "cpu": [ - "riscv64" - ], + "node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", "dev": true, - "optional": true, - "os": [ - "linux" - ], + "dependencies": { + "ms": "^2.1.3" + }, "engines": { - "node": ">=12" + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "node_modules/esbuild-linux-s390x": { - "version": "0.14.54", - "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.54.tgz", - "integrity": "sha512-zaHpW9dziAsi7lRcyV4r8dhfG1qBidQWUXweUjnw+lliChJqQr+6XD71K41oEIC3Mx1KStovEmlzm+MkGZHnHA==", - "cpu": [ - "s390x" - ], + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", "dev": true, - "optional": true, - "os": [ - "linux" - ], + "license": "Apache-2.0", "engines": { - "node": ">=12" + "node": ">=8" } }, - "node_modules/esbuild-netbsd-64": { - "version": "0.14.54", - "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.54.tgz", - "integrity": "sha512-PR01lmIMnfJTgeU9VJTDY9ZerDWVFIUzAtJuDHwwceppW7cQWjBBqP48NdeRtoP04/AtO9a7w3viI+PIDr6d+w==", - "cpu": [ - "x64" - ], + "node_modules/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "dev": true + }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "dev": true + }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", "dev": true, - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=12" + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" } }, - "node_modules/esbuild-openbsd-64": { - "version": "0.14.54", - "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.54.tgz", - "integrity": "sha512-Qyk7ikT2o7Wu76UsvvDS5q0amJvmRzDyVlL0qf5VLsLchjCa1+IAvd8kTBgUxD7VBUUVgItLkk609ZHUc1oCaw==", - "cpu": [ - "x64" - ], + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", "dev": true, - "optional": true, - "os": [ - "openbsd" + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } ], - "engines": { - "node": ">=12" - } + "license": "BSD-2-Clause" }, - "node_modules/esbuild-sunos-64": { - "version": "0.14.54", - "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.14.54.tgz", - "integrity": "sha512-28GZ24KmMSeKi5ueWzMcco6EBHStL3B6ubM7M51RmPwXQGLe0teBGJocmWhgwccA1GeFXqxzILIxXpHbl9Q/Kw==", - "cpu": [ - "x64" - ], + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", "dev": true, - "optional": true, - "os": [ - "sunos" - ], + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.3.0" + }, "engines": { - "node": ">=12" + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" } }, - "node_modules/esbuild-windows-32": { - "version": "0.14.54", - "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.14.54.tgz", - "integrity": "sha512-T+rdZW19ql9MjS7pixmZYVObd9G7kcaZo+sETqNH4RCkuuYSuv9AGHUVnPoP9hhuE1WM1ZimHz1CIBHBboLU7w==", - "cpu": [ - "ia32" - ], + "node_modules/domutils": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", + "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" } }, - "node_modules/esbuild-windows-64": { - "version": "0.14.54", - "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.14.54.tgz", - "integrity": "sha512-AoHTRBUuYwXtZhjXZbA1pGfTo8cJo3vZIcWGLiUcTNgHpJJMC1rVA44ZereBHMJtotyN71S8Qw0npiCIkW96cQ==", - "cpu": [ - "x64" - ], + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, + "node_modules/electron-to-chromium": { + "version": "1.5.353", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.353.tgz", + "integrity": "sha512-kOrWphBi8TOZyiJZqsgqIle0lw+tzmnQK83pV9dZUd01Nm2POECSyFQMAuarzZdYqQW7FH9RaYOuaRo3h+bQ3w==", "dev": true, - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=12" - } + "license": "ISC" }, - "node_modules/esbuild-windows-arm64": { - "version": "0.14.54", - "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.54.tgz", - "integrity": "sha512-M0kuUvXhot1zOISQGXwWn6YtS+Y/1RT9WrVIOywZnJHo3jCDyewAc79aKNQWFCQm+xNHVTq9h8dZKvygoXQQRg==", - "cpu": [ - "arm64" - ], + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", "dev": true, - "optional": true, - "os": [ - "win32" - ], + "license": "BSD-2-Clause", "engines": { - "node": ">=12" + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" } }, + "node_modules/es-module-lexer": { + "version": "0.10.5", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.10.5.tgz", + "integrity": "sha512-+7IwY/kiGAacQfY+YBhKMvEmyAJnw5grTUgjG85Pe7vcUI/6b7pZjZG8nQ7+48YhzEAEqrEgD2dCz/JIK+AYvw==", + "dev": true + }, "node_modules/escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } @@ -1522,7 +1485,25 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", - "dev": true + "dev": true, + "license": "MIT" + }, + "node_modules/expect-type": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", + "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" }, "node_modules/fast-glob": { "version": "3.3.2", @@ -1578,15 +1559,16 @@ } }, "node_modules/fraction.js": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", - "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz", + "integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==", "dev": true, + "license": "MIT", "engines": { "node": "*" }, "funding": { - "type": "patreon", + "type": "github", "url": "https://github.com/sponsors/rawify" } }, @@ -1627,20 +1609,13 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", "dev": true, + "license": "ISC", "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", @@ -1668,15 +1643,6 @@ "node": ">= 6" } }, - "node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true, - "engines": { - "node": ">=4" - } - }, "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", @@ -1695,35 +1661,41 @@ "node": ">= 0.4" } }, - "node_modules/htmlparser2": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-9.1.0.tgz", - "integrity": "sha512-5zfg6mHUoaer/97TxnGpxmbR7zJtPwIYFMZ/H5ucTlPZhKvtum05yiPK3Mgai3a0DyVxv7qYqoweaEd2nrYQzQ==", + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", "dev": true, - "funding": [ - "https://github.com/fb55/htmlparser2?sponsor=1", - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], - "dependencies": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3", - "domutils": "^3.1.0", - "entities": "^4.5.0" + "license": "MIT", + "bin": { + "he": "bin/he" } }, - "node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", "dev": true, + "license": "MIT", "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" + "agent-base": "^7.1.2", + "debug": "4" }, "engines": { - "node": ">=0.10.0" + "node": ">= 14" + } + }, + "node_modules/index-to-position": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/index-to-position/-/index-to-position-1.2.0.tgz", + "integrity": "sha512-Yg7+ztRkqslMAS2iFaU+Oa4KTSidr63OsFGlOrJoW981kIYO3CGCS3wA95P1mUi/IVSJkn0D479KTJpVpvFNuw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/is-binary-path": { @@ -1814,24 +1786,49 @@ } }, "node_modules/jiti": { - "version": "1.21.6", - "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.6.tgz", - "integrity": "sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==", + "version": "1.21.7", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", + "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", "dev": true, + "license": "MIT", "bin": { "jiti": "bin/jiti.js" } }, + "node_modules/js-levenshtein": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/js-levenshtein/-/js-levenshtein-1.1.6.tgz", + "integrity": "sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" }, + "node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, "node_modules/jsesc": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", "dev": true, + "license": "MIT", "bin": { "jsesc": "bin/jsesc" }, @@ -1839,17 +1836,12 @@ "node": ">=6" } }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "dev": true, - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } + "license": "MIT" }, "node_modules/jsonfile": { "version": "6.1.0", @@ -1863,6 +1855,279 @@ "graceful-fs": "^4.1.6" } }, + "node_modules/lightningcss": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", + "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.32.0", + "lightningcss-darwin-arm64": "1.32.0", + "lightningcss-darwin-x64": "1.32.0", + "lightningcss-freebsd-x64": "1.32.0", + "lightningcss-linux-arm-gnueabihf": "1.32.0", + "lightningcss-linux-arm64-gnu": "1.32.0", + "lightningcss-linux-arm64-musl": "1.32.0", + "lightningcss-linux-x64-gnu": "1.32.0", + "lightningcss-linux-x64-musl": "1.32.0", + "lightningcss-win32-arm64-msvc": "1.32.0", + "lightningcss-win32-x64-msvc": "1.32.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", + "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", + "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", + "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", + "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", + "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", + "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", + "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", + "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", + "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", + "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", + "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, "node_modules/lilconfig": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", @@ -1892,25 +2157,14 @@ "loose-envify": "cli.js" } }, - "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "dependencies": { - "yallist": "^3.0.2" - } - }, "node_modules/magic-string": { - "version": "0.26.7", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.26.7.tgz", - "integrity": "sha512-hX9XH3ziStPoPhJxLq1syWuZMxbDvGNbVchfrdCtanC7D13888bMFow61x8axrx+GfHLtVeAx2kxL7tTGRl+Ow==", + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", "dev": true, + "license": "MIT", "dependencies": { - "sourcemap-codec": "^1.4.8" - }, - "engines": { - "node": ">=12" + "@jridgewell/sourcemap-codec": "^1.5.5" } }, "node_modules/merge2": { @@ -1936,12 +2190,13 @@ } }, "node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", "dev": true, + "license": "ISC", "dependencies": { - "brace-expansion": "^2.0.1" + "brace-expansion": "^2.0.2" }, "engines": { "node": ">=16 || 14 >=14.17" @@ -1977,9 +2232,9 @@ } }, "node_modules/nanoid": { - "version": "3.3.8", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", - "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", + "version": "3.3.12", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", + "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", "dev": true, "funding": [ { @@ -1987,6 +2242,7 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "bin": { "nanoid": "bin/nanoid.cjs" }, @@ -1994,11 +2250,23 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/node-html-parser": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/node-html-parser/-/node-html-parser-7.1.0.tgz", + "integrity": "sha512-iJo8b2uYGT40Y8BTyy5ufL6IVbN8rbm/1QK2xffXU/1a/v3AAa0d1YAoqBNYqaS4R/HajkWIpIfdE6KcyFh1AQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "css-select": "^5.1.0", + "he": "1.2.0" + } + }, "node_modules/node-releases": { - "version": "2.0.19", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", - "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", - "dev": true + "version": "2.0.38", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.38.tgz", + "integrity": "sha512-3qT/88Y3FbH/Kx4szpQQ4HzUbVrHPKTLVpVocKiLfoYvw9XSGOX2FmD2d6DrXbVYyAQTF2HeF6My8jmzx7/CRw==", + "dev": true, + "license": "MIT" }, "node_modules/normalize-path": { "version": "3.0.0", @@ -2009,20 +2277,12 @@ "node": ">=0.10.0" } }, - "node_modules/normalize-range": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", - "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/nth-check": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "boolbase": "^1.0.0" }, @@ -2048,47 +2308,60 @@ "node": ">= 6" } }, - "node_modules/package-json-from-dist": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", - "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", - "dev": true - }, - "node_modules/parse5": { - "version": "7.2.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.2.1.tgz", - "integrity": "sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ==", + "node_modules/obug": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz", + "integrity": "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==", "dev": true, - "dependencies": { - "entities": "^4.5.0" - }, - "funding": { - "url": "https://github.com/inikulin/parse5?sponsor=1" - } + "funding": [ + "https://github.com/sponsors/sxzz", + "https://opencollective.com/debug" + ], + "license": "MIT" }, - "node_modules/parse5-htmlparser2-tree-adapter": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.1.0.tgz", - "integrity": "sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==", + "node_modules/openapi-typescript": { + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/openapi-typescript/-/openapi-typescript-7.13.0.tgz", + "integrity": "sha512-EFP392gcqXS7ntPvbhBzbF8TyBA+baIYEm791Hy5YkjDYKTnk/Tn5OQeKm5BIZvJihpp8Zzr4hzx0Irde1LNGQ==", "dev": true, + "license": "MIT", "dependencies": { - "domhandler": "^5.0.3", - "parse5": "^7.0.0" + "@redocly/openapi-core": "^1.34.6", + "ansi-colors": "^4.1.3", + "change-case": "^5.4.4", + "parse-json": "^8.3.0", + "supports-color": "^10.2.2", + "yargs-parser": "^21.1.1" + }, + "bin": { + "openapi-typescript": "bin/cli.js" }, - "funding": { - "url": "https://github.com/inikulin/parse5?sponsor=1" + "peerDependencies": { + "typescript": "^5.x" } }, - "node_modules/parse5-parser-stream": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/parse5-parser-stream/-/parse5-parser-stream-7.1.2.tgz", - "integrity": "sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow==", + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true + }, + "node_modules/parse-json": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-8.3.0.tgz", + "integrity": "sha512-ybiGyvspI+fAoRQbIPRddCcSTV9/LsJbf0e/S85VLowVGzRmokfneg2kwVW/KU5rOXrPSbF1qAKPMgNTqqROQQ==", "dev": true, + "license": "MIT", "dependencies": { - "parse5": "^7.0.0" + "@babel/code-frame": "^7.26.2", + "index-to-position": "^1.1.0", + "type-fest": "^4.39.1" + }, + "engines": { + "node": ">=18" }, "funding": { - "url": "https://github.com/inikulin/parse5?sponsor=1" + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/path-key": { @@ -2128,6 +2401,13 @@ "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", "dev": true }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -2135,10 +2415,11 @@ "dev": true }, "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", "dev": true, + "license": "MIT", "engines": { "node": ">=8.6" }, @@ -2164,10 +2445,67 @@ "node": ">= 6" } }, + "node_modules/playwright": { + "version": "1.59.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.59.1.tgz", + "integrity": "sha512-C8oWjPR3F81yljW9o5OxcWzfh6avkVwDD2VYdwIGqTkl+OGFISgypqzfu7dOe4QNLL2aqcWBmI3PMtLIK233lw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.59.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.59.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.59.1.tgz", + "integrity": "sha512-HBV/RJg81z5BiiZ9yPzIiClYV/QMsDCKUyogwH9p3MCP6IYjUFu/MActgYAvK0oWyV9NlwM3GLBjADyWgydVyg==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/playwright/node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/pluralize": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", + "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/postcss": { - "version": "8.4.49", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz", - "integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==", + "version": "8.5.14", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.14.tgz", + "integrity": "sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg==", "dev": true, "funding": [ { @@ -2183,8 +2521,9 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { - "nanoid": "^3.3.7", + "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, @@ -2307,17 +2646,6 @@ "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", "dev": true }, - "node_modules/q": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/q/-/q-1.5.1.tgz", - "integrity": "sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw==", - "deprecated": "You or someone you depend on is using Q, the JavaScript Promise library that gave JavaScript developers strong feelings about promises. They can almost certainly migrate to the native JavaScript promise now. Thank you literally everyone for joining me in this bet against the odds. Be excellent to each other.\n\n(For a CapTP with native promises, see @endo/eventual-send and @endo/captp)", - "dev": true, - "engines": { - "node": ">=0.6.0", - "teleport": ">=0.2.0" - } - }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -2366,6 +2694,7 @@ "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.13.0.tgz", "integrity": "sha512-XP8A9BT0CpRBD+NYLLeIhld/RqG9+gktUjW1FkE+Vm7OCinbG1SshcK5tb9ls4kzvjZr9mOQc7HYgBngEyPAXg==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -2391,6 +2720,16 @@ "node": ">=8.10.0" } }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/resolve": { "version": "1.22.9", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.9.tgz", @@ -2418,20 +2757,46 @@ "node": ">=0.10.0" } }, - "node_modules/rollup": { - "version": "2.79.2", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.2.tgz", - "integrity": "sha512-fS6iqSPZDs3dr/y7Od6y5nha8dW1YnbgtsyotCVvoFGKbERG++CVRFv1meyGDE1SNItQA8BrnCw7ScdAhRJ3XQ==", + "node_modules/rolldown": { + "version": "1.0.0-rc.18", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.18.tgz", + "integrity": "sha512-phmyKBpuBdRYDf4hgyynGAYn/rDDe+iZXKVJ7WX5b1zQzpLkP5oJRPGsfJuHdzPMlyyEO/4sPW6yfSx2gf7lVg==", "dev": true, + "license": "MIT", + "dependencies": { + "@oxc-project/types": "=0.128.0", + "@rolldown/pluginutils": "1.0.0-rc.18" + }, "bin": { - "rollup": "dist/bin/rollup" + "rolldown": "bin/cli.mjs" }, "engines": { - "node": ">=10.0.0" + "node": "^20.19.0 || >=22.12.0" }, "optionalDependencies": { - "fsevents": "~2.3.2" - } + "@rolldown/binding-android-arm64": "1.0.0-rc.18", + "@rolldown/binding-darwin-arm64": "1.0.0-rc.18", + "@rolldown/binding-darwin-x64": "1.0.0-rc.18", + "@rolldown/binding-freebsd-x64": "1.0.0-rc.18", + "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.18", + "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.18", + "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.18", + "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.18", + "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.18", + "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.18", + "@rolldown/binding-linux-x64-musl": "1.0.0-rc.18", + "@rolldown/binding-openharmony-arm64": "1.0.0-rc.18", + "@rolldown/binding-wasm32-wasi": "1.0.0-rc.18", + "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.18", + "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.18" + } + }, + "node_modules/rolldown/node_modules/@rolldown/pluginutils": { + "version": "1.0.0-rc.18", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.18.tgz", + "integrity": "sha512-CUY5Mnhe64xQBGZEEXQ5WyZwsc1JU3vAZLIxtrsBt3LO6UOb+C8GunVKqe9sT8NeWb4lqSaoJtp2xo6GxT1MNw==", + "dev": true, + "license": "MIT" }, "node_modules/run-parallel": { "version": "1.2.0", @@ -2456,11 +2821,15 @@ "queue-microtask": "^1.2.2" } }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true + "node_modules/rxjs": { + "version": "7.5.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.7.tgz", + "integrity": "sha512-z9MzKh/UcOqB3i20H6rtrlaE/CgjLOvheWK/9ILrbhROGTweAi1BaFsTT9FbwZi5Trr1qNRs+MXkhmR06awzQA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } }, "node_modules/scheduler": { "version": "0.23.2", @@ -2470,15 +2839,6 @@ "loose-envify": "^1.1.0" } }, - "node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -2500,6 +2860,13 @@ "node": ">=8" } }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, "node_modules/signal-exit": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", @@ -2521,21 +2888,19 @@ "node": ">=0.10.0" } }, - "node_modules/sourcemap-codec": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", - "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", - "deprecated": "Please use @jridgewell/sourcemap-codec instead", - "dev": true + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" }, - "node_modules/stream-buffers": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/stream-buffers/-/stream-buffers-0.2.6.tgz", - "integrity": "sha512-ZRpmWyuCdg0TtNKk8bEqvm13oQvXMmzXDsfD4cBgcx5LouborvU5pm3JMkdTP3HcszyUI08AM1dHMXA5r2g6Sg==", + "node_modules/std-env": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.1.0.tgz", + "integrity": "sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ==", "dev": true, - "engines": { - "node": ">= 0.3.0" - } + "license": "MIT" }, "node_modules/string-width": { "version": "5.1.2", @@ -2655,6 +3020,19 @@ "node": ">=16 || 14 >=14.17" } }, + "node_modules/supports-color": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-10.2.2.tgz", + "integrity": "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, "node_modules/supports-preserve-symlinks-flag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", @@ -2668,10 +3046,11 @@ } }, "node_modules/tailwindcss": { - "version": "3.4.16", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.16.tgz", - "integrity": "sha512-TI4Cyx7gDiZ6r44ewaJmt0o6BrMCT5aK5e0rmJ/G9Xq3w7CX/5VXl/zIPEJZFUK5VEqwByyhqNPycPlvcK4ZNw==", + "version": "3.4.19", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.19.tgz", + "integrity": "sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ==", "dev": true, + "license": "MIT", "dependencies": { "@alloc/quick-lru": "^5.2.0", "arg": "^5.0.2", @@ -2681,7 +3060,7 @@ "fast-glob": "^3.3.2", "glob-parent": "^6.0.2", "is-glob": "^4.0.3", - "jiti": "^1.21.6", + "jiti": "^1.21.7", "lilconfig": "^3.1.3", "micromatch": "^4.0.8", "normalize-path": "^3.0.0", @@ -2690,7 +3069,7 @@ "postcss": "^8.4.47", "postcss-import": "^15.1.0", "postcss-js": "^4.0.1", - "postcss-load-config": "^4.0.2", + "postcss-load-config": "^4.0.2 || ^5.0 || ^6.0", "postcss-nested": "^6.2.0", "postcss-selector-parser": "^6.1.2", "resolve": "^1.22.8", @@ -2737,6 +3116,81 @@ "node": ">=0.8" } }, + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.1.2.tgz", + "integrity": "sha512-dAqSqE/RabpBKI8+h26GfLq6Vb3JVXs30XYQjdMjaj/c2tS8IYYMbIzP599KtRj7c57/wYApb3QjgRgXmrCukA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.16", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", + "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/tinyrainbow": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.0.tgz", + "integrity": "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -2755,35 +3209,38 @@ "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", "dev": true }, - "node_modules/typescript": { - "version": "4.9.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", - "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "dev": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=4.2.0" - } + "license": "0BSD" }, - "node_modules/uberproto": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/uberproto/-/uberproto-1.2.0.tgz", - "integrity": "sha512-pGtPAQmLwh+R9w81WVHzui1FfedpQWQpiaIIfPCwhtsBez4q6DYbJFfyXPVHPUTNFnedAvNEnkoFiLuhXIR94w==", + "node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", "dev": true, + "license": "(MIT OR CC0-1.0)", "engines": { - "node": "*" + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/undici": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-6.21.0.tgz", - "integrity": "sha512-BUgJXc752Kou3oOIuU1i+yZZypyZRqNPW0vqoMPl8VaoalSfeR0D8/t4iAS3yirs79SSMTxTag+ZC86uswv+Cw==", + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, "engines": { - "node": ">=18.17" + "node": ">=14.17" } }, "node_modules/universalify": { @@ -2796,9 +3253,9 @@ } }, "node_modules/update-browserslist-db": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz", - "integrity": "sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", "dev": true, "funding": [ { @@ -2814,9 +3271,10 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { "escalade": "^3.2.0", - "picocolors": "^1.1.0" + "picocolors": "^1.1.1" }, "bin": { "update-browserslist-db": "cli.js" @@ -2825,6 +3283,13 @@ "browserslist": ">= 4.21.0" } }, + "node_modules/uri-js-replace": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/uri-js-replace/-/uri-js-replace-1.0.1.tgz", + "integrity": "sha512-W+C9NWNLFOoBI2QWDp4UT9pv65r2w5Cx+3sTYFvtMdDBxkKt1syCqsUdSFAChbEe1uK5TfS04wt/nGwmaeIQ0g==", + "dev": true, + "license": "MIT" + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -2832,76 +3297,204 @@ "dev": true }, "node_modules/vite": { - "version": "2.9.18", - "resolved": "https://registry.npmjs.org/vite/-/vite-2.9.18.tgz", - "integrity": "sha512-sAOqI5wNM9QvSEE70W3UGMdT8cyEn0+PmJMTFvTB8wB0YbYUWw3gUbY62AOyrXosGieF2htmeLATvNxpv/zNyQ==", + "version": "8.0.11", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.11.tgz", + "integrity": "sha512-Jz1mxtUBR5xTT65VOdJZUUeoyLtqljmFkiUXhPTLZka3RDc9vpi/xXkyrnsdRcm2lIi3l3GPMnAidTsEGIj3Ow==", "dev": true, + "license": "MIT", "dependencies": { - "esbuild": "^0.14.27", - "postcss": "^8.4.13", - "resolve": "^1.22.0", - "rollup": ">=2.59.0 <2.78.0" + "lightningcss": "^1.32.0", + "picomatch": "^4.0.4", + "postcss": "^8.5.14", + "rolldown": "1.0.0-rc.18", + "tinyglobby": "^0.2.16" }, "bin": { "vite": "bin/vite.js" }, "engines": { - "node": ">=12.2.0" + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" }, "optionalDependencies": { - "fsevents": "~2.3.2" + "fsevents": "~2.3.3" }, "peerDependencies": { - "less": "*", - "sass": "*", - "stylus": "*" + "@types/node": "^20.19.0 || >=22.12.0", + "@vitejs/devtools": "^0.1.18", + "esbuild": "^0.27.0 || ^0.28.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" }, "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "@vitejs/devtools": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jiti": { + "optional": true + }, "less": { "optional": true }, "sass": { "optional": true }, + "sass-embedded": { + "optional": true + }, "stylus": { "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true } } }, - "node_modules/vite/node_modules/rollup": { - "version": "2.77.3", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.77.3.tgz", - "integrity": "sha512-/qxNTG7FbmefJWoeeYJFbHehJ2HNWnjkAFRKzWN/45eNBBF/r8lo992CwcJXEzyVxs5FmfId+vTSTQDb+bxA+g==", + "node_modules/vite/node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, - "bin": { - "rollup": "dist/bin/rollup" - }, + "license": "MIT", "engines": { - "node": ">=10.0.0" + "node": ">=12" }, - "optionalDependencies": { - "fsevents": "~2.3.2" + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/whatwg-encoding": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", - "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", - "dev": true, - "dependencies": { - "iconv-lite": "0.6.3" + "node_modules/vitest": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.5.tgz", + "integrity": "sha512-9Xx1v3/ih3m9hN+SbfkUyy0JAs72ap3r7joc87XL6jwF0jGg6mFBvQ1SrwaX+h8BlkX6Hz9shdd1uo6AF+ZGpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/expect": "4.1.5", + "@vitest/mocker": "4.1.5", + "@vitest/pretty-format": "4.1.5", + "@vitest/runner": "4.1.5", + "@vitest/snapshot": "4.1.5", + "@vitest/spy": "4.1.5", + "@vitest/utils": "4.1.5", + "es-module-lexer": "^2.0.0", + "expect-type": "^1.3.0", + "magic-string": "^0.30.21", + "obug": "^2.1.1", + "pathe": "^2.0.3", + "picomatch": "^4.0.3", + "std-env": "^4.0.0-rc.1", + "tinybench": "^2.9.0", + "tinyexec": "^1.0.2", + "tinyglobby": "^0.2.15", + "tinyrainbow": "^3.1.0", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" }, "engines": { - "node": ">=18" + "node": "^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@opentelemetry/api": "^1.9.0", + "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", + "@vitest/browser-playwright": "4.1.5", + "@vitest/browser-preview": "4.1.5", + "@vitest/browser-webdriverio": "4.1.5", + "@vitest/coverage-istanbul": "4.1.5", + "@vitest/coverage-v8": "4.1.5", + "@vitest/ui": "4.1.5", + "happy-dom": "*", + "jsdom": "*", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@opentelemetry/api": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser-playwright": { + "optional": true + }, + "@vitest/browser-preview": { + "optional": true + }, + "@vitest/browser-webdriverio": { + "optional": true + }, + "@vitest/coverage-istanbul": { + "optional": true + }, + "@vitest/coverage-v8": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + }, + "vite": { + "optional": false + } } }, - "node_modules/whatwg-mimetype": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", - "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", + "node_modules/vitest/node_modules/es-module-lexer": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.1.0.tgz", + "integrity": "sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/vitest/node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, + "license": "MIT", "engines": { - "node": ">=18" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" } }, "node_modules/which": { @@ -2919,6 +3512,23 @@ "node": ">= 8" } }, + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/wrap-ansi": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", @@ -3010,22 +3620,47 @@ "node": ">=8" } }, - "node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true - }, "node_modules/yaml": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.6.1.tgz", - "integrity": "sha512-7r0XPzioN/Q9kXBro/XPnA6kznR73DHq+GXh5ON7ZozRO6aMjbmiBuKste2wslTFkC5d1dw0GooOCepZXJ2SAg==", + "version": "2.8.4", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.4.tgz", + "integrity": "sha512-ml/JPOj9fOQK8RNnWojA67GbZ0ApXAUlN2UQclwv2eVgTgn7O9gg9o7paZWKMp4g0H3nTLtS9LVzhkpOFIKzog==", "dev": true, + "license": "ISC", "bin": { "yaml": "bin.mjs" }, "engines": { - "node": ">= 14" + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" + } + }, + "node_modules/yaml-ast-parser": { + "version": "0.0.43", + "resolved": "https://registry.npmjs.org/yaml-ast-parser/-/yaml-ast-parser-0.0.43.tgz", + "integrity": "sha512-2PTINUwsRqSd+s8XxKaJWQlUuEMHJQyEuh2edBbW8KNJz0SJPwUSD2zRWqezFEdN7IzAgeuYHFUCF7o8zRdZ0A==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yazl": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/yazl/-/yazl-3.3.1.tgz", + "integrity": "sha512-BbETDVWG+VcMUle37k5Fqp//7SDOK2/1+T7X8TD96M3D9G8jK5VLUdQVdVjGi8im7FGkazX7kk5hkU8X4L5Bng==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-crc32": "^1.0.0" } } } diff --git a/client/package.json b/client/package.json index d8c2fba..4bef17e 100644 --- a/client/package.json +++ b/client/package.json @@ -1,10 +1,18 @@ { "name": "conspiracy-alert-plugin", "private": true, - "version": "0.0.0", + "version": "1.0.0", + "license": "MIT", "scripts": { "dev": "vite", + "generate:api-schema": "node scripts/generate-api-schema.mjs", + "generate:api-types": "npm run generate:api-schema && openapi-typescript openapi/channel-checker.openapi.json -o src/generated/apiTypes.ts", + "check:api-types": "npm run generate:api-types && git diff --exit-code -- openapi/channel-checker.openapi.json src/generated/apiTypes.ts", "build": "tsc && vite build", + "package": "npm run build && node scripts/package-extension.mjs", + "check": "tsc --noEmit", + "test": "vitest run", + "test:e2e": "playwright test", "preview": "vite preview" }, "dependencies": { @@ -12,15 +20,22 @@ "react-dom": "^18.0.0" }, "devDependencies": { - "@crxjs/vite-plugin": "^1.0.14", - "@types/chrome": "^0.0.287", - "@types/react": "^18.0.0", - "@types/react-dom": "^18.0.0", - "@vitejs/plugin-react": "^1.3.0", - "autoprefixer": "^10.4.20", + "@crxjs/vite-plugin": "^2.4.0", + "@playwright/test": "^1.59.1", + "@types/chrome": "^0.1.42", + "@types/react": "^18.3.28", + "@types/react-dom": "^18.3.7", + "@vitejs/plugin-react": "^6.0.1", + "autoprefixer": "^10.5.0", + "openapi-typescript": "^7.13.0", "postcss": "^8.4.49", - "tailwindcss": "^3.4.16", - "typescript": "^4.6.3", - "vite": "^2.9.15" + "tailwindcss": "^3.4.19", + "typescript": "^5.9.3", + "vite": "^8.0.11", + "vitest": "^4.1.5", + "yazl": "^3.3.1" + }, + "overrides": { + "rollup": "2.80.0" } } diff --git a/client/playwright.config.ts b/client/playwright.config.ts new file mode 100644 index 0000000..e1976db --- /dev/null +++ b/client/playwright.config.ts @@ -0,0 +1,11 @@ +import { defineConfig } from "@playwright/test"; + +export default defineConfig({ + testDir: "e2e", + fullyParallel: false, + workers: 1, + timeout: 30_000, + use: { + trace: "retain-on-failure", + }, +}); diff --git a/client/scripts/generate-api-schema.mjs b/client/scripts/generate-api-schema.mjs new file mode 100644 index 0000000..541ac16 --- /dev/null +++ b/client/scripts/generate-api-schema.mjs @@ -0,0 +1,40 @@ +import { mkdirSync } from "node:fs"; +import path from "node:path"; +import { spawnSync } from "node:child_process"; + +const backendDir = path.resolve(process.env.CHANNEL_CHECKER_BACKEND_DIR ?? "../../channel-checker-bot"); +const datasetDir = path.resolve( + process.env.CHANNEL_CHECKER_DATASET_DIR ?? "../../conspiracy-dataset-telegram", +); +const outputDir = path.resolve("openapi"); +const outputPath = path.join(outputDir, "channel-checker.openapi.json"); + +mkdirSync(outputDir, { recursive: true }); + +const result = spawnSync( + "python3", + [ + "-m", + "channel_checker.cli", + "export-openapi", + "--dataset-dir", + datasetDir, + "--output", + outputPath, + ], + { + cwd: backendDir, + env: { + ...process.env, + PYTHONPATH: "src", + }, + encoding: "utf8", + }, +); + +if (result.status !== 0) { + process.stderr.write(result.stderr); + process.exit(result.status ?? 1); +} + +process.stdout.write(`Wrote ${outputPath}\n`); diff --git a/client/scripts/package-extension.mjs b/client/scripts/package-extension.mjs new file mode 100644 index 0000000..27d918f --- /dev/null +++ b/client/scripts/package-extension.mjs @@ -0,0 +1,61 @@ +import { createReadStream, createWriteStream } from "node:fs"; +import { mkdir, readdir, readFile, rm, stat } from "node:fs/promises"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; + +import yazl from "yazl"; + +const scriptDir = path.dirname(fileURLToPath(import.meta.url)); +const clientDir = path.resolve(scriptDir, ".."); +const distDir = path.join(clientDir, "dist"); +const releaseDir = path.join(clientDir, "release"); +const packageJsonPath = path.join(clientDir, "package.json"); + +const packageJson = JSON.parse(await readFile(packageJsonPath, "utf-8")); +const archiveName = `${packageJson.name}-${packageJson.version}.zip`; +const archivePath = path.join(releaseDir, archiveName); + +await assertExtensionBuild(distDir); +await mkdir(releaseDir, { recursive: true }); +await rm(archivePath, { force: true }); + +const zipfile = new yazl.ZipFile(); +const output = createWriteStream(archivePath); +const completed = new Promise((resolve, reject) => { + output.on("close", resolve); + output.on("error", reject); + zipfile.outputStream.on("error", reject); +}); + +zipfile.outputStream.pipe(output); +await addDirectoryToZip(zipfile, distDir, distDir); +zipfile.end(); +await completed; + +console.log(`Packaged ${path.relative(clientDir, archivePath)}`); + +async function assertExtensionBuild(directory) { + const manifestPath = path.join(directory, "manifest.json"); + try { + await stat(manifestPath); + } catch { + throw new Error("Missing dist/manifest.json. Run npm run build before packaging."); + } +} + +async function addDirectoryToZip(zipfile, rootDirectory, currentDirectory) { + const entries = await readdir(currentDirectory, { withFileTypes: true }); + for (const entry of entries) { + const absolutePath = path.join(currentDirectory, entry.name); + const relativePath = path.relative(rootDirectory, absolutePath).split(path.sep).join("/"); + + if (entry.isDirectory()) { + await addDirectoryToZip(zipfile, rootDirectory, absolutePath); + continue; + } + + if (entry.isFile()) { + zipfile.addReadStream(createReadStream(absolutePath), relativePath); + } + } +} diff --git a/client/src/App.tsx b/client/src/App.tsx index 3cc6d99..d2c0e1f 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -1,119 +1,172 @@ -import React, { useEffect, useMemo } from "react"; -import logo from "./logo.png" +import React, { useEffect, useMemo, useState } from "react"; -interface Response { - url: string | null, - conspiracy_resource_dataset: boolean, - conspiracy_url_dataset: boolean -} +import logo from "./logo.png"; +import { EVALUATE_URL_MESSAGE, isHttpUrl, type EvaluateUrlMessage } from "./messages"; +import { + buildPopupState, + isStoredError, + selectActiveError, + selectActiveEvaluation, + type PopupTone, + type StoredError, + type StoredEvaluation, +} from "./popupState"; + +const STORAGE_KEY = "lastEvaluation"; +const ERROR_KEY = "lastError"; + +const toneClasses: Record = { + danger: "border-red-200 bg-red-50 text-red-950", + warning: "border-amber-200 bg-amber-50 text-amber-950", + neutral: "border-slate-200 bg-slate-50 text-slate-950", + loading: "border-sky-200 bg-sky-50 text-sky-950", + error: "border-amber-300 bg-amber-50 text-amber-950", +}; function App() { + const [storedEvaluation, setStoredEvaluation] = useState(null); + const [storedError, setStoredError] = useState(null); + const [currentUrl, setCurrentUrl] = useState(null); + const [currentTabId, setCurrentTabId] = useState(null); + const [isChecking, setIsChecking] = useState(false); - // load the response from the background script - const [response, setResponse] = React.useState({ - url: null, - conspiracy_resource_dataset: false, - conspiracy_url_dataset: false - }); useEffect(() => { - // get the response from the background script - chrome.storage.local.get(["response"], (result) => { - if (result.response) { - console.log("Response loaded", result.response); - setResponse(result.response); - } + chrome.storage.local.get([STORAGE_KEY, ERROR_KEY], (result) => { + setStoredEvaluation((result[STORAGE_KEY] as StoredEvaluation | undefined) ?? null); + setStoredError(isStoredError(result[ERROR_KEY]) ? result[ERROR_KEY] : null); }); + + const listener = ( + changes: Record, + areaName: string, + ) => { + if (areaName !== "local") { + return; + } + if (changes[STORAGE_KEY]) { + setStoredEvaluation((changes[STORAGE_KEY].newValue as StoredEvaluation | undefined) ?? null); + } + if (changes[ERROR_KEY]) { + const newError = changes[ERROR_KEY].newValue; + setStoredError(isStoredError(newError) ? newError : null); + } + }; + + chrome.storage.onChanged.addListener(listener); + return () => chrome.storage.onChanged.removeListener(listener); }, []); - // get the current tab url - const [currentUrl, setCurrentUrl] = React.useState(null); useEffect(() => { - // get the current tab url chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => { - if (tabs[0].url) { - setCurrentUrl(tabs[0].url); - } + const activeTab = tabs[0]; + setCurrentUrl(activeTab?.url ?? null); + setCurrentTabId(activeTab?.id ?? null); }); }, []); - // set the text based on the response - const text = useMemo(() => { - if (response.url !== null && response.url.toString().replace(/\/+$/, "") === currentUrl?.replace(/\/+$/, "")) { - if (response.conspiracy_url_dataset === true) { - return "This URL is considered a conspiracy URL." - } - if (response.conspiracy_resource_dataset === true) { - return "This URL has matched a conspiracy resource." - } - if (response.conspiracy_resource_dataset === false && response.conspiracy_url_dataset === false) { - return "This URL is safe." - } - } - return "Loading..." - }, [response, currentUrl]); - - // check if the website is dangerous based on the response - const isDangerous = useMemo(() => { - if (response.url !== null && response.url.toString().replace(/\/+$/, "") === currentUrl?.replace(/\/+$/, "")) { - if (response.conspiracy_url_dataset === true) { - return true - } - if (response.conspiracy_resource_dataset === true) { - return true - } - if (response.conspiracy_resource_dataset === false && response.conspiracy_url_dataset === false) { - return false - } + const activeEvaluation = useMemo(() => { + return selectActiveEvaluation(storedEvaluation, currentUrl); + }, [storedEvaluation, currentUrl]); + + const activeError = useMemo(() => { + return selectActiveError(storedError, currentUrl); + }, [storedError, currentUrl]); + + const state = useMemo(() => { + if (activeError) { + return { + tone: "error" as const, + title: "Backend unavailable", + description: activeError.message, + }; } - return null - }, [response, currentUrl]); - - // set the message based on the response - const message = useMemo(() => { - if (isDangerous === null) { - return ( - // loading message -
-

- {text} -

-
- ); - } else if (isDangerous === true) { - return ( - // dangerous message -
-

- {text} -

-
- ); - } else if (isDangerous === false) { - return ( - // safe message -
-

- {text} -

-
- ); + return buildPopupState(activeEvaluation); + }, [activeEvaluation, activeError]); + + const displayUrl = activeEvaluation?.normalized_url ?? currentUrl ?? "No active tab URL"; + const canCheckCurrentUrl = currentUrl !== null && isHttpUrl(currentUrl); + + function handleCheckNow(): void { + if (!canCheckCurrentUrl || currentUrl === null) { + return; } - return null - }, [text, isDangerous]); - // render the app + const message: EvaluateUrlMessage = { + type: EVALUATE_URL_MESSAGE, + url: currentUrl, + source: "manual", + ...(currentTabId === null ? {} : { tabId: currentTabId }), + }; + setIsChecking(true); + chrome.runtime.sendMessage(message, () => setIsChecking(false)); + } + return ( -
- logo -

- Conspiracy Alert -

- {message} - -
- ) + +
+ + +
+ + ); } -export default App +export default App; diff --git a/client/src/SettingsPage.tsx b/client/src/SettingsPage.tsx new file mode 100644 index 0000000..41b8505 --- /dev/null +++ b/client/src/SettingsPage.tsx @@ -0,0 +1,148 @@ +import React, { useEffect, useState } from "react"; + +import logo from "./logo.png"; +import { + DEFAULT_API_BASE_URL, + getBackendOriginPattern, + getExtensionSettings, + normalizeApiBaseUrl, + saveExtensionSettings, +} from "./settings"; + +type SaveStatus = + | { tone: "success"; message: string } + | { tone: "error"; message: string } + | null; + +function SettingsPage() { + const [apiBaseUrl, setApiBaseUrl] = useState(DEFAULT_API_BASE_URL); + const [automaticChecksEnabled, setAutomaticChecksEnabled] = useState(true); + const [status, setStatus] = useState(null); + const [isSaving, setIsSaving] = useState(false); + + useEffect(() => { + void getExtensionSettings().then((settings) => { + setApiBaseUrl(settings.apiBaseUrl); + setAutomaticChecksEnabled(settings.automaticChecksEnabled); + }); + }, []); + + async function handleSubmit(event: React.FormEvent): Promise { + event.preventDefault(); + setIsSaving(true); + setStatus(null); + + try { + const normalized = normalizeApiBaseUrl(apiBaseUrl); + const granted = await requestBackendPermission(normalized); + if (!granted) { + throw new Error("Browser permission for this backend origin was not granted."); + } + + const saved = await saveExtensionSettings({ + apiBaseUrl: normalized, + automaticChecksEnabled, + }); + setApiBaseUrl(saved.apiBaseUrl); + setAutomaticChecksEnabled(saved.automaticChecksEnabled); + setStatus({ tone: "success", message: "Settings saved." }); + } catch (error) { + setStatus({ + tone: "error", + message: error instanceof Error ? error.message : "Unable to save settings.", + }); + } finally { + setIsSaving(false); + } + } + + return ( +
+
+
+ +
+

Conspiracy Alert

+

Backend settings

+
+
+ +
void handleSubmit(event)}> + + + + + {status ? ( +

+ {status.message} +

+ ) : null} + +
+ + +
+
+
+
+ ); +} + +function requestBackendPermission(apiBaseUrl: string): Promise { + if (!chrome.permissions?.request) { + return Promise.resolve(true); + } + + return new Promise((resolve) => { + chrome.permissions.request({ origins: [getBackendOriginPattern(apiBaseUrl)] }, (granted) => { + if (chrome.runtime.lastError) { + resolve(false); + return; + } + resolve(granted); + }); + }); +} + +export default SettingsPage; diff --git a/client/src/apiTypes.ts b/client/src/apiTypes.ts new file mode 100644 index 0000000..9f0658c --- /dev/null +++ b/client/src/apiTypes.ts @@ -0,0 +1,7 @@ +import type { components } from "./generated/apiTypes"; + +export type EvaluationStatus = components["schemas"]["EvaluateUrlResponse"]["status"]; +export type SignalSeverity = components["schemas"]["SignalResponse"]["severity"]; +export type ApiSignal = components["schemas"]["SignalResponse"]; +export type ApiMatch = components["schemas"]["MatchResponse"]; +export type EvaluateUrlResponse = components["schemas"]["EvaluateUrlResponse"]; diff --git a/client/src/backendClient.test.ts b/client/src/backendClient.test.ts new file mode 100644 index 0000000..d793e53 --- /dev/null +++ b/client/src/backendClient.test.ts @@ -0,0 +1,52 @@ +import { describe, expect, it, vi } from "vitest"; + +import { evaluateUrl } from "./backendClient"; + +describe("evaluateUrl", () => { + it("posts to the backend evaluate-url endpoint", async () => { + const fetchImpl = vi.fn().mockResolvedValue({ + ok: true, + json: async () => ({ + status: "matched", + evaluation: { + raw_url: "https://zerohedge.com/covid-19/example", + normalized_url: "zerohedge.com/covid-19/example", + matches: [], + }, + normalized_url: "zerohedge.com/covid-19/example", + matches: [], + signals: [ + { + type: "known_conspiracy_url", + severity: "high", + count: 1, + message: "Questionable URL.", + }, + ], + }), + }); + + const result = await evaluateUrl("https://zerohedge.com/covid-19/example", { + apiBaseUrl: "http://127.0.0.1:8000", + fetchImpl, + }); + + expect(fetchImpl).toHaveBeenCalledWith( + "http://127.0.0.1:8000/v1/evaluate-url", + expect.objectContaining({ + method: "POST", + body: JSON.stringify({ url: "https://zerohedge.com/covid-19/example" }), + }), + ); + expect(result.status).toBe("matched"); + expect(result.signals[0].type).toBe("known_conspiracy_url"); + }); + + it("raises a useful error when the backend rejects the request", async () => { + const fetchImpl = vi.fn().mockResolvedValue({ ok: false, status: 503, statusText: "Down" }); + + await expect(evaluateUrl("https://example.test", { fetchImpl })).rejects.toThrow( + "Channel Checker backend returned 503 Down", + ); + }); +}); diff --git a/client/src/backendClient.ts b/client/src/backendClient.ts new file mode 100644 index 0000000..42cd723 --- /dev/null +++ b/client/src/backendClient.ts @@ -0,0 +1,26 @@ +import type { EvaluateUrlResponse } from "./apiTypes"; +import { DEFAULT_API_BASE_URL, normalizeApiBaseUrl } from "./settings"; + +export interface EvaluateUrlOptions { + apiBaseUrl?: string; + fetchImpl?: typeof fetch; +} + +export async function evaluateUrl( + url: string, + options: EvaluateUrlOptions = {}, +): Promise { + const apiBaseUrl = normalizeApiBaseUrl(options.apiBaseUrl ?? DEFAULT_API_BASE_URL); + const fetchImpl = options.fetchImpl ?? fetch; + const response = await fetchImpl(`${apiBaseUrl}/v1/evaluate-url`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ url }), + }); + + if (!response.ok) { + throw new Error(`Channel Checker backend returned ${response.status} ${response.statusText}`); + } + + return (await response.json()) as EvaluateUrlResponse; +} diff --git a/client/src/background.ts b/client/src/background.ts index 0015e20..3cdd97d 100644 --- a/client/src/background.ts +++ b/client/src/background.ts @@ -1,32 +1,91 @@ -export {}; +import { evaluateUrl } from "./backendClient"; +import { + HIDE_PAGE_ALERT_MESSAGE, + SHOW_PAGE_ALERT_MESSAGE, + isEvaluateUrlMessage, + type EvaluateUrlMessageResponse, + type PageAlertMessage, +} from "./messages"; +import { buildPageAlert } from "./pageAlert"; +import type { StoredError, StoredEvaluation } from "./popupState"; +import { getExtensionSettings } from "./settings"; -chrome.tabs.onUpdated.addListener((_tabId, changeInfo, tab) => { - if (changeInfo.status === "complete" && tab.url) { - checkLink(tab.url); +const STORAGE_KEY = "lastEvaluation"; +const ERROR_KEY = "lastError"; + +chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { + if (!isEvaluateUrlMessage(message)) { + return false; } + + const tabId = message.tabId ?? sender.tab?.id; + void checkTabUrl(message.url, tabId, message.source ?? "manual").then(() => { + const response: EvaluateUrlMessageResponse = { ok: true }; + sendResponse(response); + }); + return true; }); -async function checkLink(url: string) { +async function checkTabUrl( + url: string, + tabId: number | undefined, + source: "automatic" | "manual", +): Promise { try { - const response = await fetch("http://127.0.0.1:5000", { - method: "POST", - headers: { - "Content-Type": "application/json" - }, - body: JSON.stringify({ "url": url }) - }); - if (response.ok) { - const data = await response.json(); - chrome.storage.local.set({ response: data }, () => { - console.log("Response saved:", data); - }); - if (data.conspiracy_resource_dataset === true || data.conspiracy_url_dataset === true) { - chrome.action.openPopup(); - } - } else { - console.error("Error checking link:", response.statusText); + const settings = await getExtensionSettings(); + if (source === "automatic" && !settings.automaticChecksEnabled) { + setBadge(tabId, "", "#b91c1c"); + sendPageAlert(tabId, null); + return; } + + const evaluation = await evaluateUrl(url, { apiBaseUrl: settings.apiBaseUrl }); + const stored: StoredEvaluation = { + ...evaluation, + url, + checkedAt: new Date().toISOString(), + }; + + await setLocalStorage({ [STORAGE_KEY]: stored, [ERROR_KEY]: null }); + setBadge(tabId, evaluation.status === "matched" ? "!" : "", "#b91c1c"); + sendPageAlert(tabId, buildPageAlert(evaluation, url)); } catch (error) { - console.error("Error checking link:", error); + const storedError: StoredError = { + url, + message: error instanceof Error ? error.message : "Unknown backend error", + checkedAt: new Date().toISOString(), + }; + await setLocalStorage({ + [ERROR_KEY]: storedError, + }); + setBadge(tabId, "?", "#92400e"); + sendPageAlert(tabId, null); } -} \ No newline at end of file +} + +function setLocalStorage(items: Record): Promise { + return new Promise((resolve) => { + chrome.storage.local.set(items, () => resolve()); + }); +} + +function setBadge(tabId: number | undefined, text: string, color: string): void { + const target = tabId === undefined ? {} : { tabId }; + chrome.action.setBadgeText({ ...target, text }); + chrome.action.setBadgeBackgroundColor({ ...target, color }); +} + +function sendPageAlert(tabId: number | undefined, alert: ReturnType): void { + if (tabId === undefined) { + return; + } + + const message: PageAlertMessage = + alert === null + ? { type: HIDE_PAGE_ALERT_MESSAGE } + : { type: SHOW_PAGE_ALERT_MESSAGE, alert }; + + chrome.tabs.sendMessage(tabId, message, () => { + void chrome.runtime.lastError; + }); +} diff --git a/client/src/contentScript.ts b/client/src/contentScript.ts new file mode 100644 index 0000000..8af797f --- /dev/null +++ b/client/src/contentScript.ts @@ -0,0 +1,158 @@ +import { + EVALUATE_URL_MESSAGE, + HIDE_PAGE_ALERT_MESSAGE, + SHOW_PAGE_ALERT_MESSAGE, + isHttpUrl, + isPageAlertMessage, + type PageAlertMessage, +} from "./messages"; +import type { PageAlert } from "./pageAlert"; + +const HOST_ID = "conspiracy-alert-banner-host"; + +chrome.runtime.onMessage.addListener((message: unknown) => { + if (!isPageAlertMessage(message)) { + return false; + } + + handlePageAlertMessage(message); + return false; +}); + +requestCurrentPageEvaluation(); + +function requestCurrentPageEvaluation(): void { + const url = window.location.href; + if (!isHttpUrl(url)) { + return; + } + + chrome.runtime.sendMessage({ type: EVALUATE_URL_MESSAGE, url, source: "automatic" }, () => { + void chrome.runtime.lastError; + }); +} + +function handlePageAlertMessage(message: PageAlertMessage): void { + if (message.type === HIDE_PAGE_ALERT_MESSAGE) { + removeAlert(); + return; + } + + if (message.type === SHOW_PAGE_ALERT_MESSAGE) { + renderAlert(message.alert); + } +} + +function renderAlert(alert: PageAlert): void { + const host = ensureHost(); + const root = host.shadowRoot ?? host.attachShadow({ mode: "open" }); + root.replaceChildren(buildAlertElement(alert)); +} + +function buildAlertElement(alert: PageAlert): HTMLElement { + const wrapper = document.createElement("div"); + wrapper.setAttribute("role", "status"); + wrapper.innerHTML = ` + + + `; + + wrapper.querySelector(".title")!.textContent = alert.title; + wrapper.querySelector(".message")!.textContent = alert.message; + wrapper.querySelector(".url")!.textContent = alert.normalizedUrl; + wrapper.querySelector(".signals")!.textContent = alert.signalTypes.join(", "); + wrapper.querySelector(".close")!.addEventListener("click", removeAlert); + + return wrapper; +} + +function ensureHost(): HTMLElement { + const existing = document.getElementById(HOST_ID); + if (existing) { + return existing; + } + + const host = document.createElement("div"); + host.id = HOST_ID; + document.documentElement.append(host); + return host; +} + +function removeAlert(): void { + document.getElementById(HOST_ID)?.remove(); +} diff --git a/client/src/generated/apiTypes.ts b/client/src/generated/apiTypes.ts new file mode 100644 index 0000000..2b2dbeb --- /dev/null +++ b/client/src/generated/apiTypes.ts @@ -0,0 +1,368 @@ +/** + * This file was auto-generated by openapi-typescript. + * Do not make direct changes to the file. + */ + +export interface paths { + "/health": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + /** Health */ + get: operations["health_health_get"]; + put?: never; + post?: never; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/evaluate-channel": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Evaluate Channel Route */ + post: operations["evaluate_channel_route_v1_evaluate_channel_post"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/evaluate-text": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Evaluate Text Route */ + post: operations["evaluate_text_route_v1_evaluate_text_post"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/evaluate-url": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** Evaluate Url Route */ + post: operations["evaluate_url_route_v1_evaluate_url_post"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; +} +export type webhooks = Record; +export interface components { + schemas: { + /** ChannelAnalysisResponse */ + ChannelAnalysisResponse: { + /** + * Analyzed At + * Format: date-time + */ + analyzed_at: string; + /** Channel Id */ + channel_id?: number | null; + /** Evidence Urls */ + evidence_urls: string[]; + /** Handle */ + handle: string; + /** Label */ + label: string; + /** Signal Counts */ + signal_counts: { + [key: string]: number; + }; + /** Title */ + title?: string | null; + /** Url Count */ + url_count: number; + }; + /** EvaluateChannelRequest */ + EvaluateChannelRequest: { + /** + * Force Refresh + * @default false + */ + force_refresh: boolean; + /** Handle */ + handle: string; + /** + * Max Messages + * @default 200 + */ + max_messages: number; + }; + /** EvaluateChannelResponse */ + EvaluateChannelResponse: { + analysis?: components["schemas"]["ChannelAnalysisResponse"] | null; + /** Handle */ + handle: string; + /** + * Message + * @default + */ + message: string; + /** + * Status + * @enum {string} + */ + status: "cached" | "analyzed" | "unavailable" | "not_found"; + }; + /** EvaluateTextRequest */ + EvaluateTextRequest: { + /** Text */ + text: string; + }; + /** EvaluateTextResponse */ + EvaluateTextResponse: { + /** Evaluations */ + evaluations: components["schemas"]["EvaluationResponse"][]; + /** Signals */ + signals: components["schemas"]["SignalResponse"][]; + /** + * Status + * @enum {string} + */ + status: "matched" | "no_match"; + /** Urls */ + urls: string[]; + }; + /** EvaluateUrlRequest */ + EvaluateUrlRequest: { + /** Url */ + url: string; + }; + /** EvaluateUrlResponse */ + EvaluateUrlResponse: { + evaluation: components["schemas"]["EvaluationResponse"]; + /** Matches */ + matches: components["schemas"]["MatchResponse"][]; + /** Normalized Url */ + normalized_url: string; + /** Signals */ + signals: components["schemas"]["SignalResponse"][]; + /** + * Status + * @enum {string} + */ + status: "matched" | "no_match"; + }; + /** EvaluationResponse */ + EvaluationResponse: { + /** Matches */ + matches: components["schemas"]["MatchResponse"][]; + /** Normalized Url */ + normalized_url: string; + /** Raw Url */ + raw_url: string; + }; + /** HTTPValidationError */ + HTTPValidationError: { + /** Detail */ + detail?: components["schemas"]["ValidationError"][]; + }; + /** MatchResponse */ + MatchResponse: { + /** Count */ + count: number; + /** Message */ + message: string; + /** Platform */ + platform?: string | null; + /** Resource */ + resource?: string | null; + /** + * Severity + * @enum {string} + */ + severity: "high" | "warning" | "info"; + /** + * Type + * @enum {string} + */ + type: "known_conspiracy_url" | "resource_dataset" | "affiliate_url" | "donation_url" | "crowdfunding_url" | "marketplace_url" | "commerce_path_url"; + }; + /** SignalResponse */ + SignalResponse: { + /** Count */ + count: number; + /** Message */ + message: string; + /** + * Severity + * @enum {string} + */ + severity: "high" | "warning" | "info"; + /** + * Type + * @enum {string} + */ + type: "known_conspiracy_url" | "resource_dataset" | "affiliate_url" | "donation_url" | "crowdfunding_url" | "marketplace_url" | "commerce_path_url"; + }; + /** ValidationError */ + ValidationError: { + /** Context */ + ctx?: Record; + /** Input */ + input?: unknown; + /** Location */ + loc: (string | number)[]; + /** Message */ + msg: string; + /** Error Type */ + type: string; + }; + }; + responses: never; + parameters: never; + requestBodies: never; + headers: never; + pathItems: never; +} +export type $defs = Record; +export interface operations { + health_health_get: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": { + [key: string]: string; + }; + }; + }; + }; + }; + evaluate_channel_route_v1_evaluate_channel_post: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["EvaluateChannelRequest"]; + }; + }; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["EvaluateChannelResponse"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + evaluate_text_route_v1_evaluate_text_post: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["EvaluateTextRequest"]; + }; + }; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["EvaluateTextResponse"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + evaluate_url_route_v1_evaluate_url_post: { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["EvaluateUrlRequest"]; + }; + }; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["EvaluateUrlResponse"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; +} diff --git a/client/src/main.tsx b/client/src/main.tsx index b8e2097..4667e0d 100644 --- a/client/src/main.tsx +++ b/client/src/main.tsx @@ -1,13 +1,15 @@ -import React from "react" -import ReactDOM from "react-dom/client" -import App from "./App" +import React from "react"; +import ReactDOM from "react-dom/client"; -const root = document.createElement("div") -root.id = "root" -document.body.append(root) +import App from "./App"; +import "./main.css"; -ReactDOM.createRoot(root).render( - - - -) +const root = document.getElementById("root"); + +if (root) { + ReactDOM.createRoot(root).render( + + + , + ); +} diff --git a/client/src/messages.test.ts b/client/src/messages.test.ts new file mode 100644 index 0000000..64297b2 --- /dev/null +++ b/client/src/messages.test.ts @@ -0,0 +1,87 @@ +import { describe, expect, it } from "vitest"; + +import { + EVALUATE_URL_MESSAGE, + HIDE_PAGE_ALERT_MESSAGE, + SHOW_PAGE_ALERT_MESSAGE, + isEvaluateUrlMessage, + isPageAlertMessage, +} from "./messages"; + +describe("extension messages", () => { + it("accepts evaluate-url messages with an http URL", () => { + expect( + isEvaluateUrlMessage({ + type: EVALUATE_URL_MESSAGE, + url: "https://example.test/report", + tabId: 12, + source: "automatic", + }), + ).toBe(true); + expect( + isEvaluateUrlMessage({ + type: EVALUATE_URL_MESSAGE, + url: "https://example.test/report", + source: "manual", + }), + ).toBe(true); + }); + + it("rejects malformed evaluate-url messages", () => { + expect(isEvaluateUrlMessage({ type: EVALUATE_URL_MESSAGE, url: "chrome://extensions" })).toBe( + false, + ); + expect( + isEvaluateUrlMessage({ + type: EVALUATE_URL_MESSAGE, + url: "https://example.test", + tabId: -1, + }), + ).toBe(false); + expect( + isEvaluateUrlMessage({ + type: EVALUATE_URL_MESSAGE, + url: "https://example.test", + source: "timer", + }), + ).toBe(false); + expect(isEvaluateUrlMessage({ type: "unknown", url: "https://example.test" })).toBe(false); + }); +}); + +describe("page alert messages", () => { + it("accepts show and hide page-alert messages", () => { + expect( + isPageAlertMessage({ + type: SHOW_PAGE_ALERT_MESSAGE, + alert: { + severity: "high", + title: "Questionable resource detected", + message: "Questionable content.", + url: "https://example.test", + normalizedUrl: "example.test", + signalTypes: ["resource_dataset"], + }, + }), + ).toBe(true); + + expect(isPageAlertMessage({ type: HIDE_PAGE_ALERT_MESSAGE })).toBe(true); + }); + + it("rejects malformed page-alert messages", () => { + expect(isPageAlertMessage({ type: SHOW_PAGE_ALERT_MESSAGE })).toBe(false); + expect( + isPageAlertMessage({ + type: SHOW_PAGE_ALERT_MESSAGE, + alert: { + severity: "critical", + title: "Bad", + message: "Bad", + url: "https://example.test", + normalizedUrl: "example.test", + signalTypes: [], + }, + }), + ).toBe(false); + }); +}); diff --git a/client/src/messages.ts b/client/src/messages.ts new file mode 100644 index 0000000..e0190ef --- /dev/null +++ b/client/src/messages.ts @@ -0,0 +1,94 @@ +import type { PageAlert } from "./pageAlert"; + +export const EVALUATE_URL_MESSAGE = "evaluate-url"; +export const SHOW_PAGE_ALERT_MESSAGE = "show-page-alert"; +export const HIDE_PAGE_ALERT_MESSAGE = "hide-page-alert"; + +export interface EvaluateUrlMessage { + type: typeof EVALUATE_URL_MESSAGE; + url: string; + tabId?: number; + source?: "automatic" | "manual"; +} + +export interface EvaluateUrlMessageResponse { + ok: boolean; +} + +export interface ShowPageAlertMessage { + type: typeof SHOW_PAGE_ALERT_MESSAGE; + alert: PageAlert; +} + +export interface HidePageAlertMessage { + type: typeof HIDE_PAGE_ALERT_MESSAGE; +} + +export type PageAlertMessage = ShowPageAlertMessage | HidePageAlertMessage; + +export function isEvaluateUrlMessage(value: unknown): value is EvaluateUrlMessage { + return ( + typeof value === "object" && + value !== null && + "type" in value && + "url" in value && + value.type === EVALUATE_URL_MESSAGE && + typeof value.url === "string" && + isHttpUrl(value.url) && + hasValidOptionalTabId(value as Record) && + hasValidOptionalSource(value as Record) + ); +} + +export function isPageAlertMessage(value: unknown): value is PageAlertMessage { + if (!isRecord(value) || typeof value["type"] !== "string") { + return false; + } + + if (value["type"] === HIDE_PAGE_ALERT_MESSAGE) { + return true; + } + + return value["type"] === SHOW_PAGE_ALERT_MESSAGE && isPageAlert(value["alert"]); +} + +export function isHttpUrl(value: string): boolean { + try { + const parsed = new URL(value); + return parsed.protocol === "http:" || parsed.protocol === "https:"; + } catch { + return false; + } +} + +function hasValidOptionalTabId(value: Record): boolean { + const tabId = value["tabId"]; + return ( + tabId === undefined || (typeof tabId === "number" && Number.isInteger(tabId) && tabId >= 0) + ); +} + +function hasValidOptionalSource(value: Record): boolean { + const source = value["source"]; + return source === undefined || source === "automatic" || source === "manual"; +} + +function isPageAlert(value: unknown): value is PageAlert { + if (!isRecord(value)) { + return false; + } + + return ( + (value["severity"] === "high" || value["severity"] === "warning") && + typeof value["title"] === "string" && + typeof value["message"] === "string" && + typeof value["url"] === "string" && + typeof value["normalizedUrl"] === "string" && + Array.isArray(value["signalTypes"]) && + value["signalTypes"].every((signalType) => typeof signalType === "string") + ); +} + +function isRecord(value: unknown): value is Record { + return typeof value === "object" && value !== null; +} diff --git a/client/src/optionsMain.tsx b/client/src/optionsMain.tsx new file mode 100644 index 0000000..e1a3b07 --- /dev/null +++ b/client/src/optionsMain.tsx @@ -0,0 +1,15 @@ +import React from "react"; +import ReactDOM from "react-dom/client"; + +import SettingsPage from "./SettingsPage"; +import "./main.css"; + +const root = document.getElementById("root"); + +if (root) { + ReactDOM.createRoot(root).render( + + + , + ); +} diff --git a/client/src/pageAlert.test.ts b/client/src/pageAlert.test.ts new file mode 100644 index 0000000..dff8c67 --- /dev/null +++ b/client/src/pageAlert.test.ts @@ -0,0 +1,75 @@ +import { describe, expect, it } from "vitest"; + +import { buildPageAlert } from "./pageAlert"; +import type { EvaluateUrlResponse } from "./apiTypes"; + +describe("buildPageAlert", () => { + it("returns null when the backend has no match", () => { + expect( + buildPageAlert( + { + status: "no_match", + evaluation: { + raw_url: "https://example.test", + normalized_url: "example.test", + matches: [], + }, + normalized_url: "example.test", + matches: [], + signals: [], + }, + "https://example.test", + ), + ).toBeNull(); + }); + + it("builds a high-severity page alert for dataset matches", () => { + const alert = buildPageAlert( + makeResponse({ + type: "resource_dataset", + severity: "high", + count: 1, + message: "Questionable content.", + }), + "https://zerohedge.com", + ); + + expect(alert).toEqual({ + severity: "high", + title: "Questionable resource detected", + message: "Questionable content.", + url: "https://zerohedge.com", + normalizedUrl: "zerohedge.com", + signalTypes: ["resource_dataset"], + }); + }); + + it("builds a warning page alert for monetization-only matches", () => { + const alert = buildPageAlert( + makeResponse({ + type: "affiliate_url", + severity: "warning", + count: 1, + message: "Presence of URLs with referral programs.", + }), + "https://amazon.com?tag=abc-20", + ); + + expect(alert?.severity).toBe("warning"); + expect(alert?.title).toBe("Monetization signal detected"); + }); +}); + +function makeResponse(signal: EvaluateUrlResponse["signals"][number]): EvaluateUrlResponse { + return { + status: "matched", + evaluation: { + raw_url: "https://zerohedge.com", + normalized_url: "zerohedge.com", + matches: [], + }, + normalized_url: "zerohedge.com", + matches: [], + signals: [signal], + }; +} diff --git a/client/src/pageAlert.ts b/client/src/pageAlert.ts new file mode 100644 index 0000000..9953342 --- /dev/null +++ b/client/src/pageAlert.ts @@ -0,0 +1,38 @@ +import type { ApiSignal, EvaluateUrlResponse, SignalSeverity } from "./apiTypes"; + +export interface PageAlert { + severity: Extract; + title: string; + message: string; + url: string; + normalizedUrl: string; + signalTypes: string[]; +} + +export function buildPageAlert( + evaluation: EvaluateUrlResponse, + url: string, +): PageAlert | null { + if (evaluation.status !== "matched" || evaluation.signals.length === 0) { + return null; + } + + const primarySignal = selectPrimarySignal(evaluation.signals); + const severity = primarySignal.severity === "high" ? "high" : "warning"; + + return { + severity, + title: + severity === "high" + ? "Questionable resource detected" + : "Monetization signal detected", + message: primarySignal.message, + url, + normalizedUrl: evaluation.normalized_url, + signalTypes: evaluation.signals.map((signal) => signal.type), + }; +} + +function selectPrimarySignal(signals: ApiSignal[]): ApiSignal { + return signals.find((signal) => signal.severity === "high") ?? signals[0]; +} diff --git a/client/src/popupState.test.ts b/client/src/popupState.test.ts new file mode 100644 index 0000000..15341e5 --- /dev/null +++ b/client/src/popupState.test.ts @@ -0,0 +1,92 @@ +import { describe, expect, it } from "vitest"; + +import { + buildPopupState, + selectActiveError, + selectActiveEvaluation, + type StoredError, + type StoredEvaluation, +} from "./popupState"; + +describe("buildPopupState", () => { + it("shows high severity matches as questionable", () => { + const state = buildPopupState({ + url: "https://zerohedge.com", + checkedAt: "2026-05-09T08:00:00.000Z", + status: "matched", + evaluation: { + raw_url: "https://zerohedge.com", + normalized_url: "zerohedge.com", + matches: [], + }, + normalized_url: "zerohedge.com", + matches: [], + signals: [ + { + type: "known_conspiracy_url", + severity: "high", + count: 1, + message: "Questionable URL.", + }, + ], + }); + + expect(state.tone).toBe("danger"); + expect(state.title).toBe("Questionable URL detected"); + }); + + it("shows no-match results neutrally", () => { + const state = buildPopupState({ + url: "https://example.test", + checkedAt: "2026-05-09T08:00:00.000Z", + status: "no_match", + evaluation: { + raw_url: "https://example.test", + normalized_url: "example.test", + matches: [], + }, + normalized_url: "example.test", + matches: [], + signals: [], + }); + + expect(state.tone).toBe("neutral"); + expect(state.title).toBe("No dataset match"); + }); +}); + +describe("active popup storage selectors", () => { + it("selects the stored evaluation only when it belongs to the active tab", () => { + const evaluation = makeEvaluation("https://example.test/report/"); + + expect(selectActiveEvaluation(evaluation, "https://example.test/report")).toBe(evaluation); + expect(selectActiveEvaluation(evaluation, "https://other.test/report")).toBeNull(); + }); + + it("selects backend errors only when they belong to the active tab", () => { + const error: StoredError = { + url: "https://example.test/report", + message: "Backend unavailable", + checkedAt: "2026-05-09T08:00:00.000Z", + }; + + expect(selectActiveError(error, "https://example.test/report/")).toBe(error); + expect(selectActiveError(error, "https://other.test/report")).toBeNull(); + }); +}); + +function makeEvaluation(url: string): StoredEvaluation { + return { + url, + checkedAt: "2026-05-09T08:00:00.000Z", + status: "no_match", + evaluation: { + raw_url: url, + normalized_url: "example.test/report", + matches: [], + }, + normalized_url: "example.test/report", + matches: [], + signals: [], + }; +} diff --git a/client/src/popupState.ts b/client/src/popupState.ts new file mode 100644 index 0000000..b076478 --- /dev/null +++ b/client/src/popupState.ts @@ -0,0 +1,95 @@ +import type { EvaluateUrlResponse } from "./apiTypes"; + +export type PopupTone = "danger" | "warning" | "neutral" | "loading" | "error"; + +export interface StoredEvaluation extends EvaluateUrlResponse { + url: string; + checkedAt: string; +} + +export interface StoredError { + url: string; + message: string; + checkedAt: string; +} + +export interface PopupState { + tone: PopupTone; + title: string; + description: string; +} + +export function buildPopupState(evaluation: StoredEvaluation | null): PopupState { + if (evaluation === null) { + return { + tone: "loading", + title: "Waiting for analysis", + description: "Open a tab to evaluate its URL.", + }; + } + + if (evaluation.status === "no_match") { + return { + tone: "neutral", + title: "No dataset match", + description: "The current URL did not match the configured datasets.", + }; + } + + if (evaluation.signals.some((signal) => signal.severity === "high")) { + return { + tone: "danger", + title: "Questionable URL detected", + description: evaluation.signals[0].message, + }; + } + + return { + tone: "warning", + title: "Monetization signal detected", + description: evaluation.signals[0]?.message ?? "Review this URL before sharing.", + }; +} + +export function selectActiveEvaluation( + storedEvaluation: StoredEvaluation | null, + currentUrl: string | null, +): StoredEvaluation | null { + if (storedEvaluation === null || currentUrl === null) { + return null; + } + + return urlsMatch(storedEvaluation.url, currentUrl) ? storedEvaluation : null; +} + +export function selectActiveError( + storedError: StoredError | null, + currentUrl: string | null, +): StoredError | null { + if (storedError === null || currentUrl === null) { + return null; + } + + return urlsMatch(storedError.url, currentUrl) ? storedError : null; +} + +export function isStoredError(value: unknown): value is StoredError { + return ( + typeof value === "object" && + value !== null && + "url" in value && + "message" in value && + "checkedAt" in value && + typeof value.url === "string" && + typeof value.message === "string" && + typeof value.checkedAt === "string" + ); +} + +function urlsMatch(left: string, right: string): boolean { + return stripTrailingSlash(left) === stripTrailingSlash(right); +} + +function stripTrailingSlash(value: string): string { + return value.replace(/\/+$/, ""); +} diff --git a/client/src/settings.test.ts b/client/src/settings.test.ts new file mode 100644 index 0000000..ca719a5 --- /dev/null +++ b/client/src/settings.test.ts @@ -0,0 +1,99 @@ +import { describe, expect, it } from "vitest"; + +import { + DEFAULT_API_BASE_URL, + SETTINGS_KEY, + getBackendOriginPattern, + getExtensionSettings, + normalizeApiBaseUrl, + saveExtensionSettings, + type SettingsStorage, +} from "./settings"; + +describe("extension settings", () => { + it("returns the default backend URL when storage is empty", async () => { + const settings = await getExtensionSettings(createMemoryStorage()); + + expect(settings.apiBaseUrl).toBe(DEFAULT_API_BASE_URL); + expect(settings.automaticChecksEnabled).toBe(true); + }); + + it("normalizes stored backend URLs", async () => { + const settings = await getExtensionSettings( + createMemoryStorage({ + [SETTINGS_KEY]: { apiBaseUrl: " https://checker.example/api/ " }, + }), + ); + + expect(settings.apiBaseUrl).toBe("https://checker.example/api"); + expect(settings.automaticChecksEnabled).toBe(true); + }); + + it("loads stored automatic check preferences", async () => { + const settings = await getExtensionSettings( + createMemoryStorage({ + [SETTINGS_KEY]: { + apiBaseUrl: "https://checker.example/api", + automaticChecksEnabled: false, + }, + }), + ); + + expect(settings.automaticChecksEnabled).toBe(false); + }); + + it("falls back to the default backend URL when stored settings are invalid", async () => { + const settings = await getExtensionSettings( + createMemoryStorage({ + [SETTINGS_KEY]: { apiBaseUrl: "chrome://extensions" }, + }), + ); + + expect(settings.apiBaseUrl).toBe(DEFAULT_API_BASE_URL); + }); + + it("saves validated backend URLs", async () => { + const storage = createMemoryStorage(); + + const saved = await saveExtensionSettings( + { apiBaseUrl: "http://localhost:9000/", automaticChecksEnabled: false }, + storage, + ); + const loaded = await getExtensionSettings(storage); + + expect(saved.apiBaseUrl).toBe("http://localhost:9000"); + expect(saved.automaticChecksEnabled).toBe(false); + expect(loaded.apiBaseUrl).toBe("http://localhost:9000"); + expect(loaded.automaticChecksEnabled).toBe(false); + }); + + it("rejects unsupported backend URL schemes", async () => { + expect(() => normalizeApiBaseUrl("ftp://checker.example")).toThrow( + "Backend URL must use http or https.", + ); + }); + + it("builds an optional host permission origin pattern", () => { + expect(getBackendOriginPattern("https://checker.example/api")).toBe( + "https://checker.example/*", + ); + }); +}); + +function createMemoryStorage(initial: Record = {}): SettingsStorage { + const store = { ...initial }; + + return { + get(keys, callback) { + const result: Record = {}; + for (const key of keys) { + result[key] = store[key]; + } + callback(result); + }, + set(items, callback) { + Object.assign(store, items); + callback?.(); + }, + }; +} diff --git a/client/src/settings.ts b/client/src/settings.ts new file mode 100644 index 0000000..137b729 --- /dev/null +++ b/client/src/settings.ts @@ -0,0 +1,96 @@ +export const DEFAULT_API_BASE_URL = "http://127.0.0.1:8000"; +export const SETTINGS_KEY = "settings"; + +export interface ExtensionSettings { + apiBaseUrl: string; + automaticChecksEnabled: boolean; +} + +export interface SettingsStorage { + get(keys: string[], callback: (items: Record) => void): void; + set(items: Record, callback?: () => void): void; +} + +export async function getExtensionSettings( + storageArea: SettingsStorage = chrome.storage.sync, +): Promise { + return new Promise((resolve) => { + storageArea.get([SETTINGS_KEY], (result) => { + resolve(readStoredSettings(result[SETTINGS_KEY])); + }); + }); +} + +export async function saveExtensionSettings( + settings: ExtensionSettings, + storageArea: SettingsStorage = chrome.storage.sync, +): Promise { + const normalized = { + apiBaseUrl: normalizeApiBaseUrl(settings.apiBaseUrl), + automaticChecksEnabled: settings.automaticChecksEnabled, + }; + + return new Promise((resolve) => { + storageArea.set({ [SETTINGS_KEY]: normalized }, () => resolve(normalized)); + }); +} + +export function normalizeApiBaseUrl(value: string): string { + const trimmed = value.trim(); + if (!trimmed) { + throw new Error("Backend URL is required."); + } + + let parsed: URL; + try { + parsed = new URL(trimmed); + } catch { + throw new Error("Backend URL must be a valid URL."); + } + + if (parsed.protocol !== "http:" && parsed.protocol !== "https:") { + throw new Error("Backend URL must use http or https."); + } + + if (parsed.search || parsed.hash) { + throw new Error("Backend URL must not include query parameters or fragments."); + } + + return parsed.toString().replace(/\/+$/, ""); +} + +export function getBackendOriginPattern(apiBaseUrl: string): string { + return `${new URL(normalizeApiBaseUrl(apiBaseUrl)).origin}/*`; +} + +function readStoredSettings(value: unknown): ExtensionSettings { + if (!isSettingsRecord(value)) { + return defaultSettings(); + } + + try { + return { + apiBaseUrl: normalizeApiBaseUrl(value.apiBaseUrl), + automaticChecksEnabled: + typeof value.automaticChecksEnabled === "boolean" ? value.automaticChecksEnabled : true, + }; + } catch { + return defaultSettings(); + } +} + +function isSettingsRecord(value: unknown): value is ExtensionSettings { + return ( + typeof value === "object" && + value !== null && + "apiBaseUrl" in value && + typeof value.apiBaseUrl === "string" + ); +} + +function defaultSettings(): ExtensionSettings { + return { + apiBaseUrl: DEFAULT_API_BASE_URL, + automaticChecksEnabled: true, + }; +} diff --git a/client/src/versionConsistency.test.ts b/client/src/versionConsistency.test.ts new file mode 100644 index 0000000..bd2f2f6 --- /dev/null +++ b/client/src/versionConsistency.test.ts @@ -0,0 +1,10 @@ +import { describe, expect, it } from "vitest"; + +import manifest from "../manifest.json"; +import packageJson from "../package.json"; + +describe("extension version metadata", () => { + it("keeps package and manifest versions aligned", () => { + expect(packageJson.version).toBe(manifest.version); + }); +}); diff --git a/client/tsconfig.json b/client/tsconfig.json index 9f290c3..9b431e4 100644 --- a/client/tsconfig.json +++ b/client/tsconfig.json @@ -5,12 +5,12 @@ "lib": ["DOM", "DOM.Iterable", "ESNext", "ES2020"], "allowJs": false, "skipLibCheck": true, - "esModuleInterop": false, + "esModuleInterop": true, "allowSyntheticDefaultImports": true, "strict": true, "forceConsistentCasingInFileNames": true, "module": "ESNext", - "moduleResolution": "Node", + "moduleResolution": "Bundler", "resolveJsonModule": true, "isolatedModules": true, "noEmit": true, @@ -19,4 +19,4 @@ }, "include": ["src"], "references": [{ "path": "./tsconfig.node.json" }] -} \ No newline at end of file +} diff --git a/client/tsconfig.node.json b/client/tsconfig.node.json index e993792..1515fd5 100644 --- a/client/tsconfig.node.json +++ b/client/tsconfig.node.json @@ -2,7 +2,7 @@ "compilerOptions": { "composite": true, "module": "esnext", - "moduleResolution": "node" + "moduleResolution": "Bundler" }, "include": ["vite.config.ts"] } diff --git a/client/vite.config.ts b/client/vite.config.ts index 3654e41..682481f 100644 --- a/client/vite.config.ts +++ b/client/vite.config.ts @@ -1,8 +1,11 @@ -import { defineConfig } from "vite"; +import { defineConfig } from "vitest/config"; import react from "@vitejs/plugin-react"; import { crx } from "@crxjs/vite-plugin"; -import manifest from "./manifest.json" assert { type: "json" } +import manifest from "./manifest.json"; export default defineConfig({ plugins: [react(), crx({ manifest })], -}); \ No newline at end of file + test: { + exclude: ["e2e/**", "node_modules/**", "dist/**"], + }, +}); diff --git a/server/.gitignore b/server/.gitignore deleted file mode 100644 index 6693889..0000000 --- a/server/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -data -venv \ No newline at end of file diff --git a/server/requirements.txt b/server/requirements.txt deleted file mode 100644 index e3d38a6..0000000 --- a/server/requirements.txt +++ /dev/null @@ -1,44 +0,0 @@ -asttokens==3.0.0 -blinker==1.9.0 -certifi==2024.12.14 -charset-normalizer==3.4.1 -click==8.1.8 -comm==0.2.2 -debugpy==1.8.11 -decorator==5.1.1 -executing==2.1.0 -Flask==3.1.0 -idna==3.10 -ipykernel==6.29.5 -ipython==8.31.0 -itsdangerous==2.2.0 -jedi==0.19.2 -Jinja2==3.1.5 -jupyter_client==8.6.3 -jupyter_core==5.7.2 -MarkupSafe==3.0.2 -matplotlib-inline==0.1.7 -nest-asyncio==1.6.0 -numpy==2.2.1 -packaging==24.2 -pandas==2.2.3 -parso==0.8.4 -pexpect==4.9.0 -platformdirs==4.3.6 -prompt_toolkit==3.0.48 -psutil==6.1.1 -ptyprocess==0.7.0 -pure_eval==0.2.3 -Pygments==2.19.1 -python-dateutil==2.9.0.post0 -pytz==2024.2 -pyzmq==26.2.0 -requests==2.32.3 -six==1.17.0 -stack-data==0.6.3 -tornado==6.4.2 -traitlets==5.14.3 -tzdata==2024.2 -urllib3==2.3.0 -wcwidth==0.2.13 -Werkzeug==3.1.3 diff --git a/server/server.py b/server/server.py deleted file mode 100644 index 29709ff..0000000 --- a/server/server.py +++ /dev/null @@ -1,91 +0,0 @@ -from flask import Flask, jsonify, request -import pandas as pd -import re -import os -import subprocess - -def populate_data_folder(data_folder): - if not os.path.exists(data_folder): - os.makedirs(data_folder) - conspiracy_resource_dataset = "https://raw.githubusercontent.com/SystemsLab-Sapienza/conspiracy-dataset-telegram/main/conspiracy_resource_dataset.csv" - conspiracy_resource_dataset_supplementary_url = "https://raw.githubusercontent.com/SystemsLab-Sapienza/conspiracy-dataset-telegram/main/conspiracy_resource_dataset_supplementary.csv" - conspiracy_resource_dataset = pd.read_csv(conspiracy_resource_dataset) - conspiracy_resource_dataset_supplementary = pd.read_csv(conspiracy_resource_dataset_supplementary_url) - for platform in ["voat", "8kun", "reddit", "website", "youtube", "supplementary"]: - if platform != "supplementary": - tmp = conspiracy_resource_dataset[conspiracy_resource_dataset["platform"] == platform] - if platform == "voat": - tmp = tmp["resource"].apply(lambda x: f"voat.co/{x}") - if platform == "8kun": - tmp = tmp["resource"].apply(lambda x: f"8kun.top/{x}") - if platform == "reddit": - tmp = tmp["resource"].apply(lambda x: f"reddit.com/r/{x}") - if platform == "website": - tmp = tmp["resource"] - if platform == "youtube": - tmp = tmp["resource"] - if platform == "supplementary": - tmp = conspiracy_resource_dataset_supplementary["youtube_video"].dropna() - tmp.to_csv(os.path.join(data_folder, f"{platform}.txt"), index=False, header=False) - -def clear_url(url): - # Remove the protocol - url = re.sub(r"^https?://", "", url) - # Remove the www - url = re.sub(r"^www\.", "", url) - # Strip the trailing slash - url = url.rstrip("/") - return url - -def contains_substring(string, filename): - # Execute the grep command - try: - # -F to treat the lines as fixed strings - # -q to not print output, only the exit code - # -f to specify the file containing the substrings - result = subprocess.run( - ["grep", "-F", "-i", "-f", filename], - input=string, - text=True - ) - # If the exit code is 0, it means a match was found - if result.returncode == 0: - return True - else: - return False - except Exception as e: - print(f"An error occurred: {e}") - return False - -populate_data_folder("data") -conspiracy_url_dataset_url = "https://raw.githubusercontent.com/SystemsLab-Sapienza/conspiracy-dataset-telegram/main/conspiracy_url_dataset.csv" -conspiracy_url_dataset = pd.read_csv(conspiracy_url_dataset_url) - -app = Flask(__name__) - -@app.route("/", methods=["POST"]) -def url_evaluation(): - url = request.json["url"] - result = { - "url": url, - "conspiracy_resource_dataset": False, - "conspiracy_url_dataset": False - } - url = clear_url(url) - if url in conspiracy_url_dataset["url"].values: - result["conspiracy_url_dataset"] = True - for platform in ["voat", "8kun", "reddit", "website", "youtube", "supplementary"]: - filename = f"data/{platform}.txt" - if contains_substring(url, filename): - result["conspiracy_resource_dataset"] = True - print( - "------------------\n" - f"URL: {url}\n" - f"conspiracy_url_dataset: {result["conspiracy_url_dataset"]}\n" - f"conspiracy_resource_dataset: {result["conspiracy_resource_dataset"]}" - "\n------------------" - ) - return jsonify(result) - -if __name__ == "__main__": - app.run(debug=True) \ No newline at end of file