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" }, 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 000000000..add381359 Binary files /dev/null and b/playground/public/apple-touch-icon.png differ diff --git a/playground/public/favicon-96x96.png b/playground/public/favicon-96x96.png new file mode 100644 index 000000000..b8ab064e6 Binary files /dev/null and b/playground/public/favicon-96x96.png differ diff --git a/playground/public/favicon.ico b/playground/public/favicon.ico new file mode 100644 index 000000000..c8fc7fbf9 Binary files /dev/null and b/playground/public/favicon.ico differ 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 000000000..bba1f3c4d Binary files /dev/null and b/playground/public/web-app-manifest-192x192.png differ diff --git a/playground/public/web-app-manifest-512x512.png b/playground/public/web-app-manifest-512x512.png new file mode 100644 index 000000000..6f3b359a5 Binary files /dev/null and b/playground/public/web-app-manifest-512x512.png differ 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"), + ], + }, + }, +});