From ca229b889d8c03e80b187a28ca29a7084013baa7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kevin=20Mu=C3=B1oz?= Date: Wed, 25 Mar 2026 08:39:51 +0100 Subject: [PATCH 1/2] feat(playground): add initial playground scaffold (#601) This PR adds the initial playground/ base for YARA-X. This follows the discussion in #592, where the idea of having an official browser playground was raised. The scope here is intentionally small. The goal is to add the TypeScript, Lit, CSS, and Vite base for the playground, get the UI in place, and make sure it compiles cleanly. This is still a 0.0.0 version and it is not expected to work end-to-end yet. What is included: the initial playground UI and layout Monaco-based editor integration service interfaces/contracts for the engine and the language server placeholder adapters with TODO(@kevinmuoz) markers for the follow-up integration work Vite configuration already prepared for future GH Pages deployment What is intentionally not included yet: no working engine integration yet no working language server integration yet no GH Actions for deployment yet The current contracts are meant to unblock the next steps once the engine side is available through #598 and once we decide how the browser language server should live. Right now, a separate package for the language server still feels like the most logical option, but that part is not fully defined yet. I left the missing pieces explicitly marked in the code with TODO(@kevinmuoz) so they are easy to track and connect in follow-up PRs. A couple of implementation notes: even though the app uses Web Components and the overall build is fairly light, Monaco still adds noticeable weight to the bundle. The current load time is still really good, and I already disabled services that are not needed for the playground, but this is still something worth revisiting and optimizing later. some components intentionally avoid Shadow DOM. That gives up one of the nice benefits of Web Components, but in practice it makes Monaco integration much smoother, especially for overlays like popups, hover widgets, and autocomplete. I wanted to keep this PR small and focused on the playground base first, and add deployment/workflows only once the moving pieces are actually connected. It would also be possible to check in wasm output files and get to a quick 0.0.1 sooner, but I think with a bit more work we can automate that properly and keep the setup cleaner. --- playground/.gitignore | 24 + playground/.nvmrc | 1 + playground/README.MD | 64 + playground/index.html | 22 + playground/package.json | 29 + playground/pnpm-lock.yaml | 2349 +++++++++++++++++ playground/public/apple-touch-icon.png | Bin 0 -> 4999 bytes playground/public/favicon-96x96.png | Bin 0 -> 2495 bytes playground/public/favicon.ico | Bin 0 -> 15086 bytes playground/public/favicon.svg | 1 + .../public/web-app-manifest-192x192.png | Bin 0 -> 5353 bytes .../public/web-app-manifest-512x512.png | Bin 0 -> 18176 bytes playground/public/yara-mark.svg | 49 + playground/src/app/yara-playground-app.ts | 361 +++ playground/src/components/yara-file.ts | 246 ++ .../src/components/yara-result-panel.ts | 236 ++ playground/src/components/yara-status-bar.ts | 138 + .../controllers/split-resize-controller.ts | 76 + playground/src/data/sample.ts | 14 + playground/src/editor/yara-monaco.ts | 337 +++ playground/src/main.ts | 3 + playground/src/results/summarize-result.ts | 144 + playground/src/services/wasm-yara-engine.ts | 60 + .../src/services/wasm-yara-language-server.ts | 23 + playground/src/services/yara-engine.ts | 48 + .../src/services/yara-language-server.ts | 21 + playground/src/styles/base.css | 33 + playground/src/styles/playground.css | 918 +++++++ playground/src/types/execution.ts | 16 + playground/src/types/result-summary.ts | 28 + playground/src/types/service-status.ts | 1 + playground/src/workers/yara-ls.worker.ts | 18 + playground/tsconfig.json | 27 + playground/vite.config.ts | 29 + 34 files changed, 5316 insertions(+) create mode 100644 playground/.gitignore create mode 100644 playground/.nvmrc create mode 100644 playground/README.MD create mode 100644 playground/index.html create mode 100644 playground/package.json create mode 100644 playground/pnpm-lock.yaml create mode 100644 playground/public/apple-touch-icon.png create mode 100644 playground/public/favicon-96x96.png create mode 100644 playground/public/favicon.ico create mode 100644 playground/public/favicon.svg create mode 100644 playground/public/web-app-manifest-192x192.png create mode 100644 playground/public/web-app-manifest-512x512.png create mode 100644 playground/public/yara-mark.svg create mode 100644 playground/src/app/yara-playground-app.ts create mode 100644 playground/src/components/yara-file.ts create mode 100644 playground/src/components/yara-result-panel.ts create mode 100644 playground/src/components/yara-status-bar.ts create mode 100644 playground/src/controllers/split-resize-controller.ts create mode 100644 playground/src/data/sample.ts create mode 100644 playground/src/editor/yara-monaco.ts create mode 100644 playground/src/main.ts create mode 100644 playground/src/results/summarize-result.ts create mode 100644 playground/src/services/wasm-yara-engine.ts create mode 100644 playground/src/services/wasm-yara-language-server.ts create mode 100644 playground/src/services/yara-engine.ts create mode 100644 playground/src/services/yara-language-server.ts create mode 100644 playground/src/styles/base.css create mode 100644 playground/src/styles/playground.css create mode 100644 playground/src/types/execution.ts create mode 100644 playground/src/types/result-summary.ts create mode 100644 playground/src/types/service-status.ts create mode 100644 playground/src/workers/yara-ls.worker.ts create mode 100644 playground/tsconfig.json create mode 100644 playground/vite.config.ts diff --git a/playground/.gitignore b/playground/.gitignore new file mode 100644 index 000000000..a547bf36d --- /dev/null +++ b/playground/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/playground/.nvmrc b/playground/.nvmrc new file mode 100644 index 000000000..4555c44c2 --- /dev/null +++ b/playground/.nvmrc @@ -0,0 +1 @@ +22.22.1 \ No newline at end of file diff --git a/playground/README.MD b/playground/README.MD new file mode 100644 index 000000000..85bfb8356 --- /dev/null +++ b/playground/README.MD @@ -0,0 +1,64 @@ +# YARA-X Playground + +This folder contains the browser playground for YARA-X. The goal of the +playground is to provide a lightweight web interface for writing, formatting, +and eventually testing YARA rules directly in the browser. + +The current version is `0.0.0`. At this stage, the focus is on establishing the +front-end foundation of the playground: + +- TypeScript for the application code +- Lit for the UI +- Vite for development and builds +- Monaco-based editor integration for the rule and sample panes + +This version is intentionally limited. It is expected to compile and provide +the basic UI structure, but it is not expected to be fully functional end to +end yet. + +## Requirements + +- Node `22.22.1` +- `pnpm` + +The Node version is pinned in [`.nvmrc`](./.nvmrc). + +## Getting started + +```bash +nvm use +pnpm install +pnpm dev +``` + +This starts the Vite development server. Open the local URL printed in the +terminal. + +## Scripts + +```bash +pnpm dev +pnpm build +pnpm preview +pnpm format +``` + +## Project status + +At the moment, this folder contains: + +- the application shell and layout +- the editor setup +- the styling and interaction base +- the first browser-facing service contracts for the engine and the language + server + +What is still missing: + +- a working browser engine integration +- a working browser language server integration +- the final packaging strategy for browser artifacts +- deployment workflows + +The intention is to connect those pieces once the browser packages are ready +and their public interfaces are stable. diff --git a/playground/index.html b/playground/index.html new file mode 100644 index 000000000..597bd1a5f --- /dev/null +++ b/playground/index.html @@ -0,0 +1,22 @@ + + + + + + + + + + + + + YARA Playground + + + + + + diff --git a/playground/package.json b/playground/package.json new file mode 100644 index 000000000..1c3a79bf2 --- /dev/null +++ b/playground/package.json @@ -0,0 +1,29 @@ +{ + "name": "yara-x-playground", + "private": true, + "contributors": [ + "kevinmuoz" + ], + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc && vite build", + "preview": "vite preview", + "prod": "vite build && vite preview", + "format": "prettier --write ." + }, + "dependencies": { + "@codingame/monaco-vscode-editor-api": "^25.1.2", + "lit": "^3.3.2", + "monaco-languageclient": "^10.7.0", + "vscode-languageclient": "^9.0.1", + "vscode-languageserver-protocol": "^3.17.5" + }, + "devDependencies": { + "prettier": "^3.8.1", + "rollup-plugin-visualizer": "^7.0.1", + "typescript": "~5.9.3", + "vite": "^7.1.0" + } +} diff --git a/playground/pnpm-lock.yaml b/playground/pnpm-lock.yaml new file mode 100644 index 000000000..0737c63b7 --- /dev/null +++ b/playground/pnpm-lock.yaml @@ -0,0 +1,2349 @@ +lockfileVersion: "9.0" + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + .: + dependencies: + "@codingame/monaco-vscode-editor-api": + specifier: ^25.1.2 + version: 25.1.2 + lit: + specifier: ^3.3.2 + version: 3.3.2 + monaco-languageclient: + specifier: ^10.7.0 + version: 10.7.0 + vscode-languageclient: + specifier: ^9.0.1 + version: 9.0.1 + vscode-languageserver-protocol: + specifier: ^3.17.5 + version: 3.17.5 + devDependencies: + prettier: + specifier: ^3.8.1 + version: 3.8.1 + rollup-plugin-visualizer: + specifier: ^7.0.1 + version: 7.0.1(rolldown@1.0.0-rc.10)(rollup@4.60.0) + typescript: + specifier: ~5.9.3 + version: 5.9.3 + vite: + specifier: ^7.1.0 + version: 7.3.1(lightningcss@1.32.0) + +packages: + "@codingame/monaco-vscode-api@25.1.2": + resolution: + { + integrity: sha512-K04QcQA+Zb0KXucBAK/BGCT5dldiwIqdUbBQq7yuLvBLbof3cP1WSUuxasMHGYwM0MWyzIAsDtyAYMS7is8ZuA==, + } + + "@codingame/monaco-vscode-base-service-override@25.1.2": + resolution: + { + integrity: sha512-OwYs6h1ATUAeMmX+Q1c8esTG7GLMqniBs+fLEr1/9b/ciY485ArKo5UvrUxVPDtRNy/7F06vRW9IUCq9iKP14w==, + } + + "@codingame/monaco-vscode-bulk-edit-service-override@25.1.2": + resolution: + { + integrity: sha512-+EfSzjiFakCf0IIJKPZrHVGioq5N8GBsp51bXuKBR5J/B58cUaJY0Dc12PNTSpgAusAGOppUIOSBqUk4F/7IaQ==, + } + + "@codingame/monaco-vscode-configuration-service-override@25.1.2": + resolution: + { + integrity: sha512-oeoZ3WtM42zHA1IWHrx9UGEfE+TixE+G8Bl9M9bjgFj1EROnkB5yOfELwRYPo4WOEtcK1C5nvIvWIj/hL9MaLg==, + } + + "@codingame/monaco-vscode-editor-api@25.1.2": + resolution: + { + integrity: sha512-dVXoBLRN8vyFHsLY6iYISaNetZ3ispXLut0qL+jvN0e0CEFkUv1F/3EAE7myptrJSS/N1AptrRIxATT3lwFP+Q==, + } + + "@codingame/monaco-vscode-editor-service-override@25.1.2": + resolution: + { + integrity: sha512-EadvDCyWdgxOPmaIvbcVVDNjTUYuKdjYWwKbPbbcTs9t4z1/DjdE7mV3ZdT6aGh5m6zkEEUOi143l27Y5eRt+Q==, + } + + "@codingame/monaco-vscode-environment-service-override@25.1.2": + resolution: + { + integrity: sha512-8GoD3lk0CN0dIMZOrZNS/i8RCaF1YSQ6nmrf+rqneOSHG9S382EnsZZD69d4+i7JnoeyttO7Kr9KH8WOhRV6OA==, + } + + "@codingame/monaco-vscode-extension-api@25.1.2": + resolution: + { + integrity: sha512-SJW/YOhjo+9MXEyzMwQMUWdJVR3Llc6pTq5JQqs6Y30v73gTrpLqtzbd9FNdCuQR8S6bUk5ScH8GL4QrVuL5FA==, + } + + "@codingame/monaco-vscode-extensions-service-override@25.1.2": + resolution: + { + integrity: sha512-rTTZW2biPxcg+JumhVf2L+38C5ptvNNxiJlwz39VfXFEh6qOHtAsIMy7vIXa0uGg5/y8DNp0SnOQJP/RKhLYZA==, + } + + "@codingame/monaco-vscode-files-service-override@25.1.2": + resolution: + { + integrity: sha512-TenLLAFIwY7keZFF8e3beUn7OVfnNINR5Noi4PVrjeeTcy6FuNH6Jghdul2JwpRAkvyJLdFMvomE2jlT6F03jQ==, + } + + "@codingame/monaco-vscode-host-service-override@25.1.2": + resolution: + { + integrity: sha512-lgaalpA9CUQW7i0bBwgBOK0DQNDvOo3QO3p6Rz6yVsHpgA4iMqq2d11dBDUKvuQSwIHPRu8CMHCqhQk/BQN/YA==, + } + + "@codingame/monaco-vscode-keybindings-service-override@25.1.2": + resolution: + { + integrity: sha512-cp/gGyTvCTAzCYnQm0HJykXJRB0Huz8Lvq60lj5LutgWcb8S3w6dOB2Houm8dHoeUm/jOko8SQNIP8hzWN92Zw==, + } + + "@codingame/monaco-vscode-language-pack-cs@25.1.2": + resolution: + { + integrity: sha512-v0cB2uAOCwj135aGIf0arGV+DNW32lbWh04bv8ctTxcWRt1Pr2kTQ1pjfE8ynKgxabPfAk8E25/CerKSYOmZ+A==, + } + + "@codingame/monaco-vscode-language-pack-de@25.1.2": + resolution: + { + integrity: sha512-xA3WOt1w5jlAOnyx4PBwx+qV3vx8C8/zie29qjYbgJMxGKDkb0HfpuKUwywDA2uUMI2wJZS+PnNG00zPDoLIrw==, + } + + "@codingame/monaco-vscode-language-pack-es@25.1.2": + resolution: + { + integrity: sha512-1/upuO9lRJilZ3sRr0QLTpz55KYRaBWDe8wtPvghOFYOHyWgW8A4VhUQxa6L9SJgY1JkypUAm0U8WcMX2G4LnQ==, + } + + "@codingame/monaco-vscode-language-pack-fr@25.1.2": + resolution: + { + integrity: sha512-iq+xx+tv1QIMmFD0eBhFRMF4xMAsVf/HyA1WogqBofteCWeAvRE9HUjZ5JzHz7jXBPe3dLP1LOM0r0GrJZs4fQ==, + } + + "@codingame/monaco-vscode-language-pack-it@25.1.2": + resolution: + { + integrity: sha512-FajWCML9OR8ppLnJ0mcg+sFHEhYJl8zhb3/DHnd+pNysw8dLfetXoSWjaPnwPPpwiQgkNN1UsToZHOU9czVifQ==, + } + + "@codingame/monaco-vscode-language-pack-ja@25.1.2": + resolution: + { + integrity: sha512-NwKh0BnPgUrJkxsm0X6vY4ftnd9DjxkcnQqK+bohta6UOzm09J1EjZ6QD42fjWngxrp/xiegtrYQ9NA2q6VpoA==, + } + + "@codingame/monaco-vscode-language-pack-ko@25.1.2": + resolution: + { + integrity: sha512-fvaisgfcg8YaAwnyPcGmQDLwkwqzamLQUyx9HmnwDpXw0YANzd058Kwn6bz+Vfn9MjwuMNT0nllD0qQMnpdyew==, + } + + "@codingame/monaco-vscode-language-pack-pl@25.1.2": + resolution: + { + integrity: sha512-9hDRyzFJkDia5rO9QE262JgxwP/cnalFisLFo7FQcw57ZhqzqXIdQIuwcKaHuAgzeQ6W2+A3KOLfTr3m7VZrXw==, + } + + "@codingame/monaco-vscode-language-pack-pt-br@25.1.2": + resolution: + { + integrity: sha512-7fFnqOTAJGb5RuJ4uwh9sh0JmXALuHPGOl7iL9rZkcgIuVP5y6wVDUDXq5qjiRTNSFDs7Bzh463Ir5m5D6mJbA==, + } + + "@codingame/monaco-vscode-language-pack-qps-ploc@25.1.2": + resolution: + { + integrity: sha512-IFjoqrSuPtIFWb+KlPT6PFWKszzNX+TCD9drgCV6AigvBO/xfGL3QwHB68l/DLbmDbohOz4Xdkutv20wuENAeA==, + } + + "@codingame/monaco-vscode-language-pack-ru@25.1.2": + resolution: + { + integrity: sha512-0uDAeXO+GllKUPhJzP893rlDhlFV1IwCu/515rBdcyegt48iGm/xAgj26V90hNz8hmB6EuM/7d8MFeklbiIpYA==, + } + + "@codingame/monaco-vscode-language-pack-tr@25.1.2": + resolution: + { + integrity: sha512-MJhHxDyJEiuVLQ9+jb8MnnN9lsbJOjJjMswVCeJ7v/Q/msAhq25QYUfn0DbOIzESJE1f7crffRb5e38XP8sYWA==, + } + + "@codingame/monaco-vscode-language-pack-zh-hans@25.1.2": + resolution: + { + integrity: sha512-c7MMrhnSLb59NxpAa8nVy9aIbxy4gVYrCpDMq8W380LOaXTYb7nueTrw8QJ5QbJBNi2P2KZoGkn2BlONuBtJJg==, + } + + "@codingame/monaco-vscode-language-pack-zh-hant@25.1.2": + resolution: + { + integrity: sha512-ARedFTM6JCluoPLJqkBcTJaQFdJNcN86OX6B8/NMApIPrnSIAfanMndpyilt8XjzUG6IH22cypR+DAlEjf48cA==, + } + + "@codingame/monaco-vscode-languages-service-override@25.1.2": + resolution: + { + integrity: sha512-ipuS1V3NgXDkNrj0vBcgMBFnqo+19HVsZjjFGfPFH3x0uptP9aiWWK42wtDK3Qbu4teSjHL7WnSLrmw94rplWw==, + } + + "@codingame/monaco-vscode-layout-service-override@25.1.2": + resolution: + { + integrity: sha512-SxBGcMK3RgkGtUn7ZDl7dCoyNW0CWFQ/bfSRYUY06A0IA4JNS5jq1lhof57d0WXewm+5l8w1Spr/vMsfx1c9ig==, + } + + "@codingame/monaco-vscode-localization-service-override@25.1.2": + resolution: + { + integrity: sha512-QLj62A8XDOIQW3KjsZlNxs+sfsNNHYxWMjQMwZu/y2Vw3IIHGly2Lpn4t4SFbeaBHJQJy4i5s7NpzlbF9MbEzQ==, + } + + "@codingame/monaco-vscode-log-service-override@25.1.2": + resolution: + { + integrity: sha512-OoileAUtPAJ0j3RW31DFSxtOipy0EcFq+iIXEdGvoRlsQPZJ3o9ayjf1JvCXpxUjJ3QkmvQVhXsWNUFREjEFLg==, + } + + "@codingame/monaco-vscode-model-service-override@25.1.2": + resolution: + { + integrity: sha512-MGz/eV1CxibLvnl6WzK6idUHJCXJOVepJvKM6Trkv5050vRe+f/o1TjCiG8PaznAypYqZvnwkTG0B7/OTizCpQ==, + } + + "@codingame/monaco-vscode-monarch-service-override@25.1.2": + resolution: + { + integrity: sha512-akyNHOJQRS7YHyk6kf0Encnkt+shlR+bIB84UJRUHFgSeF8s5gkDkQuFJph0YeUDWJWat+yBLUSZx2nHomdbHQ==, + } + + "@codingame/monaco-vscode-quickaccess-service-override@25.1.2": + resolution: + { + integrity: sha512-7IIrXnwHiF3w9d9p9kspEUz/LCibMLUztmRpGdZQfFtWBJw043q7rk8V1O42KdXr1hVg9IR5vfffwjy9nbiiUg==, + } + + "@codingame/monaco-vscode-textmate-service-override@25.1.2": + resolution: + { + integrity: sha512-AL0FtSQBW+1vtoXYQvUqB2hfWojpK73Kq/n6KuNXxjLF/XBJ5FpeeZDfrBfwhWPPoHuBTsaFUCQy4L8xQgbVlA==, + } + + "@codingame/monaco-vscode-theme-defaults-default-extension@25.1.2": + resolution: + { + integrity: sha512-0vTMFiC89YSDSmjFckuQBUKwRuFNtsILNO3k0PBiSLN/MW+VDItjJpiVLXC42+rUWlGgY2lYxOneGVa5slCV1w==, + } + + "@codingame/monaco-vscode-theme-service-override@25.1.2": + resolution: + { + integrity: sha512-hsTwl6YYTiheFuQMmCmiEGLIdIdgYaf8Z85XWyxe6YgPtDaYGnp0fGSOXKA9/bf0JtuynzoLKtUUfDupK/A7Tw==, + } + + "@codingame/monaco-vscode-view-banner-service-override@25.1.2": + resolution: + { + integrity: sha512-zhujHd1PQ6rRXsC2OQGrx/282G2v3lpPFl9heDFGKzpdj5119SgcW+B9p/MwJ1qF3LJpuRRgefNiQtqC/KT1eA==, + } + + "@codingame/monaco-vscode-view-common-service-override@25.1.2": + resolution: + { + integrity: sha512-4Po/YaHUvVf4VmhVCZmM2lc/flOptiWSM140bIRNpMcfH0VwihYg15CcDeu1Oc+6DaauzsG3u59GtEvlMmJ9Zw==, + } + + "@codingame/monaco-vscode-view-status-bar-service-override@25.1.2": + resolution: + { + integrity: sha512-Jp9ytLaWZ6evabTPtG3Mu3dFx+7WTIPz69BsGpl9PnU0kiSWUqQhPSob0Jz7E2qmMj0ZcNv2Wqvm6bMBu5OyrA==, + } + + "@codingame/monaco-vscode-view-title-bar-service-override@25.1.2": + resolution: + { + integrity: sha512-NVYtTAFR35NV/Fx7tSlbASicvpAjK5A14fmxF7/LJJN8ZmzhA/P3Y+UzhqOQl6/VcPV4pAMU0Z7Sicgwbn37dw==, + } + + "@codingame/monaco-vscode-views-service-override@25.1.2": + resolution: + { + integrity: sha512-LfzlztsvobdP5L5EvJ/rqSEgy5fEVmrkMqRteuhEtNGd4hnmdBoX8W7BNMBPff6d4NfCK74pGHJF57RyT4Iixg==, + } + + "@codingame/monaco-vscode-workbench-service-override@25.1.2": + resolution: + { + integrity: sha512-2LMHr+na03FhOAaXpIGmamq9hf7e4wt2kULn8NqNZRd3i+0v1tx/TSSjGhsA5EkrNrFD7CMSoXayBq8tgpCq/A==, + } + + "@emnapi/core@1.9.1": + resolution: + { + integrity: sha512-mukuNALVsoix/w1BJwFzwXBN/dHeejQtuVzcDsfOEsdpCumXb/E9j8w11h5S54tT1xhifGfbbSm/ICrObRb3KA==, + } + + "@emnapi/runtime@1.9.1": + resolution: + { + integrity: sha512-VYi5+ZVLhpgK4hQ0TAjiQiZ6ol0oe4mBx7mVv7IflsiEp0OWoVsp/+f9Vc1hOhE0TtkORVrI1GvzyreqpgWtkA==, + } + + "@emnapi/wasi-threads@1.2.0": + resolution: + { + integrity: sha512-N10dEJNSsUx41Z6pZsXU8FjPjpBEplgH24sfkmITrBED1/U2Esum9F3lfLrMjKHHjmi557zQn7kR9R+XWXu5Rg==, + } + + "@esbuild/aix-ppc64@0.27.4": + resolution: + { + integrity: sha512-cQPwL2mp2nSmHHJlCyoXgHGhbEPMrEEU5xhkcy3Hs/O7nGZqEpZ2sUtLaL9MORLtDfRvVl2/3PAuEkYZH0Ty8Q==, + } + engines: { node: ">=18" } + cpu: [ppc64] + os: [aix] + + "@esbuild/android-arm64@0.27.4": + resolution: + { + integrity: sha512-gdLscB7v75wRfu7QSm/zg6Rx29VLdy9eTr2t44sfTW7CxwAtQghZ4ZnqHk3/ogz7xao0QAgrkradbBzcqFPasw==, + } + engines: { node: ">=18" } + cpu: [arm64] + os: [android] + + "@esbuild/android-arm@0.27.4": + resolution: + { + integrity: sha512-X9bUgvxiC8CHAGKYufLIHGXPJWnr0OCdR0anD2e21vdvgCI8lIfqFbnoeOz7lBjdrAGUhqLZLcQo6MLhTO2DKQ==, + } + engines: { node: ">=18" } + cpu: [arm] + os: [android] + + "@esbuild/android-x64@0.27.4": + resolution: + { + integrity: sha512-PzPFnBNVF292sfpfhiyiXCGSn9HZg5BcAz+ivBuSsl6Rk4ga1oEXAamhOXRFyMcjwr2DVtm40G65N3GLeH1Lvw==, + } + engines: { node: ">=18" } + cpu: [x64] + os: [android] + + "@esbuild/darwin-arm64@0.27.4": + resolution: + { + integrity: sha512-b7xaGIwdJlht8ZFCvMkpDN6uiSmnxxK56N2GDTMYPr2/gzvfdQN8rTfBsvVKmIVY/X7EM+/hJKEIbbHs9oA4tQ==, + } + engines: { node: ">=18" } + cpu: [arm64] + os: [darwin] + + "@esbuild/darwin-x64@0.27.4": + resolution: + { + integrity: sha512-sR+OiKLwd15nmCdqpXMnuJ9W2kpy0KigzqScqHI3Hqwr7IXxBp3Yva+yJwoqh7rE8V77tdoheRYataNKL4QrPw==, + } + engines: { node: ">=18" } + cpu: [x64] + os: [darwin] + + "@esbuild/freebsd-arm64@0.27.4": + resolution: + { + integrity: sha512-jnfpKe+p79tCnm4GVav68A7tUFeKQwQyLgESwEAUzyxk/TJr4QdGog9sqWNcUbr/bZt/O/HXouspuQDd9JxFSw==, + } + engines: { node: ">=18" } + cpu: [arm64] + os: [freebsd] + + "@esbuild/freebsd-x64@0.27.4": + resolution: + { + integrity: sha512-2kb4ceA/CpfUrIcTUl1wrP/9ad9Atrp5J94Lq69w7UwOMolPIGrfLSvAKJp0RTvkPPyn6CIWrNy13kyLikZRZQ==, + } + engines: { node: ">=18" } + cpu: [x64] + os: [freebsd] + + "@esbuild/linux-arm64@0.27.4": + resolution: + { + integrity: sha512-7nQOttdzVGth1iz57kxg9uCz57dxQLHWxopL6mYuYthohPKEK0vU0C3O21CcBK6KDlkYVcnDXY099HcCDXd9dA==, + } + engines: { node: ">=18" } + cpu: [arm64] + os: [linux] + + "@esbuild/linux-arm@0.27.4": + resolution: + { + integrity: sha512-aBYgcIxX/wd5n2ys0yESGeYMGF+pv6g0DhZr3G1ZG4jMfruU9Tl1i2Z+Wnj9/KjGz1lTLCcorqE2viePZqj4Eg==, + } + engines: { node: ">=18" } + cpu: [arm] + os: [linux] + + "@esbuild/linux-ia32@0.27.4": + resolution: + { + integrity: sha512-oPtixtAIzgvzYcKBQM/qZ3R+9TEUd1aNJQu0HhGyqtx6oS7qTpvjheIWBbes4+qu1bNlo2V4cbkISr8q6gRBFA==, + } + engines: { node: ">=18" } + cpu: [ia32] + os: [linux] + + "@esbuild/linux-loong64@0.27.4": + resolution: + { + integrity: sha512-8mL/vh8qeCoRcFH2nM8wm5uJP+ZcVYGGayMavi8GmRJjuI3g1v6Z7Ni0JJKAJW+m0EtUuARb6Lmp4hMjzCBWzA==, + } + engines: { node: ">=18" } + cpu: [loong64] + os: [linux] + + "@esbuild/linux-mips64el@0.27.4": + resolution: + { + integrity: sha512-1RdrWFFiiLIW7LQq9Q2NES+HiD4NyT8Itj9AUeCl0IVCA459WnPhREKgwrpaIfTOe+/2rdntisegiPWn/r/aAw==, + } + engines: { node: ">=18" } + cpu: [mips64el] + os: [linux] + + "@esbuild/linux-ppc64@0.27.4": + resolution: + { + integrity: sha512-tLCwNG47l3sd9lpfyx9LAGEGItCUeRCWeAx6x2Jmbav65nAwoPXfewtAdtbtit/pJFLUWOhpv0FpS6GQAmPrHA==, + } + engines: { node: ">=18" } + cpu: [ppc64] + os: [linux] + + "@esbuild/linux-riscv64@0.27.4": + resolution: + { + integrity: sha512-BnASypppbUWyqjd1KIpU4AUBiIhVr6YlHx/cnPgqEkNoVOhHg+YiSVxM1RLfiy4t9cAulbRGTNCKOcqHrEQLIw==, + } + engines: { node: ">=18" } + cpu: [riscv64] + os: [linux] + + "@esbuild/linux-s390x@0.27.4": + resolution: + { + integrity: sha512-+eUqgb/Z7vxVLezG8bVB9SfBie89gMueS+I0xYh2tJdw3vqA/0ImZJ2ROeWwVJN59ihBeZ7Tu92dF/5dy5FttA==, + } + engines: { node: ">=18" } + cpu: [s390x] + os: [linux] + + "@esbuild/linux-x64@0.27.4": + resolution: + { + integrity: sha512-S5qOXrKV8BQEzJPVxAwnryi2+Iq5pB40gTEIT69BQONqR7JH1EPIcQ/Uiv9mCnn05jff9umq/5nqzxlqTOg9NA==, + } + engines: { node: ">=18" } + cpu: [x64] + os: [linux] + + "@esbuild/netbsd-arm64@0.27.4": + resolution: + { + integrity: sha512-xHT8X4sb0GS8qTqiwzHqpY00C95DPAq7nAwX35Ie/s+LO9830hrMd3oX0ZMKLvy7vsonee73x0lmcdOVXFzd6Q==, + } + engines: { node: ">=18" } + cpu: [arm64] + os: [netbsd] + + "@esbuild/netbsd-x64@0.27.4": + resolution: + { + integrity: sha512-RugOvOdXfdyi5Tyv40kgQnI0byv66BFgAqjdgtAKqHoZTbTF2QqfQrFwa7cHEORJf6X2ht+l9ABLMP0dnKYsgg==, + } + engines: { node: ">=18" } + cpu: [x64] + os: [netbsd] + + "@esbuild/openbsd-arm64@0.27.4": + resolution: + { + integrity: sha512-2MyL3IAaTX+1/qP0O1SwskwcwCoOI4kV2IBX1xYnDDqthmq5ArrW94qSIKCAuRraMgPOmG0RDTA74mzYNQA9ow==, + } + engines: { node: ">=18" } + cpu: [arm64] + os: [openbsd] + + "@esbuild/openbsd-x64@0.27.4": + resolution: + { + integrity: sha512-u8fg/jQ5aQDfsnIV6+KwLOf1CmJnfu1ShpwqdwC0uA7ZPwFws55Ngc12vBdeUdnuWoQYx/SOQLGDcdlfXhYmXQ==, + } + engines: { node: ">=18" } + cpu: [x64] + os: [openbsd] + + "@esbuild/openharmony-arm64@0.27.4": + resolution: + { + integrity: sha512-JkTZrl6VbyO8lDQO3yv26nNr2RM2yZzNrNHEsj9bm6dOwwu9OYN28CjzZkH57bh4w0I2F7IodpQvUAEd1mbWXg==, + } + engines: { node: ">=18" } + cpu: [arm64] + os: [openharmony] + + "@esbuild/sunos-x64@0.27.4": + resolution: + { + integrity: sha512-/gOzgaewZJfeJTlsWhvUEmUG4tWEY2Spp5M20INYRg2ZKl9QPO3QEEgPeRtLjEWSW8FilRNacPOg8R1uaYkA6g==, + } + engines: { node: ">=18" } + cpu: [x64] + os: [sunos] + + "@esbuild/win32-arm64@0.27.4": + resolution: + { + integrity: sha512-Z9SExBg2y32smoDQdf1HRwHRt6vAHLXcxD2uGgO/v2jK7Y718Ix4ndsbNMU/+1Qiem9OiOdaqitioZwxivhXYg==, + } + engines: { node: ">=18" } + cpu: [arm64] + os: [win32] + + "@esbuild/win32-ia32@0.27.4": + resolution: + { + integrity: sha512-DAyGLS0Jz5G5iixEbMHi5KdiApqHBWMGzTtMiJ72ZOLhbu/bzxgAe8Ue8CTS3n3HbIUHQz/L51yMdGMeoxXNJw==, + } + engines: { node: ">=18" } + cpu: [ia32] + os: [win32] + + "@esbuild/win32-x64@0.27.4": + resolution: + { + integrity: sha512-+knoa0BDoeXgkNvvV1vvbZX4+hizelrkwmGJBdT17t8FNPwG2lKemmuMZlmaNQ3ws3DKKCxpb4zRZEIp3UxFCg==, + } + engines: { node: ">=18" } + cpu: [x64] + os: [win32] + + "@lit-labs/ssr-dom-shim@1.5.1": + resolution: + { + integrity: sha512-Aou5UdlSpr5whQe8AA/bZG0jMj96CoJIWbGfZ91qieWu5AWUMKw8VR/pAkQkJYvBNhmCcWnZlyyk5oze8JIqYA==, + } + + "@lit/reactive-element@2.1.2": + resolution: + { + integrity: sha512-pbCDiVMnne1lYUIaYNN5wrwQXDtHaYtg7YEFPeW+hws6U47WeFvISGUWekPGKWOP1ygrs0ef0o1VJMk1exos5A==, + } + + "@napi-rs/wasm-runtime@1.1.1": + resolution: + { + integrity: sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A==, + } + + "@oxc-project/types@0.120.0": + resolution: + { + integrity: sha512-k1YNu55DuvAip/MGE1FTsIuU3FUCn6v/ujG9V7Nq5Df/kX2CWb13hhwD0lmJGMGqE+bE1MXvv9SZVnMzEXlWcg==, + } + + "@rolldown/binding-android-arm64@1.0.0-rc.10": + resolution: + { + integrity: sha512-jOHxwXhxmFKuXztiu1ORieJeTbx5vrTkcOkkkn2d35726+iwhrY1w/+nYY/AGgF12thg33qC3R1LMBF5tHTZHg==, + } + engines: { node: ^20.19.0 || >=22.12.0 } + cpu: [arm64] + os: [android] + + "@rolldown/binding-darwin-arm64@1.0.0-rc.10": + resolution: + { + integrity: sha512-gED05Teg/vtTZbIJBc4VNMAxAFDUPkuO/rAIyyxZjTj1a1/s6z5TII/5yMGZ0uLRCifEtwUQn8OlYzuYc0m70w==, + } + engines: { node: ^20.19.0 || >=22.12.0 } + cpu: [arm64] + os: [darwin] + + "@rolldown/binding-darwin-x64@1.0.0-rc.10": + resolution: + { + integrity: sha512-rI15NcM1mA48lqrIxVkHfAqcyFLcQwyXWThy+BQ5+mkKKPvSO26ir+ZDp36AgYoYVkqvMcdS8zOE6SeBsR9e8A==, + } + engines: { node: ^20.19.0 || >=22.12.0 } + cpu: [x64] + os: [darwin] + + "@rolldown/binding-freebsd-x64@1.0.0-rc.10": + resolution: + { + integrity: sha512-XZRXHdTa+4ME1MuDVp021+doQ+z6Ei4CCFmNc5/sKbqb8YmkiJdj8QKlV3rCI0AJtAeSB5n0WGPuJWNL9p/L2w==, + } + engines: { node: ^20.19.0 || >=22.12.0 } + cpu: [x64] + os: [freebsd] + + "@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.10": + resolution: + { + integrity: sha512-R0SQMRluISSLzFE20sPWYHVmJdDQnRyc/FzSCN72BqQmh2SOZUFG+N3/vBZpR4C6WpEUVYJLrYUXaj43sJsNLA==, + } + engines: { node: ^20.19.0 || >=22.12.0 } + cpu: [arm] + os: [linux] + + "@rolldown/binding-linux-arm64-gnu@1.0.0-rc.10": + resolution: + { + integrity: sha512-Y1reMrV/o+cwpduYhJuOE3OMKx32RMYCidf14y+HssARRmhDuWXJ4yVguDg2R/8SyyGNo+auzz64LnPK9Hq6jg==, + } + engines: { node: ^20.19.0 || >=22.12.0 } + cpu: [arm64] + os: [linux] + libc: [glibc] + + "@rolldown/binding-linux-arm64-musl@1.0.0-rc.10": + resolution: + { + integrity: sha512-vELN+HNb2IzuzSBUOD4NHmP9yrGwl1DVM29wlQvx1OLSclL0NgVWnVDKl/8tEks79EFek/kebQKnNJkIAA4W2g==, + } + engines: { node: ^20.19.0 || >=22.12.0 } + cpu: [arm64] + os: [linux] + libc: [musl] + + "@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.10": + resolution: + { + integrity: sha512-ZqrufYTgzxbHwpqOjzSsb0UV/aV2TFIY5rP8HdsiPTv/CuAgCRjM6s9cYFwQ4CNH+hf9Y4erHW1GjZuZ7WoI7w==, + } + engines: { node: ^20.19.0 || >=22.12.0 } + cpu: [ppc64] + os: [linux] + libc: [glibc] + + "@rolldown/binding-linux-s390x-gnu@1.0.0-rc.10": + resolution: + { + integrity: sha512-gSlmVS1FZJSRicA6IyjoRoKAFK7IIHBs7xJuHRSmjImqk3mPPWbR7RhbnfH2G6bcmMEllCt2vQ/7u9e6bBnByg==, + } + engines: { node: ^20.19.0 || >=22.12.0 } + cpu: [s390x] + os: [linux] + libc: [glibc] + + "@rolldown/binding-linux-x64-gnu@1.0.0-rc.10": + resolution: + { + integrity: sha512-eOCKUpluKgfObT2pHjztnaWEIbUabWzk3qPZ5PuacuPmr4+JtQG4k2vGTY0H15edaTnicgU428XW/IH6AimcQw==, + } + engines: { node: ^20.19.0 || >=22.12.0 } + cpu: [x64] + os: [linux] + libc: [glibc] + + "@rolldown/binding-linux-x64-musl@1.0.0-rc.10": + resolution: + { + integrity: sha512-Xdf2jQbfQowJnLcgYfD/m0Uu0Qj5OdxKallD78/IPPfzaiaI4KRAwZzHcKQ4ig1gtg1SuzC7jovNiM2TzQsBXA==, + } + engines: { node: ^20.19.0 || >=22.12.0 } + cpu: [x64] + os: [linux] + libc: [musl] + + "@rolldown/binding-openharmony-arm64@1.0.0-rc.10": + resolution: + { + integrity: sha512-o1hYe8hLi1EY6jgPFyxQgQ1wcycX+qz8eEbVmot2hFkgUzPxy9+kF0u0NIQBeDq+Mko47AkaFFaChcvZa9UX9Q==, + } + engines: { node: ^20.19.0 || >=22.12.0 } + cpu: [arm64] + os: [openharmony] + + "@rolldown/binding-wasm32-wasi@1.0.0-rc.10": + resolution: + { + integrity: sha512-Ugv9o7qYJudqQO5Y5y2N2SOo6S4WiqiNOpuQyoPInnhVzCY+wi/GHltcLHypG9DEUYMB0iTB/huJrpadiAcNcA==, + } + engines: { node: ">=14.0.0" } + cpu: [wasm32] + + "@rolldown/binding-win32-arm64-msvc@1.0.0-rc.10": + resolution: + { + integrity: sha512-7UODQb4fQUNT/vmgDZBl3XOBAIOutP5R3O/rkxg0aLfEGQ4opbCgU5vOw/scPe4xOqBwL9fw7/RP1vAMZ6QlAQ==, + } + engines: { node: ^20.19.0 || >=22.12.0 } + cpu: [arm64] + os: [win32] + + "@rolldown/binding-win32-x64-msvc@1.0.0-rc.10": + resolution: + { + integrity: sha512-PYxKHMVHOb5NJuDL53vBUl1VwUjymDcYI6rzpIni0C9+9mTiJedvUxSk7/RPp7OOAm3v+EjgMu9bIy3N6b408w==, + } + engines: { node: ^20.19.0 || >=22.12.0 } + cpu: [x64] + os: [win32] + + "@rolldown/pluginutils@1.0.0-rc.10": + resolution: + { + integrity: sha512-UkVDEFk1w3mveXeKgaTuYfKWtPbvgck1dT8TUG3bnccrH0XtLTuAyfCoks4Q/M5ZGToSVJTIQYCzy2g/atAOeg==, + } + + "@rollup/rollup-android-arm-eabi@4.60.0": + resolution: + { + integrity: sha512-WOhNW9K8bR3kf4zLxbfg6Pxu2ybOUbB2AjMDHSQx86LIF4rH4Ft7vmMwNt0loO0eonglSNy4cpD3MKXXKQu0/A==, + } + cpu: [arm] + os: [android] + + "@rollup/rollup-android-arm64@4.60.0": + resolution: + { + integrity: sha512-u6JHLll5QKRvjciE78bQXDmqRqNs5M/3GVqZeMwvmjaNODJih/WIrJlFVEihvV0MiYFmd+ZyPr9wxOVbPAG2Iw==, + } + cpu: [arm64] + os: [android] + + "@rollup/rollup-darwin-arm64@4.60.0": + resolution: + { + integrity: sha512-qEF7CsKKzSRc20Ciu2Zw1wRrBz4g56F7r/vRwY430UPp/nt1x21Q/fpJ9N5l47WWvJlkNCPJz3QRVw008fi7yA==, + } + cpu: [arm64] + os: [darwin] + + "@rollup/rollup-darwin-x64@4.60.0": + resolution: + { + integrity: sha512-WADYozJ4QCnXCH4wPB+3FuGmDPoFseVCUrANmA5LWwGmC6FL14BWC7pcq+FstOZv3baGX65tZ378uT6WG8ynTw==, + } + cpu: [x64] + os: [darwin] + + "@rollup/rollup-freebsd-arm64@4.60.0": + resolution: + { + integrity: sha512-6b8wGHJlDrGeSE3aH5mGNHBjA0TTkxdoNHik5EkvPHCt351XnigA4pS7Wsj/Eo9Y8RBU6f35cjN9SYmCFBtzxw==, + } + cpu: [arm64] + os: [freebsd] + + "@rollup/rollup-freebsd-x64@4.60.0": + resolution: + { + integrity: sha512-h25Ga0t4jaylMB8M/JKAyrvvfxGRjnPQIR8lnCayyzEjEOx2EJIlIiMbhpWxDRKGKF8jbNH01NnN663dH638mA==, + } + cpu: [x64] + os: [freebsd] + + "@rollup/rollup-linux-arm-gnueabihf@4.60.0": + resolution: + { + integrity: sha512-RzeBwv0B3qtVBWtcuABtSuCzToo2IEAIQrcyB/b2zMvBWVbjo8bZDjACUpnaafaxhTw2W+imQbP2BD1usasK4g==, + } + cpu: [arm] + os: [linux] + libc: [glibc] + + "@rollup/rollup-linux-arm-musleabihf@4.60.0": + resolution: + { + integrity: sha512-Sf7zusNI2CIU1HLzuu9Tc5YGAHEZs5Lu7N1ssJG4Tkw6e0MEsN7NdjUDDfGNHy2IU+ENyWT+L2obgWiguWibWQ==, + } + cpu: [arm] + os: [linux] + libc: [musl] + + "@rollup/rollup-linux-arm64-gnu@4.60.0": + resolution: + { + integrity: sha512-DX2x7CMcrJzsE91q7/O02IJQ5/aLkVtYFryqCjduJhUfGKG6yJV8hxaw8pZa93lLEpPTP/ohdN4wFz7yp/ry9A==, + } + cpu: [arm64] + os: [linux] + libc: [glibc] + + "@rollup/rollup-linux-arm64-musl@4.60.0": + resolution: + { + integrity: sha512-09EL+yFVbJZlhcQfShpswwRZ0Rg+z/CsSELFCnPt3iK+iqwGsI4zht3secj5vLEs957QvFFXnzAT0FFPIxSrkQ==, + } + cpu: [arm64] + os: [linux] + libc: [musl] + + "@rollup/rollup-linux-loong64-gnu@4.60.0": + resolution: + { + integrity: sha512-i9IcCMPr3EXm8EQg5jnja0Zyc1iFxJjZWlb4wr7U2Wx/GrddOuEafxRdMPRYVaXjgbhvqalp6np07hN1w9kAKw==, + } + cpu: [loong64] + os: [linux] + libc: [glibc] + + "@rollup/rollup-linux-loong64-musl@4.60.0": + resolution: + { + integrity: sha512-DGzdJK9kyJ+B78MCkWeGnpXJ91tK/iKA6HwHxF4TAlPIY7GXEvMe8hBFRgdrR9Ly4qebR/7gfUs9y2IoaVEyog==, + } + cpu: [loong64] + os: [linux] + libc: [musl] + + "@rollup/rollup-linux-ppc64-gnu@4.60.0": + resolution: + { + integrity: sha512-RwpnLsqC8qbS8z1H1AxBA1H6qknR4YpPR9w2XX0vo2Sz10miu57PkNcnHVaZkbqyw/kUWfKMI73jhmfi9BRMUQ==, + } + cpu: [ppc64] + os: [linux] + libc: [glibc] + + "@rollup/rollup-linux-ppc64-musl@4.60.0": + resolution: + { + integrity: sha512-Z8pPf54Ly3aqtdWC3G4rFigZgNvd+qJlOE52fmko3KST9SoGfAdSRCwyoyG05q1HrrAblLbk1/PSIV+80/pxLg==, + } + cpu: [ppc64] + os: [linux] + libc: [musl] + + "@rollup/rollup-linux-riscv64-gnu@4.60.0": + resolution: + { + integrity: sha512-3a3qQustp3COCGvnP4SvrMHnPQ9d1vzCakQVRTliaz8cIp/wULGjiGpbcqrkv0WrHTEp8bQD/B3HBjzujVWLOA==, + } + cpu: [riscv64] + os: [linux] + libc: [glibc] + + "@rollup/rollup-linux-riscv64-musl@4.60.0": + resolution: + { + integrity: sha512-pjZDsVH/1VsghMJ2/kAaxt6dL0psT6ZexQVrijczOf+PeP2BUqTHYejk3l6TlPRydggINOeNRhvpLa0AYpCWSQ==, + } + cpu: [riscv64] + os: [linux] + libc: [musl] + + "@rollup/rollup-linux-s390x-gnu@4.60.0": + resolution: + { + integrity: sha512-3ObQs0BhvPgiUVZrN7gqCSvmFuMWvWvsjG5ayJ3Lraqv+2KhOsp+pUbigqbeWqueGIsnn+09HBw27rJ+gYK4VQ==, + } + cpu: [s390x] + os: [linux] + libc: [glibc] + + "@rollup/rollup-linux-x64-gnu@4.60.0": + resolution: + { + integrity: sha512-EtylprDtQPdS5rXvAayrNDYoJhIz1/vzN2fEubo3yLE7tfAw+948dO0g4M0vkTVFhKojnF+n6C8bDNe+gDRdTg==, + } + cpu: [x64] + os: [linux] + libc: [glibc] + + "@rollup/rollup-linux-x64-musl@4.60.0": + resolution: + { + integrity: sha512-k09oiRCi/bHU9UVFqD17r3eJR9bn03TyKraCrlz5ULFJGdJGi7VOmm9jl44vOJvRJ6P7WuBi/s2A97LxxHGIdw==, + } + cpu: [x64] + os: [linux] + libc: [musl] + + "@rollup/rollup-openbsd-x64@4.60.0": + resolution: + { + integrity: sha512-1o/0/pIhozoSaDJoDcec+IVLbnRtQmHwPV730+AOD29lHEEo4F5BEUB24H0OBdhbBBDwIOSuf7vgg0Ywxdfiiw==, + } + cpu: [x64] + os: [openbsd] + + "@rollup/rollup-openharmony-arm64@4.60.0": + resolution: + { + integrity: sha512-pESDkos/PDzYwtyzB5p/UoNU/8fJo68vcXM9ZW2V0kjYayj1KaaUfi1NmTUTUpMn4UhU4gTuK8gIaFO4UGuMbA==, + } + cpu: [arm64] + os: [openharmony] + + "@rollup/rollup-win32-arm64-msvc@4.60.0": + resolution: + { + integrity: sha512-hj1wFStD7B1YBeYmvY+lWXZ7ey73YGPcViMShYikqKT1GtstIKQAtfUI6yrzPjAy/O7pO0VLXGmUVWXQMaYgTQ==, + } + cpu: [arm64] + os: [win32] + + "@rollup/rollup-win32-ia32-msvc@4.60.0": + resolution: + { + integrity: sha512-SyaIPFoxmUPlNDq5EHkTbiKzmSEmq/gOYFI/3HHJ8iS/v1mbugVa7dXUzcJGQfoytp9DJFLhHH4U3/eTy2Bq4w==, + } + cpu: [ia32] + os: [win32] + + "@rollup/rollup-win32-x64-gnu@4.60.0": + resolution: + { + integrity: sha512-RdcryEfzZr+lAr5kRm2ucN9aVlCCa2QNq4hXelZxb8GG0NJSazq44Z3PCCc8wISRuCVnGs0lQJVX5Vp6fKA+IA==, + } + cpu: [x64] + os: [win32] + + "@rollup/rollup-win32-x64-msvc@4.60.0": + resolution: + { + integrity: sha512-PrsWNQ8BuE00O3Xsx3ALh2Df8fAj9+cvvX9AIA6o4KpATR98c9mud4XtDWVvsEuyia5U4tVSTKygawyJkjm60w==, + } + cpu: [x64] + os: [win32] + + "@tybys/wasm-util@0.10.1": + resolution: + { + integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==, + } + + "@types/estree@1.0.8": + resolution: + { + integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==, + } + + "@types/trusted-types@2.0.7": + resolution: + { + integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==, + } + + "@vscode/iconv-lite-umd@0.7.1": + resolution: + { + integrity: sha512-tK6k0DXFHW7q5+GGuGZO+phpAqpxO4WXl+BLc/8/uOk3RsM2ssAL3CQUQDb1TGfwltjsauhN6S4ghYZzs4sPFw==, + } + + ansi-regex@6.2.2: + resolution: + { + integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==, + } + engines: { node: ">=12" } + + ansi-styles@6.2.3: + resolution: + { + integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==, + } + engines: { node: ">=12" } + + balanced-match@1.0.2: + resolution: + { + integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==, + } + + brace-expansion@2.0.2: + resolution: + { + integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==, + } + + bundle-name@4.1.0: + resolution: + { + integrity: sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==, + } + engines: { node: ">=18" } + + cliui@9.0.1: + resolution: + { + integrity: sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==, + } + engines: { node: ">=20" } + + default-browser-id@5.0.1: + resolution: + { + integrity: sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==, + } + engines: { node: ">=18" } + + default-browser@5.5.0: + resolution: + { + integrity: sha512-H9LMLr5zwIbSxrmvikGuI/5KGhZ8E2zH3stkMgM5LpOWDutGM2JZaj460Udnf1a+946zc7YBgrqEWwbk7zHvGw==, + } + engines: { node: ">=18" } + + define-lazy-prop@3.0.0: + resolution: + { + integrity: sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==, + } + engines: { node: ">=12" } + + detect-libc@2.1.2: + resolution: + { + integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==, + } + engines: { node: ">=8" } + + dompurify@3.3.1: + resolution: + { + integrity: sha512-qkdCKzLNtrgPFP1Vo+98FRzJnBRGe4ffyCea9IwHB1fyxPOeNTHpLKYGd4Uk9xvNoH0ZoOjwZxNptyMwqrId1Q==, + } + + emoji-regex@10.6.0: + resolution: + { + integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==, + } + + esbuild@0.27.4: + resolution: + { + integrity: sha512-Rq4vbHnYkK5fws5NF7MYTU68FPRE1ajX7heQ/8QXXWqNgqqJ/GkmmyxIzUnf2Sr/bakf8l54716CcMGHYhMrrQ==, + } + engines: { node: ">=18" } + hasBin: true + + escalade@3.2.0: + resolution: + { + integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==, + } + engines: { node: ">=6" } + + fdir@6.5.0: + resolution: + { + integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==, + } + engines: { node: ">=12.0.0" } + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + fsevents@2.3.3: + resolution: + { + integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==, + } + engines: { node: ^8.16.0 || ^10.6.0 || >=11.0.0 } + os: [darwin] + + get-caller-file@2.0.5: + resolution: + { + integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==, + } + engines: { node: 6.* || 8.* || >= 10.* } + + get-east-asian-width@1.5.0: + resolution: + { + integrity: sha512-CQ+bEO+Tva/qlmw24dCejulK5pMzVnUOFOijVogd3KQs07HnRIgp8TGipvCCRT06xeYEbpbgwaCxglFyiuIcmA==, + } + engines: { node: ">=18" } + + is-docker@3.0.0: + resolution: + { + integrity: sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==, + } + engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 } + hasBin: true + + is-in-ssh@1.0.0: + resolution: + { + integrity: sha512-jYa6Q9rH90kR1vKB6NM7qqd1mge3Fx4Dhw5TVlK1MUBqhEOuCagrEHMevNuCcbECmXZ0ThXkRm+Ymr51HwEPAw==, + } + engines: { node: ">=20" } + + is-inside-container@1.0.0: + resolution: + { + integrity: sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==, + } + engines: { node: ">=14.16" } + hasBin: true + + is-wsl@3.1.1: + resolution: + { + integrity: sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==, + } + engines: { node: ">=16" } + + jschardet@3.1.4: + resolution: + { + integrity: sha512-/kmVISmrwVwtyYU40iQUOp3SUPk2dhNCMsZBQX0R1/jZ8maaXJ/oZIzUOiyOqcgtLnETFKYChbJ5iDC/eWmFHg==, + } + engines: { node: ">=0.1.90" } + + lightningcss-android-arm64@1.32.0: + resolution: + { + integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==, + } + engines: { node: ">= 12.0.0" } + cpu: [arm64] + os: [android] + + lightningcss-darwin-arm64@1.32.0: + resolution: + { + integrity: sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==, + } + engines: { node: ">= 12.0.0" } + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.32.0: + resolution: + { + integrity: sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==, + } + engines: { node: ">= 12.0.0" } + cpu: [x64] + os: [darwin] + + lightningcss-freebsd-x64@1.32.0: + resolution: + { + integrity: sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==, + } + engines: { node: ">= 12.0.0" } + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.32.0: + resolution: + { + integrity: sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==, + } + engines: { node: ">= 12.0.0" } + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.32.0: + resolution: + { + integrity: sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==, + } + engines: { node: ">= 12.0.0" } + cpu: [arm64] + os: [linux] + libc: [glibc] + + lightningcss-linux-arm64-musl@1.32.0: + resolution: + { + integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==, + } + engines: { node: ">= 12.0.0" } + cpu: [arm64] + os: [linux] + libc: [musl] + + lightningcss-linux-x64-gnu@1.32.0: + resolution: + { + integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==, + } + engines: { node: ">= 12.0.0" } + cpu: [x64] + os: [linux] + libc: [glibc] + + lightningcss-linux-x64-musl@1.32.0: + resolution: + { + integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==, + } + engines: { node: ">= 12.0.0" } + cpu: [x64] + os: [linux] + libc: [musl] + + lightningcss-win32-arm64-msvc@1.32.0: + resolution: + { + integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==, + } + engines: { node: ">= 12.0.0" } + cpu: [arm64] + os: [win32] + + lightningcss-win32-x64-msvc@1.32.0: + resolution: + { + integrity: sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==, + } + engines: { node: ">= 12.0.0" } + cpu: [x64] + os: [win32] + + lightningcss@1.32.0: + resolution: + { + integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==, + } + engines: { node: ">= 12.0.0" } + + lit-element@4.2.2: + resolution: + { + integrity: sha512-aFKhNToWxoyhkNDmWZwEva2SlQia+jfG0fjIWV//YeTaWrVnOxD89dPKfigCUspXFmjzOEUQpOkejH5Ly6sG0w==, + } + + lit-html@3.3.2: + resolution: + { + integrity: sha512-Qy9hU88zcmaxBXcc10ZpdK7cOLXvXpRoBxERdtqV9QOrfpMZZ6pSYP91LhpPtap3sFMUiL7Tw2RImbe0Al2/kw==, + } + + lit@3.3.2: + resolution: + { + integrity: sha512-NF9zbsP79l4ao2SNrH3NkfmFgN/hBYSQo90saIVI1o5GpjAdCPVstVzO1MrLOakHoEhYkrtRjPK6Ob521aoYWQ==, + } + + marked@14.0.0: + resolution: + { + integrity: sha512-uIj4+faQ+MgHgwUW1l2PsPglZLOLOT1uErt06dAPtx2kjteLAkbsd/0FiYg/MGS+i7ZKLb7w2WClxHkzOOuryQ==, + } + engines: { node: ">= 18" } + hasBin: true + + minimatch@5.1.9: + resolution: + { + integrity: sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==, + } + engines: { node: ">=10" } + + monaco-languageclient@10.7.0: + resolution: + { + integrity: sha512-oA5cOFixkF4bspVL2zMSn48LvlNR/Cu3vJ8MCVam3PdjobSULGgHtOASuZIi3FgWK42X1z8/6hrG0LCjvNu1Hw==, + } + engines: { node: ">=20.10.0", npm: ">=10.2.3" } + + nanoid@3.3.11: + resolution: + { + integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==, + } + engines: { node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1 } + hasBin: true + + open@11.0.0: + resolution: + { + integrity: sha512-smsWv2LzFjP03xmvFoJ331ss6h+jixfA4UUV/Bsiyuu4YJPfN+FIQGOIiv4w9/+MoHkfkJ22UIaQWRVFRfH6Vw==, + } + engines: { node: ">=20" } + + picocolors@1.1.1: + resolution: + { + integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==, + } + + picomatch@4.0.3: + resolution: + { + integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==, + } + engines: { node: ">=12" } + + postcss@8.5.8: + resolution: + { + integrity: sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==, + } + engines: { node: ^10 || ^12 || >=14 } + + powershell-utils@0.1.0: + resolution: + { + integrity: sha512-dM0jVuXJPsDN6DvRpea484tCUaMiXWjuCn++HGTqUWzGDjv5tZkEZldAJ/UMlqRYGFrD/etByo4/xOuC/snX2A==, + } + engines: { node: ">=20" } + + prettier@3.8.1: + resolution: + { + integrity: sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==, + } + engines: { node: ">=14" } + hasBin: true + + rolldown@1.0.0-rc.10: + resolution: + { + integrity: sha512-q7j6vvarRFmKpgJUT8HCAUljkgzEp4LAhPlJUvQhA5LA1SUL36s5QCysMutErzL3EbNOZOkoziSx9iZC4FddKA==, + } + engines: { node: ^20.19.0 || >=22.12.0 } + hasBin: true + + rollup-plugin-visualizer@7.0.1: + resolution: + { + integrity: sha512-UJUT4+1Ho4OcWmPYU3sYXgUqI8B8Ayfe06MX7y0qCJ1K8aGoKtR/NDd/2nZqM7ADkrzny+I99Ul7GgyoiVNAgg==, + } + engines: { node: ">=22" } + hasBin: true + peerDependencies: + rolldown: 1.x || ^1.0.0-beta || ^1.0.0-rc + rollup: 2.x || 3.x || 4.x + peerDependenciesMeta: + rolldown: + optional: true + rollup: + optional: true + + rollup@4.60.0: + resolution: + { + integrity: sha512-yqjxruMGBQJ2gG4HtjZtAfXArHomazDHoFwFFmZZl0r7Pdo7qCIXKqKHZc8yeoMgzJJ+pO6pEEHa+V7uzWlrAQ==, + } + engines: { node: ">=18.0.0", npm: ">=8.0.0" } + hasBin: true + + run-applescript@7.1.0: + resolution: + { + integrity: sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==, + } + engines: { node: ">=18" } + + semver@7.7.4: + resolution: + { + integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==, + } + engines: { node: ">=10" } + hasBin: true + + source-map-js@1.2.1: + resolution: + { + integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==, + } + engines: { node: ">=0.10.0" } + + source-map@0.7.6: + resolution: + { + integrity: sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==, + } + engines: { node: ">= 12" } + + string-width@7.2.0: + resolution: + { + integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==, + } + engines: { node: ">=18" } + + strip-ansi@7.2.0: + resolution: + { + integrity: sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==, + } + engines: { node: ">=12" } + + tinyglobby@0.2.15: + resolution: + { + integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==, + } + engines: { node: ">=12.0.0" } + + tslib@2.8.1: + resolution: + { + integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==, + } + + typescript@5.9.3: + resolution: + { + integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==, + } + engines: { node: ">=14.17" } + hasBin: true + + vite@7.3.1: + resolution: + { + integrity: sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==, + } + engines: { node: ^20.19.0 || >=22.12.0 } + hasBin: true + peerDependencies: + "@types/node": ^20.19.0 || >=22.12.0 + jiti: ">=1.21.0" + less: ^4.0.0 + lightningcss: ^1.21.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: ">=0.54.8" + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + "@types/node": + optional: true + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + vscode-jsonrpc@8.2.0: + resolution: + { + integrity: sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==, + } + engines: { node: ">=14.0.0" } + + vscode-jsonrpc@8.2.1: + resolution: + { + integrity: sha512-kdjOSJ2lLIn7r1rtrMbbNCHjyMPfRnowdKjBQ+mGq6NAW5QY2bEZC/khaC5OR8svbbjvLEaIXkOq45e2X9BIbQ==, + } + engines: { node: ">=14.0.0" } + + vscode-languageclient@9.0.1: + resolution: + { + integrity: sha512-JZiimVdvimEuHh5olxhxkht09m3JzUGwggb5eRUkzzJhZ2KjCN0nh55VfiED9oez9DyF8/fz1g1iBV3h+0Z2EA==, + } + engines: { vscode: ^1.82.0 } + + vscode-languageserver-protocol@3.17.5: + resolution: + { + integrity: sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==, + } + + vscode-languageserver-types@3.17.5: + resolution: + { + integrity: sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==, + } + + vscode-ws-jsonrpc@3.5.0: + resolution: + { + integrity: sha512-13ZDy7Od4AfEPK2HIfY3DtyRi4FVsvFql1yobVJrpIoHOKGGJpIjVvIJpMxkrHzCZzWlYlg+WEu2hrYkCTvM0Q==, + } + engines: { node: ">=20.10.0", npm: ">=10.2.3" } + + wrap-ansi@9.0.2: + resolution: + { + integrity: sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==, + } + engines: { node: ">=18" } + + wsl-utils@0.3.1: + resolution: + { + integrity: sha512-g/eziiSUNBSsdDJtCLB8bdYEUMj4jR7AGeUo96p/3dTafgjHhpF4RiCFPiRILwjQoDXx5MqkBr4fwWtR3Ky4Wg==, + } + engines: { node: ">=20" } + + y18n@5.0.8: + resolution: + { + integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==, + } + engines: { node: ">=10" } + + yargs-parser@22.0.0: + resolution: + { + integrity: sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==, + } + engines: { node: ^20.19.0 || ^22.12.0 || >=23 } + + yargs@18.0.0: + resolution: + { + integrity: sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg==, + } + engines: { node: ^20.19.0 || ^22.12.0 || >=23 } + +snapshots: + "@codingame/monaco-vscode-api@25.1.2": + dependencies: + "@codingame/monaco-vscode-base-service-override": 25.1.2 + "@codingame/monaco-vscode-environment-service-override": 25.1.2 + "@codingame/monaco-vscode-extensions-service-override": 25.1.2 + "@codingame/monaco-vscode-files-service-override": 25.1.2 + "@codingame/monaco-vscode-host-service-override": 25.1.2 + "@codingame/monaco-vscode-layout-service-override": 25.1.2 + "@codingame/monaco-vscode-quickaccess-service-override": 25.1.2 + "@vscode/iconv-lite-umd": 0.7.1 + dompurify: 3.3.1 + jschardet: 3.1.4 + marked: 14.0.0 + + "@codingame/monaco-vscode-base-service-override@25.1.2": + dependencies: + "@codingame/monaco-vscode-api": 25.1.2 + + "@codingame/monaco-vscode-bulk-edit-service-override@25.1.2": + dependencies: + "@codingame/monaco-vscode-api": 25.1.2 + + "@codingame/monaco-vscode-configuration-service-override@25.1.2": + dependencies: + "@codingame/monaco-vscode-api": 25.1.2 + "@codingame/monaco-vscode-files-service-override": 25.1.2 + + "@codingame/monaco-vscode-editor-api@25.1.2": + dependencies: + "@codingame/monaco-vscode-api": 25.1.2 + + "@codingame/monaco-vscode-editor-service-override@25.1.2": + dependencies: + "@codingame/monaco-vscode-api": 25.1.2 + + "@codingame/monaco-vscode-environment-service-override@25.1.2": + dependencies: + "@codingame/monaco-vscode-api": 25.1.2 + + "@codingame/monaco-vscode-extension-api@25.1.2": + dependencies: + "@codingame/monaco-vscode-api": 25.1.2 + "@codingame/monaco-vscode-extensions-service-override": 25.1.2 + + "@codingame/monaco-vscode-extensions-service-override@25.1.2": + dependencies: + "@codingame/monaco-vscode-api": 25.1.2 + "@codingame/monaco-vscode-files-service-override": 25.1.2 + + "@codingame/monaco-vscode-files-service-override@25.1.2": + dependencies: + "@codingame/monaco-vscode-api": 25.1.2 + + "@codingame/monaco-vscode-host-service-override@25.1.2": + dependencies: + "@codingame/monaco-vscode-api": 25.1.2 + + "@codingame/monaco-vscode-keybindings-service-override@25.1.2": + dependencies: + "@codingame/monaco-vscode-api": 25.1.2 + "@codingame/monaco-vscode-files-service-override": 25.1.2 + + "@codingame/monaco-vscode-language-pack-cs@25.1.2": + dependencies: + "@codingame/monaco-vscode-api": 25.1.2 + + "@codingame/monaco-vscode-language-pack-de@25.1.2": + dependencies: + "@codingame/monaco-vscode-api": 25.1.2 + + "@codingame/monaco-vscode-language-pack-es@25.1.2": + dependencies: + "@codingame/monaco-vscode-api": 25.1.2 + + "@codingame/monaco-vscode-language-pack-fr@25.1.2": + dependencies: + "@codingame/monaco-vscode-api": 25.1.2 + + "@codingame/monaco-vscode-language-pack-it@25.1.2": + dependencies: + "@codingame/monaco-vscode-api": 25.1.2 + + "@codingame/monaco-vscode-language-pack-ja@25.1.2": + dependencies: + "@codingame/monaco-vscode-api": 25.1.2 + + "@codingame/monaco-vscode-language-pack-ko@25.1.2": + dependencies: + "@codingame/monaco-vscode-api": 25.1.2 + + "@codingame/monaco-vscode-language-pack-pl@25.1.2": + dependencies: + "@codingame/monaco-vscode-api": 25.1.2 + + "@codingame/monaco-vscode-language-pack-pt-br@25.1.2": + dependencies: + "@codingame/monaco-vscode-api": 25.1.2 + + "@codingame/monaco-vscode-language-pack-qps-ploc@25.1.2": + dependencies: + "@codingame/monaco-vscode-api": 25.1.2 + + "@codingame/monaco-vscode-language-pack-ru@25.1.2": + dependencies: + "@codingame/monaco-vscode-api": 25.1.2 + + "@codingame/monaco-vscode-language-pack-tr@25.1.2": + dependencies: + "@codingame/monaco-vscode-api": 25.1.2 + + "@codingame/monaco-vscode-language-pack-zh-hans@25.1.2": + dependencies: + "@codingame/monaco-vscode-api": 25.1.2 + + "@codingame/monaco-vscode-language-pack-zh-hant@25.1.2": + dependencies: + "@codingame/monaco-vscode-api": 25.1.2 + + "@codingame/monaco-vscode-languages-service-override@25.1.2": + dependencies: + "@codingame/monaco-vscode-api": 25.1.2 + "@codingame/monaco-vscode-files-service-override": 25.1.2 + + "@codingame/monaco-vscode-layout-service-override@25.1.2": + dependencies: + "@codingame/monaco-vscode-api": 25.1.2 + + "@codingame/monaco-vscode-localization-service-override@25.1.2": + dependencies: + "@codingame/monaco-vscode-api": 25.1.2 + + "@codingame/monaco-vscode-log-service-override@25.1.2": + dependencies: + "@codingame/monaco-vscode-api": 25.1.2 + "@codingame/monaco-vscode-environment-service-override": 25.1.2 + + "@codingame/monaco-vscode-model-service-override@25.1.2": + dependencies: + "@codingame/monaco-vscode-api": 25.1.2 + + "@codingame/monaco-vscode-monarch-service-override@25.1.2": + dependencies: + "@codingame/monaco-vscode-api": 25.1.2 + + "@codingame/monaco-vscode-quickaccess-service-override@25.1.2": + dependencies: + "@codingame/monaco-vscode-api": 25.1.2 + + "@codingame/monaco-vscode-textmate-service-override@25.1.2": + dependencies: + "@codingame/monaco-vscode-api": 25.1.2 + "@codingame/monaco-vscode-files-service-override": 25.1.2 + + "@codingame/monaco-vscode-theme-defaults-default-extension@25.1.2": + dependencies: + "@codingame/monaco-vscode-api": 25.1.2 + + "@codingame/monaco-vscode-theme-service-override@25.1.2": + dependencies: + "@codingame/monaco-vscode-api": 25.1.2 + "@codingame/monaco-vscode-files-service-override": 25.1.2 + + "@codingame/monaco-vscode-view-banner-service-override@25.1.2": + dependencies: + "@codingame/monaco-vscode-api": 25.1.2 + + "@codingame/monaco-vscode-view-common-service-override@25.1.2": + dependencies: + "@codingame/monaco-vscode-api": 25.1.2 + "@codingame/monaco-vscode-bulk-edit-service-override": 25.1.2 + + "@codingame/monaco-vscode-view-status-bar-service-override@25.1.2": + dependencies: + "@codingame/monaco-vscode-api": 25.1.2 + + "@codingame/monaco-vscode-view-title-bar-service-override@25.1.2": + dependencies: + "@codingame/monaco-vscode-api": 25.1.2 + + "@codingame/monaco-vscode-views-service-override@25.1.2": + dependencies: + "@codingame/monaco-vscode-api": 25.1.2 + "@codingame/monaco-vscode-keybindings-service-override": 25.1.2 + "@codingame/monaco-vscode-layout-service-override": 25.1.2 + "@codingame/monaco-vscode-quickaccess-service-override": 25.1.2 + "@codingame/monaco-vscode-view-common-service-override": 25.1.2 + + "@codingame/monaco-vscode-workbench-service-override@25.1.2": + dependencies: + "@codingame/monaco-vscode-api": 25.1.2 + "@codingame/monaco-vscode-keybindings-service-override": 25.1.2 + "@codingame/monaco-vscode-quickaccess-service-override": 25.1.2 + "@codingame/monaco-vscode-view-banner-service-override": 25.1.2 + "@codingame/monaco-vscode-view-common-service-override": 25.1.2 + "@codingame/monaco-vscode-view-status-bar-service-override": 25.1.2 + "@codingame/monaco-vscode-view-title-bar-service-override": 25.1.2 + + "@emnapi/core@1.9.1": + dependencies: + "@emnapi/wasi-threads": 1.2.0 + tslib: 2.8.1 + optional: true + + "@emnapi/runtime@1.9.1": + dependencies: + tslib: 2.8.1 + optional: true + + "@emnapi/wasi-threads@1.2.0": + dependencies: + tslib: 2.8.1 + optional: true + + "@esbuild/aix-ppc64@0.27.4": + optional: true + + "@esbuild/android-arm64@0.27.4": + optional: true + + "@esbuild/android-arm@0.27.4": + optional: true + + "@esbuild/android-x64@0.27.4": + optional: true + + "@esbuild/darwin-arm64@0.27.4": + optional: true + + "@esbuild/darwin-x64@0.27.4": + optional: true + + "@esbuild/freebsd-arm64@0.27.4": + optional: true + + "@esbuild/freebsd-x64@0.27.4": + optional: true + + "@esbuild/linux-arm64@0.27.4": + optional: true + + "@esbuild/linux-arm@0.27.4": + optional: true + + "@esbuild/linux-ia32@0.27.4": + optional: true + + "@esbuild/linux-loong64@0.27.4": + optional: true + + "@esbuild/linux-mips64el@0.27.4": + optional: true + + "@esbuild/linux-ppc64@0.27.4": + optional: true + + "@esbuild/linux-riscv64@0.27.4": + optional: true + + "@esbuild/linux-s390x@0.27.4": + optional: true + + "@esbuild/linux-x64@0.27.4": + optional: true + + "@esbuild/netbsd-arm64@0.27.4": + optional: true + + "@esbuild/netbsd-x64@0.27.4": + optional: true + + "@esbuild/openbsd-arm64@0.27.4": + optional: true + + "@esbuild/openbsd-x64@0.27.4": + optional: true + + "@esbuild/openharmony-arm64@0.27.4": + optional: true + + "@esbuild/sunos-x64@0.27.4": + optional: true + + "@esbuild/win32-arm64@0.27.4": + optional: true + + "@esbuild/win32-ia32@0.27.4": + optional: true + + "@esbuild/win32-x64@0.27.4": + optional: true + + "@lit-labs/ssr-dom-shim@1.5.1": {} + + "@lit/reactive-element@2.1.2": + dependencies: + "@lit-labs/ssr-dom-shim": 1.5.1 + + "@napi-rs/wasm-runtime@1.1.1": + dependencies: + "@emnapi/core": 1.9.1 + "@emnapi/runtime": 1.9.1 + "@tybys/wasm-util": 0.10.1 + optional: true + + "@oxc-project/types@0.120.0": + optional: true + + "@rolldown/binding-android-arm64@1.0.0-rc.10": + optional: true + + "@rolldown/binding-darwin-arm64@1.0.0-rc.10": + optional: true + + "@rolldown/binding-darwin-x64@1.0.0-rc.10": + optional: true + + "@rolldown/binding-freebsd-x64@1.0.0-rc.10": + optional: true + + "@rolldown/binding-linux-arm-gnueabihf@1.0.0-rc.10": + optional: true + + "@rolldown/binding-linux-arm64-gnu@1.0.0-rc.10": + optional: true + + "@rolldown/binding-linux-arm64-musl@1.0.0-rc.10": + optional: true + + "@rolldown/binding-linux-ppc64-gnu@1.0.0-rc.10": + optional: true + + "@rolldown/binding-linux-s390x-gnu@1.0.0-rc.10": + optional: true + + "@rolldown/binding-linux-x64-gnu@1.0.0-rc.10": + optional: true + + "@rolldown/binding-linux-x64-musl@1.0.0-rc.10": + optional: true + + "@rolldown/binding-openharmony-arm64@1.0.0-rc.10": + optional: true + + "@rolldown/binding-wasm32-wasi@1.0.0-rc.10": + dependencies: + "@napi-rs/wasm-runtime": 1.1.1 + optional: true + + "@rolldown/binding-win32-arm64-msvc@1.0.0-rc.10": + optional: true + + "@rolldown/binding-win32-x64-msvc@1.0.0-rc.10": + optional: true + + "@rolldown/pluginutils@1.0.0-rc.10": + optional: true + + "@rollup/rollup-android-arm-eabi@4.60.0": + optional: true + + "@rollup/rollup-android-arm64@4.60.0": + optional: true + + "@rollup/rollup-darwin-arm64@4.60.0": + optional: true + + "@rollup/rollup-darwin-x64@4.60.0": + optional: true + + "@rollup/rollup-freebsd-arm64@4.60.0": + optional: true + + "@rollup/rollup-freebsd-x64@4.60.0": + optional: true + + "@rollup/rollup-linux-arm-gnueabihf@4.60.0": + optional: true + + "@rollup/rollup-linux-arm-musleabihf@4.60.0": + optional: true + + "@rollup/rollup-linux-arm64-gnu@4.60.0": + optional: true + + "@rollup/rollup-linux-arm64-musl@4.60.0": + optional: true + + "@rollup/rollup-linux-loong64-gnu@4.60.0": + optional: true + + "@rollup/rollup-linux-loong64-musl@4.60.0": + optional: true + + "@rollup/rollup-linux-ppc64-gnu@4.60.0": + optional: true + + "@rollup/rollup-linux-ppc64-musl@4.60.0": + optional: true + + "@rollup/rollup-linux-riscv64-gnu@4.60.0": + optional: true + + "@rollup/rollup-linux-riscv64-musl@4.60.0": + optional: true + + "@rollup/rollup-linux-s390x-gnu@4.60.0": + optional: true + + "@rollup/rollup-linux-x64-gnu@4.60.0": + optional: true + + "@rollup/rollup-linux-x64-musl@4.60.0": + optional: true + + "@rollup/rollup-openbsd-x64@4.60.0": + optional: true + + "@rollup/rollup-openharmony-arm64@4.60.0": + optional: true + + "@rollup/rollup-win32-arm64-msvc@4.60.0": + optional: true + + "@rollup/rollup-win32-ia32-msvc@4.60.0": + optional: true + + "@rollup/rollup-win32-x64-gnu@4.60.0": + optional: true + + "@rollup/rollup-win32-x64-msvc@4.60.0": + optional: true + + "@tybys/wasm-util@0.10.1": + dependencies: + tslib: 2.8.1 + optional: true + + "@types/estree@1.0.8": {} + + "@types/trusted-types@2.0.7": {} + + "@vscode/iconv-lite-umd@0.7.1": {} + + ansi-regex@6.2.2: {} + + ansi-styles@6.2.3: {} + + balanced-match@1.0.2: {} + + brace-expansion@2.0.2: + dependencies: + balanced-match: 1.0.2 + + bundle-name@4.1.0: + dependencies: + run-applescript: 7.1.0 + + cliui@9.0.1: + dependencies: + string-width: 7.2.0 + strip-ansi: 7.2.0 + wrap-ansi: 9.0.2 + + default-browser-id@5.0.1: {} + + default-browser@5.5.0: + dependencies: + bundle-name: 4.1.0 + default-browser-id: 5.0.1 + + define-lazy-prop@3.0.0: {} + + detect-libc@2.1.2: + optional: true + + dompurify@3.3.1: + optionalDependencies: + "@types/trusted-types": 2.0.7 + + emoji-regex@10.6.0: {} + + esbuild@0.27.4: + optionalDependencies: + "@esbuild/aix-ppc64": 0.27.4 + "@esbuild/android-arm": 0.27.4 + "@esbuild/android-arm64": 0.27.4 + "@esbuild/android-x64": 0.27.4 + "@esbuild/darwin-arm64": 0.27.4 + "@esbuild/darwin-x64": 0.27.4 + "@esbuild/freebsd-arm64": 0.27.4 + "@esbuild/freebsd-x64": 0.27.4 + "@esbuild/linux-arm": 0.27.4 + "@esbuild/linux-arm64": 0.27.4 + "@esbuild/linux-ia32": 0.27.4 + "@esbuild/linux-loong64": 0.27.4 + "@esbuild/linux-mips64el": 0.27.4 + "@esbuild/linux-ppc64": 0.27.4 + "@esbuild/linux-riscv64": 0.27.4 + "@esbuild/linux-s390x": 0.27.4 + "@esbuild/linux-x64": 0.27.4 + "@esbuild/netbsd-arm64": 0.27.4 + "@esbuild/netbsd-x64": 0.27.4 + "@esbuild/openbsd-arm64": 0.27.4 + "@esbuild/openbsd-x64": 0.27.4 + "@esbuild/openharmony-arm64": 0.27.4 + "@esbuild/sunos-x64": 0.27.4 + "@esbuild/win32-arm64": 0.27.4 + "@esbuild/win32-ia32": 0.27.4 + "@esbuild/win32-x64": 0.27.4 + + escalade@3.2.0: {} + + fdir@6.5.0(picomatch@4.0.3): + optionalDependencies: + picomatch: 4.0.3 + + fsevents@2.3.3: + optional: true + + get-caller-file@2.0.5: {} + + get-east-asian-width@1.5.0: {} + + is-docker@3.0.0: {} + + is-in-ssh@1.0.0: {} + + is-inside-container@1.0.0: + dependencies: + is-docker: 3.0.0 + + is-wsl@3.1.1: + dependencies: + is-inside-container: 1.0.0 + + jschardet@3.1.4: {} + + lightningcss-android-arm64@1.32.0: + optional: true + + lightningcss-darwin-arm64@1.32.0: + optional: true + + lightningcss-darwin-x64@1.32.0: + optional: true + + lightningcss-freebsd-x64@1.32.0: + optional: true + + lightningcss-linux-arm-gnueabihf@1.32.0: + optional: true + + lightningcss-linux-arm64-gnu@1.32.0: + optional: true + + lightningcss-linux-arm64-musl@1.32.0: + optional: true + + lightningcss-linux-x64-gnu@1.32.0: + optional: true + + lightningcss-linux-x64-musl@1.32.0: + optional: true + + lightningcss-win32-arm64-msvc@1.32.0: + optional: true + + lightningcss-win32-x64-msvc@1.32.0: + optional: true + + lightningcss@1.32.0: + dependencies: + detect-libc: 2.1.2 + 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 + optional: true + + lit-element@4.2.2: + dependencies: + "@lit-labs/ssr-dom-shim": 1.5.1 + "@lit/reactive-element": 2.1.2 + lit-html: 3.3.2 + + lit-html@3.3.2: + dependencies: + "@types/trusted-types": 2.0.7 + + lit@3.3.2: + dependencies: + "@lit/reactive-element": 2.1.2 + lit-element: 4.2.2 + lit-html: 3.3.2 + + marked@14.0.0: {} + + minimatch@5.1.9: + dependencies: + brace-expansion: 2.0.2 + + monaco-languageclient@10.7.0: + dependencies: + "@codingame/monaco-vscode-api": 25.1.2 + "@codingame/monaco-vscode-configuration-service-override": 25.1.2 + "@codingame/monaco-vscode-editor-api": 25.1.2 + "@codingame/monaco-vscode-editor-service-override": 25.1.2 + "@codingame/monaco-vscode-extension-api": 25.1.2 + "@codingame/monaco-vscode-extensions-service-override": 25.1.2 + "@codingame/monaco-vscode-language-pack-cs": 25.1.2 + "@codingame/monaco-vscode-language-pack-de": 25.1.2 + "@codingame/monaco-vscode-language-pack-es": 25.1.2 + "@codingame/monaco-vscode-language-pack-fr": 25.1.2 + "@codingame/monaco-vscode-language-pack-it": 25.1.2 + "@codingame/monaco-vscode-language-pack-ja": 25.1.2 + "@codingame/monaco-vscode-language-pack-ko": 25.1.2 + "@codingame/monaco-vscode-language-pack-pl": 25.1.2 + "@codingame/monaco-vscode-language-pack-pt-br": 25.1.2 + "@codingame/monaco-vscode-language-pack-qps-ploc": 25.1.2 + "@codingame/monaco-vscode-language-pack-ru": 25.1.2 + "@codingame/monaco-vscode-language-pack-tr": 25.1.2 + "@codingame/monaco-vscode-language-pack-zh-hans": 25.1.2 + "@codingame/monaco-vscode-language-pack-zh-hant": 25.1.2 + "@codingame/monaco-vscode-languages-service-override": 25.1.2 + "@codingame/monaco-vscode-localization-service-override": 25.1.2 + "@codingame/monaco-vscode-log-service-override": 25.1.2 + "@codingame/monaco-vscode-model-service-override": 25.1.2 + "@codingame/monaco-vscode-monarch-service-override": 25.1.2 + "@codingame/monaco-vscode-textmate-service-override": 25.1.2 + "@codingame/monaco-vscode-theme-defaults-default-extension": 25.1.2 + "@codingame/monaco-vscode-theme-service-override": 25.1.2 + "@codingame/monaco-vscode-views-service-override": 25.1.2 + "@codingame/monaco-vscode-workbench-service-override": 25.1.2 + vscode: "@codingame/monaco-vscode-extension-api@25.1.2" + vscode-languageclient: 9.0.1 + vscode-languageserver-protocol: 3.17.5 + vscode-ws-jsonrpc: 3.5.0 + + nanoid@3.3.11: {} + + open@11.0.0: + dependencies: + default-browser: 5.5.0 + define-lazy-prop: 3.0.0 + is-in-ssh: 1.0.0 + is-inside-container: 1.0.0 + powershell-utils: 0.1.0 + wsl-utils: 0.3.1 + + picocolors@1.1.1: {} + + picomatch@4.0.3: {} + + postcss@8.5.8: + dependencies: + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + powershell-utils@0.1.0: {} + + prettier@3.8.1: {} + + rolldown@1.0.0-rc.10: + dependencies: + "@oxc-project/types": 0.120.0 + "@rolldown/pluginutils": 1.0.0-rc.10 + optionalDependencies: + "@rolldown/binding-android-arm64": 1.0.0-rc.10 + "@rolldown/binding-darwin-arm64": 1.0.0-rc.10 + "@rolldown/binding-darwin-x64": 1.0.0-rc.10 + "@rolldown/binding-freebsd-x64": 1.0.0-rc.10 + "@rolldown/binding-linux-arm-gnueabihf": 1.0.0-rc.10 + "@rolldown/binding-linux-arm64-gnu": 1.0.0-rc.10 + "@rolldown/binding-linux-arm64-musl": 1.0.0-rc.10 + "@rolldown/binding-linux-ppc64-gnu": 1.0.0-rc.10 + "@rolldown/binding-linux-s390x-gnu": 1.0.0-rc.10 + "@rolldown/binding-linux-x64-gnu": 1.0.0-rc.10 + "@rolldown/binding-linux-x64-musl": 1.0.0-rc.10 + "@rolldown/binding-openharmony-arm64": 1.0.0-rc.10 + "@rolldown/binding-wasm32-wasi": 1.0.0-rc.10 + "@rolldown/binding-win32-arm64-msvc": 1.0.0-rc.10 + "@rolldown/binding-win32-x64-msvc": 1.0.0-rc.10 + optional: true + + rollup-plugin-visualizer@7.0.1(rolldown@1.0.0-rc.10)(rollup@4.60.0): + dependencies: + open: 11.0.0 + picomatch: 4.0.3 + source-map: 0.7.6 + yargs: 18.0.0 + optionalDependencies: + rolldown: 1.0.0-rc.10 + rollup: 4.60.0 + + rollup@4.60.0: + dependencies: + "@types/estree": 1.0.8 + optionalDependencies: + "@rollup/rollup-android-arm-eabi": 4.60.0 + "@rollup/rollup-android-arm64": 4.60.0 + "@rollup/rollup-darwin-arm64": 4.60.0 + "@rollup/rollup-darwin-x64": 4.60.0 + "@rollup/rollup-freebsd-arm64": 4.60.0 + "@rollup/rollup-freebsd-x64": 4.60.0 + "@rollup/rollup-linux-arm-gnueabihf": 4.60.0 + "@rollup/rollup-linux-arm-musleabihf": 4.60.0 + "@rollup/rollup-linux-arm64-gnu": 4.60.0 + "@rollup/rollup-linux-arm64-musl": 4.60.0 + "@rollup/rollup-linux-loong64-gnu": 4.60.0 + "@rollup/rollup-linux-loong64-musl": 4.60.0 + "@rollup/rollup-linux-ppc64-gnu": 4.60.0 + "@rollup/rollup-linux-ppc64-musl": 4.60.0 + "@rollup/rollup-linux-riscv64-gnu": 4.60.0 + "@rollup/rollup-linux-riscv64-musl": 4.60.0 + "@rollup/rollup-linux-s390x-gnu": 4.60.0 + "@rollup/rollup-linux-x64-gnu": 4.60.0 + "@rollup/rollup-linux-x64-musl": 4.60.0 + "@rollup/rollup-openbsd-x64": 4.60.0 + "@rollup/rollup-openharmony-arm64": 4.60.0 + "@rollup/rollup-win32-arm64-msvc": 4.60.0 + "@rollup/rollup-win32-ia32-msvc": 4.60.0 + "@rollup/rollup-win32-x64-gnu": 4.60.0 + "@rollup/rollup-win32-x64-msvc": 4.60.0 + fsevents: 2.3.3 + + run-applescript@7.1.0: {} + + semver@7.7.4: {} + + source-map-js@1.2.1: {} + + source-map@0.7.6: {} + + string-width@7.2.0: + dependencies: + emoji-regex: 10.6.0 + get-east-asian-width: 1.5.0 + strip-ansi: 7.2.0 + + strip-ansi@7.2.0: + dependencies: + ansi-regex: 6.2.2 + + tinyglobby@0.2.15: + dependencies: + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + + tslib@2.8.1: + optional: true + + typescript@5.9.3: {} + + vite@7.3.1(lightningcss@1.32.0): + dependencies: + esbuild: 0.27.4 + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + postcss: 8.5.8 + rollup: 4.60.0 + tinyglobby: 0.2.15 + optionalDependencies: + fsevents: 2.3.3 + lightningcss: 1.32.0 + + vscode-jsonrpc@8.2.0: {} + + vscode-jsonrpc@8.2.1: {} + + vscode-languageclient@9.0.1: + dependencies: + minimatch: 5.1.9 + semver: 7.7.4 + vscode-languageserver-protocol: 3.17.5 + + vscode-languageserver-protocol@3.17.5: + dependencies: + vscode-jsonrpc: 8.2.0 + vscode-languageserver-types: 3.17.5 + + vscode-languageserver-types@3.17.5: {} + + vscode-ws-jsonrpc@3.5.0: + dependencies: + vscode-jsonrpc: 8.2.1 + + wrap-ansi@9.0.2: + dependencies: + ansi-styles: 6.2.3 + string-width: 7.2.0 + strip-ansi: 7.2.0 + + wsl-utils@0.3.1: + dependencies: + is-wsl: 3.1.1 + powershell-utils: 0.1.0 + + y18n@5.0.8: {} + + yargs-parser@22.0.0: {} + + yargs@18.0.0: + dependencies: + cliui: 9.0.1 + escalade: 3.2.0 + get-caller-file: 2.0.5 + string-width: 7.2.0 + y18n: 5.0.8 + yargs-parser: 22.0.0 diff --git a/playground/public/apple-touch-icon.png b/playground/public/apple-touch-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..add3813594fa7b2ba8f81c806c0233ac2bd1cb74 GIT binary patch literal 4999 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 Yw;|D-+uI-bd+C7bk3_>M(bGB6zuQ9=DF6Tf literal 0 HcmV?d00001 diff --git a/playground/public/favicon.svg b/playground/public/favicon.svg new file mode 100644 index 000000000..cd1d1fcf2 --- /dev/null +++ b/playground/public/favicon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/playground/public/web-app-manifest-192x192.png b/playground/public/web-app-manifest-192x192.png new file mode 100644 index 0000000000000000000000000000000000000000..bba1f3c4db95d67322eaf48f4a8e0f51b8bdf580 GIT binary patch literal 5353 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 literal 0 HcmV?d00001 diff --git a/playground/public/web-app-manifest-512x512.png b/playground/public/web-app-manifest-512x512.png new file mode 100644 index 0000000000000000000000000000000000000000..6f3b359a5f452e58bb5106dcec4b8007705ab82c GIT binary patch literal 18176 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 literal 0 HcmV?d00001 diff --git a/playground/public/yara-mark.svg b/playground/public/yara-mark.svg new file mode 100644 index 000000000..7a99ccb30 --- /dev/null +++ b/playground/public/yara-mark.svg @@ -0,0 +1,49 @@ + + + + + + + + + + + + + diff --git a/playground/src/app/yara-playground-app.ts b/playground/src/app/yara-playground-app.ts new file mode 100644 index 000000000..7c8805db3 --- /dev/null +++ b/playground/src/app/yara-playground-app.ts @@ -0,0 +1,361 @@ +import { DEFAULT_SAMPLE_INPUT, DEFAULT_SAMPLE_RULE } from "../data/sample"; +import type { + ExecutionState, + LoadedSampleFile, + ResultMode, + SampleMode, +} from "../types/execution"; +import { LitElement, html } from "lit"; +import { customElement, query, state } from "lit/decorators.js"; + +import { YaraFile } from "../components/yara-file"; +import "../components/yara-result-panel"; +import "../components/yara-status-bar"; +import { SplitResizeController } from "../controllers/split-resize-controller"; +import { + createPlainTextEditor, + createYaraEditor, + type EditorHandle, +} from "../editor/yara-monaco"; +import { summarizeResult } from "../results/summarize-result"; +import { getWasmYaraEngine } from "../services/wasm-yara-engine"; +import type { ServiceStatus } from "../types/service-status"; +import type { YaraEngine } from "../services/yara-engine"; + +const INITIAL_EXECUTION: ExecutionState = { + raw: { + message: "Local YARA-X playground ready", + hint: "Edit the rule, tweak the sample text, then run the scanner.", + }, + durationMs: null, + summary: summarizeResult({}, "scan"), +}; + +@customElement("yara-playground-app") +export class YaraPlaygroundApp extends LitElement { + @query(".rule-editor") + private ruleEditorHost!: HTMLDivElement; + + @query(".sample-editor") + private sampleEditorHost!: HTMLDivElement; + + @query(".editor-region") + private editorRegion!: HTMLDivElement; + + @query(".workspace") + private workspace!: HTMLDivElement; + + @query("yara-file") + private filePane!: YaraFile; + + @state() + private resultMode: ResultMode = "summary"; + + @state() + private sampleMode: SampleMode = "text"; + + @state() + private isBusy = false; + + @state() + private lspStatus: ServiceStatus = "idle"; + + @state() + private coreStatus: ServiceStatus = "idle"; + + @state() + private editorSplit = 49.6; + + @state() + private workspaceSplit = 50; + + @state() + private execution = INITIAL_EXECUTION; + + @state() + private loadedSampleFile: LoadedSampleFile | null = null; + + private ruleEditor?: EditorHandle; + private sampleEditor?: EditorHandle; + private engine?: YaraEngine; + + private readonly editorResizeController = new SplitResizeController(this, { + getElement: () => this.editorRegion ?? null, + getAxis: () => (this.isCompactLayout ? "y" : "x"), + min: 28, + max: 72, + onChange: (value) => { + this.editorSplit = value; + }, + }); + + private readonly workspaceResizeController = new SplitResizeController(this, { + getElement: () => this.workspace ?? null, + getAxis: () => "y", + min: 32, + max: 78, + onChange: (value) => { + this.workspaceSplit = value; + }, + }); + + protected createRenderRoot() { + // Disable Shadow DOM so Monaco and its floating widgets can render correctly. + return this; + } + + private readonly handleKeydown = (event: KeyboardEvent) => { + if ( + (event.metaKey || event.ctrlKey) && + !event.shiftKey && + event.key.toLowerCase() === "s" + ) { + event.preventDefault(); + void this.formatRule(); + return; + } + + if ( + (event.metaKey || event.ctrlKey) && + event.shiftKey && + event.key.toLowerCase() === "f" + ) { + event.preventDefault(); + void this.formatRule(); + return; + } + + if ((event.metaKey || event.ctrlKey) && event.key === "Enter") { + event.preventDefault(); + void this.runScan(); + } + }; + + connectedCallback() { + super.connectedCallback(); + window.addEventListener("keydown", this.handleKeydown); + } + + protected async firstUpdated() { + await this.filePane.updateComplete; + + try { + const [ruleEditor, sampleEditor] = await Promise.all([ + createYaraEditor(this.ruleEditorHost, DEFAULT_SAMPLE_RULE), + createPlainTextEditor(this.sampleEditorHost, DEFAULT_SAMPLE_INPUT), + ]); + + this.ruleEditor = ruleEditor; + this.sampleEditor = sampleEditor; + this.lspStatus = "idle"; + } catch (error) { + console.error("failed to initialize editors", error); + this.lspStatus = "error"; + this.execution = { + raw: { + errors: [error instanceof Error ? error.message : String(error)], + warnings: [], + matching_rules: [], + non_matching_rules: [], + }, + durationMs: null, + summary: summarizeResult( + { + errors: [error instanceof Error ? error.message : String(error)], + }, + "scan", + ), + }; + } + } + + disconnectedCallback() { + window.removeEventListener("keydown", this.handleKeydown); + this.ruleEditor?.dispose(); + this.sampleEditor?.dispose(); + super.disconnectedCallback(); + } + + private get editorsReady() { + return Boolean( + this.ruleEditor && this.sampleEditor && this.lspStatus === "ready", + ); + } + + private get canRun() { + return ( + this.editorsReady && + !this.isBusy && + (this.sampleMode === "text" || this.loadedSampleFile !== null) + ); + } + + private get isCompactLayout() { + return window.innerWidth <= 900; + } + + private async ensureEngine() { + this.coreStatus = "loading"; + + try { + if (!this.engine) { + this.engine = getWasmYaraEngine(); + } + + this.coreStatus = "ready"; + return this.engine; + } catch (error) { + this.coreStatus = "error"; + throw error; + } + } + + private async runScan() { + if (!this.canRun) return; + + this.isBusy = true; + const startedAt = performance.now(); + + try { + const engine = await this.ensureEngine(); + let sampleBytes: Uint8Array; + + if (this.sampleMode === "file") { + sampleBytes = this.loadedSampleFile?.bytes ?? new Uint8Array(); + } else { + sampleBytes = new TextEncoder().encode( + this.sampleEditor?.getValue() ?? "", + ); + } + + const compiler = await engine.createCompiler(); + compiler.addSource(this.ruleEditor?.getValue() ?? ""); + + const rules = compiler.build(); + const raw = rules.scan(sampleBytes); + + this.execution = { + raw, + durationMs: Math.round(performance.now() - startedAt), + summary: summarizeResult(raw, "scan"), + }; + } catch (error) { + const message = error instanceof Error ? error.message : String(error); + const raw = { + errors: [message], + warnings: [], + matching_rules: [], + non_matching_rules: [], + }; + + this.execution = { + raw, + durationMs: Math.round(performance.now() - startedAt), + summary: summarizeResult(raw, "scan"), + }; + } finally { + this.isBusy = false; + } + } + + private async formatRule() { + if (!this.ruleEditor || this.lspStatus !== "ready") return; + + const formatted = await this.ruleEditor.format(); + if (!formatted) { + console.warn("format action is not available for the YARA editor"); + } + } + + private handleRunRequest = () => { + void this.runScan(); + }; + + private handleSampleModeChange = (event: CustomEvent) => { + this.sampleMode = event.detail; + }; + + private handleSampleFileLoad = (event: CustomEvent) => { + this.loadedSampleFile = event.detail; + this.sampleMode = "file"; + }; + + private handleSampleFileClear = () => { + this.loadedSampleFile = null; + this.sampleMode = "text"; + }; + + private handleResultModeChange = (event: CustomEvent) => { + this.resultMode = event.detail; + }; + + render() { + return html` +
+ + +
+
+
+
+
+

Rule editor

+
+ main.yar +
+
+
+ + + + +
+ + + + +
+
+ `; + } +} diff --git a/playground/src/components/yara-file.ts b/playground/src/components/yara-file.ts new file mode 100644 index 000000000..7cf92d2b5 --- /dev/null +++ b/playground/src/components/yara-file.ts @@ -0,0 +1,246 @@ +import { LitElement, html } from "lit"; +import type { LoadedSampleFile, SampleMode } from "../types/execution"; +import { customElement, property, query, state } from "lit/decorators.js"; + +import { classMap } from "lit/directives/class-map.js"; + +@customElement("yara-file") +export class YaraFile extends LitElement { + @property({ type: String }) + sampleMode: SampleMode = "text"; + + @property({ attribute: false }) + loadedSampleFile: LoadedSampleFile | null = null; + + @query("#sample-file-input") + private sampleFileInput!: HTMLInputElement; + + @state() + private isDragActive = false; + + protected createRenderRoot() { + return this; + } + + private dispatchSampleModeChange(mode: SampleMode) { + this.dispatchEvent( + new CustomEvent("sample-mode-change", { + detail: mode, + bubbles: true, + composed: true, + }), + ); + } + + private async createLoadedSampleFile(file: File): Promise { + return { + name: file.name, + size: file.size, + bytes: new Uint8Array(await file.arrayBuffer()), + }; + } + + private async emitLoadedSampleFile(file: File) { + const loadedSampleFile = await this.createLoadedSampleFile(file); + + this.dispatchEvent( + new CustomEvent("sample-file-load", { + detail: loadedSampleFile, + bubbles: true, + composed: true, + }), + ); + } + + private openSampleFilePicker = () => { + this.sampleFileInput?.click(); + }; + + private async handleFileChange(event: Event) { + const input = event.target as HTMLInputElement; + const file = input.files?.[0]; + + if (!file) return; + + await this.emitLoadedSampleFile(file); + input.value = ""; + } + + private handleDragEnter = (event: DragEvent) => { + if (this.sampleMode !== "file") return; + if (!event.dataTransfer?.types.includes("Files")) return; + + event.preventDefault(); + this.isDragActive = true; + }; + + private handleDragOver = (event: DragEvent) => { + if (this.sampleMode !== "file") return; + if (!event.dataTransfer?.types.includes("Files")) return; + + event.preventDefault(); + event.dataTransfer.dropEffect = "copy"; + this.isDragActive = true; + }; + + private handleDragLeave = (event: DragEvent) => { + if (this.sampleMode !== "file") return; + + const nextTarget = event.relatedTarget as Node | null; + + if (nextTarget && event.currentTarget instanceof Node) { + if (event.currentTarget.contains(nextTarget)) return; + } + + this.isDragActive = false; + }; + + private handleDrop = async (event: DragEvent) => { + if (this.sampleMode !== "file") return; + + event.preventDefault(); + this.isDragActive = false; + + const file = event.dataTransfer?.files?.[0]; + + if (!file) return; + + await this.emitLoadedSampleFile(file); + }; + + private clearLoadedFile = () => { + this.dispatchEvent( + new CustomEvent("sample-file-clear", { + bubbles: true, + composed: true, + }), + ); + }; + + private formatBytes(size: number) { + if (size < 1024) return `${size} B`; + if (size < 1024 * 1024) return `${(size / 1024).toFixed(1)} KB`; + return `${(size / (1024 * 1024)).toFixed(1)} MB`; + } + + private renderModeChip(mode: SampleMode, label: string) { + return html` + + `; + } + + private renderFileState() { + if (!this.loadedSampleFile) { + return html` +
+
+ File mode +

No file selected yet

+

+ Choose a file or drag one here to keep everything local in your + browser. Nothing leaves your device. +

+
+ +
+ `; + } + + return html` +
+
+ File ready +

${this.loadedSampleFile.name}

+

+ ${this.formatBytes(this.loadedSampleFile.size)} loaded locally in + memory for this session. Drop another file here to replace it. +

+
+
+ + +
+
+ `; + } + + render() { + return html` +
+
+
+

Sample editor

+
+
+
+
+
+ ${this.renderModeChip("text", "Text")} + ${this.renderModeChip("file", "File")} +
+ + +
+
+
+
+
${this.renderFileState()}
+
+
+ `; + } +} + +declare global { + interface HTMLElementTagNameMap { + "yara-file": YaraFile; + } +} diff --git a/playground/src/components/yara-result-panel.ts b/playground/src/components/yara-result-panel.ts new file mode 100644 index 000000000..2cc6c195c --- /dev/null +++ b/playground/src/components/yara-result-panel.ts @@ -0,0 +1,236 @@ +import type { ExecutionState, ResultMode } from "../types/execution"; +import { LitElement, html, nothing } from "lit"; +import { customElement, property } from "lit/decorators.js"; + +import { classMap } from "lit/directives/class-map.js"; +import { summarizeResult } from "../results/summarize-result"; + +const EMPTY_EXECUTION: ExecutionState = { + raw: {}, + durationMs: null, + summary: summarizeResult({}, "scan"), +}; + +@customElement("yara-result-panel") +export class YaraResultPanel extends LitElement { + @property({ type: String }) + resultMode: ResultMode = "summary"; + + @property({ attribute: false }) + execution: ExecutionState = EMPTY_EXECUTION; + + protected createRenderRoot() { + return this; + } + + private get resultLabel() { + switch (this.execution.summary.tone) { + case "match": + return "Match"; + case "clean": + return "Clean"; + case "issues": + return "Issues"; + default: + return "Ready"; + } + } + + private dispatchResultModeChange(mode: ResultMode) { + this.dispatchEvent( + new CustomEvent("result-mode-change", { + detail: mode, + bubbles: true, + composed: true, + }), + ); + } + + private renderStat(label: string, value: number) { + return html` +
+ ${label} + ${value} +
+ `; + } + + private renderIssues(title: string, issues: string[]) { + if (issues.length === 0) return nothing; + + return html` +
+
${title}
+
    + ${issues.map((issue) => html`
  • ${issue}
  • `)} +
+
+ `; + } + + private renderMatches() { + const { matchingRules } = this.execution.summary; + + return html` +
+
+
Matching rules
+ ${matchingRules.length} +
+ + ${matchingRules.length === 0 + ? html` +

+ No matching rules for the current sample. +

+ ` + : html` +
+ ${matchingRules.map( + (rule) => html` +
+
+
+

${rule.identifier}

+

${rule.namespace}

+
+ ${rule.hits} hits +
+
+ ${rule.patterns.map( + (pattern) => html` +
+ ${pattern.identifier} + ${pattern.ranges.join(", ") || + "No ranges"} +
+ `, + )} +
+
+ `, + )} +
+ `} +
+ `; + } + + private renderNonMatches() { + const { nonMatchingRules } = this.execution.summary; + + if (nonMatchingRules.length === 0) return nothing; + + return html` +
+
Non-matching rules
+
+ ${nonMatchingRules.map( + (rule) => html`${rule}`, + )} +
+
+ `; + } + + private renderSummaryResults() { + const { summary } = this.execution; + + return html` +
+ ${this.renderStat("Matches", summary.matches)} + ${this.renderStat("Non-matches", summary.nonMatches)} + ${this.renderStat("Warnings", summary.warnings)} + ${this.renderStat("Errors", summary.errors)} +
+ +
+
+

${summary.headline}

+ + ${this.resultLabel} + +
+
+ + ${this.renderIssues("Warnings", summary.warningsList)} + ${this.renderIssues("Errors", summary.errorsList)} ${this.renderMatches()} + ${this.renderNonMatches()} + `; + } + + private renderRawResults() { + return html`
+${JSON.stringify(this.execution.raw, null, 2)}
`; + } + + private renderModeChip(mode: ResultMode, label: string) { + return html` + + `; + } + + render() { + return html` +
+
+
+

Scan results

+
+ +
+
+ ${this.renderModeChip("summary", "Summary")} + ${this.renderModeChip("raw", "Raw")} +
+ + ${this.execution.durationMs === null + ? "--" + : `${this.execution.durationMs} ms`} + +
+
+ +
+ ${this.resultMode === "summary" + ? this.renderSummaryResults() + : this.renderRawResults()} +
+
+ `; + } +} + +declare global { + interface HTMLElementTagNameMap { + "yara-result-panel": YaraResultPanel; + } +} diff --git a/playground/src/components/yara-status-bar.ts b/playground/src/components/yara-status-bar.ts new file mode 100644 index 000000000..467867815 --- /dev/null +++ b/playground/src/components/yara-status-bar.ts @@ -0,0 +1,138 @@ +import { LitElement, html } from "lit"; +import { customElement, property } from "lit/decorators.js"; + +import type { ServiceStatus } from "../types/service-status"; +import { classMap } from "lit/directives/class-map.js"; + +const yaraMarkUrl = "/yara-mark.svg"; + +function getServiceLabel(kind: "core" | "lsp", status: ServiceStatus) { + if (kind === "core") { + switch (status) { + case "loading": + return "Core loading"; + case "ready": + return "Core ready"; + case "error": + return "Core error"; + default: + return "Core idle"; + } + } + + switch (status) { + case "loading": + return "LSP starting"; + case "ready": + return "LSP ready"; + case "error": + return "LSP error"; + default: + return "LSP idle"; + } +} + +@customElement("yara-status-bar") +export class YaraStatusBar extends LitElement { + @property({ type: String }) + coreStatus: ServiceStatus = "idle"; + + @property({ type: String }) + lspStatus: ServiceStatus = "idle"; + + @property({ type: Boolean }) + isBusy = false; + + @property({ type: Boolean }) + canRun = false; + + protected createRenderRoot() { + return this; + } + + private dispatchRunRequest = () => { + this.dispatchEvent( + new CustomEvent("run-request", { + bubbles: true, + composed: true, + }), + ); + }; + + private renderServiceChip(label: string, status: ServiceStatus) { + return html` + + ${label} + + `; + } + + render() { + return html` +
+
+
+ +
+
+ YARA-X Playground + Local-first playground +
+
+ +
+
+ ${this.renderServiceChip( + getServiceLabel("core", this.coreStatus), + this.coreStatus, + )} + ${this.renderServiceChip( + getServiceLabel("lsp", this.lspStatus), + this.lspStatus, + )} +
+ + +
+
+ `; + } +} + +declare global { + interface HTMLElementTagNameMap { + "yara-status-bar": YaraStatusBar; + } +} diff --git a/playground/src/controllers/split-resize-controller.ts b/playground/src/controllers/split-resize-controller.ts new file mode 100644 index 000000000..8ac867de7 --- /dev/null +++ b/playground/src/controllers/split-resize-controller.ts @@ -0,0 +1,76 @@ +import type { ReactiveController, ReactiveControllerHost } from "lit"; + +type SplitAxis = "x" | "y"; + +type SplitResizeConfig = { + getElement: () => HTMLElement | null; + getAxis: () => SplitAxis; + min: number; + max: number; + onChange: (value: number) => void; +}; + +export class SplitResizeController implements ReactiveController { + private cleanup?: () => void; + private readonly config: SplitResizeConfig; + + constructor(host: ReactiveControllerHost, config: SplitResizeConfig) { + this.config = config; + host.addController(this); + } + + hostDisconnected() { + this.stop(); + } + + readonly start = (event: PointerEvent) => { + event.preventDefault(); + this.updateFromPointer(event); + document.body.style.userSelect = "none"; + document.body.style.cursor = + this.config.getAxis() === "y" ? "row-resize" : "col-resize"; + + const onMove = (moveEvent: PointerEvent) => { + this.updateFromPointer(moveEvent); + }; + + const onUp = () => { + this.stop(); + }; + + window.addEventListener("pointermove", onMove); + window.addEventListener("pointerup", onUp); + this.cleanup = () => { + window.removeEventListener("pointermove", onMove); + window.removeEventListener("pointerup", onUp); + document.body.style.userSelect = ""; + document.body.style.cursor = ""; + this.cleanup = undefined; + }; + }; + + private updateFromPointer(event: PointerEvent) { + const element = this.config.getElement(); + + if (!element) return; + + const rect = element.getBoundingClientRect(); + const size = this.config.getAxis() === "y" ? rect.height : rect.width; + + if (size <= 0) return; + + const offset = + this.config.getAxis() === "y" + ? event.clientY - rect.top + : event.clientX - rect.left; + const next = (offset / size) * 100; + + this.config.onChange( + Math.min(this.config.max, Math.max(this.config.min, next)), + ); + } + + private stop() { + this.cleanup?.(); + } +} diff --git a/playground/src/data/sample.ts b/playground/src/data/sample.ts new file mode 100644 index 000000000..8e04ef42d --- /dev/null +++ b/playground/src/data/sample.ts @@ -0,0 +1,14 @@ +// This content is shown by default when the playground first opens in the browser +export const DEFAULT_SAMPLE_RULE = `rule suspicious_download_example { + meta: + author = "YARA-X Playground" + description = "Example rule used in the playground" + strings: + $a = "powershell" nocase + $b = "download" nocase + $c = "http://" nocase + condition: + 2 of them +}`; + +export const DEFAULT_SAMPLE_INPUT = `powershell -command download http://example.com/payload`; diff --git a/playground/src/editor/yara-monaco.ts b/playground/src/editor/yara-monaco.ts new file mode 100644 index 000000000..67a20042b --- /dev/null +++ b/playground/src/editor/yara-monaco.ts @@ -0,0 +1,337 @@ +import "@codingame/monaco-vscode-editor-api/esm/vs/editor/contrib/format/browser/formatActions.js"; + +import * as monaco from "@codingame/monaco-vscode-editor-api"; + +import { MonacoVscodeApiWrapper } from "monaco-languageclient/vscodeApiWrapper"; +import { configureDefaultWorkerFactory } from "monaco-languageclient/workerFactory"; +import { createModelReference } from "@codingame/monaco-vscode-api/monaco"; + +const RULE_URI = monaco.Uri.file("/workspace/main.yar"); +const SAMPLE_URI = monaco.Uri.file("/workspace/sample.txt"); +const THEME_NAME = "yara-studio"; + +export const YARA_CONFIG = { + codeFormatting: { + alignMetadata: true, + alignPatterns: true, + indentSectionHeaders: true, + indentSectionContents: true, + newlineBeforeCurlyBrace: false, + emptyLineBeforeSectionHeader: false, + emptyLineAfterSectionHeader: false, + }, + metadataValidation: [], + ruleNameValidation: null, + cacheWorkspace: false, +}; + +const YARA_KEYWORDS = [ + "rule", + "meta", + "strings", + "condition", + "private", + "global", + "import", +]; + +const YARA_OPERATORS = [ + "all", + "and", + "any", + "ascii", + "at", + "base64", + "base64wide", + "contains", + "entrypoint", + "false", + "filesize", + "for", + "fullword", + "in", + "matches", + "nocase", + "none", + "not", + "of", + "or", + "them", + "true", + "wide", + "xor", +]; + +export type EditorHandle = { + editor: monaco.editor.IStandaloneCodeEditor; + getValue: () => string; + setValue: (value: string) => void; + format: () => Promise; + dispose: () => void; +}; + +let vscodeApiInitPromise: Promise | undefined; +let themeRegistered = false; + +function registerStudioTheme() { + if (themeRegistered) return; + + monaco.editor.defineTheme(THEME_NAME, { + base: "vs-dark", + inherit: true, + rules: [ + { token: "keyword", foreground: "e9c46a" }, + { token: "variable", foreground: "5ce1e6" }, + { token: "identifier", foreground: "d6e6f2" }, + { token: "string", foreground: "7ce8a2" }, + { token: "number", foreground: "ffa770" }, + { token: "comment", foreground: "547282" }, + ], + colors: { + "editor.background": "#04111b", + "editor.lineHighlightBackground": "#0b2230", + "editor.foreground": "#d8e5ef", + "editorCursor.foreground": "#1ee3cf", + "editorLineNumber.foreground": "#527084", + "editorLineNumber.activeForeground": "#8fb7c8", + "editor.selectionBackground": "#12374b", + "editor.inactiveSelectionBackground": "#0d2c3d", + "editorIndentGuide.background1": "#0b2330", + "editorIndentGuide.activeBackground1": "#1f5665", + "editorWidget.background": "#071825", + "editorWidget.border": "#133141", + }, + }); + + themeRegistered = true; +} + +async function ensureVscodeApi() { + vscodeApiInitPromise ??= (async () => { + const apiWrapper = new MonacoVscodeApiWrapper({ + $type: "classic", + viewsConfig: { $type: "EditorService" }, + userConfiguration: { + json: JSON.stringify({ + "editor.colorDecorators": false, + }), + }, + advanced: { + loadThemes: false, + loadExtensionServices: false, + }, + monacoWorkerFactory: configureDefaultWorkerFactory, + }); + + await apiWrapper.start(); + registerStudioTheme(); + monaco.editor.setTheme(THEME_NAME); + })(); + + await vscodeApiInitPromise; +} + +function registerYaraLanguage() { + if (monaco.languages.getLanguages().some((lang) => lang.id === "yara")) + return; + + monaco.languages.register({ + id: "yara", + extensions: [".yar", ".yara"], + aliases: ["YARA", "yara"], + }); + + monaco.languages.setLanguageConfiguration("yara", { + comments: { + lineComment: "//", + blockComment: ["/*", "*/"], + }, + brackets: [ + ["{", "}"], + ["[", "]"], + ["(", ")"], + ], + autoClosingPairs: [ + { open: "{", close: "}" }, + { open: "[", close: "]" }, + { open: "(", close: ")" }, + { open: '"', close: '"', notIn: ["string"] }, + { open: "/*", close: " */", notIn: ["string"] }, + ], + surroundingPairs: [ + { open: "{", close: "}" }, + { open: "[", close: "]" }, + { open: "(", close: ")" }, + { open: '"', close: '"' }, + ], + }); + + monaco.languages.setMonarchTokensProvider("yara", { + keywords: YARA_KEYWORDS, + operators: YARA_OPERATORS, + tokenizer: { + root: [ + [/\$[a-zA-Z_]\w*/, "variable"], + [ + /[a-zA-Z_][\w]*/, + { + cases: { + "@keywords": "keyword", + "@operators": "keyword", + "@default": "identifier", + }, + }, + ], + [/\/\*/, "comment", "@comment"], + [/\/\/.*$/, "comment"], + [/"([^"\\]|\\.)*$/, "string.invalid"], + [/"/, { token: "string.quote", bracket: "@open", next: "@string" }], + [/\b\d+(?:\.\d+)?\b/, "number"], + [/[{}()[\]]/, "@brackets"], + [/[=> { + const action = editor.getAction("editor.action.formatDocument"); + await action?.run(); + }, + }); +} + +function toHandle( + editor: monaco.editor.IStandaloneCodeEditor, + modelRef: { dispose: () => void }, + extraDispose?: () => void, +): EditorHandle { + return { + editor, + getValue: () => editor.getValue(), + setValue: (value) => editor.setValue(value), + format: async () => { + const action = editor.getAction("editor.action.formatDocument"); + if (!action) return false; + await action.run(); + return true; + }, + dispose: () => { + extraDispose?.(); + editor.dispose(); + modelRef.dispose(); + }, + }; +} + +export async function createYaraEditor( + element: HTMLElement, + initialValue: string, +): Promise { + await ensureVscodeApi(); + registerYaraLanguage(); + + const { modelRef, model } = await createEditorModel( + RULE_URI, + initialValue, + "yara", + ); + const editor = buildEditor(element, model, { + quickSuggestions: true, + suggestOnTriggerCharacters: true, + tabSize: 2, + insertSpaces: true, + }); + const editorAction = registerYaraEditorActions(editor); + + // TODO(@kevinmuoz): Wire the browser language server package here once the + // upstream worker entrypoint and distribution strategy are settled. + + return toHandle(editor, modelRef, () => { + editorAction.dispose(); + }); +} + +export async function createPlainTextEditor( + element: HTMLElement, + initialValue: string, +): Promise { + await ensureVscodeApi(); + + const { modelRef, model } = await createEditorModel( + SAMPLE_URI, + initialValue, + "plaintext", + ); + const editor = buildEditor(element, model, { + lineNumbers: "off", + glyphMargin: false, + folding: false, + wordWrap: "on", + tabSize: 2, + insertSpaces: true, + quickSuggestions: false, + suggestOnTriggerCharacters: false, + }); + + return toHandle(editor, modelRef); +} diff --git a/playground/src/main.ts b/playground/src/main.ts new file mode 100644 index 000000000..f4207864e --- /dev/null +++ b/playground/src/main.ts @@ -0,0 +1,3 @@ +import "./styles/base.css"; +import "./styles/playground.css"; +import "./app/yara-playground-app"; diff --git a/playground/src/results/summarize-result.ts b/playground/src/results/summarize-result.ts new file mode 100644 index 000000000..d994c427b --- /dev/null +++ b/playground/src/results/summarize-result.ts @@ -0,0 +1,144 @@ +import type { ResultSummary, RuleSummary } from "../types/result-summary"; + +type MaybeObject = Record; + +type PatternMatch = { + range?: { + start?: number; + end?: number; + }; +}; + +function asObject(value: unknown): MaybeObject { + return value && typeof value === "object" ? (value as MaybeObject) : {}; +} + +function asArray(value: unknown): T[] { + return Array.isArray(value) ? (value as T[]) : []; +} + +function formatIssue(value: unknown): string { + if (typeof value === "string") return value; + if (value instanceof Error) return value?.message; + + const object = asObject(value); + if (typeof object.text === "string") return object?.text; + if (typeof object.title === "string") return object?.title; + + try { + return JSON.stringify(value); + } catch { + return String(value); + } +} + +function formatRange(match: unknown): string | null { + const matchObject = asObject(match); + const range = asObject(matchObject.range); + const start = range?.start; + const end = range?.end; + + if (typeof start === "number" && typeof end === "number") { + return `${start}-${end}`; + } + + const offset = matchObject.offset; + const length = matchObject.length; + + if (typeof offset !== "number" || typeof length !== "number") { + return null; + } + + return `${offset}-${offset + length}`; +} + +function summarizeRule(rule: unknown): RuleSummary { + const object = asObject(rule); + const patterns = asArray(object.patterns).map((pattern) => { + const patternObject = asObject(pattern); + const matches = asArray(patternObject.matches); + const ranges = matches + .map(formatRange) + .filter((value): value is string => Boolean(value)); + + return { + identifier: + typeof patternObject.identifier === "string" + ? patternObject.identifier + : "unknown", + hits: matches.length, + ranges, + }; + }); + + return { + identifier: + typeof object.identifier === "string" + ? object.identifier + : "unknown_rule", + namespace: + typeof object.namespace === "string" ? object.namespace : "default", + hits: patterns.reduce((sum, pattern) => sum + pattern.hits, 0), + patterns, + }; +} + +export function summarizeResult( + raw: unknown, + mode: "validate" | "scan", +): ResultSummary { + const object = asObject(raw); + const errorsList = asArray(object.errors).map(formatIssue); + const warningsList = asArray(object.warnings).map(formatIssue); + const matchingRules = asArray(object.matching_rules ?? object.matches).map( + summarizeRule, + ); + const nonMatchingRules = asArray(object.non_matching_rules).map( + (rule) => { + const ruleObject = asObject(rule); + return typeof ruleObject.identifier === "string" + ? ruleObject.identifier + : "unknown_rule"; + }, + ); + const hitCount = matchingRules.reduce((sum, rule) => sum + rule.hits, 0); + + let headline = "Ready to run a rule."; + let tone: ResultSummary["tone"] = "idle"; + + if (mode === "validate") { + if (errorsList.length > 0) { + headline = `Validation found ${errorsList.length} error(s).`; + tone = "issues"; + } else if (warningsList.length > 0) { + headline = `Validation completed with ${warningsList.length} warning(s).`; + tone = "issues"; + } else { + headline = "Rule validation passed without issues."; + tone = "clean"; + } + } else if (errorsList.length > 0) { + headline = `Scan finished with ${errorsList.length} error(s).`; + tone = "issues"; + } else if (matchingRules.length > 0) { + headline = `Matched ${matchingRules.length} rule(s) with ${hitCount} pattern hit(s).`; + tone = "match"; + } else { + headline = "Scan completed with no matching rules."; + tone = warningsList.length > 0 ? "issues" : "clean"; + } + + return { + errors: errorsList.length, + warnings: warningsList.length, + matches: matchingRules.length, + nonMatches: nonMatchingRules.length, + hitCount, + headline, + tone, + errorsList, + warningsList, + matchingRules, + nonMatchingRules, + }; +} diff --git a/playground/src/services/wasm-yara-engine.ts b/playground/src/services/wasm-yara-engine.ts new file mode 100644 index 000000000..486cfb813 --- /dev/null +++ b/playground/src/services/wasm-yara-engine.ts @@ -0,0 +1,60 @@ +import type { YaraCompiler, YaraEngine } from "./yara-engine"; + +function notImplemented(message: string): never { + throw new Error(message); +} + +function compilerTodo(): YaraCompiler { + return { + addSource: (_source) => { + notImplemented( + `TODO(@kevinmuoz): wire "Compiler.addSource" to the official "yara-x-wasm" npm package once PR #598 is merged and published.`, + ); + }, + newNamespace: (_namespace) => { + notImplemented( + `TODO(@kevinmuoz): wire "Compiler.newNamespace" to the official "yara-x-wasm" npm package once PR #598 is merged and published.`, + ); + }, + defineGlobal: (_identifier, _value) => { + notImplemented( + `TODO(@kevinmuoz): wire "Compiler.defineGlobal" to the official "yara-x-wasm" npm package once PR #598 is merged and published.`, + ); + }, + errors: () => { + notImplemented( + `TODO(@kevinmuoz): expose "Compiler.errors()" from the official "yara-x-wasm" npm package once PR #598 is merged and published.`, + ); + }, + warnings: () => { + notImplemented( + `TODO(@kevinmuoz): expose "Compiler.warnings()" from the official "yara-x-wasm" npm package once PR #598 is merged and published.`, + ); + }, + build: () => { + notImplemented( + `TODO(@kevinmuoz): wire "Compiler.build()" to the official "yara-x-wasm" npm package once PR #598 is merged and published.`, + ); + }, + }; +} + +export function getWasmYaraEngine(): YaraEngine { + return { + async createCompiler() { + /** + * TODO(@kevinmuoz): I'll Replace this local placeholder with an adapter around + * the upstream `yara-x-wasm` npm package once PR #598 lands + * + * Possible future config: + * + * import initYara, { Compiler } from "yara-x-wasm"; + * + * await initYara(); + * const compiler = new Compiler(); + * + */ + return compilerTodo(); + }, + }; +} diff --git a/playground/src/services/wasm-yara-language-server.ts b/playground/src/services/wasm-yara-language-server.ts new file mode 100644 index 000000000..f7f9d9afb --- /dev/null +++ b/playground/src/services/wasm-yara-language-server.ts @@ -0,0 +1,23 @@ +import type { YaraLanguageServer } from "./yara-language-server"; + +export function getWasmYaraLanguageServer(): YaraLanguageServer { + return { + async createWorker() { + /** + * TODO(@kevinmuoz): I'll Replace this placeholder once the browser + * worker entrypoint for "yara-x-ls" is upstream and we agree on how it + * should be distributed to web consumers. "createWorker()"" must keep + * resolving only once the worker is ready to accept LSP traffic + * + * Possible future config: + * + * import { createWorker } from "@virustotal/yara-x-ls-web"; + * + * const worker = await createWorker(); + */ + throw new Error( + "TODO(@kevinmuoz): wire the browser language-server package once the upstream worker entrypoint is published.", + ); + }, + }; +} diff --git a/playground/src/services/yara-engine.ts b/playground/src/services/yara-engine.ts new file mode 100644 index 000000000..0c09fad73 --- /dev/null +++ b/playground/src/services/yara-engine.ts @@ -0,0 +1,48 @@ +/** + * Playground-facing contract for the browser engine integration. + * + * TODO(@kevinmuoz): Replace the local adapter with the official `yara-x-wasm` + * npm package once PR #598 is merged and published. + * + * Expected upstream usage: + * + * import initYara, { Compiler, Scanner } from "yara-x-wasm"; + * + * await initYara(); + * + * const compiler = new Compiler(); + * compiler.addSource(rule); + * + * const rules = compiler.build(); + * const scanner = new Scanner(rules); + * const result = scanner.scan(bytes); + * + */ + +export type YaraEngineScanResult = unknown; + +export interface YaraEngine { + createCompiler(): Promise; +} + +export interface YaraCompiler { + addSource(source: string): void; + newNamespace(namespace: string): void; + defineGlobal(identifier: string, value: unknown): void; + errors(): string[]; + warnings(): string[]; + build(): YaraRules; +} + +export interface YaraRules { + scan(payload: Uint8Array): YaraEngineScanResult; + scanner(): YaraScanner; + warnings(): string[]; +} + +export interface YaraScanner { + setTimeoutMs(timeoutMs: number): void; + setMaxMatchesPerPattern(limit: number): void; + setGlobal(identifier: string, value: unknown): void; + scan(payload: Uint8Array): YaraEngineScanResult; +} diff --git a/playground/src/services/yara-language-server.ts b/playground/src/services/yara-language-server.ts new file mode 100644 index 000000000..0c3822f28 --- /dev/null +++ b/playground/src/services/yara-language-server.ts @@ -0,0 +1,21 @@ +/** + * Playground-facing contract for the browser language server integration. + * + * TODO(@kevinmuoz): Confirm with Victor whether the browser language server + * should be published as a separate npm package or bundled only as part of the + * official playground. Keep this interface Monaco-agnostic either way. + * + * Possible future config: + * + * const languageServer = getYaraLanguageServer(); + * const worker = await languageServer.createWorker(); + * + */ +export interface YaraLanguageServer { + /** + * Returns a ready-to-use worker that can be connected directly to an LSP + * client. Any package-specific bootstrap or readiness handshake should stay + * hidden behind this contract. + */ + createWorker(): Promise; +} diff --git a/playground/src/styles/base.css b/playground/src/styles/base.css new file mode 100644 index 000000000..679eb37a3 --- /dev/null +++ b/playground/src/styles/base.css @@ -0,0 +1,33 @@ +:root { + color-scheme: light; + background: #161625; + font-family: "Jost", "Avenir Next", "IBM Plex Sans", "Segoe UI", sans-serif; + font-optical-sizing: auto; + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +* { + box-sizing: border-box; +} + +html, +body { + margin: 0; + min-width: 320px; + min-height: 100svh; +} + +body { + overflow: hidden; + color: #f5f7ff; + background: #161625; +} + +@media (max-width: 900px) { + body { + overflow: auto; + } +} diff --git a/playground/src/styles/playground.css b/playground/src/styles/playground.css new file mode 100644 index 000000000..cb6fd6e21 --- /dev/null +++ b/playground/src/styles/playground.css @@ -0,0 +1,918 @@ +yara-playground-app { + display: block; + min-height: 100svh; + color: #f5f7ff; +} + +yara-status-bar { + display: block; +} + +yara-file, +yara-result-panel { + display: block; + min-width: 0; + min-height: 0; +} + +yara-file > .pane, +yara-result-panel > .results-region { + height: 100%; +} + +yara-playground-app button, +yara-playground-app input { + font: inherit; +} + +yara-playground-app h2, +yara-playground-app h3, +yara-playground-app p { + margin: 0; +} + +.studio { + width: 100%; + height: 100svh; + display: grid; + grid-template-rows: auto minmax(0, 1fr); + background: #161625; + overflow: hidden; +} + +.topbar, +.results-head { + display: flex; + align-items: center; + justify-content: space-between; + gap: 16px; +} + +.pane-head { + display: flex; + align-items: center; + gap: 16px; + min-width: 0; +} + +.topbar { + min-height: 52px; + position: relative; + z-index: 10; + padding: 9px 14px; + border-bottom: 1px solid rgba(134, 170, 249, 0.18); + background: rgba(22, 22, 37, 0.98); +} + +.brand, +.topbar-actions, +.status-row, +.sample-head-actions, +.results-controls, +.result-row, +.pattern-row, +.token-row { + display: flex; + align-items: center; + gap: 12px; +} + +.brand { + min-width: 0; +} + +.topbar-actions { + margin-left: auto; + justify-content: flex-end; + flex-wrap: wrap; +} + +.brand-mark { + width: 34px; + height: 34px; + display: grid; + place-items: center; + flex: 0 0 auto; + border-radius: 10px; + background: linear-gradient( + 135deg, + rgba(134, 170, 249, 0.22), + rgba(77, 222, 214, 0.22) + ); + color: #eef4ff; + border: 1px solid rgba(134, 170, 249, 0.22); +} + +.brand-mark-icon { + width: 24px; + height: auto; + display: block; +} + +.brand-copy, +.pane-head-copy { + display: grid; + gap: 2px; + min-width: 0; +} + +.pane-head-copy { + flex: 1 1 auto; +} + +.brand-copy strong, +yara-playground-app h2, +yara-playground-app h3 { + color: #eef2ff; + font-weight: 650; + letter-spacing: -0.02em; +} + +.brand-copy strong { + font-size: 0.88rem; + line-height: 1.1; +} + +.brand-copy span { + font-size: 0.74rem; + line-height: 1.1; +} + +yara-playground-app h2 { + font-size: 0.9rem; + line-height: 1.15; +} + +.brand-copy span, +.eyebrow, +.muted, +.stat span, +.section-heading, +yara-playground-app code { + color: #86aaf9; +} + +.eyebrow, +.section-heading { + font-size: 0.76rem; + text-transform: uppercase; + letter-spacing: 0.12em; + font-weight: 700; +} + +yara-playground-app code { + font-family: "IBM Plex Mono", ui-monospace, monospace; + font-size: 0.84rem; +} + +.service-chip, +.duration-chip, +.hit-pill, +.token { + display: inline-flex; + align-items: center; + border-radius: 999px; + border: 1px solid rgba(134, 170, 249, 0.18); + background: rgba(255, 255, 255, 0.03); + color: #cad7ff; + padding: 0.32rem 0.65rem; + line-height: 1; + font-size: 0.75rem; + white-space: nowrap; +} + +.service-chip.ready, +.hit-pill.match { + color: #55e6b0; + border-color: rgba(85, 230, 176, 0.22); + background: rgba(85, 230, 176, 0.09); +} + +.service-chip.loading { + color: #ffcf70; + border-color: rgba(255, 207, 112, 0.22); + background: rgba(255, 207, 112, 0.09); +} + +.service-chip.error, +.hit-pill.issues { + color: #ff8b9b; + border-color: rgba(255, 139, 155, 0.22); + background: rgba(255, 139, 155, 0.08); +} + +.hit-pill.clean { + color: #86aaf9; + background: rgba(134, 170, 249, 0.1); +} + +.run-button { + position: relative; + z-index: 1; + display: inline-flex; + align-items: center; + gap: 0.62rem; + appearance: none; + border: 1px solid rgba(134, 170, 249, 0.16); + border-radius: 999px; + padding: 0.42rem 0.54rem 0.42rem 0.48rem; + font-weight: 700; + color: #eef4ff; + background: rgba(255, 255, 255, 0.04); + box-shadow: 0 10px 24px rgba(8, 12, 24, 0.18); + cursor: pointer; + transition: + transform 180ms ease, + opacity 180ms ease, + box-shadow 180ms ease, + background 180ms ease, + border-color 180ms ease; +} + +.run-button:hover:not(:disabled) { + transform: translateY(-1px); + background: rgba(255, 255, 255, 0.06); + border-color: rgba(134, 170, 249, 0.28); + box-shadow: 0 16px 34px rgba(8, 12, 24, 0.24); +} + +.run-button:disabled { + opacity: 0.45; + cursor: not-allowed; + box-shadow: none; +} + +.run-icon { + display: inline-flex; + align-items: center; + justify-content: center; + width: 1.85rem; + height: 1.85rem; + flex: 0 0 auto; + border-radius: 999px; + background: linear-gradient(135deg, #86aaf9, #92f2df); + color: #0d1320; + box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.18); +} + +.run-icon svg { + width: 0.95rem; + height: 0.95rem; +} + +.spinner { + animation: spin 0.9s linear infinite; +} + +.run-tooltip { + position: absolute; + left: 50%; + top: calc(100% + 10px); + transform: translateX(-50%) translateY(-4px); + border-radius: 999px; + border: 1px solid rgba(134, 170, 249, 0.16); + background: rgba(22, 22, 37, 0.98); + color: #dbe5ff; + padding: 0.38rem 0.6rem; + font-size: 0.74rem; + line-height: 1; + white-space: nowrap; + box-shadow: 0 12px 32px rgba(8, 12, 24, 0.26); + opacity: 0; + pointer-events: none; + z-index: 12; + transition: + opacity 160ms ease, + transform 160ms ease; +} + +.run-button:hover .run-tooltip, +.run-button:focus-visible .run-tooltip { + opacity: 1; + transform: translateX(-50%) translateY(0); +} + +.workspace { + min-height: 0; + display: grid; + grid-template-rows: + minmax(260px, var(--workspace-split)) + 12px + minmax(180px, calc(100% - var(--workspace-split) - 12px)); + background: #161625; +} + +.editor-region { + min-height: 0; + display: grid; + grid-template-columns: + minmax(0, var(--editor-split)) + 12px + minmax(0, calc(100% - var(--editor-split) - 12px)); +} + +.pane, +.results-region { + min-width: 0; + min-height: 0; + display: grid; + grid-template-rows: auto minmax(0, 1fr); + background: #161625; + overflow: hidden; +} + +.sample-pane { + position: relative; +} + +.pane-head, +.results-head { + min-height: 50px; + width: 100%; + max-width: 100%; + min-width: 0; + box-sizing: border-box; + padding: 8px 14px; + border-bottom: 1px solid rgba(134, 170, 249, 0.14); + background: rgba(255, 255, 255, 0.01); +} + +.pane-head h2 { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.editor-shell { + min-height: 0; + overflow: hidden; + background: #04111b; +} + +.sample-pane-controls { + position: absolute; + top: 0; + right: 16px; + height: 50px; + display: flex; + align-items: center; + justify-content: flex-end; + z-index: 4; + max-width: calc(100% - 32px); +} + +.sample-pane-shell { + min-height: 0; + position: relative; + background: #04111b; +} + +.sample-pane-shell.is-drag-active .file-state-shell { + background: + radial-gradient( + circle at top left, + rgba(146, 242, 223, 0.16), + transparent 34% + ), + rgba(4, 17, 27, 0.96); +} + +.sample-pane-shell.is-drag-active .file-state { + border-color: rgba(146, 242, 223, 0.42); + background: rgba(146, 242, 223, 0.06); + transform: scale(1.01); +} + +.sample-pane-shell .editor-shell { + height: 100%; +} + +.sample-pane-shell.is-file-mode .editor-shell { + visibility: hidden; + pointer-events: none; +} + +.file-state-shell { + position: absolute; + inset: 0; + display: none; + padding: 18px; + overflow: auto; + background: + radial-gradient( + circle at top left, + rgba(134, 170, 249, 0.07), + transparent 30% + ), + #04111b; +} + +.sample-pane-shell.is-file-mode .file-state-shell { + display: block; +} + +.file-state { + width: min(520px, 100%); + min-height: 100%; + min-width: 0; + max-width: 100%; + box-sizing: border-box; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + gap: 18px; + color: #e6edff; + margin: 0 auto; + padding: 28px 22px; + border: 1px dashed rgba(134, 170, 249, 0.18); + border-radius: 24px; + background: rgba(255, 255, 255, 0.015); + text-align: center; + transition: + border-color 160ms ease, + background 160ms ease, + transform 160ms ease; +} + +.file-state-copy { + display: grid; + gap: 8px; + min-width: 0; + justify-items: center; +} + +.file-state-copy h3, +.file-state-copy p { + overflow-wrap: anywhere; +} + +.file-state-copy p { + max-width: 42ch; + color: #86aaf9; + line-height: 1.55; +} + +.file-state-actions { + display: flex; + align-items: center; + justify-content: center; + gap: 10px; + flex-wrap: wrap; +} + +.secondary-button, +.ghost-button { + appearance: none; + border-radius: 999px; + padding: 0.7rem 1rem; + font-weight: 600; + cursor: pointer; + transition: + background 160ms ease, + border-color 160ms ease, + transform 160ms ease; +} + +.secondary-button { + border: 1px solid rgba(134, 170, 249, 0.2); + background: rgba(134, 170, 249, 0.12); + color: #f5f7ff; +} + +.ghost-button { + border: 1px solid rgba(134, 170, 249, 0.12); + background: rgba(255, 255, 255, 0.02); + color: #86aaf9; +} + +.secondary-button:hover, +.ghost-button:hover { + transform: translateY(-1px); +} + +.editor-shell .monaco-editor textarea.inputarea, +.editor-shell .monaco-editor .inputarea { + opacity: 0 !important; + background: transparent !important; + border: 0 !important; + box-shadow: none !important; +} + +.editor-shell .monaco-editor .suggest-widget, +.editor-shell .monaco-editor .parameter-hints-widget, +.editor-shell .monaco-editor .monaco-hover, +.editor-shell .monaco-editor .monaco-editor-hover, +.editor-shell .monaco-editor .rename-box, +.editor-shell .monaco-editor .zone-widget, +.editor-shell .monaco-editor .peekview-widget, +.overflowingContentWidgets, +.overflowingOverlayWidgets, +.context-view, +.monaco-menu-container { + z-index: 40 !important; +} + +.editor-divider, +.workspace-divider { + position: relative; + background: rgba(255, 255, 255, 0.02); + touch-action: none; + z-index: 5; +} + +.editor-divider { + cursor: col-resize; + border-inline: 1px solid rgba(134, 170, 249, 0.1); +} + +.workspace-divider { + cursor: row-resize; + border-block: 1px solid rgba(134, 170, 249, 0.1); +} + +.divider-handle { + position: absolute; + inset: 50% auto auto 50%; + width: 4px; + height: 64px; + transform: translate(-50%, -50%); + border-radius: 999px; + background: linear-gradient( + 180deg, + rgba(134, 170, 249, 0.2), + rgba(134, 170, 249, 0.7), + rgba(134, 170, 249, 0.2) + ); +} + +.divider-handle.horizontal { + width: 84px; + height: 4px; + background: linear-gradient( + 90deg, + rgba(134, 170, 249, 0.2), + rgba(134, 170, 249, 0.7), + rgba(134, 170, 249, 0.2) + ); +} + +.results-region { + overflow: hidden; +} + +.results-body { + min-height: 0; + overflow: auto; + padding: 14px 16px 18px; + display: grid; + gap: 14px; +} + +.headline-strip, +.stats-strip, +.result-section, +.raw-output { + border: 1px solid rgba(134, 170, 249, 0.12); + background: rgba(255, 255, 255, 0.03); +} + +.headline-strip, +.result-section, +.raw-output { + border-radius: 20px; +} + +.headline-strip { + padding: 16px 18px; +} + +.headline-strip.clean { + background: rgba(134, 170, 249, 0.08); +} + +.headline-strip.match { + background: rgba(85, 230, 176, 0.08); + border-color: rgba(85, 230, 176, 0.14); +} + +.headline-strip.issues { + background: rgba(255, 139, 155, 0.07); + border-color: rgba(255, 139, 155, 0.14); +} + +.headline-strip p { + color: #f4f7ff; +} + +.metrics-grid { + display: flex; + flex-wrap: wrap; + gap: 10px; +} + +.stat { + min-width: 120px; + flex: 1 1 calc(25% - 10px); + display: grid; + gap: 8px; + padding: 10px 12px; + border-radius: 16px; + background: rgba(255, 255, 255, 0.03); + border: 1px solid rgba(134, 170, 249, 0.08); +} + +.stat strong { + font-size: 1.28rem; + line-height: 1; + color: #f4f7ff; +} + +.result-section { + padding: 18px; +} + +.issue-list { + margin: 12px 0 0; + padding-left: 18px; + color: #ffb8c1; + display: grid; + gap: 8px; +} + +.match-list { + display: grid; + gap: 14px; + margin-top: 14px; +} + +.match-item { + display: grid; + gap: 12px; + padding-top: 14px; + border-top: 1px solid rgba(134, 170, 249, 0.1); +} + +.match-item:first-child { + padding-top: 0; + border-top: 0; +} + +.pattern-list { + display: grid; + gap: 10px; +} + +.pattern-row { + justify-content: space-between; + padding: 10px 12px; + border-radius: 16px; + background: rgba(255, 255, 255, 0.025); + border: 1px solid rgba(134, 170, 249, 0.08); +} + +.token-row { + flex-wrap: wrap; + margin-top: 12px; +} + +.empty-copy { + margin-top: 12px; + color: #86aaf9; +} + +.mode-chip { + position: relative; + display: inline-flex; + align-items: center; + border-radius: 999px; + border: 0; + background: transparent; + color: #86aaf9; + cursor: pointer; + overflow: hidden; +} + +.mode-chip input { + position: absolute; + inset: 0; + opacity: 0; + pointer-events: none; +} + +.mode-chip span { + display: block; + padding: 0.48rem 0.82rem; + line-height: 1; + position: relative; + z-index: 1; +} + +.mode-chip.active { + background: rgba(134, 170, 249, 0.14); + color: #f4f7ff; +} + +.mode-switch { + display: inline-flex; + align-items: center; + gap: 2px; + flex: 0 0 auto; + width: max-content; + padding: 3px; + border-radius: 999px; + border: 1px solid rgba(134, 170, 249, 0.16); + background: rgba(255, 255, 255, 0.03); +} + +.sample-head-actions { + flex: 0 0 auto; + width: max-content; + min-width: max-content; + overflow: visible; +} + +.pane-meta { + flex: 0 0 auto; + margin-left: auto; +} + +.pane-meta { + color: #86aaf9; + font-family: "IBM Plex Mono", ui-monospace, monospace; + font-size: 0.8rem; + line-height: 1; +} + +@keyframes spin { + to { + transform: rotate(360deg); + } +} + +.raw-output { + margin: 0; + padding: 18px; + overflow: auto; + font-family: "IBM Plex Mono", ui-monospace, monospace; + font-size: 0.87rem; + line-height: 1.65; + color: #e8edff; +} + +@media (max-width: 900px) { + .studio { + min-height: 100svh; + height: auto; + overflow: auto; + } + + .topbar { + flex-wrap: wrap; + padding: 14px; + } + + .topbar-actions { + width: 100%; + justify-content: space-between; + } + + .workspace { + grid-template-rows: auto auto; + align-content: start; + gap: 12px; + padding-bottom: 14px; + } + + .editor-region { + grid-template-columns: 1fr; + grid-template-rows: auto auto; + gap: 12px; + } + + .pane, + .results-region { + grid-template-rows: auto minmax(0, auto); + } + + .editor-shell, + .sample-pane-shell, + .results-body { + min-height: 280px; + } + + .editor-divider { + cursor: row-resize; + border-inline: 0; + border-block: 1px solid rgba(134, 170, 249, 0.1); + } + + .editor-divider .divider-handle { + width: 84px; + height: 4px; + background: linear-gradient( + 90deg, + rgba(134, 170, 249, 0.2), + rgba(134, 170, 249, 0.7), + rgba(134, 170, 249, 0.2) + ); + } + + .editor-divider, + .workspace-divider { + display: none; + } + + .sample-pane-controls { + position: static; + height: auto; + padding: 0 16px 10px; + max-width: 100%; + } + + .sample-pane { + grid-template-rows: auto auto minmax(0, auto); + } + + .file-state-shell { + padding: 16px; + } + + .file-state { + padding: 24px 18px; + } + + .pane-head, + .results-head { + flex-wrap: wrap; + } + + .metrics-grid { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + } +} + +@media (max-width: 640px) { + .run-tooltip { + left: auto; + right: 0; + transform: translateY(-4px); + max-width: calc(100vw - 28px); + } + + .run-button:hover .run-tooltip, + .run-button:focus-visible .run-tooltip { + transform: translateY(0); + } + + .topbar, + .results-head, + .result-row, + .pattern-row { + flex-direction: column; + align-items: flex-start; + } + + .status-row, + .results-controls { + flex-wrap: wrap; + } + + .sample-pane-controls { + padding: 0 14px 10px; + } + + .sample-pane { + grid-template-rows: auto auto minmax(0, 1fr); + } + + .editor-shell, + .sample-pane-shell, + .results-body { + min-height: 240px; + } + + .results-body, + .pane-head, + .results-head { + padding-inline: 14px; + } + + .file-state-shell { + padding: 14px; + } + + .file-state { + padding: 20px 16px; + border-radius: 20px; + } + + .stat { + flex: 1 1 calc(50% - 10px); + } + + .metrics-grid { + grid-template-columns: 1fr; + } +} diff --git a/playground/src/types/execution.ts b/playground/src/types/execution.ts new file mode 100644 index 000000000..0d7727b7b --- /dev/null +++ b/playground/src/types/execution.ts @@ -0,0 +1,16 @@ +import type { ResultSummary } from "./result-summary"; + +export type ResultMode = "summary" | "raw"; +export type SampleMode = "text" | "file"; + +export type LoadedSampleFile = { + name: string; + size: number; + bytes: Uint8Array; +}; + +export type ExecutionState = { + raw: unknown; + durationMs: number | null; + summary: ResultSummary; +}; diff --git a/playground/src/types/result-summary.ts b/playground/src/types/result-summary.ts new file mode 100644 index 000000000..b474be86f --- /dev/null +++ b/playground/src/types/result-summary.ts @@ -0,0 +1,28 @@ +export type ResultTone = "idle" | "clean" | "match" | "issues"; + +export type PatternSummary = { + identifier: string; + hits: number; + ranges: string[]; +}; + +export type RuleSummary = { + identifier: string; + namespace: string; + hits: number; + patterns: PatternSummary[]; +}; + +export type ResultSummary = { + errors: number; + warnings: number; + matches: number; + nonMatches: number; + hitCount: number; + headline: string; + tone: ResultTone; + errorsList: string[]; + warningsList: string[]; + matchingRules: RuleSummary[]; + nonMatchingRules: string[]; +}; diff --git a/playground/src/types/service-status.ts b/playground/src/types/service-status.ts new file mode 100644 index 000000000..746655b9a --- /dev/null +++ b/playground/src/types/service-status.ts @@ -0,0 +1 @@ +export type ServiceStatus = "idle" | "loading" | "ready" | "error"; diff --git a/playground/src/workers/yara-ls.worker.ts b/playground/src/workers/yara-ls.worker.ts new file mode 100644 index 000000000..214b811fc --- /dev/null +++ b/playground/src/workers/yara-ls.worker.ts @@ -0,0 +1,18 @@ +async function main() { + /** + * TODO(@kevinmuoz): Replace this placeholder with the upstream browser + * worker package once the "yara-x-ls" worker entrypoint and packaging + * strategy are agreed. + * + * Possible future config: + * + * import initYaraLs, { runWorkerServer } from "@virustotal/yara-x-ls-web"; + * + * await initYaraLs(); + * runWorkerServer(); + */ +} + +void main().catch((error) => { + console.error("failed to start yara-x-ls worker", error); +}); diff --git a/playground/tsconfig.json b/playground/tsconfig.json new file mode 100644 index 000000000..4811fcdec --- /dev/null +++ b/playground/tsconfig.json @@ -0,0 +1,27 @@ +{ + "compilerOptions": { + "target": "ES2023", + "experimentalDecorators": true, + "useDefineForClassFields": false, + "module": "ESNext", + "lib": ["ES2023", "DOM", "DOM.Iterable"], + "types": ["vite/client"], + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "moduleDetection": "force", + "noEmit": true, + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "erasableSyntaxOnly": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true + }, + "include": ["src"] +} diff --git a/playground/vite.config.ts b/playground/vite.config.ts new file mode 100644 index 000000000..33c0bae78 --- /dev/null +++ b/playground/vite.config.ts @@ -0,0 +1,29 @@ +import { defineConfig } from "vite"; +import os from "node:os"; +import path from "node:path"; + +export default defineConfig({ + base: process.env.GITHUB_ACTIONS ? "/playground/" : "/", + worker: { + format: "es", + }, + optimizeDeps: { + include: ["@codingame/monaco-vscode-editor-api"], + }, + build: { + rollupOptions: { + output: { + format: "es", + }, + }, + }, + server: { + fs: { + allow: [ + path.resolve(__dirname, ".."), + path.resolve(__dirname, "node_modules"), + path.resolve(os.homedir(), "node_modules"), + ], + }, + }, +}); From 529726063cc906af9b2dfac5a4da7d6f9ab90c7c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 25 Mar 2026 08:40:10 +0100 Subject: [PATCH 2/2] chore(deps-dev): bump flatted from 3.3.3 to 3.4.2 in /ls/editors/code (#599) Bumps [flatted](https://github.com/WebReflection/flatted) from 3.3.3 to 3.4.2. - [Commits](https://github.com/WebReflection/flatted/compare/v3.3.3...v3.4.2) --- updated-dependencies: - dependency-name: flatted dependency-version: 3.4.2 dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- ls/editors/code/package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ls/editors/code/package-lock.json b/ls/editors/code/package-lock.json index 213ae7d02..073223e33 100644 --- a/ls/editors/code/package-lock.json +++ b/ls/editors/code/package-lock.json @@ -2044,9 +2044,9 @@ } }, "node_modules/flatted": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", - "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", "dev": true, "license": "ISC" },