diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
new file mode 100644
index 0000000..3ec154d
--- /dev/null
+++ b/.github/workflows/tests.yml
@@ -0,0 +1,116 @@
+name: Tests & Linting
+
+on:
+ push:
+ branches: [ main ]
+ pull_request:
+ branches: [ main ]
+
+jobs:
+ lint-js:
+ name: ๐งผ ESLint (JS/TS)
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ - name: Setup Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: 20
+
+ - name: Install Node.js dependencies
+ run: npm install
+
+ - name: Run ESLint
+ run: npm run lint:js
+
+ lint-py:
+ name: ๐ Ruff (Python)
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ - name: Setup Python
+ uses: actions/setup-python@v5
+ with:
+ python-version: '3.11'
+
+ - name: Install Python Dependencies
+ run: |
+ python -m pip install --upgrade pip
+ pip install ruff
+
+ - name: Ruff check
+ run: npm run lint:py
+
+ parser-tests:
+ name: ๐งช Parser Tests
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ - name: Setup Python
+ uses: actions/setup-python@v5
+ with:
+ python-version: '3.11'
+
+ - name: Install Python Dependencies
+ run: |
+ python -m pip install --upgrade pip
+ pip install -r parser/requirements.txt
+ pip install pytest ruff
+
+ - name: Run parser tests
+ run: npm run test:parser
+
+ webview-tests:
+ name: ๐จ Webview Visual Tests
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ - name: Setup Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: 20
+
+ - name: Install Node.js dependencies
+ run: npm install
+
+ - name: Install Playwright browsers
+ run: npx playwright install --with-deps
+
+ - name: Run webview tests (headless)
+ run: xvfb-run -a npm run test:webview
+
+ extension-tests:
+ name: ๐งฉ Extension Tests (VS Code)
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ - name: Setup Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: 20
+
+ - name: Install Node.js dependencies
+ run: npm install
+
+ - name: Setup Python
+ uses: actions/setup-python@v5
+ with:
+ python-version: '3.11'
+
+ - name: Install Python Dependencies
+ run: |
+ python -m pip install --upgrade pip
+ pip install -r parser/requirements.txt
+
+ - name: Run extension tests
+ run: xvfb-run -a npm run test:src
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index ec5efde..7127a2a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,6 +4,7 @@ Thumbs.db
**/node_modules/
**/out/
*/.vs/
+**/.venv/
**/.vscode-test/
tsconfig.lsif.json
*.lsif
diff --git a/CHANGELOG b/CHANGELOG
index ecf38dd..2549cc6 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/).
---
+## [0.1.8] - 2025-08-08
+
+### Added
+- `npm run` scripts for testing & linting
+- `ruff` and `eslint` for linting
+- GitHub Actions for testing & linting
+- Detailed `CONTRIBUTING.md`
+
## [0.1.7] - 2025-08-04
### Added
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..8793519
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,126 @@
+# ๐ค Contributing to LaunchMap
+
+Thank you for your interest in contributing to LaunchMap!
+Contributions of all kinds, features, bug fixes, tests, docs, and feedback are welcome!
+
+---
+
+## ๐ ๏ธ Project Structure
+```txt
+launchmap/
+โโโ parser/ # Python-based static launch file parser
+โโโ src/ # VS Code extension (TypeScript)
+โโโ webview/ # Webview frontend (HTML/JS)
+```
+
+---
+
+## โ๏ธ Setup Instructions
+
+### Installation
+
+```bash
+# Clone the repo
+git clone https://github.com/Kodo-Robotics/launchmap
+cd launchmap
+
+# Python setup
+python3 -m venv .venv
+source .venv/bin/activate
+pip install -r parser/requirements.txt
+
+# Node setup
+npm install
+```
+
+### Running
+
+Here is how you can get the extension running and see the output:
+
+1. Compile the code
+```bash
+npm run compile
+```
+This converts the TypeScript code into JavaScript so VS Code can run it.
+
+2. Start the extension in a debug VS Code window
+ - Open the file `src/extension.ts`.
+ - Press F5 (or go to Run and Debug โ Launch Extension).
+
+This launches a new VS Code window with the extension loaded in "dev mode" so you can test your changes live.
+
+3. Open a sample launch file
+ - In the new debug VS Code window, navigate to:
+`parser/tests/real_cases/launch_files/`
+ - Open any `.launch.py` file (these are real world test cases).
+
+4. Run the LaunchMap visualizer
+ - Press `Cmd + Shift + P` (or `Ctrl + Shift + P` on Windows/Linux).
+ - Search for `LaunchMap: Open Launch Visualizer` and run it.
+
+You should now see the visualization output.
+
+---
+
+## ๐งน Linting & Code Style
+
+We use:
+- `ruff` for Python (ultra-fast linter + formatter)
+- `eslint` for TypeScript/JavaScript
+
+### ๐ Run Python Lint (parser)
+
+```bash
+# Test
+npm run lint:py
+
+# Fix
+npm run lint:py:fix
+```
+
+### ๐ Run JS/TS Lint (extension + webview)
+
+```bash
+# Test
+npm run lint:js
+
+# Fix
+npm run lint:js:fix
+```
+
+---
+
+## ๐งช Running Tests
+
+```bash
+# Parser Tests
+npm run test:parser
+
+# Webview Visual Tests
+npm run test:webview
+
+# Extension Integration Tests
+npm run test:src
+```
+
+### โ ๏ธ Notes on Snapshot Testing
+- Visual snapshots are stored under `webview/tests/__screenshots__/`
+- Snapshots may differ between macOS, Windows, and Linux
+- Snapshots are only validated on PRs in CI (Linux)
+- If you are on macOS/Windows, avoid committing updated snapshots unless necessary
+
+---
+
+## ๐ก Tips
+- Prefer small, focused PRs
+- Follow lint rules strictly (ruff and eslint)
+- Playwright tests are validated automatically on PRs
+- Use `npx playwright test --update-snapshots` only on Linux for visual diffs
+
+---
+
+## ๐ฌ Questions?
+- Join our [Discord Server](https://discord.gg/EK3pHZxrKy)
+- Ask on GitHub
+
+Thanks again for contributing! ๐
\ No newline at end of file
diff --git a/README.md b/README.md
index 4ce9b69..35360b4 100644
--- a/README.md
+++ b/README.md
@@ -6,6 +6,7 @@
[](https://marketplace.visualstudio.com/items?itemName=KodoRobotics.launchmap)
[](./LICENSE)
[](https://discord.gg/EK3pHZxrKy)
+[](https://github.com/Kodo-Robotics/launchmap/actions/workflows/tests.yml)
**LaunchMap** is a Visual Studio Code extension that lets you visualize the structure of ROS 2 launch files as interactive graphs directly inside VSCode.
@@ -177,7 +178,7 @@ New to the ROS 2 ecosystem? Here are some great resources to get you started:
## ๐ค Contributing
Contributions are welcome!
-Please open an issue, suggest a feature, or submit a pull request.
+Refer to [CONTRIBUTING.md](CONTRIBUTING.md) for more details.
---
diff --git a/eslint.config.mjs b/eslint.config.mjs
new file mode 100644
index 0000000..99ee84c
--- /dev/null
+++ b/eslint.config.mjs
@@ -0,0 +1,32 @@
+import js from '@eslint/js';
+import globals from 'globals';
+import tseslint from 'typescript-eslint';
+import { defineConfig } from 'eslint/config';
+
+export default defineConfig([
+ {
+ files: ['**/*.{js,mjs,cjs,ts,mts,cts}'],
+ plugins: { js },
+ extends: ['js/recommended'],
+ languageOptions: {
+ globals: {
+ ...globals.browser,
+ ...globals.node
+ }
+ },
+ rules: {
+ indent: ['error', 2],
+ quotes: ['error', 'single'],
+ semi: ['error', 'always'],
+ 'comma-spacing': ['error', { before: false, after: true }],
+ 'keyword-spacing': ['error', { before: true, after: true }],
+ 'space-before-blocks': ['error', 'always'],
+ 'space-in-parens': ['error', 'never'],
+ 'array-bracket-spacing': ['error', 'never'],
+ 'object-curly-spacing': ['error', 'always'],
+ 'no-trailing-spaces': 'error',
+ 'eol-last': ['error', 'always'],
+ }
+ },
+ tseslint.configs.recommended
+]);
diff --git a/package-lock.json b/package-lock.json
index 91f3b8b..8dd44c3 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,28 +1,36 @@
{
"name": "launchmap",
- "version": "0.1.7",
+ "version": "0.1.8",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "launchmap",
- "version": "0.1.7",
+ "version": "0.1.8",
"license": "Apache-2.0",
"dependencies": {
"which": "^5.0.0"
},
"devDependencies": {
+ "@eslint/js": "^9.32.0",
"@playwright/test": "^1.54.2",
"@types/mocha": "^10.0.10",
"@types/node": "^24.0.1",
"@types/vscode": "^1.70.0",
"@types/which": "^3.0.4",
"@vscode/test-electron": "^2.5.2",
+ "eslint": "^9.32.0",
+ "eslint-config-prettier": "^10.1.8",
+ "eslint-plugin-import": "^2.32.0",
+ "eslint-plugin-jsx-a11y": "^6.10.2",
+ "eslint-plugin-prettier": "^5.5.4",
+ "globals": "^16.3.0",
"mocha": "^11.7.1",
"playwright": "^1.54.2",
"serve-handler": "^6.1.6",
"ts-node": "^10.9.2",
- "typescript": "^4.7.4"
+ "typescript": "^4.7.4",
+ "typescript-eslint": "^8.39.0"
},
"engines": {
"vscode": "^1.70.0"
@@ -41,6 +49,274 @@
"node": ">=12"
}
},
+ "node_modules/@eslint-community/eslint-utils": {
+ "version": "4.7.0",
+ "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz",
+ "integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "eslint-visitor-keys": "^3.4.3"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
+ }
+ },
+ "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
+ "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@eslint-community/regexpp": {
+ "version": "4.12.1",
+ "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz",
+ "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^12.0.0 || ^14.0.0 || >=16.0.0"
+ }
+ },
+ "node_modules/@eslint/config-array": {
+ "version": "0.21.0",
+ "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.0.tgz",
+ "integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@eslint/object-schema": "^2.1.6",
+ "debug": "^4.3.1",
+ "minimatch": "^3.1.2"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/config-array/node_modules/brace-expansion": {
+ "version": "1.1.12",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
+ "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/@eslint/config-array/node_modules/minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/@eslint/config-helpers": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.0.tgz",
+ "integrity": "sha512-ViuymvFmcJi04qdZeDc2whTHryouGcDlaxPqarTD0ZE10ISpxGUVZGZDx4w01upyIynL3iu6IXH2bS1NhclQMw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/core": {
+ "version": "0.15.1",
+ "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.1.tgz",
+ "integrity": "sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@types/json-schema": "^7.0.15"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/eslintrc": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz",
+ "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ajv": "^6.12.4",
+ "debug": "^4.3.2",
+ "espree": "^10.0.1",
+ "globals": "^14.0.0",
+ "ignore": "^5.2.0",
+ "import-fresh": "^3.2.1",
+ "js-yaml": "^4.1.0",
+ "minimatch": "^3.1.2",
+ "strip-json-comments": "^3.1.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@eslint/eslintrc/node_modules/brace-expansion": {
+ "version": "1.1.12",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
+ "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/@eslint/eslintrc/node_modules/globals": {
+ "version": "14.0.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz",
+ "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@eslint/eslintrc/node_modules/minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/@eslint/js": {
+ "version": "9.32.0",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.32.0.tgz",
+ "integrity": "sha512-BBpRFZK3eX6uMLKz8WxFOBIFFcGFJ/g8XuwjTHCqHROSIsopI+ddn/d5Cfh36+7+e5edVS8dbSHnBNhrLEX0zg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://eslint.org/donate"
+ }
+ },
+ "node_modules/@eslint/object-schema": {
+ "version": "2.1.6",
+ "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz",
+ "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/plugin-kit": {
+ "version": "0.3.4",
+ "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.4.tgz",
+ "integrity": "sha512-Ul5l+lHEcw3L5+k8POx6r74mxEYKG5kOb6Xpy2gCRW6zweT6TEhAf8vhxGgjhqrd/VO/Dirhsb+1hNpD1ue9hw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@eslint/core": "^0.15.1",
+ "levn": "^0.4.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@humanfs/core": {
+ "version": "0.19.1",
+ "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
+ "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18.18.0"
+ }
+ },
+ "node_modules/@humanfs/node": {
+ "version": "0.16.6",
+ "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz",
+ "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@humanfs/core": "^0.19.1",
+ "@humanwhocodes/retry": "^0.3.0"
+ },
+ "engines": {
+ "node": ">=18.18.0"
+ }
+ },
+ "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz",
+ "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18.18"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
+ "node_modules/@humanwhocodes/module-importer": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
+ "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=12.22"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
+ "node_modules/@humanwhocodes/retry": {
+ "version": "0.4.3",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz",
+ "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18.18"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
"node_modules/@isaacs/cliui": {
"version": "8.0.2",
"resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
@@ -87,6 +363,44 @@
"@jridgewell/sourcemap-codec": "^1.4.10"
}
},
+ "node_modules/@nodelib/fs.scandir": {
+ "version": "2.1.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
+ "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@nodelib/fs.stat": "2.0.5",
+ "run-parallel": "^1.1.9"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nodelib/fs.stat": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
+ "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nodelib/fs.walk": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
+ "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@nodelib/fs.scandir": "2.1.5",
+ "fastq": "^1.6.0"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
"node_modules/@pkgjs/parseargs": {
"version": "0.11.0",
"resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
@@ -98,6 +412,19 @@
"node": ">=14"
}
},
+ "node_modules/@pkgr/core": {
+ "version": "0.2.9",
+ "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.2.9.tgz",
+ "integrity": "sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^12.20.0 || ^14.18.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/pkgr"
+ }
+ },
"node_modules/@playwright/test": {
"version": "1.54.2",
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.54.2.tgz",
@@ -114,6 +441,13 @@
"node": ">=18"
}
},
+ "node_modules/@rtsao/scc": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz",
+ "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/@tsconfig/node10": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz",
@@ -142,6 +476,27 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/@types/estree": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
+ "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/json-schema": {
+ "version": "7.0.15",
+ "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
+ "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/json5": {
+ "version": "0.0.29",
+ "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
+ "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/@types/mocha": {
"version": "10.0.10",
"resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.10.tgz",
@@ -173,61 +528,320 @@
"dev": true,
"license": "MIT"
},
- "node_modules/@vscode/test-electron": {
- "version": "2.5.2",
- "resolved": "https://registry.npmjs.org/@vscode/test-electron/-/test-electron-2.5.2.tgz",
- "integrity": "sha512-8ukpxv4wYe0iWMRQU18jhzJOHkeGKbnw7xWRX3Zw1WJA4cEKbHcmmLPdPrPtL6rhDcrlCZN+xKRpv09n4gRHYg==",
+ "node_modules/@typescript-eslint/eslint-plugin": {
+ "version": "8.39.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.39.0.tgz",
+ "integrity": "sha512-bhEz6OZeUR+O/6yx9Jk6ohX6H9JSFTaiY0v9/PuKT3oGK0rn0jNplLmyFUGV+a9gfYnVNwGDwS/UkLIuXNb2Rw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "http-proxy-agent": "^7.0.2",
- "https-proxy-agent": "^7.0.5",
- "jszip": "^3.10.1",
- "ora": "^8.1.0",
- "semver": "^7.6.2"
+ "@eslint-community/regexpp": "^4.10.0",
+ "@typescript-eslint/scope-manager": "8.39.0",
+ "@typescript-eslint/type-utils": "8.39.0",
+ "@typescript-eslint/utils": "8.39.0",
+ "@typescript-eslint/visitor-keys": "8.39.0",
+ "graphemer": "^1.4.0",
+ "ignore": "^7.0.0",
+ "natural-compare": "^1.4.0",
+ "ts-api-utils": "^2.1.0"
},
"engines": {
- "node": ">=16"
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "@typescript-eslint/parser": "^8.39.0",
+ "eslint": "^8.57.0 || ^9.0.0",
+ "typescript": ">=4.8.4 <6.0.0"
}
},
- "node_modules/acorn": {
- "version": "8.15.0",
- "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
- "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
+ "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": {
+ "version": "7.0.5",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz",
+ "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==",
"dev": true,
"license": "MIT",
- "bin": {
- "acorn": "bin/acorn"
- },
"engines": {
- "node": ">=0.4.0"
+ "node": ">= 4"
}
},
- "node_modules/acorn-walk": {
- "version": "8.3.4",
- "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz",
- "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==",
+ "node_modules/@typescript-eslint/parser": {
+ "version": "8.39.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.39.0.tgz",
+ "integrity": "sha512-g3WpVQHngx0aLXn6kfIYCZxM6rRJlWzEkVpqEFLT3SgEDsp9cpCbxxgwnE504q4H+ruSDh/VGS6nqZIDynP+vg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "acorn": "^8.11.0"
+ "@typescript-eslint/scope-manager": "8.39.0",
+ "@typescript-eslint/types": "8.39.0",
+ "@typescript-eslint/typescript-estree": "8.39.0",
+ "@typescript-eslint/visitor-keys": "8.39.0",
+ "debug": "^4.3.4"
},
"engines": {
- "node": ">=0.4.0"
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0",
+ "typescript": ">=4.8.4 <6.0.0"
}
},
- "node_modules/agent-base": {
- "version": "7.1.4",
- "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz",
- "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==",
+ "node_modules/@typescript-eslint/project-service": {
+ "version": "8.39.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.39.0.tgz",
+ "integrity": "sha512-CTzJqaSq30V/Z2Og9jogzZt8lJRR5TKlAdXmWgdu4hgcC9Kww5flQ+xFvMxIBWVNdxJO7OifgdOK4PokMIWPew==",
"dev": true,
"license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/tsconfig-utils": "^8.39.0",
+ "@typescript-eslint/types": "^8.39.0",
+ "debug": "^4.3.4"
+ },
"engines": {
- "node": ">= 14"
- }
- },
- "node_modules/ansi-regex": {
- "version": "6.1.0",
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <6.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/scope-manager": {
+ "version": "8.39.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.39.0.tgz",
+ "integrity": "sha512-8QOzff9UKxOh6npZQ/4FQu4mjdOCGSdO3p44ww0hk8Vu+IGbg0tB/H1LcTARRDzGCC8pDGbh2rissBuuoPgH8A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.39.0",
+ "@typescript-eslint/visitor-keys": "8.39.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/tsconfig-utils": {
+ "version": "8.39.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.39.0.tgz",
+ "integrity": "sha512-Fd3/QjmFV2sKmvv3Mrj8r6N8CryYiCS8Wdb/6/rgOXAWGcFuc+VkQuG28uk/4kVNVZBQuuDHEDUpo/pQ32zsIQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <6.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/type-utils": {
+ "version": "8.39.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.39.0.tgz",
+ "integrity": "sha512-6B3z0c1DXVT2vYA9+z9axjtc09rqKUPRmijD5m9iv8iQpHBRYRMBcgxSiKTZKm6FwWw1/cI4v6em35OsKCiN5Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.39.0",
+ "@typescript-eslint/typescript-estree": "8.39.0",
+ "@typescript-eslint/utils": "8.39.0",
+ "debug": "^4.3.4",
+ "ts-api-utils": "^2.1.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0",
+ "typescript": ">=4.8.4 <6.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/types": {
+ "version": "8.39.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.39.0.tgz",
+ "integrity": "sha512-ArDdaOllnCj3yn/lzKn9s0pBQYmmyme/v1HbGIGB0GB/knFI3fWMHloC+oYTJW46tVbYnGKTMDK4ah1sC2v0Kg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree": {
+ "version": "8.39.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.39.0.tgz",
+ "integrity": "sha512-ndWdiflRMvfIgQRpckQQLiB5qAKQ7w++V4LlCHwp62eym1HLB/kw7D9f2e8ytONls/jt89TEasgvb+VwnRprsw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/project-service": "8.39.0",
+ "@typescript-eslint/tsconfig-utils": "8.39.0",
+ "@typescript-eslint/types": "8.39.0",
+ "@typescript-eslint/visitor-keys": "8.39.0",
+ "debug": "^4.3.4",
+ "fast-glob": "^3.3.2",
+ "is-glob": "^4.0.3",
+ "minimatch": "^9.0.4",
+ "semver": "^7.6.0",
+ "ts-api-utils": "^2.1.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <6.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/utils": {
+ "version": "8.39.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.39.0.tgz",
+ "integrity": "sha512-4GVSvNA0Vx1Ktwvf4sFE+exxJ3QGUorQG1/A5mRfRNZtkBT2xrA/BCO2H0eALx/PnvCS6/vmYwRdDA41EoffkQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.7.0",
+ "@typescript-eslint/scope-manager": "8.39.0",
+ "@typescript-eslint/types": "8.39.0",
+ "@typescript-eslint/typescript-estree": "8.39.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0",
+ "typescript": ">=4.8.4 <6.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/visitor-keys": {
+ "version": "8.39.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.39.0.tgz",
+ "integrity": "sha512-ldgiJ+VAhQCfIjeOgu8Kj5nSxds0ktPOSO9p4+0VDH2R2pLvQraaM5Oen2d7NxzMCm+Sn/vJT+mv2H5u6b/3fA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.39.0",
+ "eslint-visitor-keys": "^4.2.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@vscode/test-electron": {
+ "version": "2.5.2",
+ "resolved": "https://registry.npmjs.org/@vscode/test-electron/-/test-electron-2.5.2.tgz",
+ "integrity": "sha512-8ukpxv4wYe0iWMRQU18jhzJOHkeGKbnw7xWRX3Zw1WJA4cEKbHcmmLPdPrPtL6rhDcrlCZN+xKRpv09n4gRHYg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "http-proxy-agent": "^7.0.2",
+ "https-proxy-agent": "^7.0.5",
+ "jszip": "^3.10.1",
+ "ora": "^8.1.0",
+ "semver": "^7.6.2"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/acorn": {
+ "version": "8.15.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
+ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/acorn-jsx": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
+ "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ }
+ },
+ "node_modules/acorn-walk": {
+ "version": "8.3.4",
+ "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz",
+ "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "acorn": "^8.11.0"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/agent-base": {
+ "version": "7.1.4",
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz",
+ "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/ajv": {
+ "version": "6.12.6",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fast-deep-equal": "^3.1.1",
+ "fast-json-stable-stringify": "^2.0.0",
+ "json-schema-traverse": "^0.4.1",
+ "uri-js": "^4.2.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/ansi-regex": {
+ "version": "6.1.0",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz",
"integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==",
"dev": true,
@@ -269,6 +883,191 @@
"dev": true,
"license": "Python-2.0"
},
+ "node_modules/aria-query": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz",
+ "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/array-buffer-byte-length": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz",
+ "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "is-array-buffer": "^3.0.5"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/array-includes": {
+ "version": "3.1.9",
+ "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.9.tgz",
+ "integrity": "sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.4",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.24.0",
+ "es-object-atoms": "^1.1.1",
+ "get-intrinsic": "^1.3.0",
+ "is-string": "^1.1.1",
+ "math-intrinsics": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/array.prototype.findlastindex": {
+ "version": "1.2.6",
+ "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.6.tgz",
+ "integrity": "sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.4",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.9",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.1.1",
+ "es-shim-unscopables": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/array.prototype.flat": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz",
+ "integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.5",
+ "es-shim-unscopables": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/array.prototype.flatmap": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz",
+ "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.5",
+ "es-shim-unscopables": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/arraybuffer.prototype.slice": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz",
+ "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "array-buffer-byte-length": "^1.0.1",
+ "call-bind": "^1.0.8",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.5",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.6",
+ "is-array-buffer": "^3.0.4"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/ast-types-flow": {
+ "version": "0.0.8",
+ "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz",
+ "integrity": "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/async-function": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz",
+ "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/available-typed-arrays": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz",
+ "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "possible-typed-array-names": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/axe-core": {
+ "version": "4.10.3",
+ "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.10.3.tgz",
+ "integrity": "sha512-Xm7bpRXnDSX2YE2YFfBk2FnF0ep6tmG7xPh8iHee8MIcrgq762Nkce856dYtJYLkuIoYZvGfTs/PbZhideTcEg==",
+ "dev": true,
+ "license": "MPL-2.0",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/axobject-query": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz",
+ "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
"node_modules/balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
@@ -286,6 +1085,19 @@
"balanced-match": "^1.0.0"
}
},
+ "node_modules/braces": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
+ "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fill-range": "^7.1.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
"node_modules/browser-stdout": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz",
@@ -293,558 +1105,2126 @@
"dev": true,
"license": "ISC"
},
- "node_modules/bytes": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
- "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==",
+ "node_modules/bytes": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
+ "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/call-bind": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz",
+ "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.0",
+ "es-define-property": "^1.0.0",
+ "get-intrinsic": "^1.2.4",
+ "set-function-length": "^1.2.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/call-bind-apply-helpers": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
+ "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/call-bound": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
+ "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "get-intrinsic": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/callsites": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/camelcase": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz",
+ "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/chalk/node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/chokidar": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz",
+ "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "readdirp": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 14.16.0"
+ },
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
+ }
+ },
+ "node_modules/cli-cursor": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz",
+ "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "restore-cursor": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/cli-spinners": {
+ "version": "2.9.2",
+ "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz",
+ "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/cliui": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
+ "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.1",
+ "wrap-ansi": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/cliui/node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/cliui/node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/cliui/node_modules/string-width": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/cliui/node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/cliui/node_modules/wrap-ansi": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+ "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/content-disposition": {
+ "version": "0.5.2",
+ "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz",
+ "integrity": "sha512-kRGRZw3bLlFISDBgwTSA1TMBFN6J6GWDeubmDE3AF+3+yXL8hTWv8r5rkLbqYXY4RjPk/EzHnClI3zQf1cFmHA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/core-util-is": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
+ "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/create-require": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
+ "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/cross-spawn": {
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
+ "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/cross-spawn/node_modules/isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/cross-spawn/node_modules/which": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "node-which": "bin/node-which"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/damerau-levenshtein": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz",
+ "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==",
+ "dev": true,
+ "license": "BSD-2-Clause"
+ },
+ "node_modules/data-view-buffer": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz",
+ "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "es-errors": "^1.3.0",
+ "is-data-view": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/data-view-byte-length": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz",
+ "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "es-errors": "^1.3.0",
+ "is-data-view": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/inspect-js"
+ }
+ },
+ "node_modules/data-view-byte-offset": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz",
+ "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "is-data-view": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/debug": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
+ "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/decamelize": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz",
+ "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/deep-is": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
+ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/define-data-property": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
+ "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-define-property": "^1.0.0",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/define-properties": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz",
+ "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "define-data-property": "^1.0.1",
+ "has-property-descriptors": "^1.0.0",
+ "object-keys": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/diff": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz",
+ "integrity": "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.3.1"
+ }
+ },
+ "node_modules/doctrine": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
+ "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "esutils": "^2.0.2"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/dunder-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
+ "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.2.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/eastasianwidth": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
+ "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/emoji-regex": {
+ "version": "9.2.2",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
+ "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/es-abstract": {
+ "version": "1.24.0",
+ "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.0.tgz",
+ "integrity": "sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "array-buffer-byte-length": "^1.0.2",
+ "arraybuffer.prototype.slice": "^1.0.4",
+ "available-typed-arrays": "^1.0.7",
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.4",
+ "data-view-buffer": "^1.0.2",
+ "data-view-byte-length": "^1.0.2",
+ "data-view-byte-offset": "^1.0.1",
+ "es-define-property": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.1.1",
+ "es-set-tostringtag": "^2.1.0",
+ "es-to-primitive": "^1.3.0",
+ "function.prototype.name": "^1.1.8",
+ "get-intrinsic": "^1.3.0",
+ "get-proto": "^1.0.1",
+ "get-symbol-description": "^1.1.0",
+ "globalthis": "^1.0.4",
+ "gopd": "^1.2.0",
+ "has-property-descriptors": "^1.0.2",
+ "has-proto": "^1.2.0",
+ "has-symbols": "^1.1.0",
+ "hasown": "^2.0.2",
+ "internal-slot": "^1.1.0",
+ "is-array-buffer": "^3.0.5",
+ "is-callable": "^1.2.7",
+ "is-data-view": "^1.0.2",
+ "is-negative-zero": "^2.0.3",
+ "is-regex": "^1.2.1",
+ "is-set": "^2.0.3",
+ "is-shared-array-buffer": "^1.0.4",
+ "is-string": "^1.1.1",
+ "is-typed-array": "^1.1.15",
+ "is-weakref": "^1.1.1",
+ "math-intrinsics": "^1.1.0",
+ "object-inspect": "^1.13.4",
+ "object-keys": "^1.1.1",
+ "object.assign": "^4.1.7",
+ "own-keys": "^1.0.1",
+ "regexp.prototype.flags": "^1.5.4",
+ "safe-array-concat": "^1.1.3",
+ "safe-push-apply": "^1.0.0",
+ "safe-regex-test": "^1.1.0",
+ "set-proto": "^1.0.0",
+ "stop-iteration-iterator": "^1.1.0",
+ "string.prototype.trim": "^1.2.10",
+ "string.prototype.trimend": "^1.0.9",
+ "string.prototype.trimstart": "^1.0.8",
+ "typed-array-buffer": "^1.0.3",
+ "typed-array-byte-length": "^1.0.3",
+ "typed-array-byte-offset": "^1.0.4",
+ "typed-array-length": "^1.0.7",
+ "unbox-primitive": "^1.1.0",
+ "which-typed-array": "^1.1.19"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/es-define-property": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
+ "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-errors": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
+ "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-object-atoms": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
+ "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-set-tostringtag": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
+ "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.6",
+ "has-tostringtag": "^1.0.2",
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-shim-unscopables": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz",
+ "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-to-primitive": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz",
+ "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-callable": "^1.2.7",
+ "is-date-object": "^1.0.5",
+ "is-symbol": "^1.0.4"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/escalade": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
+ "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/escape-string-regexp": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/eslint": {
+ "version": "9.32.0",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.32.0.tgz",
+ "integrity": "sha512-LSehfdpgMeWcTZkWZVIJl+tkZ2nuSkyyB9C27MZqFWXuph7DvaowgcTvKqxvpLW1JZIk8PN7hFY3Rj9LQ7m7lg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.2.0",
+ "@eslint-community/regexpp": "^4.12.1",
+ "@eslint/config-array": "^0.21.0",
+ "@eslint/config-helpers": "^0.3.0",
+ "@eslint/core": "^0.15.0",
+ "@eslint/eslintrc": "^3.3.1",
+ "@eslint/js": "9.32.0",
+ "@eslint/plugin-kit": "^0.3.4",
+ "@humanfs/node": "^0.16.6",
+ "@humanwhocodes/module-importer": "^1.0.1",
+ "@humanwhocodes/retry": "^0.4.2",
+ "@types/estree": "^1.0.6",
+ "@types/json-schema": "^7.0.15",
+ "ajv": "^6.12.4",
+ "chalk": "^4.0.0",
+ "cross-spawn": "^7.0.6",
+ "debug": "^4.3.2",
+ "escape-string-regexp": "^4.0.0",
+ "eslint-scope": "^8.4.0",
+ "eslint-visitor-keys": "^4.2.1",
+ "espree": "^10.4.0",
+ "esquery": "^1.5.0",
+ "esutils": "^2.0.2",
+ "fast-deep-equal": "^3.1.3",
+ "file-entry-cache": "^8.0.0",
+ "find-up": "^5.0.0",
+ "glob-parent": "^6.0.2",
+ "ignore": "^5.2.0",
+ "imurmurhash": "^0.1.4",
+ "is-glob": "^4.0.0",
+ "json-stable-stringify-without-jsonify": "^1.0.1",
+ "lodash.merge": "^4.6.2",
+ "minimatch": "^3.1.2",
+ "natural-compare": "^1.4.0",
+ "optionator": "^0.9.3"
+ },
+ "bin": {
+ "eslint": "bin/eslint.js"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://eslint.org/donate"
+ },
+ "peerDependencies": {
+ "jiti": "*"
+ },
+ "peerDependenciesMeta": {
+ "jiti": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/eslint-config-prettier": {
+ "version": "10.1.8",
+ "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.8.tgz",
+ "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "eslint-config-prettier": "bin/cli.js"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint-config-prettier"
+ },
+ "peerDependencies": {
+ "eslint": ">=7.0.0"
+ }
+ },
+ "node_modules/eslint-import-resolver-node": {
+ "version": "0.3.9",
+ "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz",
+ "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "debug": "^3.2.7",
+ "is-core-module": "^2.13.0",
+ "resolve": "^1.22.4"
+ }
+ },
+ "node_modules/eslint-import-resolver-node/node_modules/debug": {
+ "version": "3.2.7",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+ "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.1"
+ }
+ },
+ "node_modules/eslint-module-utils": {
+ "version": "2.12.1",
+ "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.1.tgz",
+ "integrity": "sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "debug": "^3.2.7"
+ },
+ "engines": {
+ "node": ">=4"
+ },
+ "peerDependenciesMeta": {
+ "eslint": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/eslint-module-utils/node_modules/debug": {
+ "version": "3.2.7",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+ "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.1"
+ }
+ },
+ "node_modules/eslint-plugin-import": {
+ "version": "2.32.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.32.0.tgz",
+ "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@rtsao/scc": "^1.1.0",
+ "array-includes": "^3.1.9",
+ "array.prototype.findlastindex": "^1.2.6",
+ "array.prototype.flat": "^1.3.3",
+ "array.prototype.flatmap": "^1.3.3",
+ "debug": "^3.2.7",
+ "doctrine": "^2.1.0",
+ "eslint-import-resolver-node": "^0.3.9",
+ "eslint-module-utils": "^2.12.1",
+ "hasown": "^2.0.2",
+ "is-core-module": "^2.16.1",
+ "is-glob": "^4.0.3",
+ "minimatch": "^3.1.2",
+ "object.fromentries": "^2.0.8",
+ "object.groupby": "^1.0.3",
+ "object.values": "^1.2.1",
+ "semver": "^6.3.1",
+ "string.prototype.trimend": "^1.0.9",
+ "tsconfig-paths": "^3.15.0"
+ },
+ "engines": {
+ "node": ">=4"
+ },
+ "peerDependencies": {
+ "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9"
+ }
+ },
+ "node_modules/eslint-plugin-import/node_modules/brace-expansion": {
+ "version": "1.1.12",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
+ "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/eslint-plugin-import/node_modules/debug": {
+ "version": "3.2.7",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz",
+ "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.1"
+ }
+ },
+ "node_modules/eslint-plugin-import/node_modules/minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/eslint-plugin-import/node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/eslint-plugin-jsx-a11y": {
+ "version": "6.10.2",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.10.2.tgz",
+ "integrity": "sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "aria-query": "^5.3.2",
+ "array-includes": "^3.1.8",
+ "array.prototype.flatmap": "^1.3.2",
+ "ast-types-flow": "^0.0.8",
+ "axe-core": "^4.10.0",
+ "axobject-query": "^4.1.0",
+ "damerau-levenshtein": "^1.0.8",
+ "emoji-regex": "^9.2.2",
+ "hasown": "^2.0.2",
+ "jsx-ast-utils": "^3.3.5",
+ "language-tags": "^1.0.9",
+ "minimatch": "^3.1.2",
+ "object.fromentries": "^2.0.8",
+ "safe-regex-test": "^1.0.3",
+ "string.prototype.includes": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=4.0"
+ },
+ "peerDependencies": {
+ "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9"
+ }
+ },
+ "node_modules/eslint-plugin-jsx-a11y/node_modules/brace-expansion": {
+ "version": "1.1.12",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
+ "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/eslint-plugin-jsx-a11y/node_modules/minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/eslint-plugin-prettier": {
+ "version": "5.5.4",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.5.4.tgz",
+ "integrity": "sha512-swNtI95SToIz05YINMA6Ox5R057IMAmWZ26GqPxusAp1TZzj+IdY9tXNWWD3vkF/wEqydCONcwjTFpxybBqZsg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "prettier-linter-helpers": "^1.0.0",
+ "synckit": "^0.11.7"
+ },
+ "engines": {
+ "node": "^14.18.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint-plugin-prettier"
+ },
+ "peerDependencies": {
+ "@types/eslint": ">=8.0.0",
+ "eslint": ">=8.0.0",
+ "eslint-config-prettier": ">= 7.0.0 <10.0.0 || >=10.1.0",
+ "prettier": ">=3.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/eslint": {
+ "optional": true
+ },
+ "eslint-config-prettier": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/eslint-scope": {
+ "version": "8.4.0",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz",
+ "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "esrecurse": "^4.3.0",
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint-visitor-keys": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
+ "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint/node_modules/brace-expansion": {
+ "version": "1.1.12",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
+ "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/eslint/node_modules/minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/espree": {
+ "version": "10.4.0",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz",
+ "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "acorn": "^8.15.0",
+ "acorn-jsx": "^5.3.2",
+ "eslint-visitor-keys": "^4.2.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/esquery": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz",
+ "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "estraverse": "^5.1.0"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/esrecurse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
+ "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/estraverse": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/esutils": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fast-diff": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz",
+ "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==",
+ "dev": true,
+ "license": "Apache-2.0"
+ },
+ "node_modules/fast-glob": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz",
+ "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@nodelib/fs.stat": "^2.0.2",
+ "@nodelib/fs.walk": "^1.2.3",
+ "glob-parent": "^5.1.2",
+ "merge2": "^1.3.0",
+ "micromatch": "^4.0.8"
+ },
+ "engines": {
+ "node": ">=8.6.0"
+ }
+ },
+ "node_modules/fast-glob/node_modules/glob-parent": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "is-glob": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/fast-json-stable-stringify": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fast-levenshtein": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fastq": {
+ "version": "1.19.1",
+ "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz",
+ "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "reusify": "^1.0.4"
+ }
+ },
+ "node_modules/file-entry-cache": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
+ "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "flat-cache": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
+ "node_modules/fill-range": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
+ "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "to-regex-range": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/find-up": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
+ "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "locate-path": "^6.0.0",
+ "path-exists": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/flat": {
+ "version": "5.0.2",
+ "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz",
+ "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "bin": {
+ "flat": "cli.js"
+ }
+ },
+ "node_modules/flat-cache": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz",
+ "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "flatted": "^3.2.9",
+ "keyv": "^4.5.4"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/flatted": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz",
+ "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/for-each": {
+ "version": "0.3.5",
+ "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz",
+ "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-callable": "^1.2.7"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/foreground-child": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz",
+ "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "cross-spawn": "^7.0.6",
+ "signal-exit": "^4.0.1"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/fsevents": {
+ "version": "2.3.2",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
+ "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/function-bind": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/function.prototype.name": {
+ "version": "1.1.8",
+ "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz",
+ "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.3",
+ "define-properties": "^1.2.1",
+ "functions-have-names": "^1.2.3",
+ "hasown": "^2.0.2",
+ "is-callable": "^1.2.7"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/functions-have-names": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz",
+ "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-caller-file": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
+ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": "6.* || 8.* || >= 10.*"
+ }
+ },
+ "node_modules/get-east-asian-width": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.3.0.tgz",
+ "integrity": "sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/get-intrinsic": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
+ "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "es-define-property": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.1.1",
+ "function-bind": "^1.1.2",
+ "get-proto": "^1.0.1",
+ "gopd": "^1.2.0",
+ "has-symbols": "^1.1.0",
+ "hasown": "^2.0.2",
+ "math-intrinsics": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
+ "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "dunder-proto": "^1.0.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/get-symbol-description": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz",
+ "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.6"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/glob": {
+ "version": "10.4.5",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz",
+ "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "foreground-child": "^3.1.0",
+ "jackspeak": "^3.1.2",
+ "minimatch": "^9.0.4",
+ "minipass": "^7.1.2",
+ "package-json-from-dist": "^1.0.0",
+ "path-scurry": "^1.11.1"
+ },
+ "bin": {
+ "glob": "dist/esm/bin.mjs"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/glob-parent": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
+ "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "is-glob": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/globals": {
+ "version": "16.3.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-16.3.0.tgz",
+ "integrity": "sha512-bqWEnJ1Nt3neqx2q5SFfGS8r/ahumIakg3HcwtNlrVlwXIeNumWn/c7Pn/wKzGhf6SaW6H6uWXLqC30STCMchQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/globalthis": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz",
+ "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "define-properties": "^1.2.1",
+ "gopd": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/gopd": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
+ "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/graphemer": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
+ "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/has-bigints": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz",
+ "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/has-property-descriptors": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
+ "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-define-property": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-proto": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz",
+ "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "dunder-proto": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-symbols": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
+ "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-tostringtag": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
+ "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-symbols": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/hasown": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
+ "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/he": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
+ "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "he": "bin/he"
+ }
+ },
+ "node_modules/http-proxy-agent": {
+ "version": "7.0.2",
+ "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz",
+ "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "agent-base": "^7.1.0",
+ "debug": "^4.3.4"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/https-proxy-agent": {
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz",
+ "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "agent-base": "^7.1.2",
+ "debug": "4"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/ignore": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
+ "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==",
"dev": true,
"license": "MIT",
"engines": {
- "node": ">= 0.8"
+ "node": ">= 4"
}
},
- "node_modules/camelcase": {
- "version": "6.3.0",
- "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz",
- "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==",
+ "node_modules/immediate": {
+ "version": "3.0.6",
+ "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
+ "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/import-fresh": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
+ "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==",
"dev": true,
"license": "MIT",
+ "dependencies": {
+ "parent-module": "^1.0.0",
+ "resolve-from": "^4.0.0"
+ },
"engines": {
- "node": ">=10"
+ "node": ">=6"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/chalk": {
- "version": "4.1.2",
- "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
- "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "node_modules/imurmurhash": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+ "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
"dev": true,
"license": "MIT",
- "dependencies": {
- "ansi-styles": "^4.1.0",
- "supports-color": "^7.1.0"
- },
"engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/chalk/chalk?sponsor=1"
+ "node": ">=0.8.19"
}
},
- "node_modules/chalk/node_modules/supports-color": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
- "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "node_modules/inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/internal-slot": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz",
+ "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "has-flag": "^4.0.0"
+ "es-errors": "^1.3.0",
+ "hasown": "^2.0.2",
+ "side-channel": "^1.1.0"
},
"engines": {
- "node": ">=8"
+ "node": ">= 0.4"
}
},
- "node_modules/chokidar": {
- "version": "4.0.3",
- "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz",
- "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==",
+ "node_modules/is-array-buffer": {
+ "version": "3.0.5",
+ "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz",
+ "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==",
"dev": true,
"license": "MIT",
"dependencies": {
- "readdirp": "^4.0.1"
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.3",
+ "get-intrinsic": "^1.2.6"
},
"engines": {
- "node": ">= 14.16.0"
+ "node": ">= 0.4"
},
"funding": {
- "url": "https://paulmillr.com/funding/"
+ "url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/cli-cursor": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz",
- "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==",
+ "node_modules/is-async-function": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz",
+ "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "restore-cursor": "^5.0.0"
+ "async-function": "^1.0.0",
+ "call-bound": "^1.0.3",
+ "get-proto": "^1.0.1",
+ "has-tostringtag": "^1.0.2",
+ "safe-regex-test": "^1.1.0"
},
"engines": {
- "node": ">=18"
+ "node": ">= 0.4"
},
"funding": {
- "url": "https://github.com/sponsors/sindresorhus"
+ "url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/cli-spinners": {
- "version": "2.9.2",
- "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz",
- "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==",
+ "node_modules/is-bigint": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz",
+ "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==",
"dev": true,
"license": "MIT",
+ "dependencies": {
+ "has-bigints": "^1.0.2"
+ },
"engines": {
- "node": ">=6"
+ "node": ">= 0.4"
},
"funding": {
- "url": "https://github.com/sponsors/sindresorhus"
+ "url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/cliui": {
- "version": "8.0.1",
- "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
- "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
+ "node_modules/is-boolean-object": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz",
+ "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==",
"dev": true,
- "license": "ISC",
+ "license": "MIT",
"dependencies": {
- "string-width": "^4.2.0",
- "strip-ansi": "^6.0.1",
- "wrap-ansi": "^7.0.0"
+ "call-bound": "^1.0.3",
+ "has-tostringtag": "^1.0.2"
},
"engines": {
- "node": ">=12"
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/cliui/node_modules/ansi-regex": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
- "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "node_modules/is-callable": {
+ "version": "1.2.7",
+ "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz",
+ "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==",
"dev": true,
"license": "MIT",
"engines": {
- "node": ">=8"
- }
- },
- "node_modules/cliui/node_modules/emoji-regex": {
- "version": "8.0.0",
- "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
- "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/cliui/node_modules/string-width": {
- "version": "4.2.3",
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
- "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "emoji-regex": "^8.0.0",
- "is-fullwidth-code-point": "^3.0.0",
- "strip-ansi": "^6.0.1"
+ "node": ">= 0.4"
},
- "engines": {
- "node": ">=8"
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/cliui/node_modules/strip-ansi": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
- "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "node_modules/is-core-module": {
+ "version": "2.16.1",
+ "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz",
+ "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==",
"dev": true,
"license": "MIT",
"dependencies": {
- "ansi-regex": "^5.0.1"
+ "hasown": "^2.0.2"
},
"engines": {
- "node": ">=8"
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/cliui/node_modules/wrap-ansi": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
- "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+ "node_modules/is-data-view": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz",
+ "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "ansi-styles": "^4.0.0",
- "string-width": "^4.1.0",
- "strip-ansi": "^6.0.0"
+ "call-bound": "^1.0.2",
+ "get-intrinsic": "^1.2.6",
+ "is-typed-array": "^1.1.13"
},
"engines": {
- "node": ">=10"
+ "node": ">= 0.4"
},
"funding": {
- "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ "url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/color-convert": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "node_modules/is-date-object": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz",
+ "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "color-name": "~1.1.4"
+ "call-bound": "^1.0.2",
+ "has-tostringtag": "^1.0.2"
},
"engines": {
- "node": ">=7.0.0"
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/color-name": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/concat-map": {
- "version": "0.0.1",
- "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
- "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/content-disposition": {
- "version": "0.5.2",
- "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz",
- "integrity": "sha512-kRGRZw3bLlFISDBgwTSA1TMBFN6J6GWDeubmDE3AF+3+yXL8hTWv8r5rkLbqYXY4RjPk/EzHnClI3zQf1cFmHA==",
+ "node_modules/is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
"dev": true,
"license": "MIT",
"engines": {
- "node": ">= 0.6"
+ "node": ">=0.10.0"
}
},
- "node_modules/core-util-is": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
- "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/create-require": {
+ "node_modules/is-finalizationregistry": {
"version": "1.1.1",
- "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
- "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/cross-spawn": {
- "version": "7.0.6",
- "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
- "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
+ "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz",
+ "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "path-key": "^3.1.0",
- "shebang-command": "^2.0.0",
- "which": "^2.0.1"
+ "call-bound": "^1.0.3"
},
"engines": {
- "node": ">= 8"
- }
- },
- "node_modules/cross-spawn/node_modules/isexe": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
- "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
- "dev": true,
- "license": "ISC"
- },
- "node_modules/cross-spawn/node_modules/which": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
- "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "isexe": "^2.0.0"
+ "node": ">= 0.4"
},
- "bin": {
- "node-which": "bin/node-which"
- },
- "engines": {
- "node": ">= 8"
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/debug": {
- "version": "4.4.1",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
- "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
+ "node_modules/is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
"dev": true,
"license": "MIT",
- "dependencies": {
- "ms": "^2.1.3"
- },
"engines": {
- "node": ">=6.0"
- },
- "peerDependenciesMeta": {
- "supports-color": {
- "optional": true
- }
+ "node": ">=8"
}
},
- "node_modules/decamelize": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz",
- "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==",
+ "node_modules/is-generator-function": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.0.tgz",
+ "integrity": "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==",
"dev": true,
"license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "get-proto": "^1.0.0",
+ "has-tostringtag": "^1.0.2",
+ "safe-regex-test": "^1.1.0"
+ },
"engines": {
- "node": ">=10"
+ "node": ">= 0.4"
},
"funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/diff": {
- "version": "7.0.0",
- "resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz",
- "integrity": "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==",
- "dev": true,
- "license": "BSD-3-Clause",
- "engines": {
- "node": ">=0.3.1"
+ "url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/eastasianwidth": {
- "version": "0.2.0",
- "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
- "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/emoji-regex": {
- "version": "9.2.2",
- "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
- "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/escalade": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
- "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
+ "node_modules/is-glob": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
"dev": true,
"license": "MIT",
+ "dependencies": {
+ "is-extglob": "^2.1.1"
+ },
"engines": {
- "node": ">=6"
+ "node": ">=0.10.0"
}
},
- "node_modules/escape-string-regexp": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
- "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+ "node_modules/is-interactive": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz",
+ "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==",
"dev": true,
"license": "MIT",
"engines": {
- "node": ">=10"
+ "node": ">=12"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/find-up": {
- "version": "5.0.0",
- "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
- "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+ "node_modules/is-map": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz",
+ "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==",
"dev": true,
"license": "MIT",
- "dependencies": {
- "locate-path": "^6.0.0",
- "path-exists": "^4.0.0"
+ "engines": {
+ "node": ">= 0.4"
},
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-negative-zero": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz",
+ "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==",
+ "dev": true,
+ "license": "MIT",
"engines": {
- "node": ">=10"
+ "node": ">= 0.4"
},
"funding": {
- "url": "https://github.com/sponsors/sindresorhus"
+ "url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/flat": {
- "version": "5.0.2",
- "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz",
- "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==",
+ "node_modules/is-number": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
"dev": true,
- "license": "BSD-3-Clause",
- "bin": {
- "flat": "cli.js"
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.12.0"
}
},
- "node_modules/foreground-child": {
- "version": "3.3.1",
- "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz",
- "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==",
+ "node_modules/is-number-object": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz",
+ "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==",
"dev": true,
- "license": "ISC",
+ "license": "MIT",
"dependencies": {
- "cross-spawn": "^7.0.6",
- "signal-exit": "^4.0.1"
+ "call-bound": "^1.0.3",
+ "has-tostringtag": "^1.0.2"
},
"engines": {
- "node": ">=14"
+ "node": ">= 0.4"
},
"funding": {
- "url": "https://github.com/sponsors/isaacs"
+ "url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/fsevents": {
- "version": "2.3.2",
- "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
- "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
+ "node_modules/is-plain-obj": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz",
+ "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==",
"dev": true,
- "hasInstallScript": true,
"license": "MIT",
- "optional": true,
- "os": [
- "darwin"
- ],
"engines": {
- "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ "node": ">=8"
}
},
- "node_modules/get-caller-file": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
- "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
+ "node_modules/is-regex": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz",
+ "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==",
"dev": true,
- "license": "ISC",
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "gopd": "^1.2.0",
+ "has-tostringtag": "^1.0.2",
+ "hasown": "^2.0.2"
+ },
"engines": {
- "node": "6.* || 8.* || >= 10.*"
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/get-east-asian-width": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.3.0.tgz",
- "integrity": "sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==",
+ "node_modules/is-set": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz",
+ "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==",
"dev": true,
"license": "MIT",
"engines": {
- "node": ">=18"
+ "node": ">= 0.4"
},
"funding": {
- "url": "https://github.com/sponsors/sindresorhus"
+ "url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/glob": {
- "version": "10.4.5",
- "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz",
- "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==",
+ "node_modules/is-shared-array-buffer": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz",
+ "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==",
"dev": true,
- "license": "ISC",
+ "license": "MIT",
"dependencies": {
- "foreground-child": "^3.1.0",
- "jackspeak": "^3.1.2",
- "minimatch": "^9.0.4",
- "minipass": "^7.1.2",
- "package-json-from-dist": "^1.0.0",
- "path-scurry": "^1.11.1"
+ "call-bound": "^1.0.3"
},
- "bin": {
- "glob": "dist/esm/bin.mjs"
+ "engines": {
+ "node": ">= 0.4"
},
"funding": {
- "url": "https://github.com/sponsors/isaacs"
+ "url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/has-flag": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
- "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "node_modules/is-string": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz",
+ "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==",
"dev": true,
"license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "has-tostringtag": "^1.0.2"
+ },
"engines": {
- "node": ">=8"
- }
- },
- "node_modules/he": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
- "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
- "dev": true,
- "license": "MIT",
- "bin": {
- "he": "bin/he"
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/http-proxy-agent": {
- "version": "7.0.2",
- "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz",
- "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==",
+ "node_modules/is-symbol": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz",
+ "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==",
"dev": true,
"license": "MIT",
"dependencies": {
- "agent-base": "^7.1.0",
- "debug": "^4.3.4"
+ "call-bound": "^1.0.2",
+ "has-symbols": "^1.1.0",
+ "safe-regex-test": "^1.1.0"
},
"engines": {
- "node": ">= 14"
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/https-proxy-agent": {
- "version": "7.0.6",
- "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz",
- "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==",
+ "node_modules/is-typed-array": {
+ "version": "1.1.15",
+ "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz",
+ "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "agent-base": "^7.1.2",
- "debug": "4"
+ "which-typed-array": "^1.1.16"
},
"engines": {
- "node": ">= 14"
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/immediate": {
- "version": "3.0.6",
- "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
- "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/inherits": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
- "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
- "dev": true,
- "license": "ISC"
- },
- "node_modules/is-fullwidth-code-point": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
- "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "node_modules/is-unicode-supported": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz",
+ "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==",
"dev": true,
"license": "MIT",
"engines": {
- "node": ">=8"
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/is-interactive": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz",
- "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==",
+ "node_modules/is-weakmap": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz",
+ "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==",
"dev": true,
"license": "MIT",
"engines": {
- "node": ">=12"
+ "node": ">= 0.4"
},
"funding": {
- "url": "https://github.com/sponsors/sindresorhus"
+ "url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/is-plain-obj": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz",
- "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==",
+ "node_modules/is-weakref": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz",
+ "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==",
"dev": true,
"license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3"
+ },
"engines": {
- "node": ">=8"
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/is-unicode-supported": {
- "version": "0.1.0",
- "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz",
- "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==",
+ "node_modules/is-weakset": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz",
+ "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==",
"dev": true,
"license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "get-intrinsic": "^1.2.6"
+ },
"engines": {
- "node": ">=10"
+ "node": ">= 0.4"
},
"funding": {
- "url": "https://github.com/sponsors/sindresorhus"
+ "url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/isarray": {
@@ -892,6 +3272,56 @@
"js-yaml": "bin/js-yaml.js"
}
},
+ "node_modules/json-buffer": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
+ "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json-stable-stringify-without-jsonify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json5": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz",
+ "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "minimist": "^1.2.0"
+ },
+ "bin": {
+ "json5": "lib/cli.js"
+ }
+ },
+ "node_modules/jsx-ast-utils": {
+ "version": "3.3.5",
+ "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz",
+ "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "array-includes": "^3.1.6",
+ "array.prototype.flat": "^1.3.1",
+ "object.assign": "^4.1.4",
+ "object.values": "^1.1.6"
+ },
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
"node_modules/jszip": {
"version": "3.10.1",
"resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz",
@@ -905,6 +3335,50 @@
"setimmediate": "^1.0.5"
}
},
+ "node_modules/keyv": {
+ "version": "4.5.4",
+ "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
+ "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "json-buffer": "3.0.1"
+ }
+ },
+ "node_modules/language-subtag-registry": {
+ "version": "0.3.23",
+ "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz",
+ "integrity": "sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==",
+ "dev": true,
+ "license": "CC0-1.0"
+ },
+ "node_modules/language-tags": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.9.tgz",
+ "integrity": "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "language-subtag-registry": "^0.3.20"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/levn": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
+ "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "prelude-ls": "^1.2.1",
+ "type-check": "~0.4.0"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
"node_modules/lie": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz",
@@ -931,6 +3405,13 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/lodash.merge": {
+ "version": "4.6.2",
+ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
+ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/log-symbols": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz",
@@ -960,7 +3441,41 @@
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
"integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
"dev": true,
- "license": "ISC"
+ "license": "ISC"
+ },
+ "node_modules/math-intrinsics": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
+ "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/merge2": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
+ "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/micromatch": {
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
+ "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "braces": "^3.0.3",
+ "picomatch": "^2.3.1"
+ },
+ "engines": {
+ "node": ">=8.6"
+ }
},
"node_modules/mime-db": {
"version": "1.33.0",
@@ -1014,6 +3529,16 @@
"url": "https://github.com/sponsors/isaacs"
}
},
+ "node_modules/minimist": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
+ "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/minipass": {
"version": "7.1.2",
"resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
@@ -1067,6 +3592,110 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/natural-compare": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/object-inspect": {
+ "version": "1.13.4",
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
+ "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/object-keys": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
+ "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/object.assign": {
+ "version": "4.1.7",
+ "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz",
+ "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.3",
+ "define-properties": "^1.2.1",
+ "es-object-atoms": "^1.0.0",
+ "has-symbols": "^1.1.0",
+ "object-keys": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/object.fromentries": {
+ "version": "2.0.8",
+ "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz",
+ "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.2",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/object.groupby": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz",
+ "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/object.values": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz",
+ "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.3",
+ "define-properties": "^1.2.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/onetime": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz",
@@ -1083,6 +3712,24 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/optionator": {
+ "version": "0.9.4",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
+ "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "deep-is": "^0.1.3",
+ "fast-levenshtein": "^2.0.6",
+ "levn": "^0.4.1",
+ "prelude-ls": "^1.2.1",
+ "type-check": "^0.4.0",
+ "word-wrap": "^1.2.5"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
"node_modules/ora": {
"version": "8.2.0",
"resolved": "https://registry.npmjs.org/ora/-/ora-8.2.0.tgz",
@@ -1188,6 +3835,24 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/own-keys": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz",
+ "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "get-intrinsic": "^1.2.6",
+ "object-keys": "^1.1.1",
+ "safe-push-apply": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/p-limit": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
@@ -1234,6 +3899,19 @@
"dev": true,
"license": "(MIT AND Zlib)"
},
+ "node_modules/parent-module": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+ "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "callsites": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/path-exists": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
@@ -1261,6 +3939,13 @@
"node": ">=8"
}
},
+ "node_modules/path-parse": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
+ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/path-scurry": {
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz",
@@ -1292,6 +3977,19 @@
"dev": true,
"license": "ISC"
},
+ "node_modules/picomatch": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
"node_modules/playwright": {
"version": "1.54.2",
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.54.2.tgz",
@@ -1324,6 +4022,56 @@
"node": ">=18"
}
},
+ "node_modules/possible-typed-array-names": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz",
+ "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/prelude-ls": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
+ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/prettier": {
+ "version": "3.6.2",
+ "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz",
+ "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "bin": {
+ "prettier": "bin/prettier.cjs"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/prettier/prettier?sponsor=1"
+ }
+ },
+ "node_modules/prettier-linter-helpers": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz",
+ "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fast-diff": "^1.1.2"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
"node_modules/process-nextick-args": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
@@ -1331,6 +4079,37 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/punycode": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
+ "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/queue-microtask": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
+ "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT"
+ },
"node_modules/randombytes": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
@@ -1381,6 +4160,50 @@
"url": "https://paulmillr.com/funding/"
}
},
+ "node_modules/reflect.getprototypeof": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz",
+ "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.9",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.0.0",
+ "get-intrinsic": "^1.2.7",
+ "get-proto": "^1.0.1",
+ "which-builtin-type": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/regexp.prototype.flags": {
+ "version": "1.5.4",
+ "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz",
+ "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "define-properties": "^1.2.1",
+ "es-errors": "^1.3.0",
+ "get-proto": "^1.0.1",
+ "gopd": "^1.2.0",
+ "set-function-name": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/require-directory": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
@@ -1391,6 +4214,37 @@
"node": ">=0.10.0"
}
},
+ "node_modules/resolve": {
+ "version": "1.22.10",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz",
+ "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-core-module": "^2.16.0",
+ "path-parse": "^1.0.7",
+ "supports-preserve-symlinks-flag": "^1.0.0"
+ },
+ "bin": {
+ "resolve": "bin/resolve"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/resolve-from": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
"node_modules/restore-cursor": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz",
@@ -1408,6 +4262,68 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/reusify": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz",
+ "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "iojs": ">=1.0.0",
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/run-parallel": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
+ "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "queue-microtask": "^1.2.2"
+ }
+ },
+ "node_modules/safe-array-concat": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz",
+ "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.2",
+ "get-intrinsic": "^1.2.6",
+ "has-symbols": "^1.1.0",
+ "isarray": "^2.0.5"
+ },
+ "engines": {
+ "node": ">=0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/safe-array-concat/node_modules/isarray": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
+ "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
@@ -1415,6 +4331,48 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/safe-push-apply": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz",
+ "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "isarray": "^2.0.5"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/safe-push-apply/node_modules/isarray": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
+ "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/safe-regex-test": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz",
+ "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "is-regex": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/semver": {
"version": "7.7.2",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
@@ -1461,21 +4419,70 @@
"dev": true,
"license": "MIT",
"dependencies": {
- "balanced-match": "^1.0.0",
- "concat-map": "0.0.1"
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/serve-handler/node_modules/minimatch": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/set-function-length": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
+ "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "define-data-property": "^1.1.4",
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2",
+ "get-intrinsic": "^1.2.4",
+ "gopd": "^1.0.1",
+ "has-property-descriptors": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/set-function-name": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz",
+ "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "define-data-property": "^1.1.4",
+ "es-errors": "^1.3.0",
+ "functions-have-names": "^1.2.3",
+ "has-property-descriptors": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
}
},
- "node_modules/serve-handler/node_modules/minimatch": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
- "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+ "node_modules/set-proto": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz",
+ "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==",
"dev": true,
- "license": "ISC",
+ "license": "MIT",
"dependencies": {
- "brace-expansion": "^1.1.7"
+ "dunder-proto": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.0.0"
},
"engines": {
- "node": "*"
+ "node": ">= 0.4"
}
},
"node_modules/setimmediate": {
@@ -1508,6 +4515,82 @@
"node": ">=8"
}
},
+ "node_modules/side-channel": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
+ "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "object-inspect": "^1.13.3",
+ "side-channel-list": "^1.0.0",
+ "side-channel-map": "^1.0.1",
+ "side-channel-weakmap": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-list": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz",
+ "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "object-inspect": "^1.13.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-map": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
+ "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.5",
+ "object-inspect": "^1.13.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-weakmap": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
+ "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.5",
+ "object-inspect": "^1.13.3",
+ "side-channel-map": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/signal-exit": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
@@ -1534,6 +4617,20 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/stop-iteration-iterator": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz",
+ "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "internal-slot": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
"node_modules/string_decoder": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
@@ -1608,6 +4705,80 @@
"node": ">=8"
}
},
+ "node_modules/string.prototype.includes": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/string.prototype.includes/-/string.prototype.includes-2.0.1.tgz",
+ "integrity": "sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/string.prototype.trim": {
+ "version": "1.2.10",
+ "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz",
+ "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.2",
+ "define-data-property": "^1.1.4",
+ "define-properties": "^1.2.1",
+ "es-abstract": "^1.23.5",
+ "es-object-atoms": "^1.0.0",
+ "has-property-descriptors": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/string.prototype.trimend": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz",
+ "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.2",
+ "define-properties": "^1.2.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/string.prototype.trimstart": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz",
+ "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "define-properties": "^1.2.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/strip-ansi": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
@@ -1648,6 +4819,16 @@
"node": ">=8"
}
},
+ "node_modules/strip-bom": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
+ "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
"node_modules/strip-json-comments": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
@@ -1677,6 +4858,61 @@
"url": "https://github.com/chalk/supports-color?sponsor=1"
}
},
+ "node_modules/supports-preserve-symlinks-flag": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
+ "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/synckit": {
+ "version": "0.11.11",
+ "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.11.tgz",
+ "integrity": "sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@pkgr/core": "^0.2.9"
+ },
+ "engines": {
+ "node": "^14.18.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/synckit"
+ }
+ },
+ "node_modules/to-regex-range": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+ "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-number": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=8.0"
+ }
+ },
+ "node_modules/ts-api-utils": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz",
+ "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18.12"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4"
+ }
+ },
"node_modules/ts-node": {
"version": "10.9.2",
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz",
@@ -1731,6 +4967,110 @@
"node": ">=0.3.1"
}
},
+ "node_modules/tsconfig-paths": {
+ "version": "3.15.0",
+ "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz",
+ "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/json5": "^0.0.29",
+ "json5": "^1.0.2",
+ "minimist": "^1.2.6",
+ "strip-bom": "^3.0.0"
+ }
+ },
+ "node_modules/type-check": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
+ "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "prelude-ls": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/typed-array-buffer": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz",
+ "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "es-errors": "^1.3.0",
+ "is-typed-array": "^1.1.14"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/typed-array-byte-length": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz",
+ "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.8",
+ "for-each": "^0.3.3",
+ "gopd": "^1.2.0",
+ "has-proto": "^1.2.0",
+ "is-typed-array": "^1.1.14"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/typed-array-byte-offset": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz",
+ "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "available-typed-arrays": "^1.0.7",
+ "call-bind": "^1.0.8",
+ "for-each": "^0.3.3",
+ "gopd": "^1.2.0",
+ "has-proto": "^1.2.0",
+ "is-typed-array": "^1.1.15",
+ "reflect.getprototypeof": "^1.0.9"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/typed-array-length": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz",
+ "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind": "^1.0.7",
+ "for-each": "^0.3.3",
+ "gopd": "^1.0.1",
+ "is-typed-array": "^1.1.13",
+ "possible-typed-array-names": "^1.0.0",
+ "reflect.getprototypeof": "^1.0.6"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/typescript": {
"version": "4.9.5",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz",
@@ -1745,6 +5085,49 @@
"node": ">=4.2.0"
}
},
+ "node_modules/typescript-eslint": {
+ "version": "8.39.0",
+ "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.39.0.tgz",
+ "integrity": "sha512-lH8FvtdtzcHJCkMOKnN73LIn6SLTpoojgJqDAxPm1jCR14eWSGPX8ul/gggBdPMk/d5+u9V854vTYQ8T5jF/1Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/eslint-plugin": "8.39.0",
+ "@typescript-eslint/parser": "8.39.0",
+ "@typescript-eslint/typescript-estree": "8.39.0",
+ "@typescript-eslint/utils": "8.39.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0",
+ "typescript": ">=4.8.4 <6.0.0"
+ }
+ },
+ "node_modules/unbox-primitive": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz",
+ "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.3",
+ "has-bigints": "^1.0.2",
+ "has-symbols": "^1.1.0",
+ "which-boxed-primitive": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/undici-types": {
"version": "7.8.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.8.0.tgz",
@@ -1752,6 +5135,16 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/uri-js": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+ "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "punycode": "^2.1.0"
+ }
+ },
"node_modules/util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
@@ -1781,6 +5174,112 @@
"node": "^18.17.0 || >=20.5.0"
}
},
+ "node_modules/which-boxed-primitive": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz",
+ "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-bigint": "^1.1.0",
+ "is-boolean-object": "^1.2.1",
+ "is-number-object": "^1.1.1",
+ "is-string": "^1.1.1",
+ "is-symbol": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/which-builtin-type": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz",
+ "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "function.prototype.name": "^1.1.6",
+ "has-tostringtag": "^1.0.2",
+ "is-async-function": "^2.0.0",
+ "is-date-object": "^1.1.0",
+ "is-finalizationregistry": "^1.1.0",
+ "is-generator-function": "^1.0.10",
+ "is-regex": "^1.2.1",
+ "is-weakref": "^1.0.2",
+ "isarray": "^2.0.5",
+ "which-boxed-primitive": "^1.1.0",
+ "which-collection": "^1.0.2",
+ "which-typed-array": "^1.1.16"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/which-builtin-type/node_modules/isarray": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
+ "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/which-collection": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz",
+ "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-map": "^2.0.3",
+ "is-set": "^2.0.3",
+ "is-weakmap": "^2.0.2",
+ "is-weakset": "^2.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/which-typed-array": {
+ "version": "1.1.19",
+ "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz",
+ "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "available-typed-arrays": "^1.0.7",
+ "call-bind": "^1.0.8",
+ "call-bound": "^1.0.4",
+ "for-each": "^0.3.5",
+ "get-proto": "^1.0.1",
+ "gopd": "^1.2.0",
+ "has-tostringtag": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/word-wrap": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
+ "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/workerpool": {
"version": "9.3.3",
"resolved": "https://registry.npmjs.org/workerpool/-/workerpool-9.3.3.tgz",
diff --git a/package.json b/package.json
index b49fb0d..fe71fde 100644
--- a/package.json
+++ b/package.json
@@ -2,7 +2,7 @@
"name": "launchmap",
"displayName": "LaunchMap",
"description": "Visualize ROS2 Launch Files",
- "version": "0.1.7",
+ "version": "0.1.8",
"publisher": "kodorobotics",
"icon": "assets/launchmap-logo.png",
"bugs": {
@@ -65,23 +65,37 @@
},
"scripts": {
"compile": "tsc",
+ "lint:py": "ruff check parser/",
+ "lint:py:fix": "ruff check --fix parser/",
+ "lint:js": "eslint webview/ src/ --ext .js,.ts,.tsx",
+ "lint:js:fix": "eslint webview/ src/ --ext .js,.ts,.tsx --fix",
+ "lint": "npm run lint:py && npm run lint:js",
+ "lint:fix": "npm run lint:py:fix && npm run lint:js:fix",
"test:src": "npm run compile && node dist/test/runTest.js",
"test:parser": "cd parser/tests && pytest . --maxfail=1 --disable-warnings -q",
"test:webview": "cd webview/tests && npx playwright test",
"test:all": "npm run test:src && npm run test:parser && npm run test:webview"
},
"devDependencies": {
+ "@eslint/js": "^9.32.0",
"@playwright/test": "^1.54.2",
"@types/mocha": "^10.0.10",
"@types/node": "^24.0.1",
"@types/vscode": "^1.70.0",
"@types/which": "^3.0.4",
"@vscode/test-electron": "^2.5.2",
+ "eslint": "^9.32.0",
+ "eslint-config-prettier": "^10.1.8",
+ "eslint-plugin-import": "^2.32.0",
+ "eslint-plugin-jsx-a11y": "^6.10.2",
+ "eslint-plugin-prettier": "^5.5.4",
+ "globals": "^16.3.0",
"mocha": "^11.7.1",
"playwright": "^1.54.2",
"serve-handler": "^6.1.6",
"ts-node": "^10.9.2",
- "typescript": "^4.7.4"
+ "typescript": "^4.7.4",
+ "typescript-eslint": "^8.39.0"
},
"dependencies": {
"which": "^5.0.0"
diff --git a/parse.py b/parse.py
index d55c2a9..e3d8fe8 100644
--- a/parse.py
+++ b/parse.py
@@ -18,10 +18,13 @@
from parser.entrypoint.user_interface import parse_and_format_launch_file
from parser.plugin_loader import load_user_handlers_from_directory
+
def main():
parser = argparse.ArgumentParser(description="Parse a ROS2 launch file.")
parser.add_argument("launch_file", help="Path to launch file to parse.")
- parser.add_argument("--plugin-dir", help="Directory containing user-defined custom handlers.")
+ parser.add_argument(
+ "--plugin-dir", help="Directory containing user-defined custom handlers."
+ )
args = parser.parse_args()
@@ -35,5 +38,6 @@ def main():
print(f"Error: {e}")
sys.exit(1)
+
if __name__ == "__main__":
- main()
\ No newline at end of file
+ main()
diff --git a/parser/context.py b/parser/context.py
index c6a9581..6dc40f4 100644
--- a/parser/context.py
+++ b/parser/context.py
@@ -12,10 +12,12 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+import ast
from collections import defaultdict
-from parser.introspection.tracker import IntrospectionTracker
from typing import Any
-import ast
+
+from parser.introspection.tracker import IntrospectionTracker
+
class ParseContext:
def __init__(self, introspection: IntrospectionTracker = None):
@@ -46,45 +48,45 @@ def __init__(self, introspection: IntrospectionTracker = None):
## Variable Management
def define_variable(self, name: str, value: Any):
- """ Define or update a variable in context. """
+ """Define or update a variable in context."""
self.variables[name] = value
def has_variable(self, name: str) -> bool:
- """ Check variable exists in context """
+ """Check variable exists in context"""
return name in self.variables
def lookup_variable(self, name: str) -> Any:
- """ Resolve a variable by name. Raises if undefined. """
+ """Resolve a variable by name. Raises if undefined."""
if name not in self.variables:
raise NameError(f"Variable '{name}' is not defined in context.")
return self.variables[name]
-
+
## Function API
def define_function(self, name: str, fn_def: ast.FunctionDef):
- """ Define or update a function in context. """
+ """Define or update a function in context."""
self.functions[name] = fn_def
def has_function(self, name: str) -> bool:
- """ Check function exists in context. """
+ """Check function exists in context."""
return name in self.functions
def lookup_function(self, name: str) -> ast.FunctionDef | None:
- """ Return the AST of a function by name, or None if not found. """
+ """Return the AST of a function by name, or None if not found."""
return self.functions.get(name)
-
+
## Namespace stack
-
+
def push_namespace(self, ns: str):
self.namespace_stack.append(ns)
-
+
def pop_namespace(self):
if self.namespace_stack:
self.namespace_stack.pop()
def current_namespace(self) -> str | None:
return "/".join(self.namespace_stack) if self.namespace_stack else None
-
+
## Composable node groups
def register_composable_node_group(self, container_name: str, container_metadata: dict):
@@ -99,25 +101,22 @@ def get_composable_node_groups(self) -> list[dict]:
if not data["composable_nodes"]:
continue
- entry = {
- "target_container": name,
- "composable_nodes": data["composable_nodes"]
- }
+ entry = {"target_container": name, "composable_nodes": data["composable_nodes"]}
for key, value in data.items():
if key in ("composable_nodes",):
continue
if value not in (None, "", [], {}):
entry[key] = value
-
+
results.append(entry)
return results
-
+
## Utility
def get_func_name(self, func_node) -> str:
- """ Resolve a dotted name from an AST function call. """
+ """Resolve a dotted name from an AST function call."""
from parser.resolution.utils import get_func_name
+
return get_func_name(func_node)
-
\ No newline at end of file
diff --git a/parser/entrypoint/parser_runner.py b/parser/entrypoint/parser_runner.py
index f651548..d7a2088 100644
--- a/parser/entrypoint/parser_runner.py
+++ b/parser/entrypoint/parser_runner.py
@@ -13,10 +13,12 @@
# limitations under the License.
import ast
+
from parser.context import ParseContext
from parser.parser.utils.ast_utils import collect_function_defs
from parser.resolution.resolution_engine import ResolutionEngine
+
def parse_launch_file(filepath: str) -> dict:
"""
Entrypoint: parses a launch file and returns structured output
@@ -49,7 +51,7 @@ def parse_launch_file(filepath: str) -> dict:
main_fn = context.lookup_function("generate_launch_description")
if not main_fn:
raise ValueError("No generate_launch_description() function found.")
-
+
parsed.extend(_parse_launch_function_body(main_fn.body, context, engine))
return {
@@ -57,14 +59,19 @@ def parse_launch_file(filepath: str) -> dict:
"parsed": parsed,
"used_launch_config": sorted(context.introspection.used_launch_configs),
"declared_arguments": sorted(context.introspection.declared_launch_args.keys()),
- "undeclared_launch_configurations": sorted(context.introspection.get_undeclared_launch_configs()),
+ "undeclared_launch_configurations": sorted(
+ context.introspection.get_undeclared_launch_configs()
+ ),
"environment_variables": context.introspection.get_environment_variables(),
"python_expressions": context.introspection.get_python_expressions(),
"composable_node_containers": sorted(context.get_composable_node_groups()),
- "additional_components": context.introspection.get_registered_entities()
+ "additional_components": context.introspection.get_registered_entities(),
}
-def _parse_launch_function_body(body: list[ast.stmt], context: ParseContext, engine: ResolutionEngine) -> list:
+
+def _parse_launch_function_body(
+ body: list[ast.stmt], context: ParseContext, engine: ResolutionEngine
+) -> list:
parsed = []
for stmt in body:
if isinstance(stmt, ast.Assign):
@@ -72,7 +79,7 @@ def _parse_launch_function_body(body: list[ast.stmt], context: ParseContext, eng
elif isinstance(stmt, ast.If):
engine.resolve(stmt)
-
+
elif isinstance(stmt, ast.Expr):
func_name = context.get_func_name(stmt.value.func)
if func_name.endswith("add_action"):
@@ -82,12 +89,12 @@ def _parse_launch_function_body(body: list[ast.stmt], context: ParseContext, eng
parsed.append(result)
else:
engine.resolve(stmt)
-
+
elif isinstance(stmt, ast.Return) and isinstance(stmt.value, ast.Call):
resolved = engine.resolve(stmt.value)
if isinstance(resolved, list):
parsed.extend(resolved)
elif resolved is not None:
parsed.append(resolved)
-
- return parsed
\ No newline at end of file
+
+ return parsed
diff --git a/parser/entrypoint/user_interface.py b/parser/entrypoint/user_interface.py
index 7a2ac20..0de662e 100644
--- a/parser/entrypoint/user_interface.py
+++ b/parser/entrypoint/user_interface.py
@@ -13,11 +13,16 @@
# limitations under the License.
from parser.entrypoint.parser_runner import parse_launch_file
-from parser.parser.introspection_utils import (collect_environment_variable_usages, collect_launch_config_usages,
- collect_event_handler_usages, collect_python_variable_usages)
+from parser.parser.introspection_utils import (
+ collect_environment_variable_usages,
+ collect_event_handler_usages,
+ collect_launch_config_usages,
+ collect_python_variable_usages,
+)
from parser.parser.postprocessing import simplify_launch_configurations
from parser.parser.utils.common import group_entities_by_type
+
def parse_and_format_launch_file(filepath: str) -> dict:
"""
Parses a ROS2 launch file and return output in the standardized grouped format.
@@ -57,4 +62,4 @@ def parse_and_format_launch_file(filepath: str) -> dict:
if python_expression_usages:
grouped["python_expression_usages"] = python_expression_usages
- return simplify_launch_configurations(grouped)
\ No newline at end of file
+ return simplify_launch_configurations(grouped)
diff --git a/parser/full_launch_example.launch.py b/parser/full_launch_example.launch.py
index 128c301..5c70e29 100644
--- a/parser/full_launch_example.launch.py
+++ b/parser/full_launch_example.launch.py
@@ -12,26 +12,31 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+
from launch import LaunchDescription
-from launch.actions import DeclareLaunchArgument, GroupAction, IncludeLaunchDescription, OpaqueFunction
+from launch.actions import (
+ DeclareLaunchArgument,
+ GroupAction,
+ IncludeLaunchDescription,
+ OpaqueFunction,
+)
from launch.conditions import IfCondition
+from launch.launch_description_sources import PythonLaunchDescriptionSource
from launch.substitutions import LaunchConfiguration, PythonExpression
-from launch_ros.actions import Node, ComposableNodeContainer
+from launch_ros.actions import ComposableNodeContainer, Node
from launch_ros.descriptions import ComposableNode
-from launch.launch_description_sources import PythonLaunchDescriptionSource
-import os
def launch_setup(context, *args, **kwargs):
- use_sim_time = LaunchConfiguration('use_sim_time').perform(context)
- robot_description = f"..."
+ use_sim_time = LaunchConfiguration("use_sim_time").perform(context)
+ robot_description = "..."
return [
Node(
- package='robot_state_publisher',
- executable='robot_state_publisher',
- output='screen',
- parameters=[{'use_sim_time': use_sim_time, 'robot_description': robot_description}]
+ package="robot_state_publisher",
+ executable="robot_state_publisher",
+ output="screen",
+ parameters=[{"use_sim_time": use_sim_time, "robot_description": robot_description}],
)
]
@@ -39,37 +44,34 @@ def launch_setup(context, *args, **kwargs):
def generate_launch_description():
# Declare arguments
package_arg = DeclareLaunchArgument(
- 'pkg_name',
- default_value='my_robot_pkg',
- description='Package containing the launch files'
+ "pkg_name", default_value="my_robot_pkg", description="Package containing the launch files"
)
use_sim_arg = DeclareLaunchArgument(
- 'use_sim_time',
- default_value='true',
- description='Use simulation clock'
+ "use_sim_time", default_value="true", description="Use simulation clock"
)
# Include
included_launch = IncludeLaunchDescription(
- PythonLaunchDescriptionSource([
- LaunchConfiguration('pkg_name'),
- '/launch/other_launch_file.py'
- ])
+ PythonLaunchDescriptionSource(
+ [LaunchConfiguration("pkg_name"), "/launch/other_launch_file.py"]
+ )
)
# Group with condition
conditional_group = GroupAction(
actions=[
Node(
- package='demo_nodes_cpp',
- executable='talker',
- name='conditional_talker',
- output='screen',
- parameters=[{'use_sim_time': LaunchConfiguration('use_sim_time')}]
+ package="demo_nodes_cpp",
+ executable="talker",
+ name="conditional_talker",
+ output="screen",
+ parameters=[{"use_sim_time": LaunchConfiguration("use_sim_time")}],
)
],
- condition=IfCondition(PythonExpression(["'", LaunchConfiguration('use_sim_time'), "' == 'true'"])),
- namespace='sim_ns'
+ condition=IfCondition(
+ PythonExpression(["'", LaunchConfiguration("use_sim_time"), "' == 'true'"])
+ ),
+ namespace="sim_ns",
)
# OpaqueFunction
@@ -77,32 +79,27 @@ def generate_launch_description():
# Composable Container + Nodes
container = ComposableNodeContainer(
- name='my_container',
- namespace='',
- package='rclcpp_components',
- executable='component_container_mt',
+ name="my_container",
+ namespace="",
+ package="rclcpp_components",
+ executable="component_container_mt",
composable_node_descriptions=[
ComposableNode(
- package='demo_nodes_cpp',
- plugin='demo_nodes_cpp::Talker',
- name='talker_component',
- parameters=[{'use_sim_time': LaunchConfiguration('use_sim_time')}]
+ package="demo_nodes_cpp",
+ plugin="demo_nodes_cpp::Talker",
+ name="talker_component",
+ parameters=[{"use_sim_time": LaunchConfiguration("use_sim_time")}],
),
ComposableNode(
- package='demo_nodes_cpp',
- plugin='demo_nodes_cpp::Listener',
- name='listener_component',
- parameters=[{'use_sim_time': LaunchConfiguration('use_sim_time')}]
- )
+ package="demo_nodes_cpp",
+ plugin="demo_nodes_cpp::Listener",
+ name="listener_component",
+ parameters=[{"use_sim_time": LaunchConfiguration("use_sim_time")}],
+ ),
],
- output='screen'
+ output="screen",
)
- return LaunchDescription([
- package_arg,
- use_sim_arg,
- included_launch,
- conditional_group,
- opaque,
- container
- ])
+ return LaunchDescription(
+ [package_arg, use_sim_arg, included_launch, conditional_group, opaque, container]
+ )
diff --git a/parser/includes/resolver.py b/parser/includes/resolver.py
index 396ddbe..bcd4402 100644
--- a/parser/includes/resolver.py
+++ b/parser/includes/resolver.py
@@ -14,11 +14,15 @@
import os
import warnings
+
from parser.context import ParseContext
-from parser.resolution.resolution_engine import ResolutionEngine
from parser.entrypoint.parser_runner import parse_launch_file
+from parser.resolution.resolution_engine import ResolutionEngine
-def resolve_included_launch_file(filename: str, parent_context: ParseContext, passed_arguments: list) -> dict:
+
+def resolve_included_launch_file(
+ filename: str, parent_context: ParseContext, passed_arguments: list
+) -> dict:
"""
Parses the included launch file, applying passed arguments and merging introspection.
"""
@@ -37,6 +41,5 @@ def resolve_included_launch_file(filename: str, parent_context: ParseContext, pa
if isinstance(pair, (list, tuple)) and len(pair) == 2:
name, value = pair
child_context.define_variable(name, value)
-
+
return parse_launch_file(filename, child_context)
-
\ No newline at end of file
diff --git a/parser/introspection/tracker.py b/parser/introspection/tracker.py
index 58952bd..af61248 100644
--- a/parser/introspection/tracker.py
+++ b/parser/introspection/tracker.py
@@ -12,9 +12,9 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-from collections import defaultdict
from parser.parser.utils.common import compute_entity_key
+
class IntrospectionTracker:
def __init__(self):
# All declared arguments from DeclareLaunchArgument
@@ -49,14 +49,14 @@ def track_launch_config_usage(self, name: str):
Adds the name to the used set.
"""
self.used_launch_configs.add(name)
-
+
def get_undeclared_launch_configs(self) -> set[str]:
"""
Returns all LaunchConfigurations used but not declared.
Useful for validation and diagnostics.
"""
return self.used_launch_configs - set(self.declared_launch_args.keys())
-
+
# Environment Variables
def track_environment_variable(self, name: str, metadata: dict):
"""
@@ -64,13 +64,13 @@ def track_environment_variable(self, name: str, metadata: dict):
Adds the name to the used set.
"""
self.environment_variables[name] = metadata or {}
-
+
def get_environment_variables(self) -> list:
"""
Returns all EnvironmentVariable.
"""
return list(self.environment_variables.values())
-
+
# Event Handler
def add_event_handler(self, handler: str):
"""
@@ -83,7 +83,7 @@ def next_event_handler_index(self) -> int:
Return the index of the next event handler
"""
return len(self.event_handlers)
-
+
def register_entity(self, entity):
"""
Add entity to output only once (used for nodes, timers, loginfo, etc.)
@@ -91,19 +91,16 @@ def register_entity(self, entity):
key = compute_entity_key(entity)
if key not in self.entities_by_key:
self.entities_by_key[key] = entity
-
+
def get_registered_entities(self):
"""
Get all the registered entities
"""
return list(self.entities_by_key.values())
-
+
# Python Expressions
def track_python_expression(self, body: str, variables: list[str]):
- self.symbolic_python_expressions.append({
- "body": body,
- "variables": variables
- })
+ self.symbolic_python_expressions.append({"body": body, "variables": variables})
def get_python_expressions(self) -> list[dict]:
- return self.symbolic_python_expressions
\ No newline at end of file
+ return self.symbolic_python_expressions
diff --git a/parser/parser/dispatcher.py b/parser/parser/dispatcher.py
index e40241f..04af22e 100644
--- a/parser/parser/dispatcher.py
+++ b/parser/parser/dispatcher.py
@@ -13,13 +13,15 @@
# limitations under the License.
import ast
+
+from parser.context import ParseContext
from parser.parser.loader import register_builtin_handlers
from parser.parser.registry import get_handler
-from parser.context import ParseContext
from parser.resolution.utils import get_func_name
register_builtin_handlers()
+
def dispatch_call(node: ast.Call, context: ParseContext) -> dict:
"""
Dispatch a launch construct call to its registered handler.
@@ -33,5 +35,5 @@ def dispatch_call(node: ast.Call, context: ParseContext) -> dict:
if not handler:
raise ValueError(f"Unrecognized launch construct: '{func_name}'")
-
- return handler(node, context)
\ No newline at end of file
+
+ return handler(node, context)
diff --git a/parser/parser/handlers/command_handler.py b/parser/parser/handlers/command_handler.py
index 0090bc6..e25a0ad 100644
--- a/parser/parser/handlers/command_handler.py
+++ b/parser/parser/handlers/command_handler.py
@@ -13,15 +13,14 @@
# limitations under the License.
import ast
+
from parser.context import ParseContext
from parser.parser.registry import register_handler
from parser.resolution.utils import resolve_call_signature
+
@register_handler("Command", "launch.substitutions.Command")
def handle_find_package_share(node: ast.Call, context: ParseContext) -> dict:
args, _ = resolve_call_signature(node, context.engine)
- return {
- "type": "Command",
- "command": args[0]
- }
\ No newline at end of file
+ return {"type": "Command", "command": args[0]}
diff --git a/parser/parser/handlers/composable_node_container_handler.py b/parser/parser/handlers/composable_node_container_handler.py
index 797d7a1..c7b1738 100644
--- a/parser/parser/handlers/composable_node_container_handler.py
+++ b/parser/parser/handlers/composable_node_container_handler.py
@@ -13,11 +13,13 @@
# limitations under the License.
import ast
+
from parser.context import ParseContext
from parser.parser.registry import register_handler
from parser.parser.utils.common import flatten_once, group_entities_by_type
from parser.resolution.utils import resolve_call_signature
+
@register_handler("ComposableNodeContainer", "launch_ros.actions.ComposableNodeContainer")
def handle_composable_container(node: ast.Call, context: ParseContext) -> dict:
args, kwargs = resolve_call_signature(node, context.engine)
@@ -37,13 +39,10 @@ def handle_composable_container(node: ast.Call, context: ParseContext) -> dict:
}
for key, value in kwargs.items():
if key not in {"composable_node_descriptions", "name"}:
- container_metadata[key] = value
+ container_metadata[key] = value
# Register container and attach nodes
context.register_composable_node_group(container_name, container_metadata)
context.extend_composable_node_group(container_name, composable_nodes)
- return {
- "type": "ComposableNodeContainer",
- "target_container": container_name
- }
+ return {"type": "ComposableNodeContainer", "target_container": container_name}
diff --git a/parser/parser/handlers/composable_node_handler.py b/parser/parser/handlers/composable_node_handler.py
index b820044..ebec479 100644
--- a/parser/parser/handlers/composable_node_handler.py
+++ b/parser/parser/handlers/composable_node_handler.py
@@ -13,12 +13,14 @@
# limitations under the License.
import ast
+
from parser.context import ParseContext
from parser.parser.registry import register_handler
from parser.resolution.utils import resolve_call_signature
+
@register_handler("ComposableNode", "launch_ros.descriptions.ComposableNode")
def handle_composable_node(node: ast.Call, context: ParseContext) -> dict:
args, kwargs = resolve_call_signature(node, context.engine)
- return {"type": "ComposableNode", **kwargs}
\ No newline at end of file
+ return {"type": "ComposableNode", **kwargs}
diff --git a/parser/parser/handlers/condition_handler.py b/parser/parser/handlers/condition_handler.py
index 8c468b4..f39ef41 100644
--- a/parser/parser/handlers/condition_handler.py
+++ b/parser/parser/handlers/condition_handler.py
@@ -12,21 +12,24 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+import ast
+
from parser.context import ParseContext
from parser.parser.postprocessing import simplify_launch_configurations
from parser.parser.registry import register_handler
from parser.resolution.utils import resolve_call_signature
-import ast
+
@register_handler("IfCondition", "launch.conditions.IfCondition")
def handle_if_condition(node: ast.Call, context: ParseContext) -> dict:
args, kwargs = resolve_call_signature(node, context.engine)
if args:
kwargs["expression"] = args[0]
-
+
expression = simplify_launch_configurations(kwargs["expression"])
return f"${{IfCondition:{expression}}}"
+
@register_handler("UnlessCondition", "launch.conditions.UnlessCondition")
def handle_unless_condition(node: ast.Call, context: ParseContext) -> dict:
args, kwargs = resolve_call_signature(node, context.engine)
diff --git a/parser/parser/handlers/declare_launch_argument_handler.py b/parser/parser/handlers/declare_launch_argument_handler.py
index 9747bcf..68ea796 100644
--- a/parser/parser/handlers/declare_launch_argument_handler.py
+++ b/parser/parser/handlers/declare_launch_argument_handler.py
@@ -13,9 +13,11 @@
# limitations under the License.
import ast
+
from parser.context import ParseContext
-from parser.resolution.utils import resolve_call_signature
from parser.parser.registry import register_handler
+from parser.resolution.utils import resolve_call_signature
+
@register_handler("DeclareLaunchArgument", "launch.actions.DeclareLaunchArgument")
def handle_declare_launch_argument(node: ast.Call, context: ParseContext) -> dict:
@@ -28,10 +30,10 @@ def handle_declare_launch_argument(node: ast.Call, context: ParseContext) -> dic
# Positional fallback: first arg = name
if "name" not in kwargs and args:
kwargs["name"] = args[0]
-
+
# Track for introspection
arg_name = kwargs.get("name")
if arg_name:
context.introspection.track_launch_arg_declaration(arg_name, kwargs)
- return {"type": "DeclareLaunchArgument", **kwargs}
\ No newline at end of file
+ return {"type": "DeclareLaunchArgument", **kwargs}
diff --git a/parser/parser/handlers/environment_variable_handler.py b/parser/parser/handlers/environment_variable_handler.py
index 9dd1b6a..2bf9467 100644
--- a/parser/parser/handlers/environment_variable_handler.py
+++ b/parser/parser/handlers/environment_variable_handler.py
@@ -13,6 +13,7 @@
# limitations under the License.
import ast
+
from parser.context import ParseContext
from parser.parser.registry import register_handler
from parser.resolution.utils import resolve_call_signature
@@ -21,7 +22,7 @@
@register_handler("EnvironmentVariable", "launch.substitutions.EnvironmentVariable")
def handle_environment_variable(node: ast.Call, context: ParseContext) -> dict:
args, kwargs = resolve_call_signature(node, context.engine)
-
+
# Positional fallback: first arg = name
if "name" not in kwargs and args:
kwargs["name"] = args[0]
@@ -29,4 +30,4 @@ def handle_environment_variable(node: ast.Call, context: ParseContext) -> dict:
# Track environment variable usage
context.introspection.track_environment_variable(kwargs["name"], kwargs)
- return {"type": "EnvironmentVariable", **kwargs}
\ No newline at end of file
+ return {"type": "EnvironmentVariable", **kwargs}
diff --git a/parser/parser/handlers/find_executable.py b/parser/parser/handlers/find_executable.py
index 270122e..23c3f17 100644
--- a/parser/parser/handlers/find_executable.py
+++ b/parser/parser/handlers/find_executable.py
@@ -13,10 +13,12 @@
# limitations under the License.
import ast
+
from parser.context import ParseContext
from parser.parser.registry import register_handler
from parser.resolution.utils import resolve_call_signature
+
@register_handler("FindExecutable", "launch.substitutions.FindExecutable")
def handle_find_executable(node: ast.Call, context: ParseContext) -> dict:
args, kwargs = resolve_call_signature(node, context.engine)
@@ -25,7 +27,4 @@ def handle_find_executable(node: ast.Call, context: ParseContext) -> dict:
if not name:
raise ValueError("FindExecutable requires the name of the executable.")
- return {
- "type": "FindExecutable",
- "name": name
- }
\ No newline at end of file
+ return {"type": "FindExecutable", "name": name}
diff --git a/parser/parser/handlers/find_package_share.py b/parser/parser/handlers/find_package_share.py
index c43fdd9..c1ae00b 100644
--- a/parser/parser/handlers/find_package_share.py
+++ b/parser/parser/handlers/find_package_share.py
@@ -13,17 +13,16 @@
# limitations under the License.
import ast
+
from parser.context import ParseContext
from parser.parser.registry import register_handler
from parser.resolution.utils import resolve_call_signature
+
@register_handler("FindPackageShare", "launch.substitutions.FindPackageShare")
def handle_find_package_share(node: ast.Call, context: ParseContext) -> dict:
args, _ = resolve_call_signature(node, context.engine)
if not args:
raise ValueError("FindPackageShare requires a package name.")
- return {
- "type": "FindPackageShare",
- "package": args[0]
- }
\ No newline at end of file
+ return {"type": "FindPackageShare", "package": args[0]}
diff --git a/parser/parser/handlers/group_handler.py b/parser/parser/handlers/group_handler.py
index 6c98278..940e16c 100644
--- a/parser/parser/handlers/group_handler.py
+++ b/parser/parser/handlers/group_handler.py
@@ -13,11 +13,13 @@
# limitations under the License.
import ast
+
from parser.context import ParseContext
from parser.parser.registry import register_handler
from parser.parser.utils.common import flatten_once, group_entities_by_type
from parser.resolution.utils import resolve_call_signature
+
@register_handler("GroupAction", "launch.actions.GroupAction")
def handle_group_action(node: ast.Call, context: ParseContext) -> dict:
"""
@@ -39,7 +41,7 @@ def handle_group_action(node: ast.Call, context: ParseContext) -> dict:
context.push_namespace(namespace)
else:
actions.append(item)
-
+
grouped = group_entities_by_type(actions)
if namespace:
@@ -55,4 +57,3 @@ def handle_group_action(node: ast.Call, context: ParseContext) -> dict:
result["namespace"] = namespace
return result
-
\ No newline at end of file
diff --git a/parser/parser/handlers/include_handler.py b/parser/parser/handlers/include_handler.py
index c8fd9b0..a024047 100644
--- a/parser/parser/handlers/include_handler.py
+++ b/parser/parser/handlers/include_handler.py
@@ -13,12 +13,13 @@
# limitations under the License.
import ast
+
from parser.context import ParseContext
from parser.parser.postprocessing import simplify_launch_configurations
from parser.parser.registry import register_handler
-from parser.parser.utils.symbolic import is_symbolic
from parser.resolution.utils import resolve_call_signature
+
@register_handler("IncludeLaunchDescription", "launch.actions.IncludeLaunchDescription")
def handle_include(node: ast.Call, context: ParseContext) -> dict:
"""
@@ -34,7 +35,7 @@ def handle_include(node: ast.Call, context: ParseContext) -> dict:
if not isinstance(launch_source, dict) or "filename" not in launch_source:
raise ValueError("Could not resolve include path from launch_description_source")
-
+
file_value = launch_source["filename"]
# Flatten and stringify all parts
@@ -43,7 +44,7 @@ def handle_include(node: ast.Call, context: ParseContext) -> dict:
path = "".join(str(part) for part in file_value)
else:
path = str(file_value)
-
+
# Load and parse included file
# from parser.includes.resolver import resolve_included_launch_file
# included_output = resolve_included_launch_file(
@@ -56,10 +57,10 @@ def handle_include(node: ast.Call, context: ParseContext) -> dict:
"type": "IncludeLaunchDescription",
"launch_description_source": path,
"launch_arguments": launch_args,
- "included": {}
+ "included": {},
}
if condition:
result["condition"] = condition
- return result
\ No newline at end of file
+ return result
diff --git a/parser/parser/handlers/launch_configuration_handler.py b/parser/parser/handlers/launch_configuration_handler.py
index 9c25bc8..2833462 100644
--- a/parser/parser/handlers/launch_configuration_handler.py
+++ b/parser/parser/handlers/launch_configuration_handler.py
@@ -13,6 +13,7 @@
# limitations under the License.
import ast
+
from parser.context import ParseContext
from parser.parser.registry import register_handler
from parser.resolution.utils import resolve_call_signature
@@ -23,8 +24,8 @@ def handle_launch_config(node: ast.Call, context: ParseContext) -> dict:
args, _ = resolve_call_signature(node, context.engine)
if not args:
raise ValueError("LaunchConfiguration must have a name.")
-
+
name = args[0]
context.introspection.track_launch_config_usage(name)
- return {"type": "LaunchConfiguration", "name": name}
\ No newline at end of file
+ return {"type": "LaunchConfiguration", "name": name}
diff --git a/parser/parser/handlers/launch_description_handler.py b/parser/parser/handlers/launch_description_handler.py
index f8f36a9..29c51ca 100644
--- a/parser/parser/handlers/launch_description_handler.py
+++ b/parser/parser/handlers/launch_description_handler.py
@@ -13,6 +13,7 @@
# limitations under the License.
import ast
+
from parser.context import ParseContext
from parser.parser.registry import register_handler
from parser.parser.utils.common import flatten_once
@@ -27,4 +28,4 @@ def handle_launch_description(node: ast.Call, context: ParseContext) -> dict:
arg = args[0]
return flatten_once(arg) if isinstance(arg, list) else [arg]
- return []
\ No newline at end of file
+ return []
diff --git a/parser/parser/handlers/launch_sources.py b/parser/parser/handlers/launch_sources.py
index f8a453e..8465c48 100644
--- a/parser/parser/handlers/launch_sources.py
+++ b/parser/parser/handlers/launch_sources.py
@@ -12,12 +12,17 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-from parser.parser.registry import register_handler
+import ast
+
from parser.context import ParseContext
+from parser.parser.registry import register_handler
from parser.resolution.utils import resolve_call_signature
-import ast
-@register_handler("PythonLaunchDescriptionSource", "launch.launch_description_sources.PythonLaunchDescriptionSource")
+
+@register_handler(
+ "PythonLaunchDescriptionSource",
+ "launch.launch_description_sources.PythonLaunchDescriptionSource",
+)
def handle_python_launch_source(node: ast.Call, context: ParseContext) -> dict:
"""
Handles PythonLaunchDescriptionSource("sublaunch.py")
@@ -27,20 +32,16 @@ def handle_python_launch_source(node: ast.Call, context: ParseContext) -> dict:
if not args:
raise ValueError("PythonLaunchDescriptionSource must recieve a file path.")
-
+
raw_path = args[0]
- return {
- "type": "PythonLaunchDescriptionSource",
- "filename": raw_path
- }
+ return {"type": "PythonLaunchDescriptionSource", "filename": raw_path}
+
@register_handler("ThisLaunchFileDir", "launch.substitutions.ThisLaunchFileDir")
def handle_this_launch_file_dir(node: ast.Call, context: ParseContext) -> dict:
"""
Handles ThisLaunchFileDir() substitution.
- Returns: {"type": "ThisLaunchFileDir"}
+ Returns: {"type": "ThisLaunchFileDir"}
"""
- return {
- "type": "ThisLaunchFileDir"
- }
\ No newline at end of file
+ return {"type": "ThisLaunchFileDir"}
diff --git a/parser/parser/handlers/load_composable_node_handler.py b/parser/parser/handlers/load_composable_node_handler.py
index 1dab2ea..cd7fc73 100644
--- a/parser/parser/handlers/load_composable_node_handler.py
+++ b/parser/parser/handlers/load_composable_node_handler.py
@@ -13,11 +13,13 @@
# limitations under the License.
import ast
+
from parser.context import ParseContext
from parser.parser.registry import register_handler
from parser.parser.utils.common import flatten_once, group_entities_by_type
from parser.resolution.utils import resolve_call_signature
+
@register_handler("LoadComposableNodes", "launch_ros.actions.LoadComposableNodes")
def handle_load_composable_nodes(node: ast.Call, context: ParseContext) -> dict:
args, kwargs = resolve_call_signature(node, context.engine)
@@ -37,7 +39,4 @@ def handle_load_composable_nodes(node: ast.Call, context: ParseContext) -> dict:
context.register_composable_node_group(target_container, {"target_container": target_container})
context.extend_composable_node_group(target_container, composable_nodes)
- return {
- "type": "LoadComposableNodes",
- "target_container": target_container
- }
\ No newline at end of file
+ return {"type": "LoadComposableNodes", "target_container": target_container}
diff --git a/parser/parser/handlers/node_handler.py b/parser/parser/handlers/node_handler.py
index c507dcc..33a9638 100644
--- a/parser/parser/handlers/node_handler.py
+++ b/parser/parser/handlers/node_handler.py
@@ -13,10 +13,12 @@
# limitations under the License.
import ast
+
from parser.context import ParseContext
from parser.parser.registry import register_handler
from parser.resolution.utils import resolve_call_kwargs
+
@register_handler("Node", "launch_ros.actions.Node")
def handle_node(node: ast.Call, context: ParseContext) -> dict:
"""
@@ -24,7 +26,7 @@ def handle_node(node: ast.Call, context: ParseContext) -> dict:
Adds namespace context if not explicitly passed.
"""
kwargs = resolve_call_kwargs(node, context.engine)
-
+
if "namespace" not in kwargs:
ns = context.current_namespace()
if ns:
diff --git a/parser/parser/handlers/on_process_exit_handler.py b/parser/parser/handlers/on_process_exit_handler.py
index 3b473db..14f5eda 100644
--- a/parser/parser/handlers/on_process_exit_handler.py
+++ b/parser/parser/handlers/on_process_exit_handler.py
@@ -12,11 +12,12 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+import ast
+
from parser.context import ParseContext
from parser.parser.registry import register_handler
-from parser.parser.utils.common import compute_entity_key
from parser.resolution.utils import resolve_call_signature
-import ast
+
@register_handler("OnProcessExit", "launch_ros.handlers.OnProcessExit")
def handle_on_process_exit(node: ast.Call, context: ParseContext):
@@ -38,7 +39,7 @@ def handle_on_process_exit(node: ast.Call, context: ParseContext):
for target in target_actions:
target.setdefault("events", {}).setdefault("triggers", []).append(handler_ref)
context.introspection.register_entity(target)
-
+
for triggered in triggered_actions:
triggered.setdefault("events", {}).setdefault("triggered_by", []).append(handler_ref)
context.introspection.register_entity(triggered)
diff --git a/parser/parser/handlers/on_process_start_handler.py b/parser/parser/handlers/on_process_start_handler.py
index 07b79c8..cc09c31 100644
--- a/parser/parser/handlers/on_process_start_handler.py
+++ b/parser/parser/handlers/on_process_start_handler.py
@@ -12,10 +12,12 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+import ast
+
from parser.context import ParseContext
from parser.parser.registry import register_handler
from parser.resolution.utils import resolve_call_signature
-import ast
+
@register_handler("OnProcessStart", "launch_ros.handlers.OnProcessStart")
def handle_on_process_start(node: ast.Call, context: ParseContext):
@@ -37,7 +39,7 @@ def handle_on_process_start(node: ast.Call, context: ParseContext):
for target in target_actions:
target.setdefault("events", {}).setdefault("triggers", []).append(handler_ref)
context.introspection.register_entity(target)
-
+
for triggered in triggered_actions:
triggered.setdefault("events", {}).setdefault("triggered_by", []).append(handler_ref)
context.introspection.register_entity(triggered)
diff --git a/parser/parser/handlers/opaque_function_handler.py b/parser/parser/handlers/opaque_function_handler.py
index edf3ded..4ad1487 100644
--- a/parser/parser/handlers/opaque_function_handler.py
+++ b/parser/parser/handlers/opaque_function_handler.py
@@ -13,13 +13,15 @@
# limitations under the License.
import ast
+
from parser.context import ParseContext
from parser.parser.postprocessing import simplify_launch_configurations
from parser.parser.registry import register_handler
+from parser.parser.utils.ast_utils import extract_opaque_function
from parser.parser.utils.common import group_entities_by_type
from parser.resolution.resolution_engine import ResolutionEngine
from parser.resolution.utils import bind_function_args, resolve_call_signature
-from parser.parser.utils.ast_utils import extract_opaque_function
+
@register_handler("OpaqueFunction", "launch.actions.OpaqueFunction")
def handle_opaque_function(node: ast.Call, context: ParseContext) -> dict:
@@ -37,7 +39,7 @@ def handle_opaque_function(node: ast.Call, context: ParseContext) -> dict:
raise ValueError(f"Function '{function_name}' not found in parsed context.")
else:
raise ValueError("OpaqueFunction 'function' must be a string or FunctionDef.")
-
+
# Get full function signature
param_parts = [arg.arg for arg in fn_def.args.args]
if fn_def.args.vararg:
@@ -45,14 +47,18 @@ def handle_opaque_function(node: ast.Call, context: ParseContext) -> dict:
if fn_def.args.kwarg:
param_parts.append(f"**{fn_def.args.kwarg.arg}")
full_signature = f"{fn_def.name}({', '.join(param_parts)})"
-
+
# Build symbolic context
symbolic_context = ParseContext(context.introspection)
symbolic_context.strategy = "symbolic"
# Bind arguments and inject into symbolic context
- user_args = simplify_launch_configurations(kwargs.get("args", []) if isinstance(kwargs.get("args"), list) else [])
- user_kwargs = simplify_launch_configurations(kwargs.get("kwargs", []) if isinstance(kwargs.get("kwargs"), dict) else {})
+ user_args = simplify_launch_configurations(
+ kwargs.get("args", []) if isinstance(kwargs.get("args"), list) else []
+ )
+ user_kwargs = simplify_launch_configurations(
+ kwargs.get("kwargs", []) if isinstance(kwargs.get("kwargs"), dict) else {}
+ )
param_binding = bind_function_args(fn_def, user_args, user_kwargs, True)
for var_name, value in param_binding.items():
symbolic_context.define_variable(var_name, value)
@@ -65,8 +71,4 @@ def handle_opaque_function(node: ast.Call, context: ParseContext) -> dict:
result = extract_opaque_function(fn_def, context, symbolic_engine)
grouped = group_entities_by_type(result)
- return {
- "type": "OpaqueFunction",
- "name": full_signature,
- "returns": grouped
- }
\ No newline at end of file
+ return {"type": "OpaqueFunction", "name": full_signature, "returns": grouped}
diff --git a/parser/parser/handlers/parameter_file_handler.py b/parser/parser/handlers/parameter_file_handler.py
index d80c9bd..dd65dd2 100644
--- a/parser/parser/handlers/parameter_file_handler.py
+++ b/parser/parser/handlers/parameter_file_handler.py
@@ -13,11 +13,13 @@
# limitations under the License.
import ast
+
from parser.context import ParseContext
from parser.parser.postprocessing import simplify_launch_configurations
from parser.parser.registry import register_handler
from parser.resolution.utils import resolve_call_signature
+
@register_handler("ParameterFile", "launch_ros.descriptions.ParameterFile")
def handle_parameter_file(node: ast.Call, context: ParseContext) -> dict:
args, kwargs = resolve_call_signature(node, context.engine)
@@ -26,4 +28,4 @@ def handle_parameter_file(node: ast.Call, context: ParseContext) -> dict:
allow_substs = kwargs.get("allow_substs", False)
simplified_param_file = simplify_launch_configurations(param_file)
- return f"${{ParameterFile:{simplified_param_file} allow_substs={allow_substs}}}"
\ No newline at end of file
+ return f"${{ParameterFile:{simplified_param_file} allow_substs={allow_substs}}}"
diff --git a/parser/parser/handlers/path_join.py b/parser/parser/handlers/path_join.py
index 1027385..378962f 100644
--- a/parser/parser/handlers/path_join.py
+++ b/parser/parser/handlers/path_join.py
@@ -13,17 +13,16 @@
# limitations under the License.
import ast
+
from parser.context import ParseContext
from parser.parser.registry import register_handler
from parser.resolution.utils import resolve_call_signature
+
@register_handler("PathJoinSubstitution", "launch.substitutions.PathJoinSubstitution")
def handle_find_package_share(node: ast.Call, context: ParseContext) -> dict:
args, _ = resolve_call_signature(node, context.engine)
if not args or not isinstance(args[0], list):
raise ValueError("PathJoinSubstitution requires a list as its first argument.")
- return {
- "type": "PathJoinSubstitution",
- "parts": args[0]
- }
\ No newline at end of file
+ return {"type": "PathJoinSubstitution", "parts": args[0]}
diff --git a/parser/parser/handlers/perform_handler.py b/parser/parser/handlers/perform_handler.py
index 5a665d7..1456900 100644
--- a/parser/parser/handlers/perform_handler.py
+++ b/parser/parser/handlers/perform_handler.py
@@ -12,10 +12,11 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-from parser.parser.registry import register_handler
-from parser.resolution.utils import resolve_call_signature
import ast
+from parser.parser.registry import register_handler
+
+
@register_handler("perform")
def handle_perform(node: ast.Call, context):
"""
@@ -26,4 +27,4 @@ def handle_perform(node: ast.Call, context):
raise ValueError("Expected method call for '.perform()'")
base_call_node = node.func.value
- return context.engine.resolve(base_call_node)
\ No newline at end of file
+ return context.engine.resolve(base_call_node)
diff --git a/parser/parser/handlers/push_ros_namespace_handler.py b/parser/parser/handlers/push_ros_namespace_handler.py
index 17fb9e8..a9cd16c 100644
--- a/parser/parser/handlers/push_ros_namespace_handler.py
+++ b/parser/parser/handlers/push_ros_namespace_handler.py
@@ -12,17 +12,16 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+import ast
+
from parser.context import ParseContext
from parser.parser.registry import register_handler
from parser.resolution.utils import resolve_call_signature
-import ast
+
@register_handler("PushRosNamespace", "launch_ros.actions.PushRosNamespace")
def handle_push_ros_namespace(node: ast.Call, context: ParseContext):
args, kwargs = resolve_call_signature(node, context.engine)
ns = args[0] if args else kwargs.get("namespace")
- return {
- "type": "PushRosNamespace",
- "namespace": ns
- }
+ return {"type": "PushRosNamespace", "namespace": ns}
diff --git a/parser/parser/handlers/python_expression_handler.py b/parser/parser/handlers/python_expression_handler.py
index 9de7894..02969e0 100644
--- a/parser/parser/handlers/python_expression_handler.py
+++ b/parser/parser/handlers/python_expression_handler.py
@@ -12,11 +12,13 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+import ast
+
from parser.context import ParseContext
from parser.parser.postprocessing import simplify_launch_configurations
from parser.parser.registry import register_handler
from parser.resolution.utils import resolve_call_signature
-import ast
+
@register_handler("PythonExpression", "launch.substitutions.PythonExpression")
def handle_if_condition(node: ast.Call, context: ParseContext) -> str:
@@ -24,7 +26,7 @@ def handle_if_condition(node: ast.Call, context: ParseContext) -> str:
if not args:
raise ValueError("Python Expression requires at least one argument.")
-
+
# Flatten and stringify all parts
args = simplify_launch_configurations(args)
if isinstance(args[0], list):
diff --git a/parser/parser/handlers/register_event_handler.py b/parser/parser/handlers/register_event_handler.py
index 6f64218..270a181 100644
--- a/parser/parser/handlers/register_event_handler.py
+++ b/parser/parser/handlers/register_event_handler.py
@@ -12,10 +12,12 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+import ast
+
from parser.context import ParseContext
from parser.parser.registry import register_handler
from parser.resolution.utils import resolve_call_signature
-import ast
+
@register_handler("RegisterEventHandler", "launch_ros.actions.RegisterEventHandler")
def handle_register_event_handler(node: ast.Call, context: ParseContext):
diff --git a/parser/parser/handlers/set_environment_variable_handler.py b/parser/parser/handlers/set_environment_variable_handler.py
index 39d2175..4da0463 100644
--- a/parser/parser/handlers/set_environment_variable_handler.py
+++ b/parser/parser/handlers/set_environment_variable_handler.py
@@ -13,6 +13,7 @@
# limitations under the License.
import ast
+
from parser.context import ParseContext
from parser.parser.registry import register_handler
from parser.resolution.utils import resolve_call_signature
@@ -21,7 +22,7 @@
@register_handler("SetEnvironmentVariable", "launch.actions.SetEnvironmentVariable")
def handle_set_environment_variable(node: ast.Call, context: ParseContext) -> dict:
args, kwargs = resolve_call_signature(node, context.engine)
-
+
# Handle both positional and keyword style
if "name" not in kwargs and args:
kwargs["name"] = args[0]
@@ -31,4 +32,4 @@ def handle_set_environment_variable(node: ast.Call, context: ParseContext) -> di
# Track environment variable usage
context.introspection.track_environment_variable(kwargs["name"], kwargs)
- return {"type": "EnvironmentVariable", **kwargs}
\ No newline at end of file
+ return {"type": "EnvironmentVariable", **kwargs}
diff --git a/parser/parser/handlers/set_parameter_handler.py b/parser/parser/handlers/set_parameter_handler.py
index 7622e47..39dd4f9 100644
--- a/parser/parser/handlers/set_parameter_handler.py
+++ b/parser/parser/handlers/set_parameter_handler.py
@@ -13,10 +13,12 @@
# limitations under the License.
import ast
+
from parser.context import ParseContext
from parser.parser.registry import register_handler
from parser.resolution.utils import resolve_call_signature
+
@register_handler("SetParameter", "launch.actions.SetParameter")
def handle_set_parameter(node: ast.Call, context: ParseContext) -> dict:
args, kwargs = resolve_call_signature(node, context.engine)
@@ -24,8 +26,4 @@ def handle_set_parameter(node: ast.Call, context: ParseContext) -> dict:
name = kwargs.get("name")
value = kwargs.get("value")
- return {
- "type": "SetParameter",
- "name": name,
- "value": value
- }
\ No newline at end of file
+ return {"type": "SetParameter", "name": name, "value": value}
diff --git a/parser/parser/handlers/timer_action_handler.py b/parser/parser/handlers/timer_action_handler.py
index 3fae817..d10e972 100644
--- a/parser/parser/handlers/timer_action_handler.py
+++ b/parser/parser/handlers/timer_action_handler.py
@@ -13,11 +13,13 @@
# limitations under the License.
import ast
+
from parser.context import ParseContext
from parser.parser.registry import register_handler
from parser.parser.utils.common import flatten_once, group_entities_by_type
from parser.resolution.utils import resolve_call_signature
+
@register_handler("TimerAction", "launch.actions.TimerAction")
def handle_timer_action(node: ast.Call, context: ParseContext) -> dict:
"""
@@ -32,8 +34,8 @@ def handle_timer_action(node: ast.Call, context: ParseContext) -> dict:
actions = []
for item in resolved_flat:
- actions.append(item)
-
+ actions.append(item)
+
grouped = group_entities_by_type(actions)
result = {
@@ -43,4 +45,3 @@ def handle_timer_action(node: ast.Call, context: ParseContext) -> dict:
}
return result
-
\ No newline at end of file
diff --git a/parser/parser/introspection_utils.py b/parser/parser/introspection_utils.py
index 2b43d69..0aebec2 100644
--- a/parser/parser/introspection_utils.py
+++ b/parser/parser/introspection_utils.py
@@ -12,15 +12,17 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+import re
from collections import defaultdict
+
from parser.parser.type_mapping import TYPE_KEY_MAP
-import re
LAUNCH_CONFIG_REGEX = re.compile(r"\${LaunchConfiguration:([a-zA-Z0-9_]+)}")
ENVIRONMENT_VAR_REGEX = re.compile(r"\${EnvironmentVariable:([a-zA-Z0-9_]+)}")
EVENT_HANDLER_REGEX = re.compile(r"\${EventHandler\[(\d+)\]:(\w+)}")
PYTHON_VAR_REGEX = re.compile(r"\${var:([a-zA-Z_][a-zA-Z0-9_]*)}")
+
def collect_launch_config_usages(grouped: dict) -> list[dict]:
"""
Recursively walk the grouped data and return all LaunchConfiguration usages
@@ -33,10 +35,7 @@ def walk(obj, path):
for key, value in obj.items():
if key == "type" and obj.get("type") == "LaunchConfiguration":
arg = obj.get("name")
- usages.append({
- "argument": arg,
- "path": path
- })
+ usages.append({"argument": arg, "path": path})
else:
walk(value, f"{path}.{key}" if path else key)
@@ -47,20 +46,18 @@ def walk(obj, path):
elif isinstance(obj, tuple):
for idx, item in enumerate(obj):
walk(item, f"{path}[{idx}]")
-
+
elif isinstance(obj, str):
for match in LAUNCH_CONFIG_REGEX.finditer(obj):
- usages.append({
- "argument": match.group(1),
- "path": path
- })
-
+ usages.append({"argument": match.group(1), "path": path})
+
for top_key in TYPE_KEY_MAP.values():
for idx, entry in enumerate(grouped.get(top_key, [])):
walk(entry, f"{top_key}[{idx}]")
-
+
return usages
+
def collect_environment_variable_usages(grouped: dict) -> list[dict]:
"""
Recursively walk the grouped data and return all EnvironmentVariable usages
@@ -73,10 +70,7 @@ def walk(obj, path):
for key, value in obj.items():
if key == "type" and obj.get("type") == "EnvironmentVariable":
arg = obj.get("name")
- usages.append({
- "argument": arg,
- "path": path
- })
+ usages.append({"argument": arg, "path": path})
else:
walk(value, f"{path}.{key}" if path else key)
@@ -87,20 +81,18 @@ def walk(obj, path):
elif isinstance(obj, tuple):
for idx, item in enumerate(obj):
walk(item, f"{path}[{idx}]")
-
+
elif isinstance(obj, str):
for match in ENVIRONMENT_VAR_REGEX.finditer(obj):
- usages.append({
- "argument": match.group(1),
- "path": path
- })
-
+ usages.append({"argument": match.group(1), "path": path})
+
for top_key in TYPE_KEY_MAP.values():
for idx, entry in enumerate(grouped.get(top_key, [])):
walk(entry, f"{top_key}[{idx}]")
-
+
return usages
+
def collect_event_handler_usages(grouped: dict) -> list[dict]:
"""
Recursively walk the grouped data and return all event handler usages
@@ -126,19 +118,20 @@ def walk(obj, path):
usage_map[idx]["type"] = handler_type
# Remove trailing index of triggers and triggered_by
- clean_path = path.rsplit('[', 1)[0] if path.endswith(']') else path
+ clean_path = path.rsplit("[", 1)[0] if path.endswith("]") else path
if ".triggers" in clean_path:
usage_map[idx]["triggered_by"].append(clean_path)
elif ".triggered_by" in clean_path:
usage_map[idx]["triggers"].append(clean_path)
-
+
for key in TYPE_KEY_MAP.values():
for idx, item in enumerate(grouped.get(key, [])):
walk(item, f"{key}[{idx}]")
-
+
return [v for v in usage_map.values() if v["triggered_by"] or v["triggers"]]
+
def collect_python_variable_usages(grouped: dict) -> list[dict]:
"""
Recursively walk the grouped data and return all ${var:...} usages
@@ -150,7 +143,7 @@ def walk(obj, path):
if isinstance(obj, dict):
for key, value in obj.items():
walk(value, f"{path}.{key}" if path else key)
-
+
elif isinstance(obj, list):
for idx, item in enumerate(obj):
walk(item, f"{path}[{idx}]")
@@ -161,13 +154,10 @@ def walk(obj, path):
elif isinstance(obj, str):
for match in PYTHON_VAR_REGEX.finditer(obj):
- usages.append({
- "variable": match.group(1),
- "path": path
- })
-
+ usages.append({"variable": match.group(1), "path": path})
+
for top_key in TYPE_KEY_MAP.values():
for idx, entry in enumerate(grouped.get(top_key, [])):
walk(entry, f"{top_key}[{idx}]")
-
+
return usages
diff --git a/parser/parser/loader.py b/parser/parser/loader.py
index f0e2ed4..4e49c1d 100644
--- a/parser/parser/loader.py
+++ b/parser/parser/loader.py
@@ -13,8 +13,9 @@
# limitations under the License.
import importlib
-import pkgutil
import os
+import pkgutil
+
def register_builtin_handlers():
"""
@@ -24,4 +25,4 @@ def register_builtin_handlers():
package_dir = os.path.dirname(parser.parser.handlers.__file__)
for _, module_name, _ in pkgutil.iter_modules([package_dir]):
- importlib.import_module(f"parser.parser.handlers.{module_name}")
\ No newline at end of file
+ importlib.import_module(f"parser.parser.handlers.{module_name}")
diff --git a/parser/parser/postprocessing.py b/parser/parser/postprocessing.py
index aaaffb6..7418cec 100644
--- a/parser/parser/postprocessing.py
+++ b/parser/parser/postprocessing.py
@@ -12,6 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+
def simplify_launch_configurations(obj):
"""
Recursively walk the parsed output and convert
@@ -22,52 +23,68 @@ def simplify_launch_configurations(obj):
if type_key in simplifier_registry:
return simplifier_registry[type_key](obj)
-
+
# Otherwise recursively simplify dictionary
return {k: simplify_launch_configurations(v) for k, v in obj.items()}
-
+
elif isinstance(obj, (list, tuple)):
return [simplify_launch_configurations(i) for i in obj]
return obj
+def format_symbolic_part(part):
+ if isinstance(part, dict):
+ return simplify_launch_configurations(part)
+ elif isinstance(part, str):
+ return f"'{part}'"
+ else:
+ return str(part)
+
+
def _simplify_launch_config(obj):
name = obj.get("name")
return f"${{LaunchConfiguration:{name}}}"
+
def _simplify_environment_variable(obj):
name = obj.get("name")
return f"${{EnvironmentVariable:{name}}}"
+
def _simplify_path_join(obj):
- format_symbolic_part = lambda p: simplify_launch_configurations(p) if isinstance(p, dict) else f"'{p}'" if isinstance(p, str) else str(p)
parts = ", ".join(format_symbolic_part(p) for p in obj.get("parts"))
return f"${{PathJoinSubstitution:[{parts}]}}"
+
def _simplify_find_package(obj):
package = obj.get("package")
return f"${{FindPackageShare:{package}}}"
+
def _simplify_find_executable(obj):
name = obj.get("name")
return f"${{FindExecutable:{name}}}"
+
def _simplify_command(obj):
- format_symbolic_part = lambda p: simplify_launch_configurations(p) if isinstance(p, dict) else f"'{p}'" if isinstance(p, str) else str(p)
commands = ", ".join(format_symbolic_part(p) for p in obj.get("command"))
return f"${{Command:[{commands}]}}"
+
def _simplify_this_launch_file_dir(obj):
- return f"${{ThisLaunchFileDir}}"
+ return "${ThisLaunchFileDir}"
+
def _simplify_custom_handler(obj):
type_name = obj.get("type_name")
- format_symbolic_part = lambda p: simplify_launch_configurations(p) if isinstance(p, dict) else f"'{p}'" if isinstance(p, str) else str(p)
- kwarg_strs = [f"{k}={format_symbolic_part(v)}" for k, v in obj.items() if k not in {"type", "type_name"}]
+ kwarg_strs = [
+ f"{k}={format_symbolic_part(v)}" for k, v in obj.items() if k not in {"type", "type_name"}
+ ]
kwargs = ", ".join(kwarg_strs)
return f"${{CustomHandler:{type_name}({kwargs})}}"
+
# Dispatcher registry
simplifier_registry = {
"LaunchConfiguration": _simplify_launch_config,
@@ -78,4 +95,4 @@ def _simplify_custom_handler(obj):
"FindExecutable": _simplify_find_executable,
"ThisLaunchFileDir": _simplify_this_launch_file_dir,
"CustomHandler": _simplify_custom_handler,
-}
\ No newline at end of file
+}
diff --git a/parser/parser/registry.py b/parser/parser/registry.py
index 1a246f7..ebbf753 100644
--- a/parser/parser/registry.py
+++ b/parser/parser/registry.py
@@ -12,36 +12,41 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-from typing import Callable, Dict, Optional
-import warnings
import ast
+import warnings
+from typing import Callable, Dict, Optional
from parser.context import ParseContext
# Registry dictionary for known launch constructs
-_HANDLER_REGISTRY: Dict[str, Callable[[ast.Call, 'ParseContext'], Optional[dict]]] = {}
+_HANDLER_REGISTRY: Dict[str, Callable[[ast.Call, "ParseContext"], Optional[dict]]] = {}
+
def register_handler(*names: str):
"""
Decorator to register a handler for a given launch construct.
Example: @register_handler("Node") registers a handler for launch_ros.actions.Node.
"""
- def decorator(func: Callable[[ast.Call, 'ParseContext'], Optional[dict]]):
+
+ def decorator(func: Callable[[ast.Call, "ParseContext"], Optional[dict]]):
for name in names:
if name in _HANDLER_REGISTRY:
warnings.warn(f"Overwriting existing handler for '{name}'")
_HANDLER_REGISTRY[name] = func
return func
+
return decorator
-def get_handler(name: str) -> Optional[Callable[[ast.Call, 'ParseContext'], Optional[dict]]]:
+
+def get_handler(name: str) -> Optional[Callable[[ast.Call, "ParseContext"], Optional[dict]]]:
"""
Retrieve the handler for a given construct, or None if unregistered
"""
return _HANDLER_REGISTRY.get(name)
+
def all_registered() -> Dict[str, Callable]:
"""
Return the complete handler map (useful for debugging or listing).
"""
- return _HANDLER_REGISTRY.copy()
\ No newline at end of file
+ return _HANDLER_REGISTRY.copy()
diff --git a/parser/parser/type_mapping.py b/parser/parser/type_mapping.py
index c7748ea..135c71d 100644
--- a/parser/parser/type_mapping.py
+++ b/parser/parser/type_mapping.py
@@ -22,5 +22,5 @@
"OpaqueFunction": "opaque_functions",
"ComposableNode": "unattached_composable_nodes",
"ComposableNodeContainer": "composable_nodes_container",
- "CustomHandler": "custom_components"
-}
\ No newline at end of file
+ "CustomHandler": "custom_components",
+}
diff --git a/parser/parser/user_handler.py b/parser/parser/user_handler.py
index 41e7970..537f402 100644
--- a/parser/parser/user_handler.py
+++ b/parser/parser/user_handler.py
@@ -12,15 +12,18 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-from parser.parser.registry import register_handler
import inspect
+from parser.parser.registry import register_handler
+
+
def register_user_handler(type_name: str):
"""
Decorator to register a user defined handler with validation.
- Signature validation (must accept (node, context))
- Output validation (must return dict with 'type': "CustomHandler", 'type_name': )
"""
+
def decorator(fn):
# Check signature
sig = inspect.signature(fn)
@@ -32,12 +35,12 @@ def decorator(fn):
f"User handler for '{type_name}' must have signature: "
f"({', '.join(expected_args)}), but got: ({', '.join(actual_args)})"
)
-
+
# Wrap with validation
def wrapper(node, context):
result = fn(node, context)
return result
-
+
return register_handler(type_name)(wrapper)
-
- return decorator
\ No newline at end of file
+
+ return decorator
diff --git a/parser/parser/utils/ast_utils.py b/parser/parser/utils/ast_utils.py
index 48abe4b..8064dd5 100644
--- a/parser/parser/utils/ast_utils.py
+++ b/parser/parser/utils/ast_utils.py
@@ -13,8 +13,10 @@
# limitations under the License.
import ast
+
from parser.context import ParseContext
+
def collect_function_defs(body: list[ast.stmt], context: ParseContext):
"""
Recursively collect all FunctionDef nodes inside a body list
@@ -23,13 +25,14 @@ def collect_function_defs(body: list[ast.stmt], context: ParseContext):
for stmt in body:
if isinstance(stmt, ast.FunctionDef):
context.define_function(stmt.name, stmt)
-
+
# Recursively handle compound statements
for attr in ("body", "orelse", "finalbody"):
inner = getattr(stmt, attr, None)
if isinstance(inner, list):
collect_function_defs(inner, context)
+
def extract_opaque_function(fn_def: ast.FunctionDef, context, symbolic_engine):
"""
Execute the body of an OpaqueFunction symbolically and return parsed results.
@@ -47,8 +50,8 @@ def extract_opaque_function(fn_def: ast.FunctionDef, context, symbolic_engine):
elif isinstance(stmt, ast.Return):
if stmt.value is None:
return None
-
+
result = symbolic_engine.resolve(stmt.value)
break
-
- return result
\ No newline at end of file
+
+ return result
diff --git a/parser/parser/utils/common.py b/parser/parser/utils/common.py
index 6c5fa74..f6af484 100644
--- a/parser/parser/utils/common.py
+++ b/parser/parser/utils/common.py
@@ -14,6 +14,7 @@
from parser.parser.type_mapping import TYPE_KEY_MAP
+
def flatten_once(items):
"""
Flatten a list one level deep.
@@ -27,6 +28,7 @@ def flatten_once(items):
flattened.append(item)
return flattened
+
def compute_entity_key(entity: dict) -> str:
"""
Generate a unique key for deduplication and tracking event links.
@@ -38,6 +40,7 @@ def compute_entity_key(entity: dict) -> str:
name = entity.get("name", "")
return f"{t}::{pkg}::{exe}::{name}"
+
def group_entities_by_type(entities: list) -> dict:
grouped = {}
@@ -54,5 +57,5 @@ def group_entities_by_type(entities: list) -> dict:
clean_item = {k: v for k, v in item.items() if k != "type"}
grouped.setdefault(group_key, []).append(clean_item)
-
- return {k: v for k, v in grouped.items() if v}
\ No newline at end of file
+
+ return {k: v for k, v in grouped.items() if v}
diff --git a/parser/parser/utils/symbolic.py b/parser/parser/utils/symbolic.py
index 9c90475..742fc73 100644
--- a/parser/parser/utils/symbolic.py
+++ b/parser/parser/utils/symbolic.py
@@ -12,8 +12,9 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+
def is_symbolic(value):
"""
Determines if a value is symbolic (non-concrete) object from the resolution engine.
"""
- return isinstance(value, dict) and "type" in value
\ No newline at end of file
+ return isinstance(value, dict) and "type" in value
diff --git a/parser/plugin_loader.py b/parser/plugin_loader.py
index cbdc516..3e6fbaf 100644
--- a/parser/plugin_loader.py
+++ b/parser/plugin_loader.py
@@ -13,16 +13,17 @@
# limitations under the License.
import importlib.util
-import sys
import os
+import sys
+
def load_user_handlers_from_directory(base_dir):
if not os.path.isdir(base_dir):
print(f"Plugin directory '{base_dir}' not found.")
return
-
+
for file in os.listdir(base_dir):
- if file.endswith('.py'):
+ if file.endswith(".py"):
path = os.path.join(base_dir, file)
spec = importlib.util.spec_from_file_location(f"user_plugin_{file}", path)
if spec and spec.loader:
@@ -31,4 +32,4 @@ def load_user_handlers_from_directory(base_dir):
spec.loader.exec_module(module)
print(f"[LaunchMap] Loaded plugin: {file}", file=sys.stderr)
except Exception as e:
- print(f"[LaunchMap] Failed to load plugin {file}: {e}", file=sys.stderr)
\ No newline at end of file
+ print(f"[LaunchMap] Failed to load plugin {file}: {e}", file=sys.stderr)
diff --git a/parser/pyproject.toml b/parser/pyproject.toml
new file mode 100644
index 0000000..2442b11
--- /dev/null
+++ b/parser/pyproject.toml
@@ -0,0 +1,10 @@
+[tool.ruff]
+line-length = 100
+target-version = "py310"
+
+lint.extend-select = ["E", "F", "I"]
+lint.ignore = []
+
+[tool.ruff.format]
+quote-style = "double"
+indent-style = "space"
\ No newline at end of file
diff --git a/requirements.txt b/parser/requirements.txt
similarity index 87%
rename from requirements.txt
rename to parser/requirements.txt
index c1ef07b..97c8ca3 100644
--- a/requirements.txt
+++ b/parser/requirements.txt
@@ -4,3 +4,4 @@ pluggy==1.6.0
Pygments==2.19.1
pytest==8.4.0
PyYAML==6.0.2
+ruff==0.12.8
diff --git a/parser/resolution/loader.py b/parser/resolution/loader.py
index 999ad06..9c6cb0d 100644
--- a/parser/resolution/loader.py
+++ b/parser/resolution/loader.py
@@ -13,8 +13,9 @@
# limitations under the License.
import importlib
-import pkgutil
import os
+import pkgutil
+
def register_builtin_resolvers():
"""
@@ -24,4 +25,4 @@ def register_builtin_resolvers():
package_dir = os.path.dirname(parser.resolution.resolvers.__file__)
for _, module_name, _ in pkgutil.iter_modules([package_dir]):
- importlib.import_module(f"parser.resolution.resolvers.{module_name}")
\ No newline at end of file
+ importlib.import_module(f"parser.resolution.resolvers.{module_name}")
diff --git a/parser/resolution/resolution_engine.py b/parser/resolution/resolution_engine.py
index 3ecc9a7..af1f526 100644
--- a/parser/resolution/resolution_engine.py
+++ b/parser/resolution/resolution_engine.py
@@ -13,10 +13,12 @@
# limitations under the License.
import ast
+
from parser.context import ParseContext
from parser.resolution.loader import register_builtin_resolvers
from parser.resolution.utils import try_all_resolvers
+
class ResolutionEngine:
def __init__(self, context: ParseContext):
register_builtin_resolvers()
@@ -25,4 +27,3 @@ def __init__(self, context: ParseContext):
def resolve(self, node: ast.AST):
return try_all_resolvers(node, self)
-
\ No newline at end of file
diff --git a/parser/resolution/resolution_registry.py b/parser/resolution/resolution_registry.py
index 45689df..a5de94c 100644
--- a/parser/resolution/resolution_registry.py
+++ b/parser/resolution/resolution_registry.py
@@ -12,26 +12,31 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+import ast
from typing import Callable, Dict, List, Tuple, Type
+
from parser.resolution.resolution_engine import ResolutionEngine
-import ast
# (priority, resolver_function)
_RESOLVERS: Dict[Type[ast.AST], List[Tuple[int, Callable]]] = {}
+
def register_resolver(node_type: Type[ast.AST], *, priority: int = 0):
"""
Register a resolver for a given AST node type with an optional priority.
Higher priority resolvers are tried first.
"""
- def decorator(func: Callable[[ast.AST, 'ResolutionEngine'], any]):
+
+ def decorator(func: Callable[[ast.AST, "ResolutionEngine"], any]):
_RESOLVERS.setdefault(node_type, []).append((priority, func))
_RESOLVERS[node_type].sort(key=lambda pair: -pair[0])
return func
+
return decorator
+
def get_resolvers(node_type: Type[ast.AST]) -> List[Callable]:
"""
Return resolvers sorted by priority (high to low)
"""
- return [f for _, f in _RESOLVERS.get(node_type, [])]
\ No newline at end of file
+ return [f for _, f in _RESOLVERS.get(node_type, [])]
diff --git a/parser/resolution/resolvers/assign.py b/parser/resolution/resolvers/assign.py
index ff32e38..2e24fa4 100644
--- a/parser/resolution/resolvers/assign.py
+++ b/parser/resolution/resolvers/assign.py
@@ -12,13 +12,15 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-from parser.resolution.resolution_registry import register_resolver
import ast
+from parser.resolution.resolution_registry import register_resolver
+
+
@register_resolver(ast.Assign)
def resolve_assign(node: ast.Assign, engine):
value = engine.resolve(node.value)
for target in node.targets:
if isinstance(target, ast.Name):
engine.context.define_variable(target.id, value)
- return None
\ No newline at end of file
+ return None
diff --git a/parser/resolution/resolvers/attribute.py b/parser/resolution/resolvers/attribute.py
index b94698b..a414252 100644
--- a/parser/resolution/resolvers/attribute.py
+++ b/parser/resolution/resolvers/attribute.py
@@ -12,9 +12,11 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+import ast
import re
+
from parser.resolution.resolution_registry import register_resolver
-import ast
+
@register_resolver(ast.Attribute)
def resolve_attribute(node: ast.Attribute, engine):
@@ -23,27 +25,31 @@ def resolve_attribute(node: ast.Attribute, engine):
else:
return _resolve_normal_attribute(node, engine)
+
def _resolve_symbolic_attribute(node: ast.Attribute, engine):
base = engine.resolve(node.value)
if isinstance(base, str) and re.search(r"\$\{[^}]+\}", base):
return f"{base}.{node.attr}"
-
+
try:
return getattr(base, node.attr)
except AttributeError:
raise ValueError(f"Object of type {type(base).__name__} has no attribute '{node.attr}'")
+
def _resolve_normal_attribute(node: ast.Attribute, engine):
base_object = engine.resolve(node.value)
if base_object is None:
raise ValueError(f"Cannot access attribute '{node.attr}' on None")
-
+
try:
return getattr(base_object, node.attr)
except AttributeError:
- raise ValueError(f"Object of type {type(base_object).__name__} has no attribute '{node.attr}'")
+ raise ValueError(
+ f"Object of type {type(base_object).__name__} has no attribute '{node.attr}'"
+ )
def get_attr_chain(attr_node: ast.Attribute) -> str:
@@ -54,4 +60,3 @@ def get_attr_chain(attr_node: ast.Attribute) -> str:
if isinstance(attr_node, ast.Name):
parts.insert(0, attr_node.id)
return ".".join(parts)
-
\ No newline at end of file
diff --git a/parser/resolution/resolvers/binop.py b/parser/resolution/resolvers/binop.py
index d12fd84..8809f0c 100644
--- a/parser/resolution/resolvers/binop.py
+++ b/parser/resolution/resolvers/binop.py
@@ -12,13 +12,14 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+import ast
+
from parser.resolution.resolution_registry import register_resolver
from parser.resolution.utils import resolve_python_expression
-import ast
+
@register_resolver(ast.BinOp)
def resolve_binop(node: ast.BinOp, engine):
left = engine.resolve(node.left)
right = engine.resolve(node.right)
return resolve_python_expression(left, node.op, right)
-
\ No newline at end of file
diff --git a/parser/resolution/resolvers/boolop.py b/parser/resolution/resolvers/boolop.py
index 4a9dffa..433cc3b 100644
--- a/parser/resolution/resolvers/boolop.py
+++ b/parser/resolution/resolvers/boolop.py
@@ -12,9 +12,11 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-from parser.resolution.resolution_registry import register_resolver
import ast
+from parser.resolution.resolution_registry import register_resolver
+
+
@register_resolver(ast.BoolOp)
def resolve_boolop(node: ast.BoolOp, engine):
values = [engine.resolve(v) for v in node.values]
diff --git a/parser/resolution/resolvers/call.py b/parser/resolution/resolvers/call.py
index c28a04e..4c6657c 100644
--- a/parser/resolution/resolvers/call.py
+++ b/parser/resolution/resolvers/call.py
@@ -12,11 +12,13 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+import ast
import warnings
+
+from parser.parser.dispatcher import dispatch_call
from parser.parser.postprocessing import simplify_launch_configurations
from parser.resolution.resolution_registry import register_resolver
-from parser.parser.dispatcher import dispatch_call
-import ast
+
@register_resolver(ast.Call, priority=0)
def resolve_call(node: ast.Call, engine):
@@ -25,6 +27,7 @@ def resolve_call(node: ast.Call, engine):
else:
return _resolve_normal_call(node, engine)
+
def _resolve_symbolic_call(node: ast.Call, engine):
try:
# Try launching launch_ros contruct
@@ -52,10 +55,11 @@ def _resolve_symbolic_call(node: ast.Call, engine):
arg_strs = [repr(a) for a in args] + [f"{k}={repr(v)}" for k, v in kwargs.items()]
return f"{func}({', '.join(arg_strs)})"
-
+
except Exception as e:
raise ValueError(f"Unable to symbolically resolve call: {ast.dump(node)} -> {e}")
+
def _resolve_normal_call(node: ast.Call, engine):
try:
# Try launching launch_ros contruct
@@ -78,6 +82,5 @@ def _resolve_normal_call(node: ast.Call, engine):
if isinstance(result, type({}.items())):
result = dict(result)
return result
-
+
raise ValueError(f"Cannot resolve non-callable: {func}")
-
\ No newline at end of file
diff --git a/parser/resolution/resolvers/compare.py b/parser/resolution/resolvers/compare.py
index 0ca92b0..34bbe3b 100644
--- a/parser/resolution/resolvers/compare.py
+++ b/parser/resolution/resolvers/compare.py
@@ -13,6 +13,7 @@
# limitations under the License.
import ast
+
from parser.resolution.resolution_registry import register_resolver
# Mapping from AST operator to symbolic string representation
@@ -29,6 +30,7 @@
ast.IsNot: "is not",
}
+
@register_resolver(ast.Compare)
def resolve_compare(node: ast.Compare, engine):
left = engine.resolve(node.left)
@@ -50,4 +52,4 @@ def resolve_compare(node: ast.Compare, engine):
comparisons.append(expr)
left = right
- return " and ".join(comparisons) if len(comparisons) > 1 else comparisons[0]
\ No newline at end of file
+ return " and ".join(comparisons) if len(comparisons) > 1 else comparisons[0]
diff --git a/parser/resolution/resolvers/constant.py b/parser/resolution/resolvers/constant.py
index 265e5bf..a2f4f14 100644
--- a/parser/resolution/resolvers/constant.py
+++ b/parser/resolution/resolvers/constant.py
@@ -12,9 +12,11 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-from parser.resolution.resolution_registry import register_resolver
import ast
+from parser.resolution.resolution_registry import register_resolver
+
+
@register_resolver(ast.Constant)
def resolve_constant(node: ast.Constant, engine):
- return node.value
\ No newline at end of file
+ return node.value
diff --git a/parser/resolution/resolvers/dict.py b/parser/resolution/resolvers/dict.py
index 8a097b4..03fcde0 100644
--- a/parser/resolution/resolvers/dict.py
+++ b/parser/resolution/resolvers/dict.py
@@ -12,12 +12,11 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-from parser.resolution.resolution_registry import register_resolver
import ast
+from parser.resolution.resolution_registry import register_resolver
+
+
@register_resolver(ast.Dict)
def resolve_dict(node: ast.Dict, engine):
- return {
- engine.resolve(k): engine.resolve(v)
- for k, v in zip(node.keys, node.values)
- }
\ No newline at end of file
+ return {engine.resolve(k): engine.resolve(v) for k, v in zip(node.keys, node.values)}
diff --git a/parser/resolution/resolvers/expression.py b/parser/resolution/resolvers/expression.py
index 9e31b3d..79472b4 100644
--- a/parser/resolution/resolvers/expression.py
+++ b/parser/resolution/resolvers/expression.py
@@ -12,9 +12,11 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-from parser.resolution.resolution_registry import register_resolver
import ast
+from parser.resolution.resolution_registry import register_resolver
+
+
@register_resolver(ast.Expr)
def resolve_expression(node: ast.Expr, engine):
- return engine.resolve(node.value)
\ No newline at end of file
+ return engine.resolve(node.value)
diff --git a/parser/resolution/resolvers/functiondef.py b/parser/resolution/resolvers/functiondef.py
index c0f5b9a..f29944d 100644
--- a/parser/resolution/resolvers/functiondef.py
+++ b/parser/resolution/resolvers/functiondef.py
@@ -13,10 +13,12 @@
# limitations under the License.
import ast
+
from parser.resolution.resolution_registry import register_resolver
+
@register_resolver(ast.FunctionDef)
def resolve_function_def(node: ast.FunctionDef, engine):
# Register in context
engine.context.functions[node.name] = node
- return None
\ No newline at end of file
+ return None
diff --git a/parser/resolution/resolvers/functions.py b/parser/resolution/resolvers/functions.py
index c2f0a10..22c2044 100644
--- a/parser/resolution/resolvers/functions.py
+++ b/parser/resolution/resolvers/functions.py
@@ -12,13 +12,13 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-from parser.resolution.resolution_engine import ResolutionEngine
-from parser.resolution.resolution_registry import register_resolver
-import copy
import ast
+from parser.resolution.resolution_engine import ResolutionEngine
+from parser.resolution.resolution_registry import register_resolver
from parser.resolution.utils import bind_function_args
+
@register_resolver(ast.Call, priority=5)
def resolve_user_function(node: ast.Call, engine):
# Resolve function name
@@ -29,7 +29,7 @@ def resolve_user_function(node: ast.Call, engine):
fn_def = engine.context.lookup_function(func_name)
else:
return None
-
+
# Bind parameters
resolved_args = [engine.resolve(arg) for arg in node.args]
resolved_kwargs = {kw.arg: engine.resolve(kw.value) for kw in node.keywords}
@@ -49,5 +49,5 @@ def resolve_user_function(node: ast.Call, engine):
return sub_engine.resolve(stmt.value)
else:
sub_engine.resolve(stmt.value)
-
- return "Handled"
\ No newline at end of file
+
+ return "Handled"
diff --git a/parser/resolution/resolvers/get_package_share_directory.py b/parser/resolution/resolvers/get_package_share_directory.py
index cfa1f6b..40649d6 100644
--- a/parser/resolution/resolvers/get_package_share_directory.py
+++ b/parser/resolution/resolvers/get_package_share_directory.py
@@ -13,17 +13,19 @@
# limitations under the License.
import ast
+
from parser.parser.postprocessing import simplify_launch_configurations
from parser.resolution.resolution_registry import register_resolver
+
@register_resolver(ast.Call, priority=10)
def resolve_get_package_share_directory(node: ast.Call, engine):
func_name = engine.context.get_func_name(node.func)
if func_name != "get_package_share_directory":
return None
-
+
if len(node.args) != 1:
raise ValueError("get_package_share_directory expects 1 argument")
-
+
arg = engine.resolve(node.args[0])
return f"${{get_package_share_directory:{simplify_launch_configurations(arg)}}}"
diff --git a/parser/resolution/resolvers/ifelse.py b/parser/resolution/resolvers/ifelse.py
index 774aafe..6996d48 100644
--- a/parser/resolution/resolvers/ifelse.py
+++ b/parser/resolution/resolvers/ifelse.py
@@ -13,8 +13,10 @@
# limitations under the License.
import ast
-from parser.resolution.utils import collect_assigned_variable_names
+
from parser.resolution.resolution_registry import register_resolver
+from parser.resolution.utils import collect_assigned_variable_names
+
@register_resolver(ast.If)
def resolve_if_expression(node: ast.If, engine):
@@ -25,7 +27,7 @@ def resolve_if_expression(node: ast.If, engine):
# Extract code as string
code_str = ast.unparse(node)
-
+
# Collect all variables assigned in any branch
assigned_vars = set()
@@ -42,9 +44,9 @@ def collect_from_block(block):
else:
collect_from_block(orelse)
break
-
+
# Track variables
symbolic_vars = [f"${{var:{v}}}" for v in sorted(assigned_vars)]
context.introspection.track_python_expression(code_str, symbolic_vars)
- return None
\ No newline at end of file
+ return None
diff --git a/parser/resolution/resolvers/joined_str.py b/parser/resolution/resolvers/joined_str.py
index 20a503d..293dde8 100644
--- a/parser/resolution/resolvers/joined_str.py
+++ b/parser/resolution/resolvers/joined_str.py
@@ -13,8 +13,10 @@
# limitations under the License.
import ast
+
from parser.resolution.resolution_registry import register_resolver
+
@register_resolver(ast.JoinedStr)
def resolve_function_def(node: ast.FunctionDef, engine):
parts = []
@@ -26,5 +28,5 @@ def resolve_function_def(node: ast.FunctionDef, engine):
parts.append(str(value.value))
else:
parts.append(f" str:
"""
Reconstructs the fully qualified name from an AST function call,
@@ -58,14 +60,17 @@ def get_func_name(func_node: ast.expr) -> str:
return ".".join(parts)
raise TypeError(f"Unsupported function node type: {type(func_node).__name__}")
+
def resolve_call_kwargs(node: ast.Call, engine):
return {kw.arg: engine.resolve(kw.value) for kw in node.keywords}
+
def resolve_call_signature(node: ast.Call, engine):
args = [engine.resolve(arg) for arg in node.args]
kwargs = {kw.arg: engine.resolve(kw.value) for kw in node.keywords}
return args, kwargs
+
def try_all_resolvers(node: ast.AST, engine) -> object:
"""
Attempts to resolve the given AST node using all registered resolvers
@@ -81,13 +86,16 @@ def try_all_resolvers(node: ast.AST, engine) -> object:
return result
if not resolvers:
- source = getattr(node, 'lineno', '?')
+ source = getattr(node, "lineno", "?")
node_type = type(node).__name__
warnings.warn(f"Unhandled AST node ({node_type}) at line {source}: {ast.dump(node)}")
-
+
return None
-def bind_function_args(fn_def: ast.FunctionDef, args: list, kwargs: dict, exclude_first: bool = False) -> dict:
+
+def bind_function_args(
+ fn_def: ast.FunctionDef, args: list, kwargs: dict, exclude_first: bool = False
+) -> dict:
"""
Bind args and kwargs to the function definition's parameters.
Returns a mapping from parameter names to actual passed value.
@@ -106,12 +114,12 @@ def bind_function_args(fn_def: ast.FunctionDef, args: list, kwargs: dict, exclud
binding[name] = value
# Remaining positional args (*args)
- remaining_args = args[len(param_names):]
+ remaining_args = args[len(param_names) :]
if vararg_name:
binding[vararg_name] = remaining_args
-
+
# Named kwargs that match a param name
- for name in param_names[len(args):]:
+ for name in param_names[len(args) :]:
if name in kwargs:
binding[name] = kwargs[name]
@@ -123,6 +131,7 @@ def bind_function_args(fn_def: ast.FunctionDef, args: list, kwargs: dict, exclud
return binding
+
def collect_assigned_variable_names(stmt: ast.stmt) -> set[str]:
"""
Collect all variable names that are assigned in a statement.
diff --git a/parser/tests/output_helper_script.py b/parser/tests/output_helper_script.py
index 5ead862..f071448 100644
--- a/parser/tests/output_helper_script.py
+++ b/parser/tests/output_helper_script.py
@@ -12,8 +12,9 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-import os
import json
+import os
+
from parser.entrypoint.user_interface import parse_and_format_launch_file
from parser.plugin_loader import load_user_handlers_from_directory
@@ -30,6 +31,6 @@
output_path = os.path.join(OUTPUT_DIR, f"{fname}.json")
with open(output_path, "w") as f:
- json.dump(result, f, indent = 2)
-
- print(f"โ
Snapshot created: {output_path}")
\ No newline at end of file
+ json.dump(result, f, indent=2)
+
+ print(f"โ
Snapshot created: {output_path}")
diff --git a/parser/tests/real_cases/launch_files/custom_handlers/launch_config_as_bool_handler.py b/parser/tests/real_cases/launch_files/custom_handlers/launch_config_as_bool_handler.py
index d864ef2..2b1fb64 100644
--- a/parser/tests/real_cases/launch_files/custom_handlers/launch_config_as_bool_handler.py
+++ b/parser/tests/real_cases/launch_files/custom_handlers/launch_config_as_bool_handler.py
@@ -13,6 +13,7 @@
# limitations under the License.
import ast
+
from parser.context import ParseContext
from parser.parser.registry import register_handler
from parser.resolution.utils import resolve_call_signature
@@ -23,8 +24,8 @@ def handle_launch_config(node: ast.Call, context: ParseContext) -> dict:
args, _ = resolve_call_signature(node, context.engine)
if not args:
raise ValueError("LaunchConfiguration must have a name.")
-
+
name = args[0]
context.introspection.track_launch_config_usage(name)
- return {"type": "LaunchConfiguration", "name": name, "as_bool": True}
\ No newline at end of file
+ return {"type": "LaunchConfiguration", "name": name, "as_bool": True}
diff --git a/parser/tests/real_cases/launch_files/custom_handlers/rewritten_yaml_handler.py b/parser/tests/real_cases/launch_files/custom_handlers/rewritten_yaml_handler.py
index f1956f5..ac09e4d 100644
--- a/parser/tests/real_cases/launch_files/custom_handlers/rewritten_yaml_handler.py
+++ b/parser/tests/real_cases/launch_files/custom_handlers/rewritten_yaml_handler.py
@@ -13,8 +13,9 @@
# limitations under the License.
from parser.parser.postprocessing import simplify_launch_configurations
-from parser.resolution.utils import resolve_call_kwargs
from parser.parser.user_handler import register_user_handler
+from parser.resolution.utils import resolve_call_kwargs
+
@register_user_handler("RewrittenYaml")
def handle_rewritten_yaml(node, context):
@@ -22,9 +23,7 @@ def handle_rewritten_yaml(node, context):
Handler for RewrittenYaml
"""
kwargs = resolve_call_kwargs(node, context.engine)
-
- return simplify_launch_configurations({
- "type": "CustomHandler",
- "type_name": "RewrittenYaml",
- **kwargs
- })
\ No newline at end of file
+
+ return simplify_launch_configurations(
+ {"type": "CustomHandler", "type_name": "RewrittenYaml", **kwargs}
+ )
diff --git a/parser/tests/real_cases/launch_files/launch_file_1.py b/parser/tests/real_cases/launch_files/launch_file_1.py
index d5b0764..4d8d89f 100644
--- a/parser/tests/real_cases/launch_files/launch_file_1.py
+++ b/parser/tests/real_cases/launch_files/launch_file_1.py
@@ -12,10 +12,9 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-import os
from launch import LaunchDescription
-from launch.actions import DeclareLaunchArgument, OpaqueFunction, IncludeLaunchDescription
+from launch.actions import DeclareLaunchArgument, IncludeLaunchDescription, OpaqueFunction
from launch.conditions import IfCondition
from launch.launch_description_sources import PythonLaunchDescriptionSource
from launch.substitutions import LaunchConfiguration, PathJoinSubstitution
@@ -53,7 +52,7 @@ def launch_setup(context, *args, **kwargs):
)
return [
- rviz_node,
+ rviz_node,
robot_launch,
]
@@ -62,12 +61,8 @@ def generate_launch_description():
declared_arguments = []
def add_launch_arg(name: str, default_value: str = None):
- declared_arguments.append(
- DeclareLaunchArgument(name, default_value=default_value)
- )
+ declared_arguments.append(DeclareLaunchArgument(name, default_value=default_value))
add_launch_arg("enable_rviz", "False")
- return LaunchDescription(
- [*declared_arguments, OpaqueFunction(function=launch_setup)]
- )
+ return LaunchDescription([*declared_arguments, OpaqueFunction(function=launch_setup)])
diff --git a/parser/tests/real_cases/launch_files/launch_file_2.py b/parser/tests/real_cases/launch_files/launch_file_2.py
index ee61708..71bcc13 100644
--- a/parser/tests/real_cases/launch_files/launch_file_2.py
+++ b/parser/tests/real_cases/launch_files/launch_file_2.py
@@ -47,9 +47,7 @@ def generate_launch_description():
def add_launch_arg(name: str, default_value: str = None, description: str = ""):
declared_arguments.append(
- DeclareLaunchArgument(
- name, default_value=default_value, description=description
- )
+ DeclareLaunchArgument(name, default_value=default_value, description=description)
)
add_launch_arg(
@@ -58,6 +56,4 @@ def add_launch_arg(name: str, default_value: str = None, description: str = ""):
description="Path to the driver parameter YAML file",
)
- return LaunchDescription(
- [*declared_arguments, OpaqueFunction(function=launch_setup)]
- )
+ return LaunchDescription([*declared_arguments, OpaqueFunction(function=launch_setup)])
diff --git a/parser/tests/real_cases/launch_files/launch_file_3.py b/parser/tests/real_cases/launch_files/launch_file_3.py
index 7a22dbd..1614d60 100644
--- a/parser/tests/real_cases/launch_files/launch_file_3.py
+++ b/parser/tests/real_cases/launch_files/launch_file_3.py
@@ -13,10 +13,9 @@
# limitations under the License.
from launch import LaunchDescription
-from launch.actions import DeclareLaunchArgument, OpaqueFunction, IncludeLaunchDescription
+from launch.actions import DeclareLaunchArgument, IncludeLaunchDescription, OpaqueFunction
from launch.launch_description_sources import PythonLaunchDescriptionSource
-from launch.substitutions import LaunchConfiguration, PathJoinSubstitution
-from launch_ros.actions import Node
+from launch.substitutions import PathJoinSubstitution
from launch_ros.substitutions import FindPackageShare
@@ -42,25 +41,16 @@ def launch_setup(context, *args, **kwargs):
)
]
),
- launch_arguments={
- "driver1_param_file": driver1_param_path
- }.items(),
+ launch_arguments={"driver1_param_file": driver1_param_path}.items(),
)
- return [
- driver1_launch
- ]
+ return [driver1_launch]
def generate_launch_description():
declared_arguments = []
def add_launch_arg(name: str, default_value: str = None):
- declared_arguments.append(
- DeclareLaunchArgument(name, default_value=default_value)
- )
-
- return LaunchDescription(
- [*declared_arguments, OpaqueFunction(function=launch_setup)]
- )
+ declared_arguments.append(DeclareLaunchArgument(name, default_value=default_value))
+ return LaunchDescription([*declared_arguments, OpaqueFunction(function=launch_setup)])
diff --git a/parser/tests/real_cases/launch_files/launch_file_4.py b/parser/tests/real_cases/launch_files/launch_file_4.py
index 2ee9b41..0caa7a5 100644
--- a/parser/tests/real_cases/launch_files/launch_file_4.py
+++ b/parser/tests/real_cases/launch_files/launch_file_4.py
@@ -13,55 +13,58 @@
# limitations under the License.
import os
+
+from ament_index_python.packages import get_package_share_directory
from launch import LaunchDescription
from launch.actions import DeclareLaunchArgument, OpaqueFunction
from launch.substitutions import Command, LaunchConfiguration
from launch_ros.actions import Node
-from ament_index_python.packages import get_package_share_directory
+
def launch_setup(context, *args, **kwargs):
- package_name = LaunchConfiguration('package_name').perform(context)
- urdf_file = LaunchConfiguration('urdf_file').perform(context)
- use_sim_time_str = LaunchConfiguration('use_sim_time').perform(context)
+ package_name = LaunchConfiguration("package_name").perform(context)
+ urdf_file = LaunchConfiguration("urdf_file").perform(context)
+ use_sim_time_str = LaunchConfiguration("use_sim_time").perform(context)
package_directory = get_package_share_directory(package_name)
- robot_desc_path = os.path.join(package_directory, 'urdf', urdf_file)
- use_sim_time = use_sim_time_str.lower() in ['true', '1', 'yes']
+ robot_desc_path = os.path.join(package_directory, "urdf", urdf_file)
+ use_sim_time = use_sim_time_str.lower() in ["true", "1", "yes"]
params = {
- 'use_sim_time': use_sim_time,
- 'robot_description': Command(['xacro ', robot_desc_path])
+ "use_sim_time": use_sim_time,
+ "robot_description": Command(["xacro ", robot_desc_path]),
}
node_robot_state_publisher = Node(
- package='robot_state_publisher',
- executable='robot_state_publisher',
- output='screen',
- parameters=[params]
+ package="robot_state_publisher",
+ executable="robot_state_publisher",
+ output="screen",
+ parameters=[params],
)
return [node_robot_state_publisher]
+
def generate_launch_description():
declare_pkg_name = DeclareLaunchArgument(
- 'package_name',
- default_value='upao_robot_description',
- description='Name of the package containing the robot description'
+ "package_name",
+ default_value="upao_robot_description",
+ description="Name of the package containing the robot description",
)
declare_urdf_file = DeclareLaunchArgument(
- 'urdf_file',
- default_value='robot_wrapper.urdf.xacro',
- description='URDF file to load for the robot description'
+ "urdf_file",
+ default_value="robot_wrapper.urdf.xacro",
+ description="URDF file to load for the robot description",
)
declare_use_sim_time = DeclareLaunchArgument(
- 'use_sim_time',
- default_value='true',
- description='Use simulation time if true'
+ "use_sim_time", default_value="true", description="Use simulation time if true"
)
- return LaunchDescription([
- declare_pkg_name,
- declare_urdf_file,
- declare_use_sim_time,
- OpaqueFunction(function=launch_setup),
- ])
\ No newline at end of file
+ return LaunchDescription(
+ [
+ declare_pkg_name,
+ declare_urdf_file,
+ declare_use_sim_time,
+ OpaqueFunction(function=launch_setup),
+ ]
+ )
diff --git a/parser/tests/real_cases/launch_files/launch_file_5.py b/parser/tests/real_cases/launch_files/launch_file_5.py
index 830acb3..dcad0db 100644
--- a/parser/tests/real_cases/launch_files/launch_file_5.py
+++ b/parser/tests/real_cases/launch_files/launch_file_5.py
@@ -12,55 +12,66 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+import os
+
from launch import LaunchDescription
-from launch.actions import DeclareLaunchArgument, GroupAction, IncludeLaunchDescription, OpaqueFunction
+from launch.actions import (
+ DeclareLaunchArgument,
+ GroupAction,
+ IncludeLaunchDescription,
+ OpaqueFunction,
+)
from launch.launch_description_sources import PythonLaunchDescriptionSource
from launch.substitutions import LaunchConfiguration
from launch_ros.actions import Node
-import os
+
def launch_setup(context, *args, **kwargs):
use_sim_time = LaunchConfiguration("use_sim_time").perform(context)
sim = use_sim_time.lower() in ["true", "1"]
-
+
nodes = []
- nodes.append(Node(
- package="demo_camera",
- executable="camera_node",
- parameters=[{"use_sim_time": sim}],
- name="camera"
- ))
+ nodes.append(
+ Node(
+ package="demo_camera",
+ executable="camera_node",
+ parameters=[{"use_sim_time": sim}],
+ name="camera",
+ )
+ )
- nodes.append(Node(
- package="demo_robot",
- executable="robot_state_publisher",
- parameters=[{"use_sim_time": sim}],
- name="state_pub"
- ))
+ nodes.append(
+ Node(
+ package="demo_robot",
+ executable="robot_state_publisher",
+ parameters=[{"use_sim_time": sim}],
+ name="state_pub",
+ )
+ )
return nodes
+
def generate_launch_description():
- return LaunchDescription([
- DeclareLaunchArgument("use_sim_time", default_value="true"),
- DeclareLaunchArgument("sub_package"),
- GroupAction([
- Node(
- package="demo_bringup",
- executable="base_node",
- name="base"
+ return LaunchDescription(
+ [
+ DeclareLaunchArgument("use_sim_time", default_value="true"),
+ DeclareLaunchArgument("sub_package"),
+ GroupAction(
+ [
+ Node(package="demo_bringup", executable="base_node", name="base"),
+ IncludeLaunchDescription(
+ PythonLaunchDescriptionSource(
+ os.path.join(
+ LaunchConfiguration("sub_package").perform({}),
+ "launch",
+ "sub_launch.py",
+ )
+ )
+ ),
+ ]
),
- IncludeLaunchDescription(
- PythonLaunchDescriptionSource(
- os.path.join(
- LaunchConfiguration("sub_package").perform({}),
- "launch",
- "sub_launch.py"
- )
- )
- )
- ]),
-
- OpaqueFunction(function=launch_setup)
- ])
+ OpaqueFunction(function=launch_setup),
+ ]
+ )
diff --git a/parser/tests/real_cases/launch_files/launch_file_6.py b/parser/tests/real_cases/launch_files/launch_file_6.py
index 128c301..5c70e29 100644
--- a/parser/tests/real_cases/launch_files/launch_file_6.py
+++ b/parser/tests/real_cases/launch_files/launch_file_6.py
@@ -12,26 +12,31 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+
from launch import LaunchDescription
-from launch.actions import DeclareLaunchArgument, GroupAction, IncludeLaunchDescription, OpaqueFunction
+from launch.actions import (
+ DeclareLaunchArgument,
+ GroupAction,
+ IncludeLaunchDescription,
+ OpaqueFunction,
+)
from launch.conditions import IfCondition
+from launch.launch_description_sources import PythonLaunchDescriptionSource
from launch.substitutions import LaunchConfiguration, PythonExpression
-from launch_ros.actions import Node, ComposableNodeContainer
+from launch_ros.actions import ComposableNodeContainer, Node
from launch_ros.descriptions import ComposableNode
-from launch.launch_description_sources import PythonLaunchDescriptionSource
-import os
def launch_setup(context, *args, **kwargs):
- use_sim_time = LaunchConfiguration('use_sim_time').perform(context)
- robot_description = f"..."
+ use_sim_time = LaunchConfiguration("use_sim_time").perform(context)
+ robot_description = "..."
return [
Node(
- package='robot_state_publisher',
- executable='robot_state_publisher',
- output='screen',
- parameters=[{'use_sim_time': use_sim_time, 'robot_description': robot_description}]
+ package="robot_state_publisher",
+ executable="robot_state_publisher",
+ output="screen",
+ parameters=[{"use_sim_time": use_sim_time, "robot_description": robot_description}],
)
]
@@ -39,37 +44,34 @@ def launch_setup(context, *args, **kwargs):
def generate_launch_description():
# Declare arguments
package_arg = DeclareLaunchArgument(
- 'pkg_name',
- default_value='my_robot_pkg',
- description='Package containing the launch files'
+ "pkg_name", default_value="my_robot_pkg", description="Package containing the launch files"
)
use_sim_arg = DeclareLaunchArgument(
- 'use_sim_time',
- default_value='true',
- description='Use simulation clock'
+ "use_sim_time", default_value="true", description="Use simulation clock"
)
# Include
included_launch = IncludeLaunchDescription(
- PythonLaunchDescriptionSource([
- LaunchConfiguration('pkg_name'),
- '/launch/other_launch_file.py'
- ])
+ PythonLaunchDescriptionSource(
+ [LaunchConfiguration("pkg_name"), "/launch/other_launch_file.py"]
+ )
)
# Group with condition
conditional_group = GroupAction(
actions=[
Node(
- package='demo_nodes_cpp',
- executable='talker',
- name='conditional_talker',
- output='screen',
- parameters=[{'use_sim_time': LaunchConfiguration('use_sim_time')}]
+ package="demo_nodes_cpp",
+ executable="talker",
+ name="conditional_talker",
+ output="screen",
+ parameters=[{"use_sim_time": LaunchConfiguration("use_sim_time")}],
)
],
- condition=IfCondition(PythonExpression(["'", LaunchConfiguration('use_sim_time'), "' == 'true'"])),
- namespace='sim_ns'
+ condition=IfCondition(
+ PythonExpression(["'", LaunchConfiguration("use_sim_time"), "' == 'true'"])
+ ),
+ namespace="sim_ns",
)
# OpaqueFunction
@@ -77,32 +79,27 @@ def generate_launch_description():
# Composable Container + Nodes
container = ComposableNodeContainer(
- name='my_container',
- namespace='',
- package='rclcpp_components',
- executable='component_container_mt',
+ name="my_container",
+ namespace="",
+ package="rclcpp_components",
+ executable="component_container_mt",
composable_node_descriptions=[
ComposableNode(
- package='demo_nodes_cpp',
- plugin='demo_nodes_cpp::Talker',
- name='talker_component',
- parameters=[{'use_sim_time': LaunchConfiguration('use_sim_time')}]
+ package="demo_nodes_cpp",
+ plugin="demo_nodes_cpp::Talker",
+ name="talker_component",
+ parameters=[{"use_sim_time": LaunchConfiguration("use_sim_time")}],
),
ComposableNode(
- package='demo_nodes_cpp',
- plugin='demo_nodes_cpp::Listener',
- name='listener_component',
- parameters=[{'use_sim_time': LaunchConfiguration('use_sim_time')}]
- )
+ package="demo_nodes_cpp",
+ plugin="demo_nodes_cpp::Listener",
+ name="listener_component",
+ parameters=[{"use_sim_time": LaunchConfiguration("use_sim_time")}],
+ ),
],
- output='screen'
+ output="screen",
)
- return LaunchDescription([
- package_arg,
- use_sim_arg,
- included_launch,
- conditional_group,
- opaque,
- container
- ])
+ return LaunchDescription(
+ [package_arg, use_sim_arg, included_launch, conditional_group, opaque, container]
+ )
diff --git a/parser/tests/real_cases/launch_files/nav2_bringup.launch.py b/parser/tests/real_cases/launch_files/nav2_bringup.launch.py
index 36e0600..5a0be08 100644
--- a/parser/tests/real_cases/launch_files/nav2_bringup.launch.py
+++ b/parser/tests/real_cases/launch_files/nav2_bringup.launch.py
@@ -16,8 +16,12 @@
from ament_index_python.packages import get_package_share_directory
from launch import LaunchDescription
-from launch.actions import (DeclareLaunchArgument, GroupAction, IncludeLaunchDescription,
- SetEnvironmentVariable)
+from launch.actions import (
+ DeclareLaunchArgument,
+ GroupAction,
+ IncludeLaunchDescription,
+ SetEnvironmentVariable,
+)
from launch.conditions import IfCondition
from launch.launch_description_sources import PythonLaunchDescriptionSource
from launch.substitutions import LaunchConfiguration, PythonExpression
@@ -28,32 +32,32 @@
def generate_launch_description() -> LaunchDescription:
# Get the launch directory
- bringup_dir = get_package_share_directory('nav2_bringup')
- launch_dir = os.path.join(bringup_dir, 'launch')
+ bringup_dir = get_package_share_directory("nav2_bringup")
+ launch_dir = os.path.join(bringup_dir, "launch")
# Create the launch configuration variables
- namespace = LaunchConfiguration('namespace')
- slam = LaunchConfigAsBool('slam')
- map_yaml_file = LaunchConfiguration('map')
- keepout_mask_yaml_file = LaunchConfiguration('keepout_mask')
- speed_mask_yaml_file = LaunchConfiguration('speed_mask')
- graph_filepath = LaunchConfiguration('graph')
- use_sim_time = LaunchConfigAsBool('use_sim_time')
- params_file = LaunchConfiguration('params_file')
- autostart = LaunchConfigAsBool('autostart')
- use_composition = LaunchConfigAsBool('use_composition')
- use_respawn = LaunchConfigAsBool('use_respawn')
- log_level = LaunchConfiguration('log_level')
- use_localization = LaunchConfigAsBool('use_localization')
- use_keepout_zones = LaunchConfigAsBool('use_keepout_zones')
- use_speed_zones = LaunchConfigAsBool('use_speed_zones')
+ namespace = LaunchConfiguration("namespace")
+ slam = LaunchConfigAsBool("slam")
+ map_yaml_file = LaunchConfiguration("map")
+ keepout_mask_yaml_file = LaunchConfiguration("keepout_mask")
+ speed_mask_yaml_file = LaunchConfiguration("speed_mask")
+ graph_filepath = LaunchConfiguration("graph")
+ use_sim_time = LaunchConfigAsBool("use_sim_time")
+ params_file = LaunchConfiguration("params_file")
+ autostart = LaunchConfigAsBool("autostart")
+ use_composition = LaunchConfigAsBool("use_composition")
+ use_respawn = LaunchConfigAsBool("use_respawn")
+ log_level = LaunchConfiguration("log_level")
+ use_localization = LaunchConfigAsBool("use_localization")
+ use_keepout_zones = LaunchConfigAsBool("use_keepout_zones")
+ use_speed_zones = LaunchConfigAsBool("use_speed_zones")
# Map fully qualified names to relative ones so the node's namespace can be prepended.
- remappings = [('/tf', 'tf'), ('/tf_static', 'tf_static')]
+ remappings = [("/tf", "tf"), ("/tf_static", "tf_static")]
yaml_substitutions = {
- 'KEEPOUT_ZONE_ENABLED': use_keepout_zones,
- 'SPEED_ZONE_ENABLED': use_speed_zones,
+ "KEEPOUT_ZONE_ENABLED": use_keepout_zones,
+ "SPEED_ZONE_ENABLED": use_speed_zones,
}
configured_params = ParameterFile(
@@ -67,84 +71,80 @@ def generate_launch_description() -> LaunchDescription:
allow_substs=True,
)
- stdout_linebuf_envvar = SetEnvironmentVariable(
- 'RCUTILS_LOGGING_BUFFERED_STREAM', '1'
- )
+ stdout_linebuf_envvar = SetEnvironmentVariable("RCUTILS_LOGGING_BUFFERED_STREAM", "1")
declare_namespace_cmd = DeclareLaunchArgument(
- 'namespace', default_value='', description='Top-level namespace'
+ "namespace", default_value="", description="Top-level namespace"
)
declare_slam_cmd = DeclareLaunchArgument(
- 'slam', default_value='False', description='Whether run a SLAM'
+ "slam", default_value="False", description="Whether run a SLAM"
)
declare_map_yaml_cmd = DeclareLaunchArgument(
- 'map', default_value='', description='Full path to map yaml file to load'
+ "map", default_value="", description="Full path to map yaml file to load"
)
declare_keepout_mask_yaml_cmd = DeclareLaunchArgument(
- 'keepout_mask', default_value='',
- description='Full path to keepout mask yaml file to load'
+ "keepout_mask", default_value="", description="Full path to keepout mask yaml file to load"
)
declare_speed_mask_yaml_cmd = DeclareLaunchArgument(
- 'speed_mask', default_value='',
- description='Full path to speed mask yaml file to load'
+ "speed_mask", default_value="", description="Full path to speed mask yaml file to load"
)
declare_graph_file_cmd = DeclareLaunchArgument(
- 'graph',
- default_value='', description='Path to the graph file to load'
+ "graph", default_value="", description="Path to the graph file to load"
)
declare_use_localization_cmd = DeclareLaunchArgument(
- 'use_localization', default_value='True',
- description='Whether to enable localization or not'
+ "use_localization",
+ default_value="True",
+ description="Whether to enable localization or not",
)
declare_use_keepout_zones_cmd = DeclareLaunchArgument(
- 'use_keepout_zones', default_value='True',
- description='Whether to enable keepout zones or not'
+ "use_keepout_zones",
+ default_value="True",
+ description="Whether to enable keepout zones or not",
)
declare_use_speed_zones_cmd = DeclareLaunchArgument(
- 'use_speed_zones', default_value='True',
- description='Whether to enable speed zones or not'
+ "use_speed_zones", default_value="True", description="Whether to enable speed zones or not"
)
declare_use_sim_time_cmd = DeclareLaunchArgument(
- 'use_sim_time',
- default_value='false',
- description='Use simulation (Gazebo) clock if true',
+ "use_sim_time",
+ default_value="false",
+ description="Use simulation (Gazebo) clock if true",
)
declare_params_file_cmd = DeclareLaunchArgument(
- 'params_file',
- default_value=os.path.join(bringup_dir, 'params', 'nav2_params.yaml'),
- description='Full path to the ROS2 parameters file to use for all launched nodes',
+ "params_file",
+ default_value=os.path.join(bringup_dir, "params", "nav2_params.yaml"),
+ description="Full path to the ROS2 parameters file to use for all launched nodes",
)
declare_autostart_cmd = DeclareLaunchArgument(
- 'autostart',
- default_value='true',
- description='Automatically startup the nav2 stack',
+ "autostart",
+ default_value="true",
+ description="Automatically startup the nav2 stack",
)
declare_use_composition_cmd = DeclareLaunchArgument(
- 'use_composition',
- default_value='True',
- description='Whether to use composed bringup',
+ "use_composition",
+ default_value="True",
+ description="Whether to use composed bringup",
)
declare_use_respawn_cmd = DeclareLaunchArgument(
- 'use_respawn',
- default_value='False',
- description='Whether to respawn if a node crashes. Applied when composition is disabled.',
+ "use_respawn",
+ default_value="False",
+ description="Whether to respawn if a node crashes. Applied when composition is disabled.",
)
declare_log_level_cmd = DeclareLaunchArgument(
- 'log_level', default_value='info', description='log level'
+ "log_level", default_value="info", description="log level"
)
# Specify the actions
@@ -152,92 +152,79 @@ def generate_launch_description() -> LaunchDescription:
[
Node(
condition=IfCondition(use_composition),
- name='nav2_container',
+ name="nav2_container",
namespace=namespace,
- package='rclcpp_components',
- executable='component_container_isolated',
- parameters=[configured_params, {'autostart': autostart}],
- arguments=['--ros-args', '--log-level', log_level],
+ package="rclcpp_components",
+ executable="component_container_isolated",
+ parameters=[configured_params, {"autostart": autostart}],
+ arguments=["--ros-args", "--log-level", log_level],
remappings=remappings,
- output='screen',
+ output="screen",
),
IncludeLaunchDescription(
- PythonLaunchDescriptionSource(
- os.path.join(launch_dir, 'slam_launch.py')
- ),
- condition=IfCondition(PythonExpression([slam, ' and ', use_localization])),
+ PythonLaunchDescriptionSource(os.path.join(launch_dir, "slam_launch.py")),
+ condition=IfCondition(PythonExpression([slam, " and ", use_localization])),
launch_arguments={
- 'namespace': namespace,
- 'use_sim_time': use_sim_time,
- 'autostart': autostart,
- 'use_respawn': use_respawn,
- 'params_file': params_file,
+ "namespace": namespace,
+ "use_sim_time": use_sim_time,
+ "autostart": autostart,
+ "use_respawn": use_respawn,
+ "params_file": params_file,
}.items(),
),
IncludeLaunchDescription(
- PythonLaunchDescriptionSource(
- os.path.join(launch_dir, 'localization_launch.py')
- ),
- condition=IfCondition(PythonExpression(['not ', slam, ' and ', use_localization])),
+ PythonLaunchDescriptionSource(os.path.join(launch_dir, "localization_launch.py")),
+ condition=IfCondition(PythonExpression(["not ", slam, " and ", use_localization])),
launch_arguments={
- 'namespace': namespace,
- 'map': map_yaml_file,
- 'use_sim_time': use_sim_time,
- 'autostart': autostart,
- 'params_file': params_file,
- 'use_composition': use_composition,
- 'use_respawn': use_respawn,
- 'container_name': 'nav2_container',
+ "namespace": namespace,
+ "map": map_yaml_file,
+ "use_sim_time": use_sim_time,
+ "autostart": autostart,
+ "params_file": params_file,
+ "use_composition": use_composition,
+ "use_respawn": use_respawn,
+ "container_name": "nav2_container",
}.items(),
),
-
IncludeLaunchDescription(
- PythonLaunchDescriptionSource(
- os.path.join(launch_dir, 'keepout_zone_launch.py')
- ),
+ PythonLaunchDescriptionSource(os.path.join(launch_dir, "keepout_zone_launch.py")),
condition=IfCondition(use_keepout_zones),
launch_arguments={
- 'namespace': namespace,
- 'keepout_mask': keepout_mask_yaml_file,
- 'use_sim_time': use_sim_time,
- 'params_file': params_file,
- 'use_composition': use_composition,
- 'use_respawn': use_respawn,
- 'container_name': 'nav2_container',
+ "namespace": namespace,
+ "keepout_mask": keepout_mask_yaml_file,
+ "use_sim_time": use_sim_time,
+ "params_file": params_file,
+ "use_composition": use_composition,
+ "use_respawn": use_respawn,
+ "container_name": "nav2_container",
}.items(),
),
-
IncludeLaunchDescription(
- PythonLaunchDescriptionSource(
- os.path.join(launch_dir, 'speed_zone_launch.py')
- ),
+ PythonLaunchDescriptionSource(os.path.join(launch_dir, "speed_zone_launch.py")),
condition=IfCondition(use_speed_zones),
launch_arguments={
- 'namespace': namespace,
- 'speed_mask': speed_mask_yaml_file,
- 'use_sim_time': use_sim_time,
- 'params_file': params_file,
- 'use_composition': use_composition,
- 'use_respawn': use_respawn,
- 'container_name': 'nav2_container',
+ "namespace": namespace,
+ "speed_mask": speed_mask_yaml_file,
+ "use_sim_time": use_sim_time,
+ "params_file": params_file,
+ "use_composition": use_composition,
+ "use_respawn": use_respawn,
+ "container_name": "nav2_container",
}.items(),
),
-
IncludeLaunchDescription(
- PythonLaunchDescriptionSource(
- os.path.join(launch_dir, 'navigation_launch.py')
- ),
+ PythonLaunchDescriptionSource(os.path.join(launch_dir, "navigation_launch.py")),
launch_arguments={
- 'namespace': namespace,
- 'use_sim_time': use_sim_time,
- 'autostart': autostart,
- 'graph': graph_filepath,
- 'params_file': params_file,
- 'use_composition': use_composition,
- 'use_respawn': use_respawn,
- 'use_keepout_zones': use_keepout_zones,
- 'use_speed_zones': use_speed_zones,
- 'container_name': 'nav2_container',
+ "namespace": namespace,
+ "use_sim_time": use_sim_time,
+ "autostart": autostart,
+ "graph": graph_filepath,
+ "params_file": params_file,
+ "use_composition": use_composition,
+ "use_respawn": use_respawn,
+ "use_keepout_zones": use_keepout_zones,
+ "use_speed_zones": use_speed_zones,
+ "container_name": "nav2_container",
}.items(),
),
]
@@ -269,4 +256,4 @@ def generate_launch_description() -> LaunchDescription:
# Add the actions to launch all of the navigation nodes
ld.add_action(bringup_cmd_group)
- return ld
\ No newline at end of file
+ return ld
diff --git a/parser/tests/real_cases/launch_files/rrbot.launch.py b/parser/tests/real_cases/launch_files/rrbot.launch.py
index 1c7e3ea..ca044c3 100644
--- a/parser/tests/real_cases/launch_files/rrbot.launch.py
+++ b/parser/tests/real_cases/launch_files/rrbot.launch.py
@@ -18,7 +18,6 @@
from launch.conditions import IfCondition
from launch.event_handlers import OnProcessExit
from launch.substitutions import Command, FindExecutable, LaunchConfiguration, PathJoinSubstitution
-
from launch_ros.actions import Node
from launch_ros.substitutions import FindPackageShare
@@ -122,4 +121,4 @@ def generate_launch_description():
delay_joint_state_broadcaster_after_robot_controller_spawner,
]
- return LaunchDescription(declared_arguments + nodes)
\ No newline at end of file
+ return LaunchDescription(declared_arguments + nodes)
diff --git a/parser/tests/real_cases/launch_files/turtlebot3_bringup.launch.py b/parser/tests/real_cases/launch_files/turtlebot3_bringup.launch.py
index a526630..a164c81 100644
--- a/parser/tests/real_cases/launch_files/turtlebot3_bringup.launch.py
+++ b/parser/tests/real_cases/launch_files/turtlebot3_bringup.launch.py
@@ -20,100 +20,101 @@
from ament_index_python.packages import get_package_share_directory
from launch import LaunchDescription
-from launch.actions import DeclareLaunchArgument
-from launch.actions import IncludeLaunchDescription
+from launch.actions import DeclareLaunchArgument, IncludeLaunchDescription
from launch.launch_description_sources import PythonLaunchDescriptionSource
-from launch.substitutions import LaunchConfiguration
-from launch.substitutions import ThisLaunchFileDir
-from launch_ros.actions import Node
-from launch_ros.actions import PushRosNamespace
+from launch.substitutions import LaunchConfiguration, ThisLaunchFileDir
+from launch_ros.actions import Node, PushRosNamespace
def generate_launch_description():
- TURTLEBOT3_MODEL = os.environ.get('TURTLEBOT3_MODEL')
- ROS_DISTRO = os.environ.get('ROS_DISTRO')
- LDS_MODEL = os.environ.get('LDS_MODEL')
+ TURTLEBOT3_MODEL = os.environ.get("TURTLEBOT3_MODEL")
+ ROS_DISTRO = os.environ.get("ROS_DISTRO")
+ LDS_MODEL = os.environ.get("LDS_MODEL")
- namespace = LaunchConfiguration('namespace', default='')
+ namespace = LaunchConfiguration("namespace", default="")
- usb_port = LaunchConfiguration('usb_port', default='/dev/ttyACM0')
+ usb_port = LaunchConfiguration("usb_port", default="/dev/ttyACM0")
- if ROS_DISTRO == 'humble':
+ if ROS_DISTRO == "humble":
tb3_param_dir = LaunchConfiguration(
- 'tb3_param_dir',
+ "tb3_param_dir",
default=os.path.join(
- get_package_share_directory('turtlebot3_bringup'),
- 'param',
+ get_package_share_directory("turtlebot3_bringup"),
+ "param",
ROS_DISTRO,
- TURTLEBOT3_MODEL + '.yaml'))
+ TURTLEBOT3_MODEL + ".yaml",
+ ),
+ )
else:
tb3_param_dir = LaunchConfiguration(
- 'tb3_param_dir',
+ "tb3_param_dir",
default=os.path.join(
- get_package_share_directory('turtlebot3_bringup'),
- 'param',
- TURTLEBOT3_MODEL + '.yaml'))
+ get_package_share_directory("turtlebot3_bringup"),
+ "param",
+ TURTLEBOT3_MODEL + ".yaml",
+ ),
+ )
- if LDS_MODEL == 'LDS-01':
+ if LDS_MODEL == "LDS-01":
lidar_pkg_dir = LaunchConfiguration(
- 'lidar_pkg_dir',
- default=os.path.join(get_package_share_directory('hls_lfcd_lds_driver'), 'launch'))
- elif LDS_MODEL == 'LDS-02':
+ "lidar_pkg_dir",
+ default=os.path.join(get_package_share_directory("hls_lfcd_lds_driver"), "launch"),
+ )
+ elif LDS_MODEL == "LDS-02":
lidar_pkg_dir = LaunchConfiguration(
- 'lidar_pkg_dir',
- default=os.path.join(get_package_share_directory('ld08_driver'), 'launch'))
- LDS_LAUNCH_FILE = '/ld08.launch.py'
- elif LDS_MODEL == 'LDS-03':
+ "lidar_pkg_dir",
+ default=os.path.join(get_package_share_directory("ld08_driver"), "launch"),
+ )
+ LDS_LAUNCH_FILE = "/ld08.launch.py"
+ elif LDS_MODEL == "LDS-03":
lidar_pkg_dir = LaunchConfiguration(
- 'lidar_pkg_dir',
- default=os.path.join(get_package_share_directory('coin_d4_driver'), 'launch'))
- LDS_LAUNCH_FILE = '/single_lidar_node.launch.py'
+ "lidar_pkg_dir",
+ default=os.path.join(get_package_share_directory("coin_d4_driver"), "launch"),
+ )
+ LDS_LAUNCH_FILE = "/single_lidar_node.launch.py"
else:
lidar_pkg_dir = LaunchConfiguration(
- 'lidar_pkg_dir',
- default=os.path.join(get_package_share_directory('hls_lfcd_lds_driver'), 'launch'))
- LDS_LAUNCH_FILE = '/hlds_laser.launch.py'
-
- use_sim_time = LaunchConfiguration('use_sim_time', default='false')
-
- return LaunchDescription([
- DeclareLaunchArgument(
- 'use_sim_time',
- default_value=use_sim_time,
- description='Use simulation (Gazebo) clock if true'),
-
- DeclareLaunchArgument(
- 'usb_port',
- default_value=usb_port,
- description='Connected USB port with OpenCR'),
-
- DeclareLaunchArgument(
- 'namespace',
- default_value=namespace,
- description='Namespace for nodes'),
-
- PushRosNamespace(namespace),
-
- IncludeLaunchDescription(
- PythonLaunchDescriptionSource(
- [ThisLaunchFileDir(), '/turtlebot3_state_publisher.launch.py']),
- launch_arguments={'use_sim_time': use_sim_time,
- 'namespace': namespace}.items(),
- ),
-
- IncludeLaunchDescription(
- PythonLaunchDescriptionSource([lidar_pkg_dir, LDS_LAUNCH_FILE]),
- launch_arguments={'port': '/dev/ttyUSB0',
- 'frame_id': 'base_scan',
- 'namespace': namespace}.items(),
- ),
-
- Node(
- package='turtlebot3_node',
- executable='turtlebot3_ros',
- parameters=[
- tb3_param_dir,
- {'namespace': namespace}],
- arguments=['-i', usb_port],
- output='screen'),
- ])
\ No newline at end of file
+ "lidar_pkg_dir",
+ default=os.path.join(get_package_share_directory("hls_lfcd_lds_driver"), "launch"),
+ )
+ LDS_LAUNCH_FILE = "/hlds_laser.launch.py"
+
+ use_sim_time = LaunchConfiguration("use_sim_time", default="false")
+
+ return LaunchDescription(
+ [
+ DeclareLaunchArgument(
+ "use_sim_time",
+ default_value=use_sim_time,
+ description="Use simulation (Gazebo) clock if true",
+ ),
+ DeclareLaunchArgument(
+ "usb_port", default_value=usb_port, description="Connected USB port with OpenCR"
+ ),
+ DeclareLaunchArgument(
+ "namespace", default_value=namespace, description="Namespace for nodes"
+ ),
+ PushRosNamespace(namespace),
+ IncludeLaunchDescription(
+ PythonLaunchDescriptionSource(
+ [ThisLaunchFileDir(), "/turtlebot3_state_publisher.launch.py"]
+ ),
+ launch_arguments={"use_sim_time": use_sim_time, "namespace": namespace}.items(),
+ ),
+ IncludeLaunchDescription(
+ PythonLaunchDescriptionSource([lidar_pkg_dir, LDS_LAUNCH_FILE]),
+ launch_arguments={
+ "port": "/dev/ttyUSB0",
+ "frame_id": "base_scan",
+ "namespace": namespace,
+ }.items(),
+ ),
+ Node(
+ package="turtlebot3_node",
+ executable="turtlebot3_ros",
+ parameters=[tb3_param_dir, {"namespace": namespace}],
+ arguments=["-i", usb_port],
+ output="screen",
+ ),
+ ]
+ )
diff --git a/parser/tests/real_cases/launch_files/turtlebot4_bringup.launch.py b/parser/tests/real_cases/launch_files/turtlebot4_bringup.launch.py
index 899d809..2be3a0d 100644
--- a/parser/tests/real_cases/launch_files/turtlebot4_bringup.launch.py
+++ b/parser/tests/real_cases/launch_files/turtlebot4_bringup.launch.py
@@ -17,100 +17,103 @@
from ament_index_python.packages import get_package_share_directory
-
from launch import LaunchDescription
-from launch.actions import DeclareLaunchArgument, GroupAction, IncludeLaunchDescription, TimerAction # noqa: E501
+from launch.actions import ( # noqa: E501
+ DeclareLaunchArgument,
+ GroupAction,
+ IncludeLaunchDescription,
+ TimerAction,
+)
+from launch.conditions import IfCondition
from launch.launch_description_sources import PythonLaunchDescriptionSource
from launch.substitutions import EnvironmentVariable, LaunchConfiguration, PathJoinSubstitution
-from launch.conditions import IfCondition
from launch_ros.actions import PushRosNamespace
-
from nav2_common.launch import RewrittenYaml
def generate_launch_description():
ld = LaunchDescription()
- diagnostics_enable = EnvironmentVariable('TURTLEBOT4_DIAGNOSTICS', default_value='1')
- namespace = EnvironmentVariable('ROBOT_NAMESPACE', default_value='')
+ diagnostics_enable = EnvironmentVariable("TURTLEBOT4_DIAGNOSTICS", default_value="1")
+ namespace = EnvironmentVariable("ROBOT_NAMESPACE", default_value="")
- pkg_turtlebot4_bringup = get_package_share_directory('turtlebot4_bringup')
- pkg_turtlebot4_diagnostics = get_package_share_directory('turtlebot4_diagnostics')
- pkg_turtlebot4_description = get_package_share_directory('turtlebot4_description')
+ pkg_turtlebot4_bringup = get_package_share_directory("turtlebot4_bringup")
+ pkg_turtlebot4_diagnostics = get_package_share_directory("turtlebot4_diagnostics")
+ pkg_turtlebot4_description = get_package_share_directory("turtlebot4_description")
param_file_cmd = DeclareLaunchArgument(
- 'param_file',
- default_value=PathJoinSubstitution(
- [pkg_turtlebot4_bringup, 'config', 'turtlebot4.yaml']),
- description='Turtlebot4 Robot param file'
+ "param_file",
+ default_value=PathJoinSubstitution([pkg_turtlebot4_bringup, "config", "turtlebot4.yaml"]),
+ description="Turtlebot4 Robot param file",
)
- param_file = LaunchConfiguration('param_file')
+ param_file = LaunchConfiguration("param_file")
namespaced_param_file = RewrittenYaml(
- source_file=param_file,
- root_key=namespace,
- param_rewrites={},
- convert_types=True)
+ source_file=param_file, root_key=namespace, param_rewrites={}, convert_types=True
+ )
# Launch files
turtlebot4_robot_launch_file = PathJoinSubstitution(
- [pkg_turtlebot4_bringup, 'launch', 'robot.launch.py'])
+ [pkg_turtlebot4_bringup, "launch", "robot.launch.py"]
+ )
joy_teleop_launch_file = PathJoinSubstitution(
- [pkg_turtlebot4_bringup, 'launch', 'joy_teleop.launch.py'])
+ [pkg_turtlebot4_bringup, "launch", "joy_teleop.launch.py"]
+ )
diagnostics_launch_file = PathJoinSubstitution(
- [pkg_turtlebot4_diagnostics, 'launch', 'diagnostics.launch.py'])
+ [pkg_turtlebot4_diagnostics, "launch", "diagnostics.launch.py"]
+ )
rplidar_launch_file = PathJoinSubstitution(
- [pkg_turtlebot4_bringup, 'launch', 'rplidar.launch.py'])
- oakd_launch_file = PathJoinSubstitution(
- [pkg_turtlebot4_bringup, 'launch', 'oakd.launch.py'])
+ [pkg_turtlebot4_bringup, "launch", "rplidar.launch.py"]
+ )
+ oakd_launch_file = PathJoinSubstitution([pkg_turtlebot4_bringup, "launch", "oakd.launch.py"])
description_launch_file = PathJoinSubstitution(
- [pkg_turtlebot4_description, 'launch', 'robot_description.launch.py']
+ [pkg_turtlebot4_description, "launch", "robot_description.launch.py"]
)
actions = [
- PushRosNamespace(namespace),
-
- IncludeLaunchDescription(
- PythonLaunchDescriptionSource([turtlebot4_robot_launch_file]),
- launch_arguments=[('model', 'lite'),
- ('param_file', namespaced_param_file)]),
-
- IncludeLaunchDescription(
- PythonLaunchDescriptionSource([joy_teleop_launch_file]),
- launch_arguments=[('namespace', namespace)]),
-
- IncludeLaunchDescription(
- PythonLaunchDescriptionSource([rplidar_launch_file])),
-
- # Delay the OAK-D startup for a bit
- # This prevents spiking the current on the USB by having the lidar and camera
- # start up at the same time as everything else
- TimerAction(
- period=30.0,
- actions=[
- IncludeLaunchDescription(
- PythonLaunchDescriptionSource([oakd_launch_file]),
- launch_arguments=[
- ('camera', 'oakd_lite'),
- ('namespace', namespace),
- ])
- ]
- ),
-
- IncludeLaunchDescription(
- PythonLaunchDescriptionSource([description_launch_file]),
- launch_arguments=[('model', 'lite')]),
- ]
-
- actions.append(IncludeLaunchDescription(
+ PushRosNamespace(namespace),
+ IncludeLaunchDescription(
+ PythonLaunchDescriptionSource([turtlebot4_robot_launch_file]),
+ launch_arguments=[("model", "lite"), ("param_file", namespaced_param_file)],
+ ),
+ IncludeLaunchDescription(
+ PythonLaunchDescriptionSource([joy_teleop_launch_file]),
+ launch_arguments=[("namespace", namespace)],
+ ),
+ IncludeLaunchDescription(PythonLaunchDescriptionSource([rplidar_launch_file])),
+ # Delay the OAK-D startup for a bit
+ # This prevents spiking the current on the USB by having the lidar and camera
+ # start up at the same time as everything else
+ TimerAction(
+ period=30.0,
+ actions=[
+ IncludeLaunchDescription(
+ PythonLaunchDescriptionSource([oakd_launch_file]),
+ launch_arguments=[
+ ("camera", "oakd_lite"),
+ ("namespace", namespace),
+ ],
+ )
+ ],
+ ),
+ IncludeLaunchDescription(
+ PythonLaunchDescriptionSource([description_launch_file]),
+ launch_arguments=[("model", "lite")],
+ ),
+ ]
+
+ actions.append(
+ IncludeLaunchDescription(
PythonLaunchDescriptionSource([diagnostics_launch_file]),
- launch_arguments=[('namespace', namespace)],
- condition=IfCondition(diagnostics_enable)))
+ launch_arguments=[("namespace", namespace)],
+ condition=IfCondition(diagnostics_enable),
+ )
+ )
turtlebot4_lite = GroupAction(actions)
ld = LaunchDescription()
ld.add_action(param_file_cmd)
ld.add_action(turtlebot4_lite)
- return ld
\ No newline at end of file
+ return ld
diff --git a/parser/tests/test_from_yaml.py b/parser/tests/test_from_yaml.py
index 6918783..b50d7da 100644
--- a/parser/tests/test_from_yaml.py
+++ b/parser/tests/test_from_yaml.py
@@ -13,66 +13,104 @@
# limitations under the License.
import pytest
-from parser.tests.test_helpers import load_yaml_tests, load_custom_handler_tests, parse_launch_string
+
+from parser.tests.test_helpers import (
+ load_custom_handler_tests,
+ load_yaml_tests,
+ parse_launch_string,
+)
+
@pytest.mark.parametrize("code,expected", load_yaml_tests("test_cases/node_tests.yaml"))
def test_node_parsing(code, expected):
result = parse_launch_string(code)
assert result.get("nodes") == expected.get("nodes")
+
@pytest.mark.parametrize("code,expected", load_yaml_tests("test_cases/launch_config_tests.yaml"))
def test_launch_configuration_parsing(code, expected):
result = parse_launch_string(code)
- for key in ["nodes", "arguments", "includes", "groups", "parameters",
- "launch_argument_usages", "undeclared_launch_configurations"]:
+ for key in [
+ "nodes",
+ "arguments",
+ "includes",
+ "groups",
+ "parameters",
+ "launch_argument_usages",
+ "undeclared_launch_configurations",
+ ]:
assert result.get(key, []) == expected.get(key, [])
+
@pytest.mark.parametrize("code,expected", load_yaml_tests("test_cases/group_action_tests.yaml"))
def test_group_action_parsing(code, expected):
result = parse_launch_string(code)
for key in ["nodes", "arguments", "includes", "groups", "launch_argument_usages"]:
assert result.get(key, []) == expected.get(key, [])
+
@pytest.mark.parametrize("code,expected", load_yaml_tests("test_cases/include_launch_tests.yaml"))
def test_include_launch_parsing(code, expected):
result = parse_launch_string(code)
- for key in ["arguments", "includes", "launch_argument_usages", "undeclared_launch_configurations"]:
+ for key in [
+ "arguments",
+ "includes",
+ "launch_argument_usages",
+ "undeclared_launch_configurations",
+ ]:
assert result.get(key, []) == expected.get(key, [])
+
@pytest.mark.parametrize("code,expected", load_yaml_tests("test_cases/opaque_function_tests.yaml"))
def test_opaque_functions_parsing(code, expected):
result = parse_launch_string(code)
- for key in ["arguments", "opaque_functions", "launch_argument_usages", "undeclared_launch_configurations"]:
+ for key in [
+ "arguments",
+ "opaque_functions",
+ "launch_argument_usages",
+ "undeclared_launch_configurations",
+ ]:
assert result.get(key, []) == expected.get(key, [])
+
@pytest.mark.parametrize("code,expected", load_yaml_tests("test_cases/condition_tests.yaml"))
def test_conditions_parsing(code, expected):
result = parse_launch_string(code)
- for key in ["arguments", "nodes", "groups", "launch_argument_usages", "undeclared_launch_configurations"]:
+ for key in [
+ "arguments",
+ "nodes",
+ "groups",
+ "launch_argument_usages",
+ "undeclared_launch_configurations",
+ ]:
assert result.get(key, []) == expected.get(key, [])
+
@pytest.mark.parametrize("code,expected", load_yaml_tests("test_cases/composable_node_tests.yaml"))
def test_composable_nodes_parsing(code, expected):
result = parse_launch_string(code)
- for key in ["arguments", "composable_nodes", "unattached_composable_nodes", "launch_argument_usages", "undeclared_launch_configurations"]:
+ for key in [
+ "arguments",
+ "composable_nodes",
+ "unattached_composable_nodes",
+ "launch_argument_usages",
+ "undeclared_launch_configurations",
+ ]:
assert result.get(key, []) == expected.get(key, [])
+
@pytest.mark.parametrize("code,expected", load_yaml_tests("test_cases/event_handler_tests.yaml"))
def test_event_handlers_parsing(code, expected):
result = parse_launch_string(code)
for key in ["nodes", "event_handlers"]:
assert result.get(key, []) == expected.get(key, [])
-@pytest.mark.parametrize("code,expected", load_custom_handler_tests("test_cases/custom_handlers_tests.yaml", "test_handlers"))
+
+@pytest.mark.parametrize(
+ "code,expected",
+ load_custom_handler_tests("test_cases/custom_handlers_tests.yaml", "test_handlers"),
+)
def test_custom_handlers_parsing(code, expected):
result = parse_launch_string(code)
for key in ["arguments", "nodes", "launch_argument_usages", "custom_components"]:
assert result.get(key, []) == expected.get(key, [])
-
-# @pytest.mark.parametrize("code,expected,files", load_yaml_tests("test_cases/recursive_include_tests.yaml", with_files=True))
-# def test_recursive_include_parsing(code, expected, files):
-# with mock_tempfile_and_fs(code, files):
-# result = parse_launch_string(code)
-
-# for key in ["nodes", "arguments", "includes", "groups", "launch_argument_usages"]:
-# assert result.get(key, []) == expected.get(key, [])
\ No newline at end of file
diff --git a/parser/tests/test_handlers/custom_launch_thing_handler.py b/parser/tests/test_handlers/custom_launch_thing_handler.py
index 6fdc0a1..eae4cca 100644
--- a/parser/tests/test_handlers/custom_launch_thing_handler.py
+++ b/parser/tests/test_handlers/custom_launch_thing_handler.py
@@ -14,12 +14,11 @@
from parser.parser.user_handler import register_user_handler
+
@register_user_handler("MyCustomLaunchThing")
def handle_custom_launch_thing(node, context):
return {
"type": "CustomHandler",
"type_name": "MyCustomLaunchThing",
- "metadata": {
- "info": "example"
- }
- }
\ No newline at end of file
+ "metadata": {"info": "example"},
+ }
diff --git a/parser/tests/test_handlers/my_launch_handler.py b/parser/tests/test_handlers/my_launch_handler.py
index f56a9e4..c057cbe 100644
--- a/parser/tests/test_handlers/my_launch_handler.py
+++ b/parser/tests/test_handlers/my_launch_handler.py
@@ -16,16 +16,15 @@
from parser.parser.user_handler import register_user_handler
from parser.resolution.utils import resolve_call_signature
+
@register_user_handler("MyLaunch")
def handle_my_launch(node, context):
args, _ = resolve_call_signature(node, context.engine)
if not args:
raise ValueError("MyLaunch must have a name.")
-
+
name = args[0]
- return simplify_launch_configurations({
- "type": "CustomHandler",
- "type_name": "MyLaunch",
- "name": name
- })
\ No newline at end of file
+ return simplify_launch_configurations(
+ {"type": "CustomHandler", "type_name": "MyLaunch", "name": name}
+ )
diff --git a/parser/tests/test_helpers.py b/parser/tests/test_helpers.py
index fe7699a..3ebbd8d 100644
--- a/parser/tests/test_helpers.py
+++ b/parser/tests/test_helpers.py
@@ -13,16 +13,18 @@
# limitations under the License.
import tempfile
-import yaml
+
import pytest
-from parser.entrypoint.parser_runner import parse_launch_file
+import yaml
+
from parser.entrypoint.user_interface import parse_and_format_launch_file
from parser.plugin_loader import load_user_handlers_from_directory
+
def load_yaml_tests(file_path):
- with open(file_path, 'r') as f:
+ with open(file_path, "r") as f:
data = yaml.safe_load(f)
-
+
tests = []
for test in data["tests"]:
code = test["input"]
@@ -32,12 +34,14 @@ def load_yaml_tests(file_path):
return tests
+
def load_custom_handler_tests(file_path, handler_directory):
load_user_handlers_from_directory(handler_directory)
return load_yaml_tests(file_path)
+
def parse_launch_string(code: str) -> dict:
with tempfile.NamedTemporaryFile("w", suffix=".py", delete=False) as tmp:
tmp.write(code)
tmp.flush()
- return parse_and_format_launch_file(tmp.name)
\ No newline at end of file
+ return parse_and_format_launch_file(tmp.name)
diff --git a/parser/tests/test_real_launch_files.py b/parser/tests/test_real_launch_files.py
index 0c527e7..6a9a841 100644
--- a/parser/tests/test_real_launch_files.py
+++ b/parser/tests/test_real_launch_files.py
@@ -12,20 +12,21 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-import os
import json
+import os
+
import pytest
-from parser.plugin_loader import load_user_handlers_from_directory
+
from parser.entrypoint.user_interface import parse_and_format_launch_file
+from parser.plugin_loader import load_user_handlers_from_directory
BASE_DIR = os.path.dirname(__file__)
INPUT_DIR = os.path.join(BASE_DIR, "real_cases/launch_files")
OUTPUT_DIR = os.path.join(BASE_DIR, "real_cases/expected_outputs")
PLUGIN_DIR = os.path.join(BASE_DIR, "real_cases/launch_files/custom_handlers")
-@pytest.mark.parametrize("filename", [
- f for f in os.listdir(INPUT_DIR) if f.endswith(".py")
-])
+
+@pytest.mark.parametrize("filename", [f for f in os.listdir(INPUT_DIR) if f.endswith(".py")])
def test_real_launch_file_snapshot(filename):
input_path = os.path.join(INPUT_DIR, filename)
output_path = os.path.join(OUTPUT_DIR, f"{filename}.json")
@@ -37,4 +38,3 @@ def test_real_launch_file_snapshot(filename):
expected = json.load(open(output_path))
assert result == expected, f"Mismatch in output for {filename}"
-
diff --git a/parser/tests/test_utils.py b/parser/tests/test_utils.py
index e0f6b8d..d569b6f 100644
--- a/parser/tests/test_utils.py
+++ b/parser/tests/test_utils.py
@@ -13,10 +13,9 @@
# limitations under the License.
import io
-import os
-import tempfile
-from unittest.mock import patch, mock_open, MagicMock
from contextlib import contextmanager
+from unittest.mock import patch
+
@contextmanager
def mock_tempfile_and_fs(code: str, files: dict):
@@ -38,7 +37,9 @@ def fake_file_loader(path, *args, **kwargs):
return io.StringIO(content)
raise FileNotFoundError(f"No such file: {path}")
- with patch("tempfile.NamedTemporaryFile", side_effect=fake_named_tempfile), \
- patch("builtins.open", side_effect=fake_file_loader), \
- patch("os.path.exists", side_effect=lambda p: any(p.endswith(name) for name in files)):
- yield
\ No newline at end of file
+ with (
+ patch("tempfile.NamedTemporaryFile", side_effect=fake_named_tempfile),
+ patch("builtins.open", side_effect=fake_file_loader),
+ patch("os.path.exists", side_effect=lambda p: any(p.endswith(name) for name in files)),
+ ):
+ yield
diff --git a/setup.sh b/setup.sh
new file mode 100755
index 0000000..e164184
--- /dev/null
+++ b/setup.sh
@@ -0,0 +1,62 @@
+#!/usr/bin/env bash
+set -e
+
+echo "๐ Starting environment setup..."
+
+# Install Python
+if ! command -v python3 &>/dev/null; then
+ echo "โ ๏ธ Python3 not found. Please install Python 3.8+ manually."
+ exit 1
+fi
+echo "โ
Python version: $(python3 --version)"
+
+# Install Node.js + npm
+if ! command -v npm &>/dev/null; then
+ if [[ "$OSTYPE" == "linux-gnu"* ]]; then
+ echo "โ ๏ธ npm not found. Installing Node.js..."
+ curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
+ sudo apt-get install -y nodejs
+ elif [[ "$OSTYPE" == "darwin"* ]]; then
+ brew install node
+ else
+ echo "โ Unsupported OS for automatic Node.js install"
+ exit 1
+ fi
+fi
+echo "โ
Node.js version: $(node -v)"
+echo "โ
npm version: $(npm -v)"
+
+# Create virtual environment
+if [ ! -d ".venv" ]; then
+ python3 -m venv .venv
+ echo "โ
Created virtual environment in .venv"
+fi
+
+# Activate virtual environment
+source .venv/bin/activate
+echo "โ
Activated virtual environment"
+
+# Install Python Dependencies
+if [ -f "requirements.txt" ]; then
+ pip install --upgrade pip
+ pip install -r requirements.txt
+ echo "โ
Installed Python dependencies"
+else
+ echo "โ ๏ธ No requirements.txt found, skipping Python deps install"
+fi
+
+# Install npm Dependencies
+if [ -f "package.json" ]; then
+ npm install
+ echo "โ
Installed npm dependencies"
+else
+ echo "โ ๏ธ No package.json found, skipping npm deps install"
+fi
+
+# Install Playwright & browser dependencies
+echo "๐ฆ Installing Playwright..."
+npm install -D playwright
+npx playwright install --with-deps
+echo "โ
Playwright installed with dependencies"
+
+echo "๐ Setup complete!"
\ No newline at end of file
diff --git a/src/commands/exportJson.ts b/src/commands/exportJson.ts
index d0dd0b0..b2a8bf1 100644
--- a/src/commands/exportJson.ts
+++ b/src/commands/exportJson.ts
@@ -17,47 +17,47 @@ import { runPythonParser } from '../python/runParser';
import { getLastParsedData, setLastParsedData } from './openVisualizer';
export function registerExportJson(context: vscode.ExtensionContext) {
- context.subscriptions.push(
- vscode.commands.registerCommand('launchmap.exportAsJson', async (): Promise => {
- let graphDataToExport = null;
+ context.subscriptions.push(
+ vscode.commands.registerCommand('launchmap.exportAsJson', async (): Promise => {
+ let graphDataToExport = null;
- const editor = vscode.window.activeTextEditor;
+ const editor = vscode.window.activeTextEditor;
- if (editor) {
- const filePath = editor.document.fileName;
- try {
- const result = await runPythonParser(filePath);
- graphDataToExport = JSON.parse(result);
- setLastParsedData(graphDataToExport);
- } catch (error) {
- vscode.window.showErrorMessage("Failed to parse the launch file.");
- return null;
- }
- } else {
- graphDataToExport = getLastParsedData();
- if (!graphDataToExport) {
- vscode.window.showErrorMessage("No active editor and no graph data available to export.");
- return null;
- }
- }
+ if (editor) {
+ const filePath = editor.document.fileName;
+ try {
+ const result = await runPythonParser(filePath);
+ graphDataToExport = JSON.parse(result);
+ setLastParsedData(graphDataToExport);
+ } catch {
+ vscode.window.showErrorMessage('Failed to parse the launch file.');
+ return null;
+ }
+ } else {
+ graphDataToExport = getLastParsedData();
+ if (!graphDataToExport) {
+ vscode.window.showErrorMessage('No active editor and no graph data available to export.');
+ return null;
+ }
+ }
- const uri = await vscode.window.showSaveDialog({
- filters: { 'JSON': ['json'] },
- defaultUri: vscode.Uri.file('launch_graph.json'),
- saveLabel: 'Export Launch Graph'
- });
+ const uri = await vscode.window.showSaveDialog({
+ filters: { 'JSON': ['json'] },
+ defaultUri: vscode.Uri.file('launch_graph.json'),
+ saveLabel: 'Export Launch Graph'
+ });
- if (!uri) return null;
+ if (!uri) return null;
- const jsonString = JSON.stringify(graphDataToExport, null, 2);
- try {
- await vscode.workspace.fs.writeFile(uri, Buffer.from(jsonString, 'utf8'));
- vscode.window.showInformationMessage(`Launch graph exported to ${uri.fsPath}`);
- return uri.fsPath;
- } catch (error) {
- vscode.window.showErrorMessage("Failed to save JSON: " + (error as Error).message);
- return null;
- }
- })
- );
-}
\ No newline at end of file
+ const jsonString = JSON.stringify(graphDataToExport, null, 2);
+ try {
+ await vscode.workspace.fs.writeFile(uri, Buffer.from(jsonString, 'utf8'));
+ vscode.window.showInformationMessage(`Launch graph exported to ${uri.fsPath}`);
+ return uri.fsPath;
+ } catch (error) {
+ vscode.window.showErrorMessage('Failed to save JSON: ' + (error as Error).message);
+ return null;
+ }
+ })
+ );
+}
diff --git a/src/commands/importJson.ts b/src/commands/importJson.ts
index 2bbd547..bb4072c 100644
--- a/src/commands/importJson.ts
+++ b/src/commands/importJson.ts
@@ -18,28 +18,28 @@ import { createVisualizerPanel } from '../panel/createVisualizerPanel';
import { setLastParsedData } from './openVisualizer';
export function registerImportJson(context: vscode.ExtensionContext) {
- context.subscriptions.push(
- vscode.commands.registerCommand('launchmap.importJson', async () => {
- const fileUris = await vscode.window.showOpenDialog({
- canSelectMany: false,
- filters: { 'JSON': ['json'] },
- openLabel: 'Import Launch Graph JSON'
- });
+ context.subscriptions.push(
+ vscode.commands.registerCommand('launchmap.importJson', async () => {
+ const fileUris = await vscode.window.showOpenDialog({
+ canSelectMany: false,
+ filters: { 'JSON': ['json'] },
+ openLabel: 'Import Launch Graph JSON'
+ });
- if (!fileUris || fileUris.length === 0) return;
+ if (!fileUris || fileUris.length === 0) return;
- try {
- const importedName = path.basename(fileUris[0].fsPath);
+ try {
+ const importedName = path.basename(fileUris[0].fsPath);
- const contentBytes = await vscode.workspace.fs.readFile(fileUris[0]);
- const content = Buffer.from(contentBytes).toString('utf8');
- const parsed = JSON.parse(content);
+ const contentBytes = await vscode.workspace.fs.readFile(fileUris[0]);
+ const content = Buffer.from(contentBytes).toString('utf8');
+ const parsed = JSON.parse(content);
- setLastParsedData(parsed);
- createVisualizerPanel(context, parsed, importedName);
- } catch (error) {
- vscode.window.showErrorMessage("Failed to load or parse the JSON file.");
- }
- })
- );
-}
\ No newline at end of file
+ setLastParsedData(parsed);
+ createVisualizerPanel(context, parsed, importedName);
+ } catch {
+ vscode.window.showErrorMessage('Failed to load or parse the JSON file.');
+ }
+ })
+ );
+}
diff --git a/src/commands/openVisualizer.ts b/src/commands/openVisualizer.ts
index 0ebe900..25081f3 100644
--- a/src/commands/openVisualizer.ts
+++ b/src/commands/openVisualizer.ts
@@ -17,31 +17,31 @@ import * as path from 'path';
import { runPythonParser } from '../python/runParser';
import { createVisualizerPanel } from '../panel/createVisualizerPanel';
-let lastParsedData: any = null;
+let lastParsedData: null = null;
export function registerOpenVisualizer(context: vscode.ExtensionContext) {
- context.subscriptions.push(
- vscode.commands.registerCommand('launchmap.openVisualizer', async() => {
- const editor = vscode.window.activeTextEditor;
- if (!editor) {
- vscode.window.showErrorMessage("No active editor with a launch file.");
- return;
- }
+ context.subscriptions.push(
+ vscode.commands.registerCommand('launchmap.openVisualizer', async() => {
+ const editor = vscode.window.activeTextEditor;
+ if (!editor) {
+ vscode.window.showErrorMessage('No active editor with a launch file.');
+ return;
+ }
- const filePath = editor.document.fileName;
- const fileName = path.basename(filePath);
- const result = await runPythonParser(filePath);
+ const filePath = editor.document.fileName;
+ const fileName = path.basename(filePath);
+ const result = await runPythonParser(filePath);
- lastParsedData = JSON.parse(result);
- createVisualizerPanel(context, lastParsedData, fileName);
- })
- );
+ lastParsedData = JSON.parse(result);
+ createVisualizerPanel(context, lastParsedData, fileName);
+ })
+ );
}
export function getLastParsedData() {
- return lastParsedData;
+ return lastParsedData;
}
-export function setLastParsedData(data: any) {
- lastParsedData = data;
-}
\ No newline at end of file
+export function setLastParsedData(data: null) {
+ lastParsedData = data;
+}
diff --git a/src/commands/setPluginDir.ts b/src/commands/setPluginDir.ts
index 42406de..5091e02 100644
--- a/src/commands/setPluginDir.ts
+++ b/src/commands/setPluginDir.ts
@@ -17,20 +17,20 @@ import { setPluginDir } from '../utils/launchmapConfig';
import { updatePluginDirStatusBar } from '../ui/pluginDirStatusBar';
export function registerSetPluginDir(context: vscode.ExtensionContext) {
- context.subscriptions.push(
- vscode.commands.registerCommand('launchmap.setPluginDir', async () => {
- const selected = await vscode.window.showOpenDialog({
- canSelectFiles: false,
- canSelectFolders: true,
- canSelectMany: false,
- openLabel: 'Select Plugin Directory'
- });
+ context.subscriptions.push(
+ vscode.commands.registerCommand('launchmap.setPluginDir', async () => {
+ const selected = await vscode.window.showOpenDialog({
+ canSelectFiles: false,
+ canSelectFolders: true,
+ canSelectMany: false,
+ openLabel: 'Select Plugin Directory'
+ });
- if (!selected || selected.length === 0) return;
+ if (!selected || selected.length === 0) return;
- const pluginPath = selected[0].fsPath;
- await setPluginDir(pluginPath);
- await updatePluginDirStatusBar();
- })
- );
-}
\ No newline at end of file
+ const pluginPath = selected[0].fsPath;
+ await setPluginDir(pluginPath);
+ await updatePluginDirStatusBar();
+ })
+ );
+}
diff --git a/src/extension.ts b/src/extension.ts
index 1e740e2..c016741 100644
--- a/src/extension.ts
+++ b/src/extension.ts
@@ -20,10 +20,10 @@ import { registerSetPluginDir } from './commands/setPluginDir';
import { initPluginDirStatusBar } from './ui/pluginDirStatusBar';
export function activate(context: vscode.ExtensionContext) {
- registerOpenVisualizer(context);
- registerExportJson(context);
- registerImportJson(context);
- registerSetPluginDir(context);
+ registerOpenVisualizer(context);
+ registerExportJson(context);
+ registerImportJson(context);
+ registerSetPluginDir(context);
- initPluginDirStatusBar();
-}
\ No newline at end of file
+ initPluginDirStatusBar();
+}
diff --git a/src/panel/createVisualizerPanel.ts b/src/panel/createVisualizerPanel.ts
index c25b7d2..6046b58 100644
--- a/src/panel/createVisualizerPanel.ts
+++ b/src/panel/createVisualizerPanel.ts
@@ -16,41 +16,41 @@ import * as vscode from 'vscode';
import { getWebviewHtml } from './getWebviewHtml';
export function createVisualizerPanel(
- context: vscode.ExtensionContext,
- data: any,
- titleHint?: string
+ context: vscode.ExtensionContext,
+ data: null,
+ titleHint?: string
): vscode.WebviewPanel {
- const panelTitle = titleHint
- ? `${titleHint} : ROS2 LaunchMap Visualizer`
- : 'ROS2 LaunchMap Visualizer'
+ const panelTitle = titleHint
+ ? `${titleHint} : ROS2 LaunchMap Visualizer`
+ : 'ROS2 LaunchMap Visualizer';
- const panel = vscode.window.createWebviewPanel(
- 'launchmap',
- panelTitle,
- vscode.ViewColumn.One,
- {
- enableScripts: true,
- retainContextWhenHidden: true
- }
- );
+ const panel = vscode.window.createWebviewPanel(
+ 'launchmap',
+ panelTitle,
+ vscode.ViewColumn.One,
+ {
+ enableScripts: true,
+ retainContextWhenHidden: true
+ }
+ );
- panel.webview.html = getWebviewHtml(panel.webview, context.extensionUri);
- panel.webview.postMessage({ type: 'launchmap-data', data });
+ panel.webview.html = getWebviewHtml(panel.webview, context.extensionUri);
+ panel.webview.postMessage({ type: 'launchmap-data', data });
- panel.webview.onDidReceiveMessage(async (message) => {
- if (message.type === 'export-json') {
- const result = await vscode.commands.executeCommand('launchmap.exportAsJson');
- if (result) {
- panel.webview.postMessage({ type: 'export-complete', path: result });
- }
- }
- });
+ panel.webview.onDidReceiveMessage(async (message) => {
+ if (message.type === 'export-json') {
+ const result = await vscode.commands.executeCommand('launchmap.exportAsJson');
+ if (result) {
+ panel.webview.postMessage({ type: 'export-complete', path: result });
+ }
+ }
+ });
- panel.onDidChangeViewState((e) => {
- if (e.webviewPanel.visible) {
- panel.webview.postMessage({ type: 'launchmap-data', data });
- }
- });
+ panel.onDidChangeViewState((e) => {
+ if (e.webviewPanel.visible) {
+ panel.webview.postMessage({ type: 'launchmap-data', data });
+ }
+ });
- return panel;
-}
\ No newline at end of file
+ return panel;
+}
diff --git a/src/panel/getWebviewHtml.ts b/src/panel/getWebviewHtml.ts
index 48ed8fb..031a69a 100644
--- a/src/panel/getWebviewHtml.ts
+++ b/src/panel/getWebviewHtml.ts
@@ -15,10 +15,10 @@
import * as vscode from 'vscode';
export function getWebviewHtml(webview: vscode.Webview, extensionUri: vscode.Uri): string {
- const scriptUri = webview.asWebviewUri(vscode.Uri.joinPath(extensionUri, 'webview', 'script.js'));
- const styleUri = webview.asWebviewUri(vscode.Uri.joinPath(extensionUri, 'webview', 'style.css'));
+ const scriptUri = webview.asWebviewUri(vscode.Uri.joinPath(extensionUri, 'webview', 'script.js'));
+ const styleUri = webview.asWebviewUri(vscode.Uri.joinPath(extensionUri, 'webview', 'style.css'));
- return `
+ return `
@@ -33,5 +33,5 @@ export function getWebviewHtml(webview: vscode.Webview, extensionUri: vscode.Uri
- `
-}
\ No newline at end of file
+ `;
+}
diff --git a/src/python/runParser.ts b/src/python/runParser.ts
index f12fff8..53183b2 100644
--- a/src/python/runParser.ts
+++ b/src/python/runParser.ts
@@ -19,44 +19,45 @@ import * as which from 'which';
import { getPluginDir } from '../utils/launchmapConfig';
export async function runPythonParser(filePath: string): Promise {
- return new Promise(async (resolve, reject) => {
- const pythonCmd = detectPythonCommand();
+ // eslint-disable-next-line no-async-promise-executor
+ return new Promise(async (resolve, reject) => {
+ const pythonCmd = detectPythonCommand();
- if (!pythonCmd) {
- vscode.window.showErrorMessage(
- "Python iterpreter not found. Please install Python 3 and make sure it is available in your PATH."
- );
- return reject(new Error("No Python interpreter found."));
- }
+ if (!pythonCmd) {
+ vscode.window.showErrorMessage(
+ 'Python iterpreter not found. Please install Python 3 and make sure it is available in your PATH.'
+ );
+ return reject(new Error('No Python interpreter found.'));
+ }
- const scriptPath = path.join(__dirname, '..', '..', 'parse.py');
- const cmdParts = [`"${pythonCmd}"`, `"${scriptPath}"`, `"${filePath}"`];
+ const scriptPath = path.join(__dirname, '..', '..', 'parse.py');
+ const cmdParts = [`"${pythonCmd}"`, `"${scriptPath}"`, `"${filePath}"`];
- const pluginDir = await getPluginDir();
- if (pluginDir) {
- cmdParts.push('--plugin-dir', `"${pluginDir}"`);
- }
+ const pluginDir = await getPluginDir();
+ if (pluginDir) {
+ cmdParts.push('--plugin-dir', `"${pluginDir}"`);
+ }
- const cmd = cmdParts.join(' ');
- cp.exec(cmd, (err, stdout, stderr) => {
- if (err) {
- vscode.window.showErrorMessage("Parser error: " + stderr);
- return reject(err);
- }
- resolve(stdout);
- });
+ const cmd = cmdParts.join(' ');
+ cp.exec(cmd, (err, stdout, stderr) => {
+ if (err) {
+ vscode.window.showErrorMessage('Parser error: ' + stderr);
+ return reject(err);
+ }
+ resolve(stdout);
});
+ });
}
function detectPythonCommand(): string | null {
- const candidates = ['python3', 'python', 'py'];
- for (const cmd of candidates) {
- try {
- which.sync(cmd);
- return cmd;
- } catch (_) {
- continue;
- }
+ const candidates = ['python3', 'python', 'py'];
+ for (const cmd of candidates) {
+ try {
+ which.sync(cmd);
+ return cmd;
+ } catch {
+ continue;
}
- return null;
-}
\ No newline at end of file
+ }
+ return null;
+}
diff --git a/src/test/e2e/index.ts b/src/test/e2e/index.ts
index 6c4b49c..52e1149 100644
--- a/src/test/e2e/index.ts
+++ b/src/test/e2e/index.ts
@@ -16,22 +16,22 @@ import * as path from 'path';
import Mocha from 'mocha';
export async function run(): Promise {
- const mocha = new Mocha({ ui: 'tdd', timeout: 10000 });
-
- mocha.addFile(path.resolve(__dirname, 'workflow.test'));
+ const mocha = new Mocha({ ui: 'tdd', timeout: 10000 });
- await new Promise((resolve, reject) => {
- mocha.run(failures => {
- if (failures > 0) {
- console.error(`${failures} tests failed.`);
- reject(new Error("Test failures"));
- } else {
- console.log('โ
All tests passed.');
- resolve();
- }
- });
+ mocha.addFile(path.resolve(__dirname, 'workflow.test'));
+
+ await new Promise((resolve, reject) => {
+ mocha.run(failures => {
+ if (failures > 0) {
+ console.error(`${failures} tests failed.`);
+ reject(new Error('Test failures'));
+ } else {
+ console.log('โ
All tests passed.');
+ resolve();
+ }
});
+ });
- console.log("All tests done, exiting...");
- process.exit(0);
-}
\ No newline at end of file
+ console.log('All tests done, exiting...');
+ process.exit(0);
+}
diff --git a/src/test/e2e/workflow.test.ts b/src/test/e2e/workflow.test.ts
index 6774ec4..7d9f1f0 100644
--- a/src/test/e2e/workflow.test.ts
+++ b/src/test/e2e/workflow.test.ts
@@ -23,87 +23,89 @@ const mkdtemp = promisify(fs.mkdtemp);
const rm = promisify(fs.rm || fs.rmdir);
suite('End-to-End Workflow Test', () => {
- let tempDir: string;
- let exportPath: string;
- const fixturePath = path.resolve(__dirname, '../../../src/test/fixtures/test.launch.py');
-
- suiteSetup(async () => {
- // Create a temp directory for export
- tempDir = await mkdtemp(path.join(os.tmpdir(), 'launchmap-test-'));
- exportPath = path.join(tempDir, 'launch_graph.json');
+ let tempDir: string;
+ let exportPath: string;
+ const fixturePath = path.resolve(__dirname, '../../../src/test/fixtures/test.launch.py');
+
+ suiteSetup(async () => {
+ // Create a temp directory for export
+ tempDir = await mkdtemp(path.join(os.tmpdir(), 'launchmap-test-'));
+ exportPath = path.join(tempDir, 'launch_graph.json');
+ });
+
+ test('1๏ธโฃ Open Visualizer โ Panel created', async () => {
+ const doc = await vscode.workspace.openTextDocument(fixturePath);
+ await vscode.window.showTextDocument(doc);
+
+ await vscode.commands.executeCommand('launchmap.openVisualizer');
+ assert.ok(true, 'Visualizer command executed without error');
+ });
+
+ test('2๏ธโฃ Export as JSON โ File saved', async () => {
+ // Mock save dialog to return temp file path
+ vscode.window.showSaveDialog = async () => vscode.Uri.file(exportPath);
+
+ await vscode.commands.executeCommand('launchmap.exportAsJson');
+ const exists = fs.existsSync(exportPath);
+ assert.ok(exists, 'Expected exported JSON file to exist');
+ });
+
+ test('3๏ธโฃ Import JSON โ Panel created again', async () => {
+ // Mock open dialog to return exported file path
+ vscode.window.showOpenDialog = async () => [vscode.Uri.file(exportPath)];
+
+ await vscode.commands.executeCommand('launchmap.importJson');
+ assert.ok(true, 'Import command executed without error');
+ });
+
+ test('4๏ธโฃ Set Plugin Dir via command โ Stored in .launchmap file', async () => {
+ const pluginDir = path.join(tempDir, 'plugins');
+ await fs.promises.mkdir(pluginDir, { recursive: true });
+ const configPath = path.join(tempDir, '.launchmap');
+
+ // Stub workspace folder to point to temp directory
+ Object.defineProperty(vscode.workspace, 'workspaceFolders', {
+ get: () => [{ uri: vscode.Uri.file(tempDir) }],
+ configurable: true
});
- test('1๏ธโฃ Open Visualizer โ Panel created', async () => {
- const doc = await vscode.workspace.openTextDocument(fixturePath);
- await vscode.window.showTextDocument(doc);
+ vscode.window.showOpenDialog = async () => [vscode.Uri.file(pluginDir)];
- await vscode.commands.executeCommand('launchmap.openVisualizer');
- assert.ok(true, 'Visualizer command executed without error');
- });
-
- test('2๏ธโฃ Export as JSON โ File saved', async () => {
- // Mock save dialog to return temp file path
- vscode.window.showSaveDialog = async () => vscode.Uri.file(exportPath);
-
- await vscode.commands.executeCommand('launchmap.exportAsJson');
- const exists = fs.existsSync(exportPath);
- assert.ok(exists, 'Expected exported JSON file to exist');
- });
-
- test('3๏ธโฃ Import JSON โ Panel created again', async () => {
- // Mock open dialog to return exported file path
- vscode.window.showOpenDialog = async () => [vscode.Uri.file(exportPath)];
-
- await vscode.commands.executeCommand('launchmap.importJson');
- assert.ok(true, 'Import command executed without error');
- });
+ await vscode.commands.executeCommand('launchmap.setPluginDir');
- test('4๏ธโฃ Set Plugin Dir via command โ Stored in .launchmap file', async () => {
- const workspaceDir = path.join(tempDir, 'workspace');
- await fs.promises.mkdir(workspaceDir, { recursive: true });
+ const configContent = await fs.promises.readFile(configPath, 'utf8');
+ const parsed = JSON.parse(configContent);
- vscode.workspace.updateWorkspaceFolders(0, 0, { uri: vscode.Uri.file(workspaceDir) });
+ assert.strictEqual(parsed.pluginDir, pluginDir, 'Expected pluginDir to be set correctly in .launchmap');
+ });
- const pluginDir = path.join(workspaceDir, 'plugins');
- await fs.promises.mkdir(pluginDir, { recursive: true });
- const configPath = path.join(workspaceDir, '.launchmap');
+ test('5๏ธโฃ Plugin Dir via .launchmap โ Used in parser call', async () => {
+ const pluginDir = path.join(tempDir, 'plugins');
+ await fs.promises.mkdir(pluginDir, { recursive: true });
+ await fs.promises.writeFile(path.join(pluginDir, 'dummpy.py'), '# dummy plugin');
- vscode.window.showOpenDialog = async () => [vscode.Uri.file(pluginDir)];
+ const configPath = path.join(tempDir, '.launchmap');
+ const config = { pluginDir };
+ await fs.promises.writeFile(configPath, JSON.stringify(config, null, 2));
- await vscode.commands.executeCommand('launchmap.setPluginDir');
-
- const configContent = await fs.promises.readFile(configPath, 'utf8');
- const parsed = JSON.parse(configContent);
-
- assert.strictEqual(parsed.pluginDir, pluginDir, 'Expected pluginDir to be set correctly in .launchmap');
+ // Stub workspace folder to point to temp directory
+ Object.defineProperty(vscode.workspace, 'workspaceFolders', {
+ get: () => [{ uri: vscode.Uri.file(tempDir) }],
+ configurable: true,
});
- test('5๏ธโฃ Plugin Dir via .launchmap โ Used in parser call', async () => {
- const workspaceDir = path.join(tempDir, 'workspace');
- await fs.promises.mkdir(workspaceDir, { recursive: true });
-
- vscode.workspace.updateWorkspaceFolders(0, 0, { uri: vscode.Uri.file(workspaceDir) });
-
- const pluginDir = path.join(workspaceDir, 'plugins');
- await fs.promises.mkdir(pluginDir, { recursive: true });
- await fs.promises.writeFile(path.join(pluginDir, 'dummpy.py'), '# dummy plugin');
-
- const configPath = path.join(workspaceDir, '.launchmap');
- const config = { pluginDir };
- await fs.promises.writeFile(configPath, JSON.stringify(config, null, 2));
+ const doc = await vscode.workspace.openTextDocument(fixturePath);
+ await vscode.window.showTextDocument(doc);
- const doc = await vscode.workspace.openTextDocument(fixturePath);
- await vscode.window.showTextDocument(doc);
+ await vscode.commands.executeCommand('launchmap.openVisualizer');
- await vscode.commands.executeCommand('launchmap.openVisualizer');
+ assert.ok(true, 'Visualizer executed with .launchmap pluginDir present');
- assert.ok(true, 'Visualizer executed with .launchmap pluginDir present');
+ await fs.promises.unlink(configPath);
+ });
- await fs.promises.unlink(configPath);
- });
-
- suiteTeardown(async () => {
- // Clean up temp directory after all tests
- await rm(tempDir, { recursive: true, force: true });
- });
-});
\ No newline at end of file
+ suiteTeardown(async () => {
+ // Clean up temp directory after all tests
+ await rm(tempDir, { recursive: true, force: true });
+ });
+});
diff --git a/src/test/fixtures/test.launch.py b/src/test/fixtures/test.launch.py
index 387989a..b4efa2f 100644
--- a/src/test/fixtures/test.launch.py
+++ b/src/test/fixtures/test.launch.py
@@ -15,7 +15,8 @@
from launch import LaunchDescription
from launch_ros.actions import Node
+
def generate_launch_description():
- return LaunchDescription([
- Node(package='demo_nodes_cpp', executable='talker', name='talker')
- ])
\ No newline at end of file
+ return LaunchDescription(
+ [Node(package="demo_nodes_cpp", executable="talker", name="talker")]
+ )
diff --git a/src/test/runTest.ts b/src/test/runTest.ts
index 93ed7ad..92cf1cd 100644
--- a/src/test/runTest.ts
+++ b/src/test/runTest.ts
@@ -16,16 +16,16 @@ import * as path from 'path';
import { runTests } from '@vscode/test-electron';
async function main() {
- try {
- await runTests({
- extensionDevelopmentPath: path.resolve(__dirname, '../../'),
- extensionTestsPath: path.resolve(__dirname, './e2e/index'),
- launchArgs: ['--disable-extensions', '--log-level=error']
- });
- } catch (err) {
- console.error('Failed to run tests', err);
- process.exit(1);
- }
+ try {
+ await runTests({
+ extensionDevelopmentPath: path.resolve(__dirname, '../../'),
+ extensionTestsPath: path.resolve(__dirname, './e2e/index'),
+ launchArgs: ['--disable-extensions', '--log-level=error']
+ });
+ } catch (err) {
+ console.error('Failed to run tests', err);
+ process.exit(1);
+ }
}
-main();
\ No newline at end of file
+main();
diff --git a/src/ui/pluginDirStatusBar.ts b/src/ui/pluginDirStatusBar.ts
index ee5cf87..0793209 100644
--- a/src/ui/pluginDirStatusBar.ts
+++ b/src/ui/pluginDirStatusBar.ts
@@ -19,23 +19,23 @@ import * as path from 'path';
let statusBarItem: vscode.StatusBarItem | undefined;
export async function initPluginDirStatusBar() {
- if (!statusBarItem) {
- statusBarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left, 100);
- statusBarItem.command = 'launchmap.setPluginDir';
- statusBarItem.tooltip = 'LaunchMap Plugin Directory';
- }
+ if (!statusBarItem) {
+ statusBarItem = vscode.window.createStatusBarItem(vscode.StatusBarAlignment.Left, 100);
+ statusBarItem.command = 'launchmap.setPluginDir';
+ statusBarItem.tooltip = 'LaunchMap Plugin Directory';
+ }
- await updatePluginDirStatusBar();
- statusBarItem.show();
+ await updatePluginDirStatusBar();
+ statusBarItem.show();
}
export async function updatePluginDirStatusBar() {
- const pluginDir = await getPluginDir();
+ const pluginDir = await getPluginDir();
- if (pluginDir) {
- const label = path.basename(pluginDir);
+ if (pluginDir) {
+ const label = path.basename(pluginDir);
statusBarItem!.text = `๐ฆ Plugin: ${label}`;
- } else {
+ } else {
statusBarItem!.text = '๐ฆ No Plugin Dir';
- }
-}
\ No newline at end of file
+ }
+}
diff --git a/src/utils/launchmapConfig.ts b/src/utils/launchmapConfig.ts
index 9b7f011..ffbfeaa 100644
--- a/src/utils/launchmapConfig.ts
+++ b/src/utils/launchmapConfig.ts
@@ -19,30 +19,30 @@ import * as fs from 'fs/promises';
const CONFIG_FILENAME = '.launchmap';
export async function getPluginDir(): Promise {
- const workspaceFolder = vscode.workspace.workspaceFolders?.[0];
- if (!workspaceFolder) return null;
-
- const configPath = path.join(workspaceFolder.uri.fsPath, CONFIG_FILENAME);
-
- try {
- const content = await fs.readFile(configPath, 'utf-8');
- const json = JSON.parse(content);
- return json.pluginDir || null;
- } catch {
- return null;
- }
+ const workspaceFolder = vscode.workspace.workspaceFolders?.[0];
+ if (!workspaceFolder) return null;
+
+ const configPath = path.join(workspaceFolder.uri.fsPath, CONFIG_FILENAME);
+
+ try {
+ const content = await fs.readFile(configPath, 'utf-8');
+ const json = JSON.parse(content);
+ return json.pluginDir || null;
+ } catch {
+ return null;
+ }
}
export async function setPluginDir(pluginDir: string): Promise {
- const workspaceFolder = vscode.workspace.workspaceFolders?.[0];
- if (!workspaceFolder) {
- vscode.window.showErrorMessage("No workspace folder found to save plugin directory.");
- return;
- }
-
- const configPath = path.join(workspaceFolder.uri.fsPath, CONFIG_FILENAME);
- const config = { pluginDir };
-
- await fs.writeFile(configPath, JSON.stringify(config, null, 2), 'utf-8');
- vscode.window.showInformationMessage(`Plugin directory saved to ${CONFIG_FILENAME}`);
-}
\ No newline at end of file
+ const workspaceFolder = vscode.workspace.workspaceFolders?.[0];
+ if (!workspaceFolder) {
+ vscode.window.showErrorMessage('No workspace folder found to save plugin directory.');
+ return;
+ }
+
+ const configPath = path.join(workspaceFolder.uri.fsPath, CONFIG_FILENAME);
+ const config = { pluginDir };
+
+ await fs.writeFile(configPath, JSON.stringify(config, null, 2), 'utf-8');
+ vscode.window.showInformationMessage(`Plugin directory saved to ${CONFIG_FILENAME}`);
+}
diff --git a/webview/components/renderArguments.js b/webview/components/renderArguments.js
index f6f94f4..c7f6419 100644
--- a/webview/components/renderArguments.js
+++ b/webview/components/renderArguments.js
@@ -16,26 +16,26 @@ import { renderBaseBlock } from './renderBaseBlock.js';
import { renderSection } from './renderSection.js';
export function renderArguments(container, argumentsList, options) {
- if (!argumentsList || argumentsList.length === 0) return;
+ if (!argumentsList || argumentsList.length === 0) return;
- argumentsList.forEach((arg, idx) => {
- const path = `${options.pathPrefix || "arguments"}[${idx}]`;
- const block = renderArgument(arg, { ...options, path });
+ argumentsList.forEach((arg, idx) => {
+ const path = `${options.pathPrefix || 'arguments'}[${idx}]`;
+ const block = renderArgument(arg, { ...options, path });
- container.appendChild(block);
- options.renderBlock(block, "argument");
- });
+ container.appendChild(block);
+ options.renderBlock(block, 'argument');
+ });
}
export function renderArgument(arg, options) {
- const block = renderBaseBlock({ type: "argument", options });
+ const block = renderBaseBlock({ type: 'argument', options });
- const value = arg.default_value !== undefined ? arg.default_value : "";
- const argSection = renderSection("argument", "๐", arg.name, value,
- { includeRightPort: true, portIdPrefix: `argument:${arg.name}`, portRegistry: options.portRegistry });
- block.appendChild(argSection);
+ const value = arg.default_value !== undefined ? arg.default_value : '';
+ const argSection = renderSection('argument', '๐', arg.name, value,
+ { includeRightPort: true, portIdPrefix: `argument:${arg.name}`, portRegistry: options.portRegistry });
+ block.appendChild(argSection);
- block.dataset.argument = arg.name;
+ block.dataset.argument = arg.name;
- return block;
-}
\ No newline at end of file
+ return block;
+}
diff --git a/webview/components/renderAutoResizableBody.js b/webview/components/renderAutoResizableBody.js
index 03102f4..3ccbe6c 100644
--- a/webview/components/renderAutoResizableBody.js
+++ b/webview/components/renderAutoResizableBody.js
@@ -12,43 +12,43 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-export function renderAutoResizableBody(container, type = "block", fixedSelectors = []) {
- requestAnimationFrame(() => {
- let offsetY = 0;
-
- if (fixedSelectors.length > 0) {
- fixedSelectors.forEach(sel => {
- const header = container.querySelector(sel);
- if (header) {
- const height = header.getBoundingClientRect().height;
- offsetY = Math.max(offsetY, height + 45);
- }
- });
- }
+export function renderAutoResizableBody(container, type = 'block', fixedSelectors = []) {
+ requestAnimationFrame(() => {
+ let offsetY = 0;
- const blockSelector = `[class$="-${type}"]`
- const allSelectors = [blockSelector, ...fixedSelectors].join(", ");
- const children = container.querySelectorAll(allSelectors);
-
- let maxRight = 0;
- let maxBottom = 0;
-
- children.forEach(child => {
- const rect = child.getBoundingClientRect();
- const parentRect = container.getBoundingClientRect();
- const right = rect.right - parentRect.left;
- const bottom = rect.bottom - parentRect.top;
-
- if (right > maxRight) maxRight = right;
- if (bottom > maxBottom) maxBottom = bottom;
- });
-
- container.style.width = `${maxRight + 20}px`;
- container.style.height = `${maxBottom + 20}px`;
-
- const body = container.querySelector(`[class$="-body"]`);
- if (body) {
- body.style.top = `${offsetY}px`;
+ if (fixedSelectors.length > 0) {
+ fixedSelectors.forEach(sel => {
+ const header = container.querySelector(sel);
+ if (header) {
+ const height = header.getBoundingClientRect().height;
+ offsetY = Math.max(offsetY, height + 45);
}
+ });
+ }
+
+ const blockSelector = `[class$="-${type}"]`;
+ const allSelectors = [blockSelector, ...fixedSelectors].join(', ');
+ const children = container.querySelectorAll(allSelectors);
+
+ let maxRight = 0;
+ let maxBottom = 0;
+
+ children.forEach(child => {
+ const rect = child.getBoundingClientRect();
+ const parentRect = container.getBoundingClientRect();
+ const right = rect.right - parentRect.left;
+ const bottom = rect.bottom - parentRect.top;
+
+ if (right > maxRight) maxRight = right;
+ if (bottom > maxBottom) maxBottom = bottom;
});
-}
\ No newline at end of file
+
+ container.style.width = `${maxRight + 20}px`;
+ container.style.height = `${maxBottom + 20}px`;
+
+ const body = container.querySelector('[class$="-body"]');
+ if (body) {
+ body.style.top = `${offsetY}px`;
+ }
+ });
+}
diff --git a/webview/components/renderBaseBlock.js b/webview/components/renderBaseBlock.js
index 560a07e..2cf623f 100644
--- a/webview/components/renderBaseBlock.js
+++ b/webview/components/renderBaseBlock.js
@@ -12,54 +12,54 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import { makeDraggable } from "../utils/drag.js";
-import { getTypeLabel } from "../utils/labels.js";
-import { renderEventPortRow } from "./renderEventPortRow.js";
+import { makeDraggable } from '../utils/drag.js';
+import { getTypeLabel } from '../utils/labels.js';
+import { renderEventPortRow } from './renderEventPortRow.js';
export function renderBaseBlock({ type, options }) {
- const block = document.createElement("div");
- block.className = `block ${type}-block`;
- block.style.position = "absolute";
+ const block = document.createElement('div');
+ block.className = `block ${type}-block`;
+ block.style.position = 'absolute';
- // Header container
- const header = document.createElement("div");
- header.className = "block-header";
+ // Header container
+ const header = document.createElement('div');
+ header.className = 'block-header';
- // Top label
- const heading = document.createElement("div");
- heading.className = "block-title";
- heading.innerText = getTypeLabel(type);
- header.appendChild(heading);
+ // Top label
+ const heading = document.createElement('div');
+ heading.className = 'block-title';
+ heading.innerText = getTypeLabel(type);
+ header.appendChild(heading);
- // Event ports
- const path = options?.path;
- const portRegistry = options?.portRegistry;
- if (options?.events?.triggered_by?.length || options?.events?.triggers?.length) {
- const leftLabel = options.eventLabels?.left || "โ triggered by";
- const rightLabel = options.eventLabels?.right || "triggers โ";
+ // Event ports
+ const path = options?.path;
+ const portRegistry = options?.portRegistry;
+ if (options?.events?.triggered_by?.length || options?.events?.triggers?.length) {
+ const leftLabel = options.eventLabels?.left || 'โ triggered by';
+ const rightLabel = options.eventLabels?.right || 'triggers โ';
- const eventPortRow = renderEventPortRow(path, portRegistry, leftLabel, rightLabel);
- header.appendChild(eventPortRow);
- }
+ const eventPortRow = renderEventPortRow(path, portRegistry, leftLabel, rightLabel);
+ header.appendChild(eventPortRow);
+ }
- block.appendChild(header);
+ block.appendChild(header);
- // Metadata
- if (options?.path) {
- block.dataset.path = options.path;
- block.dataset.type = type;
- }
+ // Metadata
+ if (options?.path) {
+ block.dataset.path = options.path;
+ block.dataset.type = type;
+ }
- // Draggable
- makeDraggable(block, {
- ...options,
- onDrag: () => {
- if (options.renderEdges && options.parsedData) {
- options.renderEdges(options.parsedData, options.portRegistry);
- }
- }
- });
+ // Draggable
+ makeDraggable(block, {
+ ...options,
+ onDrag: () => {
+ if (options.renderEdges && options.parsedData) {
+ options.renderEdges(options.parsedData, options.portRegistry);
+ }
+ }
+ });
- return block;
+ return block;
}
diff --git a/webview/components/renderComponent.js b/webview/components/renderComponent.js
index ab4b0b8..c95fcb3 100644
--- a/webview/components/renderComponent.js
+++ b/webview/components/renderComponent.js
@@ -12,32 +12,32 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import { getRenderer } from "../core/dispatcher.js";
+import { getRenderer } from '../core/dispatcher.js';
export function renderComponent(obj, container, options = {}) {
- if (!obj || typeof obj !== "object") return;
+ if (!obj || typeof obj !== 'object') return;
- const type = obj.type || detectType(obj);
- const renderer = getRenderer(type);
+ const type = obj.type || detectType(obj);
+ const renderer = getRenderer(type);
- if (!renderer) {
- console.warn(`No renderer for type: ${type}`, obj);
- return;
- }
+ if (!renderer) {
+ console.warn(`No renderer for type: ${type}`, obj);
+ return;
+ }
- renderer(obj, container, {
- ...options,
- renderComponent
- });
+ renderer(obj, container, {
+ ...options,
+ renderComponent
+ });
}
function detectType(obj) {
- if (obj.nodes || obj.groups || obj.composable_nodes) return "group";
- if (Array.isArray(obj.arguments)) return "arguments";
- if (Array.isArray(obj.includes)) return "includes";
- if (Array.isArray(obj.nodes)) return "nodes";
- if (Array.isArray(obj.opaque_functions)) return "opaque_functions";
- if (Array.isArray(obj.composable_nodes)) return "composable-nodes";
- if (Array.isArray(obj.composable_nodes_container)) return "composable-nodes-container";
- return null;
-}
\ No newline at end of file
+ if (obj.nodes || obj.groups || obj.composable_nodes) return 'group';
+ if (Array.isArray(obj.arguments)) return 'arguments';
+ if (Array.isArray(obj.includes)) return 'includes';
+ if (Array.isArray(obj.nodes)) return 'nodes';
+ if (Array.isArray(obj.opaque_functions)) return 'opaque_functions';
+ if (Array.isArray(obj.composable_nodes)) return 'composable-nodes';
+ if (Array.isArray(obj.composable_nodes_container)) return 'composable-nodes-container';
+ return null;
+}
diff --git a/webview/components/renderComposableContainer.js b/webview/components/renderComposableContainer.js
index 198b67f..15ab457 100644
--- a/webview/components/renderComposableContainer.js
+++ b/webview/components/renderComposableContainer.js
@@ -19,74 +19,74 @@ import { renderSection } from './renderSection.js';
import { LayoutManager } from '../core/layoutManager.js';
export function renderComposableContainerGroup(container, groups, options = {}) {
- groups.forEach((group, idx) => {
- const path = options.pathPrefix ? `${options.pathPrefix}.composable_nodes_container[${idx}]` : `composable_nodes_container[${idx}]`;
- const block = renderComposableContainer(group, { ...options, path });
-
- container.appendChild(block);
- options.renderBlock(block, "composable-container");
- });
+ groups.forEach((group, idx) => {
+ const path = options.pathPrefix ? `${options.pathPrefix}.composable_nodes_container[${idx}]` : `composable_nodes_container[${idx}]`;
+ const block = renderComposableContainer(group, { ...options, path });
+
+ container.appendChild(block);
+ options.renderBlock(block, 'composable-container');
+ });
}
export function renderComposableContainer(group, options = {}) {
- const composableContainer = renderBaseBlock({
- type: "composable-container",
- options: {
- ...options,
- events: group.events
- }
- });
+ const composableContainer = renderBaseBlock({
+ type: 'composable-container',
+ options: {
+ ...options,
+ events: group.events
+ }
+ });
- // Header
- const header = document.createElement("div");
- header.className = "composable-container-header";
+ // Header
+ const header = document.createElement('div');
+ header.className = 'composable-container-header';
- // Render additional sections
- const metaSections = [
- { key: "container", icon: "๐", label: "Container", value: group.target_container },
- { key: "package", icon: "๐ฆ", label: "Package", value: group.package },
- { key: "executable", icon: "โถ๏ธ", label: "Executable", value: group.executable },
- { key: "output", icon: "๐ฅ๏ธ", label: "Output", value: group.output}
- ];
+ // Render additional sections
+ const metaSections = [
+ { key: 'container', icon: '๐', label: 'Container', value: group.target_container },
+ { key: 'package', icon: '๐ฆ', label: 'Package', value: group.package },
+ { key: 'executable', icon: 'โถ๏ธ', label: 'Executable', value: group.executable },
+ { key: 'output', icon: '๐ฅ๏ธ', label: 'Output', value: group.output }
+ ];
- metaSections.forEach(( {key, icon, label, value }) => {
- if (value) {
- const section = renderSection(key, icon, label, value, {
- includeLeftPort: true,
- portIdPrefix: options.path,
- portRegistry: options.portRegistry
- });
- header.appendChild(section);
- }
- });
+ metaSections.forEach(({ key, icon, label, value }) => {
+ if (value) {
+ const section = renderSection(key, icon, label, value, {
+ includeLeftPort: true,
+ portIdPrefix: options.path,
+ portRegistry: options.portRegistry
+ });
+ header.appendChild(section);
+ }
+ });
- composableContainer.append(header);
+ composableContainer.append(header);
- // Body
- const body = document.createElement("div");
- body.className = "composable-container-body";
- composableContainer.appendChild(body);
+ // Body
+ const body = document.createElement('div');
+ body.className = 'composable-container-body';
+ composableContainer.appendChild(body);
- const innerLayoutManager = new LayoutManager(20, 40, 80, 40);
- const childOptions = {
- ...options,
- stopPropagation: true,
- constrainToParent: true,
- pathPrefix: `${options.path}`,
- renderBlock: (block, columnType) => {
- requestAnimationFrame(() => {
- innerLayoutManager.placeBlock(block, columnType);
- })
- }
- };
+ const innerLayoutManager = new LayoutManager(20, 40, 80, 40);
+ const childOptions = {
+ ...options,
+ stopPropagation: true,
+ constrainToParent: true,
+ pathPrefix: `${options.path}`,
+ renderBlock: (block, columnType) => {
+ requestAnimationFrame(() => {
+ innerLayoutManager.placeBlock(block, columnType);
+ });
+ }
+ };
- // Render composable nodes
- renderComponent(
- { type: "composable_nodes", value: group.composable_nodes, namespace: group.namespace },
- body, childOptions
- );
+ // Render composable nodes
+ renderComponent(
+ { type: 'composable_nodes', value: group.composable_nodes, namespace: group.namespace },
+ body, childOptions
+ );
- renderAutoResizableBody(composableContainer, "block", [".composable-container-header"]);
+ renderAutoResizableBody(composableContainer, 'block', ['.composable-container-header']);
- return composableContainer;
-}
\ No newline at end of file
+ return composableContainer;
+}
diff --git a/webview/components/renderComposableNode.js b/webview/components/renderComposableNode.js
index fae8e69..48250e6 100644
--- a/webview/components/renderComposableNode.js
+++ b/webview/components/renderComposableNode.js
@@ -16,40 +16,40 @@ import { renderSection } from './renderSection.js';
import { renderBaseBlock } from './renderBaseBlock.js';
export function renderComposableNodeGroup(container, nodes, options={}) {
- nodes.forEach((node, idx) => {
- const path = options.pathPrefix ? `${options.pathPrefix}.composable_nodes[${idx}]` : `composable_nodes[${idx}]`;
- const block = renderComposableNode(node, { ...options, path });
-
- container.appendChild(block);
- options.renderBlock(block, "composable-node");
- });
+ nodes.forEach((node, idx) => {
+ const path = options.pathPrefix ? `${options.pathPrefix}.composable_nodes[${idx}]` : `composable_nodes[${idx}]`;
+ const block = renderComposableNode(node, { ...options, path });
+
+ container.appendChild(block);
+ options.renderBlock(block, 'composable-node');
+ });
}
function renderComposableNode(node, options) {
- const block = renderBaseBlock({
- type: 'composable-node',
- options: {
- ...options,
- events: node.events
- }
- })
-
- // Node name
- const titleLabel = node.name || node.plugin || "(anonymous)";
-
- // Sections
- const renderOptions = { includeLeftPort: true, portIdPrefix: options.path, portRegistry: options.portRegistry };
- block.appendChild(renderSection("name", "๐", "Name", titleLabel, renderOptions));
- block.appendChild(renderSection("package", "๐ฆ", "Package", node.package, renderOptions));
- block.appendChild(renderSection("plugin", "๐", "Plugin", node.plugin, renderOptions));
-
- if (node.parameters?.length > 0) {
- block.appendChild(renderSection("parameters", "โ๏ธ", "Params", node.parameters, renderOptions));
+ const block = renderBaseBlock({
+ type: 'composable-node',
+ options: {
+ ...options,
+ events: node.events
}
+ });
- if (node.arguments?.length > 0) {
- block.appendChild(renderSection("arguments", "๐ฌ", "Args", node.arguments, renderOptions));
- }
+ // Node name
+ const titleLabel = node.name || node.plugin || '(anonymous)';
+
+ // Sections
+ const renderOptions = { includeLeftPort: true, portIdPrefix: options.path, portRegistry: options.portRegistry };
+ block.appendChild(renderSection('name', '๐', 'Name', titleLabel, renderOptions));
+ block.appendChild(renderSection('package', '๐ฆ', 'Package', node.package, renderOptions));
+ block.appendChild(renderSection('plugin', '๐', 'Plugin', node.plugin, renderOptions));
- return block;
-}
\ No newline at end of file
+ if (node.parameters?.length > 0) {
+ block.appendChild(renderSection('parameters', 'โ๏ธ', 'Params', node.parameters, renderOptions));
+ }
+
+ if (node.arguments?.length > 0) {
+ block.appendChild(renderSection('arguments', '๐ฌ', 'Args', node.arguments, renderOptions));
+ }
+
+ return block;
+}
diff --git a/webview/components/renderEnvironmentVariables.js b/webview/components/renderEnvironmentVariables.js
index 7a56727..f256b80 100644
--- a/webview/components/renderEnvironmentVariables.js
+++ b/webview/components/renderEnvironmentVariables.js
@@ -16,28 +16,28 @@ import { renderBaseBlock } from './renderBaseBlock.js';
import { renderSection } from './renderSection.js';
export function renderEnvironmentVariables(container, argumentsList, options) {
- if (!argumentsList || argumentsList.length === 0) return;
+ if (!argumentsList || argumentsList.length === 0) return;
- argumentsList.forEach((arg, idx) => {
- const path = `${options.pathPrefix || "environment_variables"}[${idx}]`;
- const block = renderEnvironmentVariable(arg, { ...options, path });
+ argumentsList.forEach((arg, idx) => {
+ const path = `${options.pathPrefix || 'environment_variables'}[${idx}]`;
+ const block = renderEnvironmentVariable(arg, { ...options, path });
- container.appendChild(block);
- options.renderBlock(block, "environment-variable");
- });
+ container.appendChild(block);
+ options.renderBlock(block, 'environment-variable');
+ });
}
export function renderEnvironmentVariable(arg, options) {
- const block = renderBaseBlock({ type: "environment-variable", options });
+ const block = renderBaseBlock({ type: 'environment-variable', options });
- const value = arg.default_value !== undefined
- ? arg.default_value
- : (arg.value !== undefined ? arg.value : "");
- const argSection = renderSection("argument", "๐", arg.name, value,
- { includeRightPort: true, portIdPrefix: `argument:${arg.name}`, portRegistry: options.portRegistry });
- block.appendChild(argSection);
+ const value = arg.default_value !== undefined
+ ? arg.default_value
+ : (arg.value !== undefined ? arg.value : '');
+ const argSection = renderSection('argument', '๐', arg.name, value,
+ { includeRightPort: true, portIdPrefix: `argument:${arg.name}`, portRegistry: options.portRegistry });
+ block.appendChild(argSection);
- block.dataset.argument = arg.name;
+ block.dataset.argument = arg.name;
- return block;
-}
\ No newline at end of file
+ return block;
+}
diff --git a/webview/components/renderEventHandler.js b/webview/components/renderEventHandler.js
index 68e2833..b688caa 100644
--- a/webview/components/renderEventHandler.js
+++ b/webview/components/renderEventHandler.js
@@ -16,38 +16,38 @@ import { renderSection } from './renderSection.js';
import { renderBaseBlock } from './renderBaseBlock.js';
export function renderEventHandlerGroup(container, handlers, options={}) {
- handlers.forEach((handler, idx) => {
- const path = `events[${idx}]`;
- const block = renderEventHandler(handler, { ...options, path });
+ handlers.forEach((handler, idx) => {
+ const path = `events[${idx}]`;
+ const block = renderEventHandler(handler, { ...options, path });
- container.appendChild(block);
- options.renderBlock(block, "event-handler");
- });
+ container.appendChild(block);
+ options.renderBlock(block, 'event-handler');
+ });
}
function renderEventHandler(handler, options) {
- const block = renderBaseBlock({
- type: "event-handler",
- options: {
- ...options,
- events: {
- triggered_by: handler.triggered_by,
- triggers: handler.triggers
- },
- eventLabels: handler.type === "OnProcessExit"
- ? { left: "โ target_action", right: "on_exit โ" }
- : handler.type === "OnProcessStart"
- ? { left: "โ target_action", right: "on_start โ" }
- : undefined
- }
- });
+ const block = renderBaseBlock({
+ type: 'event-handler',
+ options: {
+ ...options,
+ events: {
+ triggered_by: handler.triggered_by,
+ triggers: handler.triggers
+ },
+ eventLabels: handler.type === 'OnProcessExit'
+ ? { left: 'โ target_action', right: 'on_exit โ' }
+ : handler.type === 'OnProcessStart'
+ ? { left: 'โ target_action', right: 'on_start โ' }
+ : undefined
+ }
+ });
- // Main Section
- const type = handler.type || 'EventHandler';
- block.appendChild(renderSection("type", "๐ฃ", "Type", type, {
- includeLeftPort: false,
- includeRightPort: false
- }));
+ // Main Section
+ const type = handler.type || 'EventHandler';
+ block.appendChild(renderSection('type', '๐ฃ', 'Type', type, {
+ includeLeftPort: false,
+ includeRightPort: false
+ }));
- return block;
-}
\ No newline at end of file
+ return block;
+}
diff --git a/webview/components/renderEventPortRow.js b/webview/components/renderEventPortRow.js
index a9b9471..3ef7db0 100644
--- a/webview/components/renderEventPortRow.js
+++ b/webview/components/renderEventPortRow.js
@@ -12,44 +12,44 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-export function renderEventPortRow(path, portRegistry,
- leftLabelText = "โ triggered by", rightLabelText = "triggers โ") {
- const eventPortRow = document.createElement("div");
- eventPortRow.className = "event-port-row";
+export function renderEventPortRow(path, portRegistry,
+ leftLabelText = 'โ triggered by', rightLabelText = 'triggers โ') {
+ const eventPortRow = document.createElement('div');
+ eventPortRow.className = 'event-port-row';
- // Left port (triggered_by)
- const leftWrapper = document.createElement("div");
- leftWrapper.className = "event-port-wrapper left";
+ // Left port (triggered_by)
+ const leftWrapper = document.createElement('div');
+ leftWrapper.className = 'event-port-wrapper left';
- const leftLabel = document.createElement("span");
- leftLabel.className = "event-label";
- leftLabel.innerText = leftLabelText;
+ const leftLabel = document.createElement('span');
+ leftLabel.className = 'event-label';
+ leftLabel.innerText = leftLabelText;
- const leftPort = document.createElement("div");
- leftPort.className = "port left";
- leftWrapper.appendChild(leftPort);
- leftWrapper.appendChild(leftLabel);
+ const leftPort = document.createElement('div');
+ leftPort.className = 'port left';
+ leftWrapper.appendChild(leftPort);
+ leftWrapper.appendChild(leftLabel);
- eventPortRow.appendChild(leftWrapper);
+ eventPortRow.appendChild(leftWrapper);
- if (portRegistry) portRegistry[`${path}.events.triggered_by`] = leftPort;
+ if (portRegistry) portRegistry[`${path}.events.triggered_by`] = leftPort;
- // Right port (triggers)
- const rightWrapper = document.createElement("div");
- rightWrapper.className = "event-port-wrapper right";
+ // Right port (triggers)
+ const rightWrapper = document.createElement('div');
+ rightWrapper.className = 'event-port-wrapper right';
- const rightLabel = document.createElement("span");
- rightLabel.className = "event-label";
- rightLabel.innerText = rightLabelText;
+ const rightLabel = document.createElement('span');
+ rightLabel.className = 'event-label';
+ rightLabel.innerText = rightLabelText;
- const rightPort = document.createElement("div");
- rightPort.className = "port right";
- rightWrapper.appendChild(rightLabel);
- rightWrapper.appendChild(rightPort);
+ const rightPort = document.createElement('div');
+ rightPort.className = 'port right';
+ rightWrapper.appendChild(rightLabel);
+ rightWrapper.appendChild(rightPort);
- eventPortRow.appendChild(rightWrapper);
-
- if (portRegistry) portRegistry[`${path}.events.triggers`] = rightPort;
+ eventPortRow.appendChild(rightWrapper);
- return eventPortRow;
-}
\ No newline at end of file
+ if (portRegistry) portRegistry[`${path}.events.triggers`] = rightPort;
+
+ return eventPortRow;
+}
diff --git a/webview/components/renderGroup.js b/webview/components/renderGroup.js
index edb45e9..2e8df6c 100644
--- a/webview/components/renderGroup.js
+++ b/webview/components/renderGroup.js
@@ -19,73 +19,73 @@ import { renderSection } from './renderSection.js';
import { LayoutManager } from '../core/layoutManager.js';
export function renderGroupGroup(container, groups, options = {}) {
- groups.forEach((group, idx) => {
- const path = options.pathPrefix ? `${options.pathPrefix}.groups[${idx}]` : `groups[${idx}]`;
- const block = renderGroup(group, { ...options, path });
+ groups.forEach((group, idx) => {
+ const path = options.pathPrefix ? `${options.pathPrefix}.groups[${idx}]` : `groups[${idx}]`;
+ const block = renderGroup(group, { ...options, path });
- container.appendChild(block);
- options.renderBlock(block, "group");
- });
+ container.appendChild(block);
+ options.renderBlock(block, 'group');
+ });
}
export function renderGroup(group, options = {}) {
- const ns = group.namespace || "";
+ const ns = group.namespace || '';
- const groupBox = renderBaseBlock({
- type: "group",
- options: {
- ...options,
- events: group.events
- }
- });
-
- // Header
- const header = document.createElement("div");
- header.className = "group-header";
+ const groupBox = renderBaseBlock({
+ type: 'group',
+ options: {
+ ...options,
+ events: group.events
+ }
+ });
- // Render additional sections
- const metaSections = [
- { key: "namespace", icon: "๐งญ", label: "Namespace", value: ns },
- { key: "condition", icon: "โ", label: "Condition", value: group.condition }
- ];
+ // Header
+ const header = document.createElement('div');
+ header.className = 'group-header';
- metaSections.forEach(( {key, icon, label, value }) => {
- if (value) {
- const section = renderSection(key, icon, label, value, {
- includeLeftPort: true,
- portIdPrefix: options.path,
- portRegistry: options.portRegistry
- });
- header.appendChild(section);
- }
- });
+ // Render additional sections
+ const metaSections = [
+ { key: 'namespace', icon: '๐งญ', label: 'Namespace', value: ns },
+ { key: 'condition', icon: 'โ', label: 'Condition', value: group.condition }
+ ];
- groupBox.append(header);
+ metaSections.forEach(({ key, icon, label, value }) => {
+ if (value) {
+ const section = renderSection(key, icon, label, value, {
+ includeLeftPort: true,
+ portIdPrefix: options.path,
+ portRegistry: options.portRegistry
+ });
+ header.appendChild(section);
+ }
+ });
- // Body
- const body = document.createElement("div");
- body.className = "group-body";
- groupBox.appendChild(body);
+ groupBox.append(header);
- const innerLayoutManager = new LayoutManager(20, 40, 80, 40);
- const childOptions = {
- ...options,
- stopPropagation: true,
- constrainToParent: true,
- pathPrefix: `${options.path}.actions`,
- renderBlock: (block, columnType) => {
- requestAnimationFrame(() => {
- innerLayoutManager.placeBlock(block, columnType);
- });
- }
- };
+ // Body
+ const body = document.createElement('div');
+ body.className = 'group-body';
+ groupBox.appendChild(body);
- const actions = group.actions || {};
- for (const [key, value] of Object.entries(actions)) {
- renderComponent({ type: key, value: value, namespace: ns }, body, childOptions);
+ const innerLayoutManager = new LayoutManager(20, 40, 80, 40);
+ const childOptions = {
+ ...options,
+ stopPropagation: true,
+ constrainToParent: true,
+ pathPrefix: `${options.path}.actions`,
+ renderBlock: (block, columnType) => {
+ requestAnimationFrame(() => {
+ innerLayoutManager.placeBlock(block, columnType);
+ });
}
+ };
- renderAutoResizableBody(groupBox, "block", [".group-header"]);
+ const actions = group.actions || {};
+ for (const [key, value] of Object.entries(actions)) {
+ renderComponent({ type: key, value: value, namespace: ns }, body, childOptions);
+ }
- return groupBox;
-}
\ No newline at end of file
+ renderAutoResizableBody(groupBox, 'block', ['.group-header']);
+
+ return groupBox;
+}
diff --git a/webview/components/renderInclude.js b/webview/components/renderInclude.js
index 1db03f0..19c271e 100644
--- a/webview/components/renderInclude.js
+++ b/webview/components/renderInclude.js
@@ -16,35 +16,35 @@ import { renderBaseBlock } from './renderBaseBlock.js';
import { renderSection } from './renderSection.js';
export function renderIncludesGroup(container, includes, options={}) {
- includes.forEach((include, idx) => {
- const path = options.pathPrefix ? `${options.pathPrefix}.includes[${idx}]` : `includes[${idx}]`;
- const block = renderInclude(include, { ...options, path });
+ includes.forEach((include, idx) => {
+ const path = options.pathPrefix ? `${options.pathPrefix}.includes[${idx}]` : `includes[${idx}]`;
+ const block = renderInclude(include, { ...options, path });
- container.appendChild(block);
- options.renderBlock(block, "include");
- });
+ container.appendChild(block);
+ options.renderBlock(block, 'include');
+ });
}
function renderInclude(include, options) {
- const block = renderBaseBlock({
- type: 'include',
- options: {
- ...options,
- events: include.events
- }
- });
-
- // Render
- const renderOptions = { includeLeftPort: true, portIdPrefix: options.path, portRegistry: options.portRegistry };
- const path = include.launch_description_source || "";
- block.appendChild(renderSection("launch_description_source", "๐", "Path", path, renderOptions));
-
- const args = include.launch_arguments || {};
- block.appendChild(renderSection("launch_arguments", "๐ฅ", "Args", args, renderOptions))
-
- if (include.condition) {
- block.appendChild(renderSection("condition", "โ", "Condition", include.condition, renderOptions));
+ const block = renderBaseBlock({
+ type: 'include',
+ options: {
+ ...options,
+ events: include.events
}
+ });
- return block;
-}
\ No newline at end of file
+ // Render
+ const renderOptions = { includeLeftPort: true, portIdPrefix: options.path, portRegistry: options.portRegistry };
+ const path = include.launch_description_source || '';
+ block.appendChild(renderSection('launch_description_source', '๐', 'Path', path, renderOptions));
+
+ const args = include.launch_arguments || {};
+ block.appendChild(renderSection('launch_arguments', '๐ฅ', 'Args', args, renderOptions));
+
+ if (include.condition) {
+ block.appendChild(renderSection('condition', 'โ', 'Condition', include.condition, renderOptions));
+ }
+
+ return block;
+}
diff --git a/webview/components/renderNode.js b/webview/components/renderNode.js
index ad2ad72..62f8323 100644
--- a/webview/components/renderNode.js
+++ b/webview/components/renderNode.js
@@ -16,45 +16,45 @@ import { renderSection } from './renderSection.js';
import { renderBaseBlock } from './renderBaseBlock.js';
export function renderNodeGroup(container, nodes, options={}) {
- nodes.forEach((node, idx) => {
- const path = options.pathPrefix ? `${options.pathPrefix}.nodes[${idx}]` : `nodes[${idx}]`;
- const block = renderNode(node, { ...options, path });
+ nodes.forEach((node, idx) => {
+ const path = options.pathPrefix ? `${options.pathPrefix}.nodes[${idx}]` : `nodes[${idx}]`;
+ const block = renderNode(node, { ...options, path });
- container.appendChild(block);
- options.renderBlock(block, "node");
- });
+ container.appendChild(block);
+ options.renderBlock(block, 'node');
+ });
}
function renderNode(node, options) {
- const block = renderBaseBlock({
- type: 'node',
- options: {
- ...options,
- events: node.events
- }
- })
-
- // Node name
- const titleLabel = node.name || node.executable || "(anonymous)";
-
- // Sections
- const renderOptions = { includeLeftPort: true, portIdPrefix: options.path, portRegistry: options.portRegistry };
- block.appendChild(renderSection("name", "๐", "Name", titleLabel, renderOptions));
- block.appendChild(renderSection("package", "๐ฆ", "Package", node.package, renderOptions));
- block.appendChild(renderSection("executable", "โถ๏ธ", "Executable", node.executable, renderOptions));
- block.appendChild(renderSection("output", "๐ฅ๏ธ", "Output", node.output || "โ", renderOptions));
-
- if (node.condition) {
- block.appendChild(renderSection("condition", "โ", "Condition", node.condition, renderOptions));
+ const block = renderBaseBlock({
+ type: 'node',
+ options: {
+ ...options,
+ events: node.events
}
+ });
- if (node.parameters?.length > 0) {
- block.appendChild(renderSection("parameters", "โ๏ธ", "Params", node.parameters, renderOptions));
- }
+ // Node name
+ const titleLabel = node.name || node.executable || '(anonymous)';
- if (node.arguments?.length > 0) {
- block.appendChild(renderSection("arguments", "๐ฌ", "Args", node.arguments, renderOptions));
- }
+ // Sections
+ const renderOptions = { includeLeftPort: true, portIdPrefix: options.path, portRegistry: options.portRegistry };
+ block.appendChild(renderSection('name', '๐', 'Name', titleLabel, renderOptions));
+ block.appendChild(renderSection('package', '๐ฆ', 'Package', node.package, renderOptions));
+ block.appendChild(renderSection('executable', 'โถ๏ธ', 'Executable', node.executable, renderOptions));
+ block.appendChild(renderSection('output', '๐ฅ๏ธ', 'Output', node.output || 'โ', renderOptions));
+
+ if (node.condition) {
+ block.appendChild(renderSection('condition', 'โ', 'Condition', node.condition, renderOptions));
+ }
- return block;
-}
\ No newline at end of file
+ if (node.parameters?.length > 0) {
+ block.appendChild(renderSection('parameters', 'โ๏ธ', 'Params', node.parameters, renderOptions));
+ }
+
+ if (node.arguments?.length > 0) {
+ block.appendChild(renderSection('arguments', '๐ฌ', 'Args', node.arguments, renderOptions));
+ }
+
+ return block;
+}
diff --git a/webview/components/renderOpaqueFunction.js b/webview/components/renderOpaqueFunction.js
index 2441c32..6fa8a4a 100644
--- a/webview/components/renderOpaqueFunction.js
+++ b/webview/components/renderOpaqueFunction.js
@@ -18,60 +18,60 @@ import { renderComponent } from './renderComponent.js';
import { LayoutManager } from '../core/layoutManager.js';
export function renderOpaqueFunctionGroup(container, opaqueFcns, options = {}) {
- opaqueFcns.forEach((opaqueFcn, idx) => {
- const path = options.pathPrefix ? `${options.pathPrefix}.opaque_functions[${idx}]` : `opaque_functions[${idx}]`;
- const block = renderOpaqueFunction(opaqueFcn, { ...options, path });
+ opaqueFcns.forEach((opaqueFcn, idx) => {
+ const path = options.pathPrefix ? `${options.pathPrefix}.opaque_functions[${idx}]` : `opaque_functions[${idx}]`;
+ const block = renderOpaqueFunction(opaqueFcn, { ...options, path });
- container.appendChild(block);
- options.renderBlock(block, "opaque-function")
- });
+ container.appendChild(block);
+ options.renderBlock(block, 'opaque-function');
+ });
}
export function renderOpaqueFunction(opaqueFcn, options = {}) {
- const fcnBox = renderBaseBlock({
- type: "opaque-function",
- options: {
- ...options,
- events: opaqueFcn.events
- }
- });
-
- // Header
- const header = document.createElement("div");
- header.className = "opaque-function-header";
+ const fcnBox = renderBaseBlock({
+ type: 'opaque-function',
+ options: {
+ ...options,
+ events: opaqueFcn.events
+ }
+ });
- // Title
- const title = document.createElement("div");
- title.className = "opaque-function-title";
- title.innerText = `${opaqueFcn.name}`;
- header.appendChild(title);
+ // Header
+ const header = document.createElement('div');
+ header.className = 'opaque-function-header';
- fcnBox.append(header);
+ // Title
+ const title = document.createElement('div');
+ title.className = 'opaque-function-title';
+ title.innerText = `${opaqueFcn.name}`;
+ header.appendChild(title);
- // Body
- const body = document.createElement("div");
- body.className = "opaque-function-body";
- fcnBox.appendChild(body);
+ fcnBox.append(header);
- const innerLayoutManager = new LayoutManager(20, 40, 80, 40);
- const childOptions = {
- ...options,
- stopPropagation: true,
- constrainToParent: true,
- pathPrefix: `${options.path}.returns`,
- renderBlock: (block, columnType) => {
- requestAnimationFrame(() => {
- innerLayoutManager.placeBlock(block, columnType);
- })
- }
- };
+ // Body
+ const body = document.createElement('div');
+ body.className = 'opaque-function-body';
+ fcnBox.appendChild(body);
- const returns = opaqueFcn.returns || {};
- for (const [key, value] of Object.entries(returns)) {
- renderComponent({ type: key, value: value }, body, childOptions);
+ const innerLayoutManager = new LayoutManager(20, 40, 80, 40);
+ const childOptions = {
+ ...options,
+ stopPropagation: true,
+ constrainToParent: true,
+ pathPrefix: `${options.path}.returns`,
+ renderBlock: (block, columnType) => {
+ requestAnimationFrame(() => {
+ innerLayoutManager.placeBlock(block, columnType);
+ });
}
+ };
- renderAutoResizableBody(fcnBox, "block", [".opaque-function-header"]);
+ const returns = opaqueFcn.returns || {};
+ for (const [key, value] of Object.entries(returns)) {
+ renderComponent({ type: key, value: value }, body, childOptions);
+ }
- return fcnBox;
-}
\ No newline at end of file
+ renderAutoResizableBody(fcnBox, 'block', ['.opaque-function-header']);
+
+ return fcnBox;
+}
diff --git a/webview/components/renderPythonExpressions.js b/webview/components/renderPythonExpressions.js
index 2fe9806..689bb76 100644
--- a/webview/components/renderPythonExpressions.js
+++ b/webview/components/renderPythonExpressions.js
@@ -16,41 +16,41 @@ import { renderBaseBlock } from './renderBaseBlock.js';
import { renderSection } from './renderSection.js';
export function renderPythonExpressions(container, expressionsList, options) {
- if (!expressionsList || expressionsList.length === 0) return;
-
- expressionsList.forEach((expr, idx) => {
- const path = `python_expressions[${idx}]`;
- const block = renderPythonExpression(expr, { ...options, path });
-
- container.appendChild(block);
- options.renderBlock(block, "python-expression");
- });
+ if (!expressionsList || expressionsList.length === 0) return;
+
+ expressionsList.forEach((expr, idx) => {
+ const path = `python_expressions[${idx}]`;
+ const block = renderPythonExpression(expr, { ...options, path });
+
+ container.appendChild(block);
+ options.renderBlock(block, 'python-expression');
+ });
}
export function renderPythonExpression(expr, options) {
- const block = renderBaseBlock({
- type: "python-expression",
- options
- })
-
- // Code section
- const codeSection = document.createElement("pre");
- codeSection.className = "python-code";
- codeSection.innerText = expr.body;
- block.appendChild(codeSection);
-
- // Variables section (right port active)
- if (expr.variables?.length > 0) {
- expr.variables.forEach((v, i) => {
- const varName = v.replace("${var:", "").replace("}", "");
- const varSection = renderSection("variable", "๐ค", varName, "", {
- includeRightPort: true,
- portIdPrefix: `variable:${varName}`,
- portRegistry: options.portRegistry
- });
- block.appendChild(varSection);
- });
- }
-
- return block
-}
\ No newline at end of file
+ const block = renderBaseBlock({
+ type: 'python-expression',
+ options
+ });
+
+ // Code section
+ const codeSection = document.createElement('pre');
+ codeSection.className = 'python-code';
+ codeSection.innerText = expr.body;
+ block.appendChild(codeSection);
+
+ // Variables section (right port active)
+ if (expr.variables?.length > 0) {
+ expr.variables.forEach((v) => {
+ const varName = v.replace('${var:', '').replace('}', '');
+ const varSection = renderSection('variable', '๐ค', varName, '', {
+ includeRightPort: true,
+ portIdPrefix: `variable:${varName}`,
+ portRegistry: options.portRegistry
+ });
+ block.appendChild(varSection);
+ });
+ }
+
+ return block;
+}
diff --git a/webview/components/renderSection.js b/webview/components/renderSection.js
index 2223e1b..dc73351 100644
--- a/webview/components/renderSection.js
+++ b/webview/components/renderSection.js
@@ -13,101 +13,101 @@
// limitations under the License.
export function renderSection(fieldName, icon, label, contentValue, {
- includeLeftPort = false,
- includeRightPort = false,
- portIdPrefix = "",
- portRegistry = null
+ includeLeftPort = false,
+ includeRightPort = false,
+ portIdPrefix = '',
+ portRegistry = null
}) {
- const wrapper = document.createElement("div");
- wrapper.className = "section";
- wrapper.dataset.field = fieldName;
+ const wrapper = document.createElement('div');
+ wrapper.className = 'section';
+ wrapper.dataset.field = fieldName;
- // Port ID
- const fullPath = portIdPrefix
- ? (fieldName ? `${portIdPrefix}.${fieldName}` : portIdPrefix)
- : fieldName;
+ // Port ID
+ const fullPath = portIdPrefix
+ ? (fieldName ? `${portIdPrefix}.${fieldName}` : portIdPrefix)
+ : fieldName;
- const content = document.createElement("div");
- content.className = "content";
+ const content = document.createElement('div');
+ content.className = 'content';
- // Try primitive
- if (typeof contentValue !== "object" || contentValue === null) {
- if (contentValue === "") {
- content.innerHTML = `${icon} ${label}`;
- } else {
- content.innerHTML = `${icon} ${label}: ${escapeHtml(String(contentValue))}`;
- }
- // Left Port
- if (includeLeftPort) {
- const leftPort = document.createElement("div");
- leftPort.className = "port left";
- wrapper.appendChild(leftPort);
-
- if (portRegistry) portRegistry[fullPath] = leftPort;
- }
+ // Try primitive
+ if (typeof contentValue !== 'object' || contentValue === null) {
+ if (contentValue === '') {
+ content.innerHTML = `${icon} ${label}`;
+ } else {
+ content.innerHTML = `${icon} ${label}: ${escapeHtml(String(contentValue))}`;
+ }
+ // Left Port
+ if (includeLeftPort) {
+ const leftPort = document.createElement('div');
+ leftPort.className = 'port left';
+ wrapper.appendChild(leftPort);
- wrapper.appendChild(content);
+ if (portRegistry) portRegistry[fullPath] = leftPort;
+ }
- // Right Port
- if (includeRightPort) {
- const rightPort = document.createElement("div");
- rightPort.className = "port right";
- wrapper.appendChild(rightPort);
+ wrapper.appendChild(content);
- if (portRegistry) portRegistry[fullPath] = rightPort;
- }
+ // Right Port
+ if (includeRightPort) {
+ const rightPort = document.createElement('div');
+ rightPort.className = 'port right';
+ wrapper.appendChild(rightPort);
- return wrapper;
+ if (portRegistry) portRegistry[fullPath] = rightPort;
}
- // Complex data type
- content.innerHTML = `${icon} ${label}`;
- wrapper.classList.add("has-nested");
- wrapper.appendChild(content);
- const nested = renderNestedValue(contentValue, fullPath, {
- includeLeftPort, includeRightPort, portRegistry
- });
- const nestedWrapper = document.createElement("div");
- nestedWrapper.className = "section-nested";
- nestedWrapper.appendChild(nested);
- wrapper.appendChild(nestedWrapper);
-
return wrapper;
+ }
+
+ // Complex data type
+ content.innerHTML = `${icon} ${label}`;
+ wrapper.classList.add('has-nested');
+ wrapper.appendChild(content);
+ const nested = renderNestedValue(contentValue, fullPath, {
+ includeLeftPort, includeRightPort, portRegistry
+ });
+ const nestedWrapper = document.createElement('div');
+ nestedWrapper.className = 'section-nested';
+ nestedWrapper.appendChild(nested);
+ wrapper.appendChild(nestedWrapper);
+
+ return wrapper;
}
function renderNestedValue(value, basePath, options) {
- const list = document.createElement("ul");
- list.style.margin = "4px 0";
- list.style.paddingLeft = "16px";
+ const list = document.createElement('ul');
+ list.style.margin = '4px 0';
+ list.style.paddingLeft = '16px';
- if (Array.isArray(value)) {
- value.forEach((item, idx) => {
- const itemPath = `${basePath}[${idx}]`;
- const li = document.createElement("li");
- li.appendChild(renderSection("", "", "", item, {
- ...options,
- portIdPrefix: itemPath
- }));
- list.appendChild(li);
- });
- } else {
- Object.entries(value).forEach(([key, val]) => {
- const li = document.createElement("li");
- li.appendChild(renderSection(key, "", key, val, {
- ...options,
- portIdPrefix: basePath
- }));
- list.appendChild(li);
- });
- }
+ if (Array.isArray(value)) {
+ value.forEach((item, idx) => {
+ const itemPath = `${basePath}[${idx}]`;
+ const li = document.createElement('li');
+ li.appendChild(renderSection('', '', '', item, {
+ ...options,
+ portIdPrefix: itemPath
+ }));
+ list.appendChild(li);
+ });
+ } else {
+ Object.entries(value).forEach(([key, val]) => {
+ const li = document.createElement('li');
+ li.appendChild(renderSection(key, '', key, val, {
+ ...options,
+ portIdPrefix: basePath
+ }));
+ list.appendChild(li);
+ });
+ }
- return list;
+ return list;
}
function escapeHtml(text) {
- return text
- .replace(/&/g, "&")
- .replace(//g, ">")
- .replace(/"/g, """);
-}
\ No newline at end of file
+ return text
+ .replace(/&/g, '&')
+ .replace(//g, '>')
+ .replace(/"/g, '"');
+}
diff --git a/webview/components/renderTimerActions.js b/webview/components/renderTimerActions.js
index 2bc77d3..dfd6254 100644
--- a/webview/components/renderTimerActions.js
+++ b/webview/components/renderTimerActions.js
@@ -19,67 +19,67 @@ import { renderSection } from './renderSection.js';
import { LayoutManager } from '../core/layoutManager.js';
export function renderTimerActions(container, timerActions, options = {}) {
- timerActions.forEach((group, idx) => {
- const path = options.pathPrefix ? `${options.pathPrefix}.timer_actions[${idx}]` : `timer_actions[${idx}]`;
- const block = renderTimerAction(group, { ...options, path });
+ timerActions.forEach((group, idx) => {
+ const path = options.pathPrefix ? `${options.pathPrefix}.timer_actions[${idx}]` : `timer_actions[${idx}]`;
+ const block = renderTimerAction(group, { ...options, path });
- container.appendChild(block);
- options.renderBlock(block, "timer-action");
- });
+ container.appendChild(block);
+ options.renderBlock(block, 'timer-action');
+ });
}
export function renderTimerAction(timerAction, options = {}) {
- const timerActionBox = renderBaseBlock({
- type: "timer-action",
- options
- });
+ const timerActionBox = renderBaseBlock({
+ type: 'timer-action',
+ options
+ });
- // Header
- const header = document.createElement("div");
- header.className = "timer-action-header";
+ // Header
+ const header = document.createElement('div');
+ header.className = 'timer-action-header';
- // Render additional sections
- const metaSections = [
- { key: "period", icon: "๐งญ", label: "Period", value: timerAction.period },
- ];
+ // Render additional sections
+ const metaSections = [
+ { key: 'period', icon: '๐งญ', label: 'Period', value: timerAction.period },
+ ];
- metaSections.forEach(( {key, icon, label, value }) => {
- if (value) {
- const section = renderSection(key, icon, label, value, {
- includeLeftPort: true,
- portIdPrefix: options.path,
- portRegistry: options.portRegistry
- });
- header.appendChild(section);
- }
- });
-
- timerActionBox.append(header);
+ metaSections.forEach(({ key, icon, label, value }) => {
+ if (value) {
+ const section = renderSection(key, icon, label, value, {
+ includeLeftPort: true,
+ portIdPrefix: options.path,
+ portRegistry: options.portRegistry
+ });
+ header.appendChild(section);
+ }
+ });
- // Body
- const body = document.createElement("div");
- body.className = "timer-action-body";
- timerActionBox.appendChild(body);
+ timerActionBox.append(header);
- const innerLayoutManager = new LayoutManager(20, 40, 80, 40);
- const childOptions = {
- ...options,
- stopPropagation: true,
- constrainToParent: true,
- pathPrefix: `${options.path}.actions`,
- renderBlock: (block, columnType) => {
- requestAnimationFrame(() => {
- innerLayoutManager.placeBlock(block, columnType);
- });
- }
- };
+ // Body
+ const body = document.createElement('div');
+ body.className = 'timer-action-body';
+ timerActionBox.appendChild(body);
- const actions = timerAction.actions || {};
- for (const [key, value] of Object.entries(actions)) {
- renderComponent({ type: key, value: value }, body, childOptions);
+ const innerLayoutManager = new LayoutManager(20, 40, 80, 40);
+ const childOptions = {
+ ...options,
+ stopPropagation: true,
+ constrainToParent: true,
+ pathPrefix: `${options.path}.actions`,
+ renderBlock: (block, columnType) => {
+ requestAnimationFrame(() => {
+ innerLayoutManager.placeBlock(block, columnType);
+ });
}
+ };
- renderAutoResizableBody(timerActionBox, "block", [".timer-action-header"]);
+ const actions = timerAction.actions || {};
+ for (const [key, value] of Object.entries(actions)) {
+ renderComponent({ type: key, value: value }, body, childOptions);
+ }
- return timerActionBox;
-}
\ No newline at end of file
+ renderAutoResizableBody(timerActionBox, 'block', ['.timer-action-header']);
+
+ return timerActionBox;
+}
diff --git a/webview/components/setupRenderers.js b/webview/components/setupRenderers.js
index f2470b1..c56279e 100644
--- a/webview/components/setupRenderers.js
+++ b/webview/components/setupRenderers.js
@@ -12,59 +12,59 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import { registerRenderer } from "../core/dispatcher.js";
-import { renderArguments } from "./renderArguments.js";
-import { renderIncludesGroup } from "./renderInclude.js";
-import { renderNodeGroup } from "./renderNode.js";
-import { renderGroupGroup } from "./renderGroup.js";
-import { renderOpaqueFunctionGroup } from "./renderOpaqueFunction.js";
-import { renderComposableContainerGroup } from "./renderComposableContainer.js";
-import { renderComposableNodeGroup } from "./renderComposableNode.js";
-import { renderEventHandlerGroup } from "./renderEventHandler.js";
-import { renderPythonExpressions } from "./renderPythonExpressions.js";
-import { renderEnvironmentVariables } from "./renderEnvironmentVariables.js";
-import { renderTimerActions } from "./renderTimerActions.js";
+import { registerRenderer } from '../core/dispatcher.js';
+import { renderArguments } from './renderArguments.js';
+import { renderIncludesGroup } from './renderInclude.js';
+import { renderNodeGroup } from './renderNode.js';
+import { renderGroupGroup } from './renderGroup.js';
+import { renderOpaqueFunctionGroup } from './renderOpaqueFunction.js';
+import { renderComposableContainerGroup } from './renderComposableContainer.js';
+import { renderComposableNodeGroup } from './renderComposableNode.js';
+import { renderEventHandlerGroup } from './renderEventHandler.js';
+import { renderPythonExpressions } from './renderPythonExpressions.js';
+import { renderEnvironmentVariables } from './renderEnvironmentVariables.js';
+import { renderTimerActions } from './renderTimerActions.js';
-registerRenderer("arguments", (obj, container, options) => {
- renderArguments(container, obj.value || [], options);
+registerRenderer('arguments', (obj, container, options) => {
+ renderArguments(container, obj.value || [], options);
});
-registerRenderer("environment_variables", (obj, container, options) => {
- renderEnvironmentVariables(container, obj.value || [], options);
+registerRenderer('environment_variables', (obj, container, options) => {
+ renderEnvironmentVariables(container, obj.value || [], options);
});
-registerRenderer("python_expressions", (obj, container, options) => {
- renderPythonExpressions(container, obj.value || [], options);
+registerRenderer('python_expressions', (obj, container, options) => {
+ renderPythonExpressions(container, obj.value || [], options);
});
-registerRenderer("event_handlers", (obj, container, options) => {
- renderEventHandlerGroup(container, obj.value || [], options);
+registerRenderer('event_handlers', (obj, container, options) => {
+ renderEventHandlerGroup(container, obj.value || [], options);
});
-registerRenderer("nodes", (obj, container, options) => {
- renderNodeGroup(container, obj.value || [], options);
+registerRenderer('nodes', (obj, container, options) => {
+ renderNodeGroup(container, obj.value || [], options);
});
-registerRenderer("includes", (obj, container, options) => {
- renderIncludesGroup(container, obj.value || [], options);
+registerRenderer('includes', (obj, container, options) => {
+ renderIncludesGroup(container, obj.value || [], options);
});
-registerRenderer("groups", (obj, container, options) => {
- renderGroupGroup(container, obj.value || [], options);
+registerRenderer('groups', (obj, container, options) => {
+ renderGroupGroup(container, obj.value || [], options);
});
-registerRenderer("timer_actions", (obj, container, options) => {
- renderTimerActions(container, obj.value || [], options);
+registerRenderer('timer_actions', (obj, container, options) => {
+ renderTimerActions(container, obj.value || [], options);
});
-registerRenderer("opaque_functions", (obj, container, options) => {
- renderOpaqueFunctionGroup(container, obj.value || [], options);
+registerRenderer('opaque_functions', (obj, container, options) => {
+ renderOpaqueFunctionGroup(container, obj.value || [], options);
});
-registerRenderer("composable_nodes_container", (obj, container, options) => {
- renderComposableContainerGroup(container, obj.value || [], options);
+registerRenderer('composable_nodes_container', (obj, container, options) => {
+ renderComposableContainerGroup(container, obj.value || [], options);
});
-registerRenderer("composable_nodes", (obj, container, options) => {
- renderComposableNodeGroup(container, obj.value || [], options);
-});
\ No newline at end of file
+registerRenderer('composable_nodes', (obj, container, options) => {
+ renderComposableNodeGroup(container, obj.value || [], options);
+});
diff --git a/webview/core/dispatcher.js b/webview/core/dispatcher.js
index 68d9931..299353d 100644
--- a/webview/core/dispatcher.js
+++ b/webview/core/dispatcher.js
@@ -15,13 +15,13 @@
const registry = new Map();
export function registerRenderer(type, fn) {
- registry.set(type, fn);
+ registry.set(type, fn);
}
export function getRenderer(type) {
- return registry.get(type);
+ return registry.get(type);
}
export function getRegisteredRenderKeys() {
- return Array.from(registry.keys());
-}
\ No newline at end of file
+ return Array.from(registry.keys());
+}
diff --git a/webview/core/layoutManager.js b/webview/core/layoutManager.js
index 3e2ae3d..cc94ae5 100644
--- a/webview/core/layoutManager.js
+++ b/webview/core/layoutManager.js
@@ -13,82 +13,81 @@
// limitations under the License.
export class LayoutManager {
- constructor(startX = 100, startY = 100, columnSpacing = 350, rowSpacing = 40) {
- this.startX = startX;
- this.startY = startY;
- this.columnSpacing = columnSpacing;
- this.rowSpacing = rowSpacing;
- this.columns = [];
+ constructor(startX = 100, startY = 100, columnSpacing = 350, rowSpacing = 40) {
+ this.startX = startX;
+ this.startY = startY;
+ this.columnSpacing = columnSpacing;
+ this.rowSpacing = rowSpacing;
+ this.columns = [];
- this.typeToColumn = {
- "argument": 0,
- "environment-variable": 0,
- "python-expression": 0,
- "event-handler": 1,
- "node": 2,
- "composable-node": 2,
- "include": 2,
- "group": 3,
- "timer-action": 3,
- "composable-container": 3,
- "opaque-function": 3
- };
- }
-
- getColumnIndexForType(type) {
- return this.typeToColumn[type] ?? 0;
- }
+ this.typeToColumn = {
+ 'argument': 0,
+ 'environment-variable': 0,
+ 'python-expression': 0,
+ 'event-handler': 1,
+ 'node': 2,
+ 'composable-node': 2,
+ 'include': 2,
+ 'group': 3,
+ 'timer-action': 3,
+ 'composable-container': 3,
+ 'opaque-function': 3
+ };
+ }
- ensureColumn(columnIndex) {
- // Build all missing columns up to columnIndex
- for (let i = 0; i <= columnIndex; i++) {
- if (!this.columns[i]) {
- const prev = this.columns[i-1];
- this.columns[i] = {
- x: prev ? prev.x + prev.maxWidth + this.columnSpacing : this.startX,
- y: this.startY,
- maxWidth: 0,
- blocks: []
- };
- }
- }
+ getColumnIndexForType(type) {
+ return this.typeToColumn[type] ?? 0;
+ }
- return this.columns[columnIndex];
+ ensureColumn(columnIndex) {
+ // Build all missing columns up to columnIndex
+ for (let i = 0; i <= columnIndex; i++) {
+ if (!this.columns[i]) {
+ const prev = this.columns[i-1];
+ this.columns[i] = {
+ x: prev ? prev.x + prev.maxWidth + this.columnSpacing : this.startX,
+ y: this.startY,
+ maxWidth: 0,
+ blocks: []
+ };
+ }
}
- placeBlock(block, type) {
- const columnIndex = this.getColumnIndexForType(type);
- const col = this.ensureColumn(columnIndex);
- const rect = block.getBoundingClientRect();
+ return this.columns[columnIndex];
+ }
+
+ placeBlock(block, type) {
+ const columnIndex = this.getColumnIndexForType(type);
+ const col = this.ensureColumn(columnIndex);
+ const rect = block.getBoundingClientRect();
- block.style.left = `${col.x}px`;
- block.style.top = `${col.y}px`;
+ block.style.left = `${col.x}px`;
+ block.style.top = `${col.y}px`;
- col.blocks.push(block);
+ col.blocks.push(block);
- col.y += rect.height + this.rowSpacing;
- if (rect.width > col.maxWidth) {
- col.maxWidth = rect.width;
- this.reflowColumnsFrom(columnIndex + 1);
- }
+ col.y += rect.height + this.rowSpacing;
+ if (rect.width > col.maxWidth) {
+ col.maxWidth = rect.width;
+ this.reflowColumnsFrom(columnIndex + 1);
}
+ }
- reflowColumnsFrom(startIndex) {
- for (let i = startIndex; i < this.columns.length; i++) {
- const prev = this.columns[i - 1];
- const curr = this.columns[i];
- curr.x = prev.x + prev.maxWidth + this.columnSpacing;
+ reflowColumnsFrom(startIndex) {
+ for (let i = startIndex; i < this.columns.length; i++) {
+ const prev = this.columns[i - 1];
+ const curr = this.columns[i];
+ curr.x = prev.x + prev.maxWidth + this.columnSpacing;
- curr.blocks.forEach(block => {
- const rect = block.getBoundingClientRect();
- const top = parseInt(block.style.top, 10);
- block.style.left = `${curr.x}px`;
- block.style.top = `${top}px`;
- });
- }
+ curr.blocks.forEach(block => {
+ const top = parseInt(block.style.top, 10);
+ block.style.left = `${curr.x}px`;
+ block.style.top = `${top}px`;
+ });
}
+ }
- nextColumnIndex() {
- return this.columns.length;
- }
-}
\ No newline at end of file
+ nextColumnIndex() {
+ return this.columns.length;
+ }
+}
diff --git a/webview/core/registry.js b/webview/core/registry.js
index e31ef75..08b4080 100644
--- a/webview/core/registry.js
+++ b/webview/core/registry.js
@@ -13,5 +13,5 @@
// limitations under the License.
export const registrySystem = {
- portRegistry: {},
-}
\ No newline at end of file
+ portRegistry: {},
+};
diff --git a/webview/core/renderAll.js b/webview/core/renderAll.js
index b6ad80f..4f404b4 100644
--- a/webview/core/renderAll.js
+++ b/webview/core/renderAll.js
@@ -12,55 +12,55 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-import { registrySystem } from "./registry.js";
-import { renderComponent } from "../components/renderComponent.js";
-import { enableZoomAndPan } from "./zoomPanController.js";
-import { renderEdges } from "./renderEdges.js";
-import { getRegisteredRenderKeys } from "./dispatcher.js";
-import { LayoutManager } from "./layoutManager.js";
-import { autoFitToScreen } from "../utils/autoFitToScreen.js";
+import { registrySystem } from './registry.js';
+import { renderComponent } from '../components/renderComponent.js';
+import { enableZoomAndPan } from './zoomPanController.js';
+import { renderEdges } from './renderEdges.js';
+import { getRegisteredRenderKeys } from './dispatcher.js';
+import { LayoutManager } from './layoutManager.js';
+import { autoFitToScreen } from '../utils/autoFitToScreen.js';
export function renderAll(data) {
- const editor = document.getElementById("editor");
- editor.innerHTML = "";
+ const editor = document.getElementById('editor');
+ editor.innerHTML = '';
- // Create zoom layer
- const zoomLayer = document.createElement("div");
- zoomLayer.id = "zoom-layer";
- editor.appendChild(zoomLayer);
+ // Create zoom layer
+ const zoomLayer = document.createElement('div');
+ zoomLayer.id = 'zoom-layer';
+ editor.appendChild(zoomLayer);
- // Edges
- const edgeLayer = document.createElementNS("http://www.w3.org/2000/svg", "svg");
- edgeLayer.setAttribute("id", "edge-layer");
- edgeLayer.classList.add("edge-layer");
- zoomLayer.appendChild(edgeLayer);
+ // Edges
+ const edgeLayer = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
+ edgeLayer.setAttribute('id', 'edge-layer');
+ edgeLayer.classList.add('edge-layer');
+ zoomLayer.appendChild(edgeLayer);
- // Render
- const layoutManager = new LayoutManager();
- const context = {
- parsedData: data,
- portRegistry: registrySystem.portRegistry,
- renderEdges,
- renderBlock: (block, columnType) => {
- requestAnimationFrame(() => {
- layoutManager.placeBlock(block, columnType);
- });
- }
- };
+ // Render
+ const layoutManager = new LayoutManager();
+ const context = {
+ parsedData: data,
+ portRegistry: registrySystem.portRegistry,
+ renderEdges,
+ renderBlock: (block, columnType) => {
+ requestAnimationFrame(() => {
+ layoutManager.placeBlock(block, columnType);
+ });
+ }
+ };
- const renderKeys = getRegisteredRenderKeys();
- for (const key of Object.keys(data)) {
- if (renderKeys.includes(key)) {
- const value = data[key];
- const typeHint = Array.isArray(value) ? key : value.type || key;
- renderComponent({ value: value, type: typeHint }, zoomLayer, context);
- }
+ const renderKeys = getRegisteredRenderKeys();
+ for (const key of Object.keys(data)) {
+ if (renderKeys.includes(key)) {
+ const value = data[key];
+ const typeHint = Array.isArray(value) ? key : value.type || key;
+ renderComponent({ value: value, type: typeHint }, zoomLayer, context);
}
+ }
+
+ requestAnimationFrame(() => {
+ autoFitToScreen(editor, zoomLayer);
+ renderEdges(data, registrySystem.portRegistry);
+ });
- requestAnimationFrame(() => {
- autoFitToScreen(editor, zoomLayer);
- renderEdges(data, registrySystem.portRegistry);
- });
-
- enableZoomAndPan(editor, zoomLayer, () => renderEdges(data, registrySystem.portRegistry));
-}
\ No newline at end of file
+ enableZoomAndPan(editor, zoomLayer, () => renderEdges(data, registrySystem.portRegistry));
+}
diff --git a/webview/core/renderEdges.js b/webview/core/renderEdges.js
index bec2c33..39faf44 100644
--- a/webview/core/renderEdges.js
+++ b/webview/core/renderEdges.js
@@ -13,76 +13,76 @@
// limitations under the License.
export function renderEdges(data, portRegistry) {
- document.getElementById("edge-layer").innerHTML = "";
-
- (data.launch_argument_usages || []).forEach(usage => {
- const fromPortId = `argument:${usage.argument}.argument`;
- const toPortId = `${usage.path}`;
- connectPorts(fromPortId, toPortId, portRegistry);
- });
-
- (data.environment_variable_usages || []).forEach(usage => {
- const fromPortId = `argument:${usage.argument}.argument`;
- const toPortId = `${usage.path}`;
- connectPorts(fromPortId, toPortId, portRegistry);
- });
-
- (data.python_expression_usages || []).forEach(usage => {
- const fromPortId = `variable:${usage.variable}.variable`;
- const toPortId = `${usage.path}`;
- connectPorts(fromPortId, toPortId, portRegistry);
- });
-
- (data.event_handlers || []).forEach((handler, index) => {
- const triggersPortId = `${handler.triggered_by[0]}`;
- const eventTriggeredPortId = `events[${index}].events.triggered_by`;
- connectPorts(triggersPortId, eventTriggeredPortId, portRegistry, "event-handler");
-
- const eventTriggersPortId = `events[${index}].events.triggers`;
- const triggeredPortId = `${handler.triggers[0]}`;
- connectPorts(eventTriggersPortId, triggeredPortId, portRegistry, "event-handler");
- });
+ document.getElementById('edge-layer').innerHTML = '';
+
+ (data.launch_argument_usages || []).forEach(usage => {
+ const fromPortId = `argument:${usage.argument}.argument`;
+ const toPortId = `${usage.path}`;
+ connectPorts(fromPortId, toPortId, portRegistry);
+ });
+
+ (data.environment_variable_usages || []).forEach(usage => {
+ const fromPortId = `argument:${usage.argument}.argument`;
+ const toPortId = `${usage.path}`;
+ connectPorts(fromPortId, toPortId, portRegistry);
+ });
+
+ (data.python_expression_usages || []).forEach(usage => {
+ const fromPortId = `variable:${usage.variable}.variable`;
+ const toPortId = `${usage.path}`;
+ connectPorts(fromPortId, toPortId, portRegistry);
+ });
+
+ (data.event_handlers || []).forEach((handler, index) => {
+ const triggersPortId = `${handler.triggered_by[0]}`;
+ const eventTriggeredPortId = `events[${index}].events.triggered_by`;
+ connectPorts(triggersPortId, eventTriggeredPortId, portRegistry, 'event-handler');
+
+ const eventTriggersPortId = `events[${index}].events.triggers`;
+ const triggeredPortId = `${handler.triggers[0]}`;
+ connectPorts(eventTriggersPortId, triggeredPortId, portRegistry, 'event-handler');
+ });
}
-function connectPorts(fromPortId, toPortId, portRegistry, className="") {
- const fromPort = portRegistry[fromPortId];
- const toPort = portRegistry[toPortId];
+function connectPorts(fromPortId, toPortId, portRegistry, className='') {
+ const fromPort = portRegistry[fromPortId];
+ const toPort = portRegistry[toPortId];
- if (!fromPort || !toPort) return;
+ if (!fromPort || !toPort) return;
- fromPort.classList.add("used-port");
- toPort.classList.add("used-port");
+ fromPort.classList.add('used-port');
+ toPort.classList.add('used-port');
- const from = getCenter(fromPort);
- const to = getCenter(toPort);
+ const from = getCenter(fromPort);
+ const to = getCenter(toPort);
- drawEdge(from, to, className);
+ drawEdge(from, to, className);
}
function getCenter(el) {
- const zoomLayer = document.getElementById("zoom-layer");
- const zoomRect = zoomLayer.getBoundingClientRect();
- const rect = el.getBoundingClientRect();
+ const zoomLayer = document.getElementById('zoom-layer');
+ const zoomRect = zoomLayer.getBoundingClientRect();
+ const rect = el.getBoundingClientRect();
- const zoom = window.zoomState?.scale || 1;
+ const zoom = window.zoomState?.scale || 1;
- return {
- x: (rect.left - zoomRect.left + rect.width / 2) / zoom ,
- y: (rect.top - zoomRect.top + rect.height / 2) / zoom
- };
+ return {
+ x: (rect.left - zoomRect.left + rect.width / 2) / zoom,
+ y: (rect.top - zoomRect.top + rect.height / 2) / zoom
+ };
}
function drawEdge(from, to, className) {
- const svg = document.getElementById("edge-layer");
- const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
+ const svg = document.getElementById('edge-layer');
+ const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
- const dx = Math.abs(to.x - from.x);
- const controlOffset = Math.min(100, dx / 2);
+ const dx = Math.abs(to.x - from.x);
+ const controlOffset = Math.min(100, dx / 2);
- const d = `M ${from.x},${from.y} C ${from.x + controlOffset},${from.y} ${to.x - controlOffset},${to.y} ${to.x},${to.y}`;
+ const d = `M ${from.x},${from.y} C ${from.x + controlOffset},${from.y} ${to.x - controlOffset},${to.y} ${to.x},${to.y}`;
- path.setAttribute("d", d);
- path.setAttribute("class", `edge-line ${className}`.trim());
+ path.setAttribute('d', d);
+ path.setAttribute('class', `edge-line ${className}`.trim());
- svg.appendChild(path);
+ svg.appendChild(path);
}
diff --git a/webview/core/zoomPanController.js b/webview/core/zoomPanController.js
index aae6dcc..61d2097 100644
--- a/webview/core/zoomPanController.js
+++ b/webview/core/zoomPanController.js
@@ -13,62 +13,62 @@
// limitations under the License.
export function enableZoomAndPan(editor, zoomLayer, renderEdgesCallback) {
- const zoomState = {
- scale: 1,
- offsetX: 0,
- offsetY: 0
- };
- window.zoomState = zoomState;
+ const zoomState = {
+ scale: 1,
+ offsetX: 0,
+ offsetY: 0
+ };
+ window.zoomState = zoomState;
- let isPanning = false;
- let startX, startY;
+ let isPanning = false;
+ let startX, startY;
- const ZOOM_MIN = 0.2;
- const ZOOM_MAX = 3;
+ const ZOOM_MIN = 0.2;
+ const ZOOM_MAX = 3;
- function applyTransform() {
- zoomLayer.style.transform = `translate(${zoomState.offsetX}px, ${zoomState.offsetY}px) scale(${zoomState.scale})`;
- if (renderEdgesCallback) renderEdgesCallback();
- }
+ function applyTransform() {
+ zoomLayer.style.transform = `translate(${zoomState.offsetX}px, ${zoomState.offsetY}px) scale(${zoomState.scale})`;
+ if (renderEdgesCallback) renderEdgesCallback();
+ }
- // Shift + Drag to pan
- editor.addEventListener('mousedown', (e) => {
- if (e.shiftKey) {
- isPanning = true;
- startX = e.clientX;
- startY = e.clientY;
- e.preventDefault();
- }
- });
+ // Shift + Drag to pan
+ editor.addEventListener('mousedown', (e) => {
+ if (e.shiftKey) {
+ isPanning = true;
+ startX = e.clientX;
+ startY = e.clientY;
+ e.preventDefault();
+ }
+ });
- window.addEventListener('mousemove', (e) => {
- if (!isPanning) return;
- zoomState.offsetX += e.clientX - startX;
- zoomState.offsetY += e.clientY - startY;
- startX = e.clientX;
- startY = e.clientY;
- applyTransform();
- });
+ window.addEventListener('mousemove', (e) => {
+ if (!isPanning) return;
+ zoomState.offsetX += e.clientX - startX;
+ zoomState.offsetY += e.clientY - startY;
+ startX = e.clientX;
+ startY = e.clientY;
+ applyTransform();
+ });
- window.addEventListener('mouseup', () => isPanning = false);
+ window.addEventListener('mouseup', () => isPanning = false);
- // Scroll to zoom
- editor.addEventListener("wheel", (e) => {
- if (!e.ctrlKey && !e.shiftKey) {
- e.preventDefault();
- const zoomFactor = 1 - e.deltaY * 0.001;
- const newZoom = Math.min(ZOOM_MAX, Math.max(ZOOM_MIN, zoomState.scale * zoomFactor));
+ // Scroll to zoom
+ editor.addEventListener('wheel', (e) => {
+ if (!e.ctrlKey && !e.shiftKey) {
+ e.preventDefault();
+ const zoomFactor = 1 - e.deltaY * 0.001;
+ const newZoom = Math.min(ZOOM_MAX, Math.max(ZOOM_MIN, zoomState.scale * zoomFactor));
- const rect = zoomLayer.getBoundingClientRect();
- const dx = (e.clientX - rect.left - zoomState.offsetX) / zoomState.scale;
- const dy = (e.clientY - rect.top - zoomState.offsetY) / zoomState.scale;
+ const rect = zoomLayer.getBoundingClientRect();
+ const dx = (e.clientX - rect.left - zoomState.offsetX) / zoomState.scale;
+ const dy = (e.clientY - rect.top - zoomState.offsetY) / zoomState.scale;
- zoomState.offsetX -= dx * (newZoom - zoomState.scale);
- zoomState.offsetY -= dy * (newZoom - zoomState.scale);
- zoomState.scale = newZoom;
- applyTransform();
- }
- }, { passive: false });
+ zoomState.offsetX -= dx * (newZoom - zoomState.scale);
+ zoomState.offsetY -= dy * (newZoom - zoomState.scale);
+ zoomState.scale = newZoom;
+ applyTransform();
+ }
+ }, { passive: false });
- applyTransform();
-}
\ No newline at end of file
+ applyTransform();
+}
diff --git a/webview/script.js b/webview/script.js
index 1b843ea..12b0f3c 100644
--- a/webview/script.js
+++ b/webview/script.js
@@ -16,13 +16,14 @@ import './components/setupRenderers.js';
import { renderAll } from './core/renderAll.js';
window.addEventListener('message', (event) => {
- const message = event.data;
- if (message.type == 'launchmap-data') {
- renderAll(message.data);
- }
+ const message = event.data;
+ if (message.type == 'launchmap-data') {
+ renderAll(message.data);
+ }
});
+// eslint-disable-next-line no-undef
const vscode = acquireVsCodeApi();
document.getElementById('export-btn')?.addEventListener('click', () => {
- vscode.postMessage({ type: 'export-json' });
-});
\ No newline at end of file
+ vscode.postMessage({ type: 'export-json' });
+});
diff --git a/webview/tests/__screenshots__/component-drag.spec.js-snapshots/argument-drag-darwin.png b/webview/tests/__screenshots__/component-drag.spec.js-snapshots/argument-drag-darwin.png
deleted file mode 100644
index c373d5b..0000000
Binary files a/webview/tests/__screenshots__/component-drag.spec.js-snapshots/argument-drag-darwin.png and /dev/null differ
diff --git a/webview/tests/__screenshots__/component-drag.spec.js-snapshots/argument-drag-linux.png b/webview/tests/__screenshots__/component-drag.spec.js-snapshots/argument-drag-linux.png
new file mode 100644
index 0000000..078c327
Binary files /dev/null and b/webview/tests/__screenshots__/component-drag.spec.js-snapshots/argument-drag-linux.png differ
diff --git a/webview/tests/__screenshots__/component-drag.spec.js-snapshots/composable-container-drag-darwin.png b/webview/tests/__screenshots__/component-drag.spec.js-snapshots/composable-container-drag-darwin.png
deleted file mode 100644
index 332c9c3..0000000
Binary files a/webview/tests/__screenshots__/component-drag.spec.js-snapshots/composable-container-drag-darwin.png and /dev/null differ
diff --git a/webview/tests/__screenshots__/component-drag.spec.js-snapshots/composable-container-drag-linux.png b/webview/tests/__screenshots__/component-drag.spec.js-snapshots/composable-container-drag-linux.png
new file mode 100644
index 0000000..f2d9685
Binary files /dev/null and b/webview/tests/__screenshots__/component-drag.spec.js-snapshots/composable-container-drag-linux.png differ
diff --git a/webview/tests/__screenshots__/component-drag.spec.js-snapshots/composable-node-drag-darwin.png b/webview/tests/__screenshots__/component-drag.spec.js-snapshots/composable-node-drag-darwin.png
deleted file mode 100644
index 0acd71c..0000000
Binary files a/webview/tests/__screenshots__/component-drag.spec.js-snapshots/composable-node-drag-darwin.png and /dev/null differ
diff --git a/webview/tests/__screenshots__/component-drag.spec.js-snapshots/composable-node-drag-linux.png b/webview/tests/__screenshots__/component-drag.spec.js-snapshots/composable-node-drag-linux.png
new file mode 100644
index 0000000..7b77d3c
Binary files /dev/null and b/webview/tests/__screenshots__/component-drag.spec.js-snapshots/composable-node-drag-linux.png differ
diff --git a/webview/tests/__screenshots__/component-drag.spec.js-snapshots/env-var-drag-darwin.png b/webview/tests/__screenshots__/component-drag.spec.js-snapshots/env-var-drag-darwin.png
deleted file mode 100644
index b0103ee..0000000
Binary files a/webview/tests/__screenshots__/component-drag.spec.js-snapshots/env-var-drag-darwin.png and /dev/null differ
diff --git a/webview/tests/__screenshots__/component-drag.spec.js-snapshots/env-var-drag-linux.png b/webview/tests/__screenshots__/component-drag.spec.js-snapshots/env-var-drag-linux.png
new file mode 100644
index 0000000..b9ac7cc
Binary files /dev/null and b/webview/tests/__screenshots__/component-drag.spec.js-snapshots/env-var-drag-linux.png differ
diff --git a/webview/tests/__screenshots__/component-drag.spec.js-snapshots/event-handler-drag-darwin.png b/webview/tests/__screenshots__/component-drag.spec.js-snapshots/event-handler-drag-darwin.png
deleted file mode 100644
index debe2c8..0000000
Binary files a/webview/tests/__screenshots__/component-drag.spec.js-snapshots/event-handler-drag-darwin.png and /dev/null differ
diff --git a/webview/tests/__screenshots__/component-drag.spec.js-snapshots/event-handler-drag-linux.png b/webview/tests/__screenshots__/component-drag.spec.js-snapshots/event-handler-drag-linux.png
new file mode 100644
index 0000000..6073630
Binary files /dev/null and b/webview/tests/__screenshots__/component-drag.spec.js-snapshots/event-handler-drag-linux.png differ
diff --git a/webview/tests/__screenshots__/component-drag.spec.js-snapshots/group-drag-darwin.png b/webview/tests/__screenshots__/component-drag.spec.js-snapshots/group-drag-darwin.png
deleted file mode 100644
index 2323d27..0000000
Binary files a/webview/tests/__screenshots__/component-drag.spec.js-snapshots/group-drag-darwin.png and /dev/null differ
diff --git a/webview/tests/__screenshots__/component-drag.spec.js-snapshots/group-drag-linux.png b/webview/tests/__screenshots__/component-drag.spec.js-snapshots/group-drag-linux.png
new file mode 100644
index 0000000..1bcca84
Binary files /dev/null and b/webview/tests/__screenshots__/component-drag.spec.js-snapshots/group-drag-linux.png differ
diff --git a/webview/tests/__screenshots__/component-drag.spec.js-snapshots/group-node-drag-darwin.png b/webview/tests/__screenshots__/component-drag.spec.js-snapshots/group-node-drag-darwin.png
deleted file mode 100644
index 0b7e42b..0000000
Binary files a/webview/tests/__screenshots__/component-drag.spec.js-snapshots/group-node-drag-darwin.png and /dev/null differ
diff --git a/webview/tests/__screenshots__/component-drag.spec.js-snapshots/group-node-drag-linux.png b/webview/tests/__screenshots__/component-drag.spec.js-snapshots/group-node-drag-linux.png
new file mode 100644
index 0000000..7e3e6c6
Binary files /dev/null and b/webview/tests/__screenshots__/component-drag.spec.js-snapshots/group-node-drag-linux.png differ
diff --git a/webview/tests/__screenshots__/component-drag.spec.js-snapshots/include-drag-darwin.png b/webview/tests/__screenshots__/component-drag.spec.js-snapshots/include-drag-darwin.png
deleted file mode 100644
index ae8e834..0000000
Binary files a/webview/tests/__screenshots__/component-drag.spec.js-snapshots/include-drag-darwin.png and /dev/null differ
diff --git a/webview/tests/__screenshots__/component-drag.spec.js-snapshots/include-drag-linux.png b/webview/tests/__screenshots__/component-drag.spec.js-snapshots/include-drag-linux.png
new file mode 100644
index 0000000..3c348b2
Binary files /dev/null and b/webview/tests/__screenshots__/component-drag.spec.js-snapshots/include-drag-linux.png differ
diff --git a/webview/tests/__screenshots__/component-drag.spec.js-snapshots/node-drag-darwin.png b/webview/tests/__screenshots__/component-drag.spec.js-snapshots/node-drag-darwin.png
deleted file mode 100644
index 8eb52ef..0000000
Binary files a/webview/tests/__screenshots__/component-drag.spec.js-snapshots/node-drag-darwin.png and /dev/null differ
diff --git a/webview/tests/__screenshots__/component-drag.spec.js-snapshots/node-drag-linux.png b/webview/tests/__screenshots__/component-drag.spec.js-snapshots/node-drag-linux.png
new file mode 100644
index 0000000..ff44029
Binary files /dev/null and b/webview/tests/__screenshots__/component-drag.spec.js-snapshots/node-drag-linux.png differ
diff --git a/webview/tests/__screenshots__/component-drag.spec.js-snapshots/opaque-drag-darwin.png b/webview/tests/__screenshots__/component-drag.spec.js-snapshots/opaque-drag-darwin.png
deleted file mode 100644
index 936b0c5..0000000
Binary files a/webview/tests/__screenshots__/component-drag.spec.js-snapshots/opaque-drag-darwin.png and /dev/null differ
diff --git a/webview/tests/__screenshots__/component-drag.spec.js-snapshots/opaque-drag-linux.png b/webview/tests/__screenshots__/component-drag.spec.js-snapshots/opaque-drag-linux.png
new file mode 100644
index 0000000..a4d1c67
Binary files /dev/null and b/webview/tests/__screenshots__/component-drag.spec.js-snapshots/opaque-drag-linux.png differ
diff --git a/webview/tests/__screenshots__/component-drag.spec.js-snapshots/opaque-node-drag-darwin.png b/webview/tests/__screenshots__/component-drag.spec.js-snapshots/opaque-node-drag-darwin.png
deleted file mode 100644
index 696938c..0000000
Binary files a/webview/tests/__screenshots__/component-drag.spec.js-snapshots/opaque-node-drag-darwin.png and /dev/null differ
diff --git a/webview/tests/__screenshots__/component-drag.spec.js-snapshots/opaque-node-drag-linux.png b/webview/tests/__screenshots__/component-drag.spec.js-snapshots/opaque-node-drag-linux.png
new file mode 100644
index 0000000..96d7961
Binary files /dev/null and b/webview/tests/__screenshots__/component-drag.spec.js-snapshots/opaque-node-drag-linux.png differ
diff --git a/webview/tests/__screenshots__/component-drag.spec.js-snapshots/python-expression-drag-darwin.png b/webview/tests/__screenshots__/component-drag.spec.js-snapshots/python-expression-drag-darwin.png
deleted file mode 100644
index dd69e0b..0000000
Binary files a/webview/tests/__screenshots__/component-drag.spec.js-snapshots/python-expression-drag-darwin.png and /dev/null differ
diff --git a/webview/tests/__screenshots__/component-drag.spec.js-snapshots/python-expression-drag-linux.png b/webview/tests/__screenshots__/component-drag.spec.js-snapshots/python-expression-drag-linux.png
new file mode 100644
index 0000000..959b220
Binary files /dev/null and b/webview/tests/__screenshots__/component-drag.spec.js-snapshots/python-expression-drag-linux.png differ
diff --git a/webview/tests/__screenshots__/component-drag.spec.js-snapshots/timer-action-drag-darwin.png b/webview/tests/__screenshots__/component-drag.spec.js-snapshots/timer-action-drag-darwin.png
deleted file mode 100644
index 3671b85..0000000
Binary files a/webview/tests/__screenshots__/component-drag.spec.js-snapshots/timer-action-drag-darwin.png and /dev/null differ
diff --git a/webview/tests/__screenshots__/component-drag.spec.js-snapshots/timer-action-drag-linux.png b/webview/tests/__screenshots__/component-drag.spec.js-snapshots/timer-action-drag-linux.png
new file mode 100644
index 0000000..2709190
Binary files /dev/null and b/webview/tests/__screenshots__/component-drag.spec.js-snapshots/timer-action-drag-linux.png differ
diff --git a/webview/tests/__screenshots__/component-drag.spec.js-snapshots/timer-action-node-drag-darwin.png b/webview/tests/__screenshots__/component-drag.spec.js-snapshots/timer-action-node-drag-darwin.png
deleted file mode 100644
index 8609ba5..0000000
Binary files a/webview/tests/__screenshots__/component-drag.spec.js-snapshots/timer-action-node-drag-darwin.png and /dev/null differ
diff --git a/webview/tests/__screenshots__/component-drag.spec.js-snapshots/timer-action-node-drag-linux.png b/webview/tests/__screenshots__/component-drag.spec.js-snapshots/timer-action-node-drag-linux.png
new file mode 100644
index 0000000..33b3a2c
Binary files /dev/null and b/webview/tests/__screenshots__/component-drag.spec.js-snapshots/timer-action-node-drag-linux.png differ
diff --git a/webview/tests/__screenshots__/interaction.spec.js-snapshots/launch-file-1-py-json-final-darwin.png b/webview/tests/__screenshots__/interaction.spec.js-snapshots/launch-file-1-py-json-final-darwin.png
deleted file mode 100644
index d62e065..0000000
Binary files a/webview/tests/__screenshots__/interaction.spec.js-snapshots/launch-file-1-py-json-final-darwin.png and /dev/null differ
diff --git a/webview/tests/__screenshots__/interaction.spec.js-snapshots/launch-file-1-py-json-final-linux.png b/webview/tests/__screenshots__/interaction.spec.js-snapshots/launch-file-1-py-json-final-linux.png
new file mode 100644
index 0000000..ebe59fc
Binary files /dev/null and b/webview/tests/__screenshots__/interaction.spec.js-snapshots/launch-file-1-py-json-final-linux.png differ
diff --git a/webview/tests/__screenshots__/interaction.spec.js-snapshots/launch-file-2-py-json-final-darwin.png b/webview/tests/__screenshots__/interaction.spec.js-snapshots/launch-file-2-py-json-final-darwin.png
deleted file mode 100644
index 8c80a05..0000000
Binary files a/webview/tests/__screenshots__/interaction.spec.js-snapshots/launch-file-2-py-json-final-darwin.png and /dev/null differ
diff --git a/webview/tests/__screenshots__/interaction.spec.js-snapshots/launch-file-2-py-json-final-linux.png b/webview/tests/__screenshots__/interaction.spec.js-snapshots/launch-file-2-py-json-final-linux.png
new file mode 100644
index 0000000..365cbeb
Binary files /dev/null and b/webview/tests/__screenshots__/interaction.spec.js-snapshots/launch-file-2-py-json-final-linux.png differ
diff --git a/webview/tests/__screenshots__/interaction.spec.js-snapshots/launch-file-3-py-json-final-darwin.png b/webview/tests/__screenshots__/interaction.spec.js-snapshots/launch-file-3-py-json-final-darwin.png
deleted file mode 100644
index de0550c..0000000
Binary files a/webview/tests/__screenshots__/interaction.spec.js-snapshots/launch-file-3-py-json-final-darwin.png and /dev/null differ
diff --git a/webview/tests/__screenshots__/interaction.spec.js-snapshots/launch-file-3-py-json-final-linux.png b/webview/tests/__screenshots__/interaction.spec.js-snapshots/launch-file-3-py-json-final-linux.png
new file mode 100644
index 0000000..c260e82
Binary files /dev/null and b/webview/tests/__screenshots__/interaction.spec.js-snapshots/launch-file-3-py-json-final-linux.png differ
diff --git a/webview/tests/__screenshots__/interaction.spec.js-snapshots/launch-file-4-py-json-final-darwin.png b/webview/tests/__screenshots__/interaction.spec.js-snapshots/launch-file-4-py-json-final-darwin.png
deleted file mode 100644
index f1d1ae9..0000000
Binary files a/webview/tests/__screenshots__/interaction.spec.js-snapshots/launch-file-4-py-json-final-darwin.png and /dev/null differ
diff --git a/webview/tests/__screenshots__/interaction.spec.js-snapshots/launch-file-4-py-json-final-linux.png b/webview/tests/__screenshots__/interaction.spec.js-snapshots/launch-file-4-py-json-final-linux.png
new file mode 100644
index 0000000..55c9065
Binary files /dev/null and b/webview/tests/__screenshots__/interaction.spec.js-snapshots/launch-file-4-py-json-final-linux.png differ
diff --git a/webview/tests/__screenshots__/interaction.spec.js-snapshots/launch-file-5-py-json-final-darwin.png b/webview/tests/__screenshots__/interaction.spec.js-snapshots/launch-file-5-py-json-final-darwin.png
deleted file mode 100644
index b22e924..0000000
Binary files a/webview/tests/__screenshots__/interaction.spec.js-snapshots/launch-file-5-py-json-final-darwin.png and /dev/null differ
diff --git a/webview/tests/__screenshots__/interaction.spec.js-snapshots/launch-file-5-py-json-final-linux.png b/webview/tests/__screenshots__/interaction.spec.js-snapshots/launch-file-5-py-json-final-linux.png
new file mode 100644
index 0000000..ae00d94
Binary files /dev/null and b/webview/tests/__screenshots__/interaction.spec.js-snapshots/launch-file-5-py-json-final-linux.png differ
diff --git a/webview/tests/__screenshots__/interaction.spec.js-snapshots/launch-file-6-py-json-final-darwin.png b/webview/tests/__screenshots__/interaction.spec.js-snapshots/launch-file-6-py-json-final-darwin.png
deleted file mode 100644
index 216930c..0000000
Binary files a/webview/tests/__screenshots__/interaction.spec.js-snapshots/launch-file-6-py-json-final-darwin.png and /dev/null differ
diff --git a/webview/tests/__screenshots__/interaction.spec.js-snapshots/launch-file-6-py-json-final-linux.png b/webview/tests/__screenshots__/interaction.spec.js-snapshots/launch-file-6-py-json-final-linux.png
new file mode 100644
index 0000000..b805a0b
Binary files /dev/null and b/webview/tests/__screenshots__/interaction.spec.js-snapshots/launch-file-6-py-json-final-linux.png differ
diff --git a/webview/tests/__screenshots__/interaction.spec.js-snapshots/nav2-bringup-launch-py-json-final-darwin.png b/webview/tests/__screenshots__/interaction.spec.js-snapshots/nav2-bringup-launch-py-json-final-darwin.png
deleted file mode 100644
index 1128271..0000000
Binary files a/webview/tests/__screenshots__/interaction.spec.js-snapshots/nav2-bringup-launch-py-json-final-darwin.png and /dev/null differ
diff --git a/webview/tests/__screenshots__/interaction.spec.js-snapshots/nav2-bringup-launch-py-json-final-linux.png b/webview/tests/__screenshots__/interaction.spec.js-snapshots/nav2-bringup-launch-py-json-final-linux.png
new file mode 100644
index 0000000..adf18d8
Binary files /dev/null and b/webview/tests/__screenshots__/interaction.spec.js-snapshots/nav2-bringup-launch-py-json-final-linux.png differ
diff --git a/webview/tests/__screenshots__/interaction.spec.js-snapshots/rrbot-launch-py-json-final-darwin.png b/webview/tests/__screenshots__/interaction.spec.js-snapshots/rrbot-launch-py-json-final-darwin.png
deleted file mode 100644
index 4bf95bf..0000000
Binary files a/webview/tests/__screenshots__/interaction.spec.js-snapshots/rrbot-launch-py-json-final-darwin.png and /dev/null differ
diff --git a/webview/tests/__screenshots__/interaction.spec.js-snapshots/rrbot-launch-py-json-final-linux.png b/webview/tests/__screenshots__/interaction.spec.js-snapshots/rrbot-launch-py-json-final-linux.png
new file mode 100644
index 0000000..96f5540
Binary files /dev/null and b/webview/tests/__screenshots__/interaction.spec.js-snapshots/rrbot-launch-py-json-final-linux.png differ
diff --git a/webview/tests/__screenshots__/interaction.spec.js-snapshots/turtlebot3-bringup-launch-py-json-final-darwin.png b/webview/tests/__screenshots__/interaction.spec.js-snapshots/turtlebot3-bringup-launch-py-json-final-darwin.png
deleted file mode 100644
index d90a8fe..0000000
Binary files a/webview/tests/__screenshots__/interaction.spec.js-snapshots/turtlebot3-bringup-launch-py-json-final-darwin.png and /dev/null differ
diff --git a/webview/tests/__screenshots__/interaction.spec.js-snapshots/turtlebot3-bringup-launch-py-json-final-linux.png b/webview/tests/__screenshots__/interaction.spec.js-snapshots/turtlebot3-bringup-launch-py-json-final-linux.png
new file mode 100644
index 0000000..61dd2da
Binary files /dev/null and b/webview/tests/__screenshots__/interaction.spec.js-snapshots/turtlebot3-bringup-launch-py-json-final-linux.png differ
diff --git a/webview/tests/__screenshots__/interaction.spec.js-snapshots/turtlebot4-bringup-launch-py-json-final-darwin.png b/webview/tests/__screenshots__/interaction.spec.js-snapshots/turtlebot4-bringup-launch-py-json-final-darwin.png
deleted file mode 100644
index b224f9c..0000000
Binary files a/webview/tests/__screenshots__/interaction.spec.js-snapshots/turtlebot4-bringup-launch-py-json-final-darwin.png and /dev/null differ
diff --git a/webview/tests/__screenshots__/interaction.spec.js-snapshots/turtlebot4-bringup-launch-py-json-final-linux.png b/webview/tests/__screenshots__/interaction.spec.js-snapshots/turtlebot4-bringup-launch-py-json-final-linux.png
new file mode 100644
index 0000000..b3fda47
Binary files /dev/null and b/webview/tests/__screenshots__/interaction.spec.js-snapshots/turtlebot4-bringup-launch-py-json-final-linux.png differ
diff --git a/webview/tests/__screenshots__/visual.spec.js-snapshots/launch-file-1-py-json-darwin.png b/webview/tests/__screenshots__/visual.spec.js-snapshots/launch-file-1-py-json-darwin.png
deleted file mode 100644
index c82e166..0000000
Binary files a/webview/tests/__screenshots__/visual.spec.js-snapshots/launch-file-1-py-json-darwin.png and /dev/null differ
diff --git a/webview/tests/__screenshots__/visual.spec.js-snapshots/launch-file-1-py-json-linux.png b/webview/tests/__screenshots__/visual.spec.js-snapshots/launch-file-1-py-json-linux.png
new file mode 100644
index 0000000..53486e4
Binary files /dev/null and b/webview/tests/__screenshots__/visual.spec.js-snapshots/launch-file-1-py-json-linux.png differ
diff --git a/webview/tests/__screenshots__/visual.spec.js-snapshots/launch-file-2-py-json-darwin.png b/webview/tests/__screenshots__/visual.spec.js-snapshots/launch-file-2-py-json-darwin.png
deleted file mode 100644
index faca2c7..0000000
Binary files a/webview/tests/__screenshots__/visual.spec.js-snapshots/launch-file-2-py-json-darwin.png and /dev/null differ
diff --git a/webview/tests/__screenshots__/visual.spec.js-snapshots/launch-file-2-py-json-linux.png b/webview/tests/__screenshots__/visual.spec.js-snapshots/launch-file-2-py-json-linux.png
new file mode 100644
index 0000000..186fa57
Binary files /dev/null and b/webview/tests/__screenshots__/visual.spec.js-snapshots/launch-file-2-py-json-linux.png differ
diff --git a/webview/tests/__screenshots__/visual.spec.js-snapshots/launch-file-3-py-json-darwin.png b/webview/tests/__screenshots__/visual.spec.js-snapshots/launch-file-3-py-json-darwin.png
deleted file mode 100644
index 514bd03..0000000
Binary files a/webview/tests/__screenshots__/visual.spec.js-snapshots/launch-file-3-py-json-darwin.png and /dev/null differ
diff --git a/webview/tests/__screenshots__/visual.spec.js-snapshots/launch-file-3-py-json-linux.png b/webview/tests/__screenshots__/visual.spec.js-snapshots/launch-file-3-py-json-linux.png
new file mode 100644
index 0000000..744e9fb
Binary files /dev/null and b/webview/tests/__screenshots__/visual.spec.js-snapshots/launch-file-3-py-json-linux.png differ
diff --git a/webview/tests/__screenshots__/visual.spec.js-snapshots/launch-file-4-py-json-darwin.png b/webview/tests/__screenshots__/visual.spec.js-snapshots/launch-file-4-py-json-darwin.png
deleted file mode 100644
index 2764974..0000000
Binary files a/webview/tests/__screenshots__/visual.spec.js-snapshots/launch-file-4-py-json-darwin.png and /dev/null differ
diff --git a/webview/tests/__screenshots__/visual.spec.js-snapshots/launch-file-4-py-json-linux.png b/webview/tests/__screenshots__/visual.spec.js-snapshots/launch-file-4-py-json-linux.png
new file mode 100644
index 0000000..89e3508
Binary files /dev/null and b/webview/tests/__screenshots__/visual.spec.js-snapshots/launch-file-4-py-json-linux.png differ
diff --git a/webview/tests/__screenshots__/visual.spec.js-snapshots/launch-file-5-py-json-darwin.png b/webview/tests/__screenshots__/visual.spec.js-snapshots/launch-file-5-py-json-darwin.png
deleted file mode 100644
index 9265e17..0000000
Binary files a/webview/tests/__screenshots__/visual.spec.js-snapshots/launch-file-5-py-json-darwin.png and /dev/null differ
diff --git a/webview/tests/__screenshots__/visual.spec.js-snapshots/launch-file-5-py-json-linux.png b/webview/tests/__screenshots__/visual.spec.js-snapshots/launch-file-5-py-json-linux.png
new file mode 100644
index 0000000..b120def
Binary files /dev/null and b/webview/tests/__screenshots__/visual.spec.js-snapshots/launch-file-5-py-json-linux.png differ
diff --git a/webview/tests/__screenshots__/visual.spec.js-snapshots/launch-file-6-py-json-darwin.png b/webview/tests/__screenshots__/visual.spec.js-snapshots/launch-file-6-py-json-darwin.png
deleted file mode 100644
index 03f6f32..0000000
Binary files a/webview/tests/__screenshots__/visual.spec.js-snapshots/launch-file-6-py-json-darwin.png and /dev/null differ
diff --git a/webview/tests/__screenshots__/visual.spec.js-snapshots/launch-file-6-py-json-linux.png b/webview/tests/__screenshots__/visual.spec.js-snapshots/launch-file-6-py-json-linux.png
new file mode 100644
index 0000000..3834a80
Binary files /dev/null and b/webview/tests/__screenshots__/visual.spec.js-snapshots/launch-file-6-py-json-linux.png differ
diff --git a/webview/tests/__screenshots__/visual.spec.js-snapshots/nav2-bringup-launch-py-json-darwin.png b/webview/tests/__screenshots__/visual.spec.js-snapshots/nav2-bringup-launch-py-json-darwin.png
deleted file mode 100644
index abc192b..0000000
Binary files a/webview/tests/__screenshots__/visual.spec.js-snapshots/nav2-bringup-launch-py-json-darwin.png and /dev/null differ
diff --git a/webview/tests/__screenshots__/visual.spec.js-snapshots/nav2-bringup-launch-py-json-linux.png b/webview/tests/__screenshots__/visual.spec.js-snapshots/nav2-bringup-launch-py-json-linux.png
new file mode 100644
index 0000000..5f3cbc6
Binary files /dev/null and b/webview/tests/__screenshots__/visual.spec.js-snapshots/nav2-bringup-launch-py-json-linux.png differ
diff --git a/webview/tests/__screenshots__/visual.spec.js-snapshots/rrbot-launch-py-json-darwin.png b/webview/tests/__screenshots__/visual.spec.js-snapshots/rrbot-launch-py-json-darwin.png
deleted file mode 100644
index c8fd060..0000000
Binary files a/webview/tests/__screenshots__/visual.spec.js-snapshots/rrbot-launch-py-json-darwin.png and /dev/null differ
diff --git a/webview/tests/__screenshots__/visual.spec.js-snapshots/rrbot-launch-py-json-linux.png b/webview/tests/__screenshots__/visual.spec.js-snapshots/rrbot-launch-py-json-linux.png
new file mode 100644
index 0000000..9796358
Binary files /dev/null and b/webview/tests/__screenshots__/visual.spec.js-snapshots/rrbot-launch-py-json-linux.png differ
diff --git a/webview/tests/__screenshots__/visual.spec.js-snapshots/turtlebot3-bringup-launch-py-json-darwin.png b/webview/tests/__screenshots__/visual.spec.js-snapshots/turtlebot3-bringup-launch-py-json-darwin.png
deleted file mode 100644
index 0ceddba..0000000
Binary files a/webview/tests/__screenshots__/visual.spec.js-snapshots/turtlebot3-bringup-launch-py-json-darwin.png and /dev/null differ
diff --git a/webview/tests/__screenshots__/visual.spec.js-snapshots/turtlebot3-bringup-launch-py-json-linux.png b/webview/tests/__screenshots__/visual.spec.js-snapshots/turtlebot3-bringup-launch-py-json-linux.png
new file mode 100644
index 0000000..9b59cae
Binary files /dev/null and b/webview/tests/__screenshots__/visual.spec.js-snapshots/turtlebot3-bringup-launch-py-json-linux.png differ
diff --git a/webview/tests/__screenshots__/visual.spec.js-snapshots/turtlebot4-bringup-launch-py-json-darwin.png b/webview/tests/__screenshots__/visual.spec.js-snapshots/turtlebot4-bringup-launch-py-json-darwin.png
deleted file mode 100644
index b281f8f..0000000
Binary files a/webview/tests/__screenshots__/visual.spec.js-snapshots/turtlebot4-bringup-launch-py-json-darwin.png and /dev/null differ
diff --git a/webview/tests/__screenshots__/visual.spec.js-snapshots/turtlebot4-bringup-launch-py-json-linux.png b/webview/tests/__screenshots__/visual.spec.js-snapshots/turtlebot4-bringup-launch-py-json-linux.png
new file mode 100644
index 0000000..896c285
Binary files /dev/null and b/webview/tests/__screenshots__/visual.spec.js-snapshots/turtlebot4-bringup-launch-py-json-linux.png differ
diff --git a/webview/tests/fixtures/global-setup.js b/webview/tests/fixtures/global-setup.js
index 9202b0f..a3ac4ae 100644
--- a/webview/tests/fixtures/global-setup.js
+++ b/webview/tests/fixtures/global-setup.js
@@ -16,15 +16,15 @@ import { TestServer } from './server.js';
let server;
-async function globalSetup(config) {
- server = new TestServer();
- await server.start();
+async function globalSetup() {
+ server = new TestServer();
+ await server.start();
- process.env.TEST_SERVER_URL = server.url;
+ process.env.TEST_SERVER_URL = server.url;
- return async () => {
- await server.stop();
- };
+ return async () => {
+ await server.stop();
+ };
}
-export default globalSetup;
\ No newline at end of file
+export default globalSetup;
diff --git a/webview/tests/fixtures/sample-data.js b/webview/tests/fixtures/sample-data.js
index b07c9d1..686cb4b 100644
--- a/webview/tests/fixtures/sample-data.js
+++ b/webview/tests/fixtures/sample-data.js
@@ -13,77 +13,77 @@
// limitations under the License.
export const SAMPLE_DATA = {
- arguments: [
- { name: "robot_name", default_value: "rrbot" }
- ],
- environment_variables: [
- { name: "ROS_DISTRO", default_value: "humble" }
- ],
- python_expressions: [
- {
- body: "if ROS_DISTRO == 'humble': tb3_param_dir = LaunchConfiguration('tb3_param_dir')",
- variables: ["${var:tb3_param_dir}"]
- }
- ],
- nodes: [
- { package: "demo_nodes", executable: "talker", name: "talker_node", output: "screen" }
- ],
- includes: [
- { launch_file: "demo.launch.py", arguments: [] }
- ],
- groups: [
- { namespace: "test_ns", actions: { nodes: [{ package: "demo_pkg", executable: "listener" }]}}
- ],
- timer_actions: [
- {
- period: "5.0",
- actions: {
- nodes: [{ package: "demo_nodes", executable: "delayed_talker" }]
- }
- }
- ],
- opaque_functions: [
- {
- name: "launch_setup",
- returns: {
- nodes: [
- { package: "robot_state_publisher", executable: "rsp", name: "rsp_node" }
- ]
- }
- }
- ],
- composable_nodes_container: [
- {
- target_container: "my_container",
- package: "rclcpp_components",
- executable: "component_container_mt",
- composable_nodes: [
- { package: "bar_pkg", plugin: "bar_pkg/BarNode", name: "bar" }
- ]
- }
- ],
- event_handlers: [
- {
- type: "OnProcessExit",
- triggered_by: ["nodes[0].events.triggers"],
- triggers: ["nodes[0].events.triggered_by"]
- }
- ]
-}
+ arguments: [
+ { name: 'robot_name', default_value: 'rrbot' }
+ ],
+ environment_variables: [
+ { name: 'ROS_DISTRO', default_value: 'humble' }
+ ],
+ python_expressions: [
+ {
+ body: 'if ROS_DISTRO == \'humble\': tb3_param_dir = LaunchConfiguration(\'tb3_param_dir\')',
+ variables: ['${var:tb3_param_dir}']
+ }
+ ],
+ nodes: [
+ { package: 'demo_nodes', executable: 'talker', name: 'talker_node', output: 'screen' }
+ ],
+ includes: [
+ { launch_file: 'demo.launch.py', arguments: [] }
+ ],
+ groups: [
+ { namespace: 'test_ns', actions: { nodes: [{ package: 'demo_pkg', executable: 'listener' }] } }
+ ],
+ timer_actions: [
+ {
+ period: '5.0',
+ actions: {
+ nodes: [{ package: 'demo_nodes', executable: 'delayed_talker' }]
+ }
+ }
+ ],
+ opaque_functions: [
+ {
+ name: 'launch_setup',
+ returns: {
+ nodes: [
+ { package: 'robot_state_publisher', executable: 'rsp', name: 'rsp_node' }
+ ]
+ }
+ }
+ ],
+ composable_nodes_container: [
+ {
+ target_container: 'my_container',
+ package: 'rclcpp_components',
+ executable: 'component_container_mt',
+ composable_nodes: [
+ { package: 'bar_pkg', plugin: 'bar_pkg/BarNode', name: 'bar' }
+ ]
+ }
+ ],
+ event_handlers: [
+ {
+ type: 'OnProcessExit',
+ triggered_by: ['nodes[0].events.triggers'],
+ triggers: ['nodes[0].events.triggered_by']
+ }
+ ]
+};
export const COMPONENT_SELECTORS = [
- { type: "argument", selector: ".argument-block" },
- { type: "env-var", selector: ".environment-variable-block" },
- { type: "node", selector: ".node-block" },
- { type: "include", selector: ".include-block" },
- { type: "group", selector: ".group-header" },
- { type: "group-node", selector: ".group-block .node-block" },
- { type: "opaque", selector: ".opaque-function-header" },
- { type: "opaque-node", selector: ".opaque-function-block .node-block" },
- { type: "composable-container", selector: ".composable-container-header" },
- { type: "composable-node", selector: ".composable-node-block" },
- { type: "python-expression", selector: ".python-expression-block" },
- { type: "timer-action", selector: ".timer-action-header" },
- { type: "timer-action-node", selector: ".timer-action-block .node-block" },
- { type: "event-handler", selector: ".event-handler-block" }
-];
\ No newline at end of file
+ { type: 'argument', selector: '.argument-block' },
+ { type: 'env-var', selector: '.environment-variable-block' },
+ { type: 'node', selector: '.node-block' },
+ { type: 'include', selector: '.include-block' },
+ { type: 'group', selector: '.group-header' },
+ { type: 'group-node', selector: '.group-block .node-block' },
+ { type: 'opaque', selector: '.opaque-function-header' },
+ { type: 'opaque-node', selector: '.opaque-function-block .node-block' },
+ { type: 'composable-container', selector: '.composable-container-header' },
+ { type: 'composable-node', selector: '.composable-node-block' },
+ { type: 'python-expression', selector: '.python-expression-block' },
+ { type: 'timer-action', selector: '.timer-action-header' },
+ { type: 'timer-action-node', selector: '.timer-action-block .node-block' },
+ { type: 'event-handler', selector: '.event-handler-block' }
+];
diff --git a/webview/tests/fixtures/server.js b/webview/tests/fixtures/server.js
index 71dc033..b4c19db 100644
--- a/webview/tests/fixtures/server.js
+++ b/webview/tests/fixtures/server.js
@@ -17,27 +17,27 @@ import http from 'http';
import serveHandler from 'serve-handler';
export class TestServer {
- constructor(port = 3000, publicDir = path.join(__dirname, '../../../webview')) {
- this.port = port;
- this.publicDir = publicDir;
- this.server = null;
- }
+ constructor(port = 3000, publicDir = path.join(__dirname, '../../../webview')) {
+ this.port = port;
+ this.publicDir = publicDir;
+ this.server = null;
+ }
- async start() {
- this.server = http.createServer((req, res) =>
- serveHandler(req, res, { public: this.publicDir })
- );
- await new Promise((resolve) => this.server.listen(this.port, resolve));
- console.log(`โ
Test server running at http://localhost:${this.port}`);
- }
+ async start() {
+ this.server = http.createServer((req, res) =>
+ serveHandler(req, res, { public: this.publicDir })
+ );
+ await new Promise((resolve) => this.server.listen(this.port, resolve));
+ console.log(`โ
Test server running at http://localhost:${this.port}`);
+ }
- async stop() {
- if (!this.server) return;
- await new Promise((resolve) => this.server.close(resolve));
- console.log(`๐ Test server stopped`);
- }
+ async stop() {
+ if (!this.server) return;
+ await new Promise((resolve) => this.server.close(resolve));
+ console.log('๐ Test server stopped');
+ }
- get url() {
- return `http://localhost:${this.port}/tests/assets/index.html`;
- }
-}
\ No newline at end of file
+ get url() {
+ return `http://localhost:${this.port}/tests/assets/index.html`;
+ }
+}
diff --git a/webview/tests/playwright.config.js b/webview/tests/playwright.config.js
index 4541a98..fedfdad 100644
--- a/webview/tests/playwright.config.js
+++ b/webview/tests/playwright.config.js
@@ -15,13 +15,13 @@
import { defineConfig } from '@playwright/test';
export default defineConfig({
- testDir: './specs',
- snapshotDir: './__screenshots__',
- globalSetup: './fixtures/global-setup.js',
- use: {
- headless: true,
- viewport: { width: 1600, height: 900 },
- baseURL: process.env.TEST_SERVER_URL || 'http://localhost:3000',
- },
- reporter: [['list'], ['html']],
-});
\ No newline at end of file
+ testDir: './specs',
+ snapshotDir: './__screenshots__',
+ globalSetup: './fixtures/global-setup.js',
+ use: {
+ headless: true,
+ viewport: { width: 1600, height: 900 },
+ baseURL: process.env.TEST_SERVER_URL || 'http://localhost:3000',
+ },
+ reporter: [['list'], ['html']],
+});
diff --git a/webview/tests/specs/component-drag.spec.js b/webview/tests/specs/component-drag.spec.js
index ebcdce7..126704e 100644
--- a/webview/tests/specs/component-drag.spec.js
+++ b/webview/tests/specs/component-drag.spec.js
@@ -15,30 +15,30 @@
import { test, expect } from '@playwright/test';
import { SAMPLE_DATA, COMPONENT_SELECTORS } from '../fixtures/sample-data';
-test.describe("Component Drag Tests", () => {
- COMPONENT_SELECTORS.forEach(({ type, selector }) => {
- test(`dragging ${type} works`, async ({ page }) => {
- await page.goto(process.env.TEST_SERVER_URL);
+test.describe('Component Drag Tests', () => {
+ COMPONENT_SELECTORS.forEach(({ type, selector }) => {
+ test(`dragging ${type} works`, async ({ page }) => {
+ await page.goto(process.env.TEST_SERVER_URL);
- await page.evaluate((data) => {
- window.postMessage({ type: 'launchmap-data', data }, '*');
- }, SAMPLE_DATA);
+ await page.evaluate((data) => {
+ window.postMessage({ type: 'launchmap-data', data }, '*');
+ }, SAMPLE_DATA);
- const block = page.locator(selector).first();
- const boxBefore = await block.boundingBox();
+ const block = page.locator(selector).first();
+ const boxBefore = await block.boundingBox();
- await block.dragTo(page.locator('body'), {
- targetPosition: { x: 300, y: 200 },
- });
+ await block.dragTo(page.locator('body'), {
+ targetPosition: { x: 300, y: 200 },
+ });
- const boxAfter = await block.boundingBox();
- expect(boxAfter.x).not.toBe(boxBefore.x);
- expect(boxAfter.y).not.toBe(boxBefore.y);
+ const boxAfter = await block.boundingBox();
+ expect(boxAfter.x).not.toBe(boxBefore.x);
+ expect(boxAfter.y).not.toBe(boxBefore.y);
- await expect(page).toHaveScreenshot(`${type}-drag.png`, {
- fullPage: true,
- maxDiffPixelRatio: 0.02
- });
- });
+ await expect(page).toHaveScreenshot(`${type}-drag.png`, {
+ fullPage: true,
+ maxDiffPixelRatio: 0.02
+ });
});
-});
\ No newline at end of file
+ });
+});
diff --git a/webview/tests/specs/interaction.spec.js b/webview/tests/specs/interaction.spec.js
index f5a456b..d8f2e01 100644
--- a/webview/tests/specs/interaction.spec.js
+++ b/webview/tests/specs/interaction.spec.js
@@ -18,94 +18,94 @@ import path from 'path';
const launchFilesDir = path.resolve(__dirname, '../../../parser/tests/real_cases/expected_outputs');
-test.describe("Zoom, Pan, and Drag interactions with visualization state checks", () => {
- const files = fs.readdirSync(launchFilesDir).filter(f => f.endsWith(".json"));
-
- for (const file of files) {
- test(`state consistent for ${file}`, async ({ page }) => {
- await page.goto(process.env.TEST_SERVER_URL);
-
- const launchData = JSON.parse(
- fs.readFileSync(path.join(launchFilesDir, file), "utf8")
- );
-
- await page.evaluate((data) => {
- window.postMessage({ type: 'launchmap-data', data }, '*');
- }, launchData);
-
- await page.waitForSelector(".block");
-
- // Capture initial edge positions
- const edgesBefore = await page.$$eval('#edge-layer path', paths =>
- paths.map(p => p.getAttribute('d'))
- );
- if (edgesBefore.length === 0) {
- console.warn(`โ ๏ธ No edges found in ${file}, skipping edge checks.`);
- }
-
- // Drag Test
- const block = page.locator('.block-header').first();
- const boxBefore = await block.boundingBox();
-
- await block.dragTo(page.locator('body'), {
- targetPosition: { x: 300, y: 200 },
- });
-
- const boxAfter = await block.boundingBox();
- expect(boxAfter.x).not.toBe(boxBefore.x);
- expect(boxAfter.y).not.toBe(boxBefore.y);
-
- const edgesAfterDrag = await page.$$eval('#edge-layer path', paths =>
- paths.map(p => p.getAttribute('d'))
- );
- if (edgesBefore.length > 0) {
- expect(edgesAfterDrag).not.toEqual(edgesBefore);
- }
-
- // Pan test
- const zoomLayer = page.locator('#zoom-layer');
- const transformBeforePan = await zoomLayer.evaluate(el => el.style.transform);
-
- await page.keyboard.down('Shift');
- await page.mouse.move(300, 300);
- await page.mouse.down();
- await page.mouse.move(500, 400);
- await page.mouse.up();
- await page.keyboard.up('Shift');
-
- const transformAfterPan = await zoomLayer.evaluate(el => el.style.transform);
- expect(transformAfterPan).not.toBe(transformBeforePan);
-
- const edgesAfterPan = await page.$$eval('#edge-layer path', paths =>
- paths.map(p => p.getAttribute('d'))
- );
- if (edgesBefore.length > 0) {
- expect(edgesAfterPan).not.toEqual(edgesAfterDrag);
- }
-
- // Zoom Test
- const transformBeforeZoom = await zoomLayer.evaluate(el => el.style.transform);
-
- await page.mouse.wheel(0, -200);
- const transformAfterZoomIn = await zoomLayer.evaluate(el => el.style.transform);
- expect(transformAfterZoomIn).not.toBe(transformBeforeZoom);
-
- await page.mouse.wheel(0, 200);
- const transformAfterZoomOut = await zoomLayer.evaluate(el => el.style.transform);
- expect(transformAfterZoomOut).not.toBe(transformAfterZoomIn);
-
- const edgesAfterZoom = await page.$$eval('#edge-layer path', paths =>
- paths.map(p => p.getAttribute('d'))
- );
- if (edgesBefore.length > 0) {
- expect(edgesAfterZoom).not.toEqual(edgesAfterPan);
- }
-
- // Final Visual Snapshot
- await expect(page).toHaveScreenshot(`${file}-final.png`, {
- fullPage: true,
- maxDiffPixelRatio: 0.02
- });
- });
- }
-});
\ No newline at end of file
+test.describe('Zoom, Pan, and Drag interactions with visualization state checks', () => {
+ const files = fs.readdirSync(launchFilesDir).filter(f => f.endsWith('.json'));
+
+ for (const file of files) {
+ test(`state consistent for ${file}`, async ({ page }) => {
+ await page.goto(process.env.TEST_SERVER_URL);
+
+ const launchData = JSON.parse(
+ fs.readFileSync(path.join(launchFilesDir, file), 'utf8')
+ );
+
+ await page.evaluate((data) => {
+ window.postMessage({ type: 'launchmap-data', data }, '*');
+ }, launchData);
+
+ await page.waitForSelector('.block');
+
+ // Capture initial edge positions
+ const edgesBefore = await page.$$eval('#edge-layer path', paths =>
+ paths.map(p => p.getAttribute('d'))
+ );
+ if (edgesBefore.length === 0) {
+ console.warn(`โ ๏ธ No edges found in ${file}, skipping edge checks.`);
+ }
+
+ // Drag Test
+ const block = page.locator('.block-header').first();
+ const boxBefore = await block.boundingBox();
+
+ await block.dragTo(page.locator('body'), {
+ targetPosition: { x: 300, y: 200 },
+ });
+
+ const boxAfter = await block.boundingBox();
+ expect(boxAfter.x).not.toBe(boxBefore.x);
+ expect(boxAfter.y).not.toBe(boxBefore.y);
+
+ const edgesAfterDrag = await page.$$eval('#edge-layer path', paths =>
+ paths.map(p => p.getAttribute('d'))
+ );
+ if (edgesBefore.length > 0) {
+ expect(edgesAfterDrag).not.toEqual(edgesBefore);
+ }
+
+ // Pan test
+ const zoomLayer = page.locator('#zoom-layer');
+ const transformBeforePan = await zoomLayer.evaluate(el => el.style.transform);
+
+ await page.keyboard.down('Shift');
+ await page.mouse.move(300, 300);
+ await page.mouse.down();
+ await page.mouse.move(500, 400);
+ await page.mouse.up();
+ await page.keyboard.up('Shift');
+
+ const transformAfterPan = await zoomLayer.evaluate(el => el.style.transform);
+ expect(transformAfterPan).not.toBe(transformBeforePan);
+
+ const edgesAfterPan = await page.$$eval('#edge-layer path', paths =>
+ paths.map(p => p.getAttribute('d'))
+ );
+ if (edgesBefore.length > 0) {
+ expect(edgesAfterPan).not.toEqual(edgesAfterDrag);
+ }
+
+ // Zoom Test
+ const transformBeforeZoom = await zoomLayer.evaluate(el => el.style.transform);
+
+ await page.mouse.wheel(0, -200);
+ const transformAfterZoomIn = await zoomLayer.evaluate(el => el.style.transform);
+ expect(transformAfterZoomIn).not.toBe(transformBeforeZoom);
+
+ await page.mouse.wheel(0, 200);
+ const transformAfterZoomOut = await zoomLayer.evaluate(el => el.style.transform);
+ expect(transformAfterZoomOut).not.toBe(transformAfterZoomIn);
+
+ const edgesAfterZoom = await page.$$eval('#edge-layer path', paths =>
+ paths.map(p => p.getAttribute('d'))
+ );
+ if (edgesBefore.length > 0) {
+ expect(edgesAfterZoom).not.toEqual(edgesAfterPan);
+ }
+
+ // Final Visual Snapshot
+ await expect(page).toHaveScreenshot(`${file}-final.png`, {
+ fullPage: true,
+ maxDiffPixelRatio: 0.02
+ });
+ });
+ }
+});
diff --git a/webview/tests/specs/visual.spec.js b/webview/tests/specs/visual.spec.js
index 22a7e8d..8d7243d 100644
--- a/webview/tests/specs/visual.spec.js
+++ b/webview/tests/specs/visual.spec.js
@@ -18,26 +18,26 @@ import path from 'path';
const launchFilesDir = path.resolve(__dirname, '../../../parser/tests/real_cases/expected_outputs');
-test.describe("LaunchMap Visual Tests", () => {
- const files = fs.readdirSync(launchFilesDir).filter(f => f.endsWith(".json"));
+test.describe('LaunchMap Visual Tests', () => {
+ const files = fs.readdirSync(launchFilesDir).filter(f => f.endsWith('.json'));
- for (const file of files) {
- test(`renders correctly for ${file}`, async ({ page }) => {
- await page.goto(process.env.TEST_SERVER_URL);
+ for (const file of files) {
+ test(`renders correctly for ${file}`, async ({ page }) => {
+ await page.goto(process.env.TEST_SERVER_URL);
- const launchData = JSON.parse(
- fs.readFileSync(path.join(launchFilesDir, file), "utf8")
- );
+ const launchData = JSON.parse(
+ fs.readFileSync(path.join(launchFilesDir, file), 'utf8')
+ );
- await page.evaluate((data) => {
- window.postMessage({ type: 'launchmap-data', data }, '*');
- }, launchData);
+ await page.evaluate((data) => {
+ window.postMessage({ type: 'launchmap-data', data }, '*');
+ }, launchData);
- await page.waitForSelector(".block");
- await expect(page).toHaveScreenshot(`${file}.png`, {
- fullPage: true,
- maxDiffPixelRatio: 0.01
- });
- });
- }
-});
\ No newline at end of file
+ await page.waitForSelector('.block');
+ await expect(page).toHaveScreenshot(`${file}.png`, {
+ fullPage: true,
+ maxDiffPixelRatio: 0.01
+ });
+ });
+ }
+});
diff --git a/webview/utils/autoFitToScreen.js b/webview/utils/autoFitToScreen.js
index 7df8fb3..662bb65 100644
--- a/webview/utils/autoFitToScreen.js
+++ b/webview/utils/autoFitToScreen.js
@@ -13,49 +13,49 @@
// limitations under the License.
export function autoFitToScreen(editor, zoomLayer, margin = 40) {
- if (!window.zoomState) return;
- const zoomState = window.zoomState;
+ if (!window.zoomState) return;
+ const zoomState = window.zoomState;
- const prevTransform = zoomLayer.style.transform;
- zoomLayer.style.transform = "none";
+ const prevTransform = zoomLayer.style.transform;
+ zoomLayer.style.transform = 'none';
- const children = zoomLayer.querySelectorAll(".block");
- if (!children.length) {
- zoomLayer.style.transform = prevTransform;
- return;
- }
+ const children = zoomLayer.querySelectorAll('.block');
+ if (!children.length) {
+ zoomLayer.style.transform = prevTransform;
+ return;
+ }
- let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
- children.forEach(child => {
- const rect = child.getBoundingClientRect();
- const editorRect = editor.getBoundingClientRect();
+ let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
+ children.forEach(child => {
+ const rect = child.getBoundingClientRect();
+ const editorRect = editor.getBoundingClientRect();
- const relativeLeft = rect.left - editorRect.left;
- const relativeTop = rect.top - editorRect.top;
+ const relativeLeft = rect.left - editorRect.left;
+ const relativeTop = rect.top - editorRect.top;
- minX = Math.min(minX, relativeLeft);
- minY = Math.min(minY, relativeTop);
- maxX = Math.max(maxX, relativeLeft + rect.width);
- maxY = Math.max(maxY, relativeTop + rect.height);
- });
+ minX = Math.min(minX, relativeLeft);
+ minY = Math.min(minY, relativeTop);
+ maxX = Math.max(maxX, relativeLeft + rect.width);
+ maxY = Math.max(maxY, relativeTop + rect.height);
+ });
- const contentWidth = maxX - minX;
- const contentHeight = maxY - minY;
+ const contentWidth = maxX - minX;
+ const contentHeight = maxY - minY;
- const editorRect = editor.getBoundingClientRect();
- const availableWidth = editorRect.width - margin * 2;
- const availableHeight = editorRect.height - margin * 2;
+ const editorRect = editor.getBoundingClientRect();
+ const availableWidth = editorRect.width - margin * 2;
+ const availableHeight = editorRect.height - margin * 2;
- let scale = Math.min(availableWidth / contentWidth, availableHeight / contentHeight);
- scale = Math.max(0.2, Math.min(3, scale));
+ let scale = Math.min(availableWidth / contentWidth, availableHeight / contentHeight);
+ scale = Math.max(0.2, Math.min(3, scale));
- zoomState.scale = scale;
- zoomState.offsetX = -minX * scale + (availableWidth - contentWidth * scale) / 2 + margin;
- zoomState.offsetY = -minY * scale + (availableHeight - contentHeight * scale) / 2 + margin;
+ zoomState.scale = scale;
+ zoomState.offsetX = -minX * scale + (availableWidth - contentWidth * scale) / 2 + margin;
+ zoomState.offsetY = -minY * scale + (availableHeight - contentHeight * scale) / 2 + margin;
- zoomLayer.style.transform = prevTransform;
- const applyTransform = () => {
- zoomLayer.style.transform = `translate(${zoomState.offsetX}px, ${zoomState.offsetY}px) scale(${zoomState.scale})`;
- }
- applyTransform();
-}
\ No newline at end of file
+ zoomLayer.style.transform = prevTransform;
+ const applyTransform = () => {
+ zoomLayer.style.transform = `translate(${zoomState.offsetX}px, ${zoomState.offsetY}px) scale(${zoomState.scale})`;
+ };
+ applyTransform();
+}
diff --git a/webview/utils/drag.js b/webview/utils/drag.js
index 1661937..3b16444 100644
--- a/webview/utils/drag.js
+++ b/webview/utils/drag.js
@@ -13,64 +13,64 @@
// limitations under the License.
export function makeDraggable(element, options={}) {
- let offsetX = 0, offsetY = 0, startX = 0, startY = 0;
+ let offsetX = 0, offsetY = 0, startX = 0, startY = 0;
- // Ensure element has initial position
- if (!element.style.left) element.style.left = "0px";
- if (!element.style.top) element.style.top = "0px";
+ // Ensure element has initial position
+ if (!element.style.left) element.style.left = '0px';
+ if (!element.style.top) element.style.top = '0px';
- element.onmousedown = dragMouseDown;
+ element.onmousedown = dragMouseDown;
- function dragMouseDown(e) {
- if (options.stopPropagation) e.stopPropagation();
- e.preventDefault();
- startX = e.clientX;
- startY = e.clientY;
+ function dragMouseDown(e) {
+ if (options.stopPropagation) e.stopPropagation();
+ e.preventDefault();
+ startX = e.clientX;
+ startY = e.clientY;
- document.onmousemove = elementDrag;
- document.onmouseup = stopDrag;
- }
+ document.onmousemove = elementDrag;
+ document.onmouseup = stopDrag;
+ }
- function elementDrag(e) {
- e.preventDefault();
+ function elementDrag(e) {
+ e.preventDefault();
- const zoomScale = window.zoomState?.scale || 1;
+ const zoomScale = window.zoomState?.scale || 1;
- offsetX = (e.clientX - startX) / zoomScale;
- offsetY = (e.clientY - startY) / zoomScale;
- startX = e.clientX;
- startY = e.clientY;
+ offsetX = (e.clientX - startX) / zoomScale;
+ offsetY = (e.clientY - startY) / zoomScale;
+ startX = e.clientX;
+ startY = e.clientY;
- const currentLeft = parseFloat(element.style.left || "0");
- const currentTop = parseFloat(element.style.top || "0");
+ const currentLeft = parseFloat(element.style.left || '0');
+ const currentTop = parseFloat(element.style.top || '0');
- let newLeft = currentLeft + offsetX;
- let newTop = currentTop + offsetY;
+ let newLeft = currentLeft + offsetX;
+ let newTop = currentTop + offsetY;
- if (options.constrainToParent) {
- const parent = element.parentElement;
- const parentWidth = parent.clientWidth;
- const parentHeight = parent.clientHeight;
- const elementWidth = element.offsetWidth;
- const elementHeight = element.offsetHeight;
+ if (options.constrainToParent) {
+ const parent = element.parentElement;
+ const parentWidth = parent.clientWidth;
+ const parentHeight = parent.clientHeight;
+ const elementWidth = element.offsetWidth;
+ const elementHeight = element.offsetHeight;
- const maxLeft = parentWidth - elementWidth;
- const maxTop = parentHeight - elementHeight;
+ const maxLeft = parentWidth - elementWidth;
+ const maxTop = parentHeight - elementHeight;
- newLeft = Math.max(0, Math.min(newLeft, maxLeft));
- newTop = Math.max(0, Math.min(newTop, maxTop));
- }
+ newLeft = Math.max(0, Math.min(newLeft, maxLeft));
+ newTop = Math.max(0, Math.min(newTop, maxTop));
+ }
- element.style.left = `${newLeft}px`;
- element.style.top = `${newTop}px`;
+ element.style.left = `${newLeft}px`;
+ element.style.top = `${newTop}px`;
- if (typeof options.onDrag === "function") {
- requestAnimationFrame(() => options.onDrag());
- }
+ if (typeof options.onDrag === 'function') {
+ requestAnimationFrame(() => options.onDrag());
}
+ }
- function stopDrag() {
- document.onmouseup = null;
- document.onmousemove = null;
- }
-}
\ No newline at end of file
+ function stopDrag() {
+ document.onmouseup = null;
+ document.onmousemove = null;
+ }
+}
diff --git a/webview/utils/labels.js b/webview/utils/labels.js
index 320f297..9b048d2 100644
--- a/webview/utils/labels.js
+++ b/webview/utils/labels.js
@@ -13,18 +13,18 @@
// limitations under the License.
export function getTypeLabel(type) {
- switch (type) {
- case 'node': return 'NODE';
- case 'include': return 'INCLUDE LAUNCH DESCRIPTION';
- case 'group': return 'GROUP ACTION';
- case 'timer-action': return 'TIMER ACTION';
- case 'argument': return 'DECLARE LAUNCH ARGUMENT';
- case 'environment-variable': return 'ENVIRONMENT VARIABLE';
- case 'opaque-function': return 'OPAQUE FUNCTION';
- case 'composable-node': return 'COMPOSABLE NODE';
- case 'composable-container': return 'COMPOSABLE NODE CONTAINER';
- case 'event-handler': return 'EVENT HANDLER';
- case 'python-expression': return 'PYTHON EXPRESSION';
- default: return `${type}`;
- }
-}
\ No newline at end of file
+ switch (type) {
+ case 'node': return 'NODE';
+ case 'include': return 'INCLUDE LAUNCH DESCRIPTION';
+ case 'group': return 'GROUP ACTION';
+ case 'timer-action': return 'TIMER ACTION';
+ case 'argument': return 'DECLARE LAUNCH ARGUMENT';
+ case 'environment-variable': return 'ENVIRONMENT VARIABLE';
+ case 'opaque-function': return 'OPAQUE FUNCTION';
+ case 'composable-node': return 'COMPOSABLE NODE';
+ case 'composable-container': return 'COMPOSABLE NODE CONTAINER';
+ case 'event-handler': return 'EVENT HANDLER';
+ case 'python-expression': return 'PYTHON EXPRESSION';
+ default: return `${type}`;
+ }
+}