diff --git a/src/lib/litegraph/.cursor/rules/unit-test.mdc b/src/lib/litegraph/.cursor/rules/unit-test.mdc
new file mode 100644
index 0000000000..2c6704f3e2
--- /dev/null
+++ b/src/lib/litegraph/.cursor/rules/unit-test.mdc
@@ -0,0 +1,21 @@
+---
+description: Creating unit tests
+globs:
+alwaysApply: false
+---
+
+# Creating unit tests
+
+- This project uses `vitest` for unit testing
+- Tests are stored in the `test/` directory
+- Tests should be cross-platform compatible; able to run on Windows, macOS, and linux
+ - e.g. the use of `path.resolve`, or `path.join` and `path.sep` to ensure that tests work the same on all platforms
+- Tests should be mocked properly
+ - Mocks should be cleanly written and easy to understand
+ - Mocks should be re-usable where possible
+
+## Unit test style
+
+- Prefer the use of `test.extend` over loose variables
+ - To achieve this, import `test as baseTest` from `vitest`
+- Never use `it`; `test` should be used in place of this
\ No newline at end of file
diff --git a/src/lib/litegraph/.github/workflows/dev-release.yml b/src/lib/litegraph/.github/workflows/dev-release.yml
new file mode 100644
index 0000000000..d947abdfb3
--- /dev/null
+++ b/src/lib/litegraph/.github/workflows/dev-release.yml
@@ -0,0 +1,62 @@
+name: Create Dev Release
+description: Create a nightly-style npm package for a development / experimental branch. Do not use "latest" tag. This will not have a GitHub release / tag by default.
+
+on:
+ workflow_dispatch:
+ inputs:
+ tag:
+ description: "npm tag (`ni pacakge@tag`)"
+ required: true
+ default: "subgraph"
+ gh-release:
+ description: "Draft a GitHub release"
+ type: boolean
+ required: false
+ default: false
+
+jobs:
+ create-release:
+ runs-on: ubuntu-latest
+ if: inputs.gh-release == true
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - uses: actions/setup-node@v3
+ with:
+ node-version: lts/*
+
+ - name: Get current version
+ id: current_version
+ run: |
+ CURRENT_VERSION=$(node -p "require('./package.json').version")
+ echo "version=$CURRENT_VERSION" >> $GITHUB_OUTPUT
+
+ - name: Create release
+ id: create_release
+ uses: softprops/action-gh-release@da05d552573ad5aba039eaac05058a918a7bf631
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ with:
+ tag_name: v${{ steps.current_version.outputs.version }}
+ draft: true
+ prerelease: true
+ generate_release_notes: true
+ make_latest: "false"
+
+ publish:
+ runs-on: ubuntu-latest
+ if: inputs.tag != 'latest'
+ steps:
+ - uses: actions/checkout@v4
+
+ - uses: actions/setup-node@v3
+ with:
+ node-version: lts/*
+ registry-url: "https://registry.npmjs.org"
+
+ - run: npm ci
+ - run: npm run build
+ - run: npm publish --access public --tag ${{ inputs.tag }}
+ env:
+ NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
diff --git a/src/lib/litegraph/.github/workflows/github-release.yml b/src/lib/litegraph/.github/workflows/github-release.yml
new file mode 100644
index 0000000000..22c1bcb7ea
--- /dev/null
+++ b/src/lib/litegraph/.github/workflows/github-release.yml
@@ -0,0 +1,62 @@
+name: Create GitHub Release
+description: Automatically creates a release when a PR is merged that both that changes package.json and has the Release label.
+
+on:
+ pull_request:
+ types: [closed]
+ branches:
+ - main
+ - master
+ paths:
+ - "package.json"
+
+jobs:
+ create-release:
+ runs-on: ubuntu-latest
+ if: >
+ github.event.pull_request.merged == true &&
+ contains(github.event.pull_request.labels.*.name, 'Release')
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - uses: actions/setup-node@v3
+ with:
+ node-version: lts/*
+
+ - name: Get current version
+ id: current_version
+ run: |
+ CURRENT_VERSION=$(node -p "require('./package.json').version")
+ echo "version=$CURRENT_VERSION" >> $GITHUB_OUTPUT
+
+ - name: Create release
+ id: create_release
+ uses: softprops/action-gh-release@da05d552573ad5aba039eaac05058a918a7bf631
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ with:
+ tag_name: v${{ steps.current_version.outputs.version }}
+ draft: false
+ prerelease: false
+ generate_release_notes: true
+ make_latest: "true"
+
+ publish:
+ runs-on: ubuntu-latest
+ if: >
+ github.event.pull_request.merged == true &&
+ contains(github.event.pull_request.labels.*.name, 'Release')
+ steps:
+ - uses: actions/checkout@v4
+
+ - uses: actions/setup-node@v3
+ with:
+ node-version: lts/*
+ registry-url: "https://registry.npmjs.org"
+
+ - run: npm ci
+ - run: npm run build
+ - run: npm publish --access public
+ env:
+ NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
diff --git a/src/lib/litegraph/.github/workflows/release-version.yml b/src/lib/litegraph/.github/workflows/release-version.yml
new file mode 100644
index 0000000000..3638e17246
--- /dev/null
+++ b/src/lib/litegraph/.github/workflows/release-version.yml
@@ -0,0 +1,60 @@
+name: Release a New Version
+description: Creates a PR to increment the version. When the PR is merged, it will automatically create a release.
+
+on:
+ workflow_dispatch:
+ inputs:
+ version_type:
+ description: Version increment type
+ required: true
+ default: patch
+ type: choice
+ options: [patch, minor, major, prepatch, preminor, premajor, prerelease]
+ pre_release:
+ description: Pre-release ID (suffix)
+ required: false
+ default: ""
+ type: string
+
+jobs:
+ bump-version:
+ runs-on: ubuntu-latest
+ permissions:
+ contents: write
+ pull-requests: write
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ - name: Setup Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: lts/*
+ cache: "npm"
+
+ - name: Bump version
+ id: bump-version
+ run: |
+ npm version ${{ github.event.inputs.version_type }} --preid ${{ github.event.inputs.pre_release }} --no-git-tag-version
+ NEW_VERSION=$(node -p "require('./package.json').version")
+ echo "NEW_VERSION=$NEW_VERSION" >> $GITHUB_OUTPUT
+
+ - name: Format PR string
+ id: capitalised
+ run: |
+ CAPITALISED_TYPE=${{ github.event.inputs.version_type }}
+ echo "capitalised=${CAPITALISED_TYPE@u}" >> $GITHUB_OUTPUT
+
+ - name: Create Pull Request
+ uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e
+ with:
+ token: ${{ secrets.PR_GH_TOKEN }}
+ commit-message: "[Release] Increment version to ${{ steps.bump-version.outputs.NEW_VERSION }}"
+ title: ${{ steps.bump-version.outputs.NEW_VERSION }}
+ body: |
+ ${{ steps.capitalised.outputs.capitalised }} version increment to ${{ steps.bump-version.outputs.NEW_VERSION }}
+ branch: version-bump-${{ steps.bump-version.outputs.NEW_VERSION }}
+ base: master
+ labels: |
+ Release
diff --git a/src/lib/litegraph/.github/workflows/test-comfyui-frontend.yml b/src/lib/litegraph/.github/workflows/test-comfyui-frontend.yml
new file mode 100644
index 0000000000..d8f416fb6b
--- /dev/null
+++ b/src/lib/litegraph/.github/workflows/test-comfyui-frontend.yml
@@ -0,0 +1,115 @@
+# This action should be kept in sync with
+# https://github.com/Comfy-Org/ComfyUI_frontend/blob/main/.github/workflows/test-ui.yaml
+
+name: Test ComfyUI Frontend
+
+on:
+ workflow_dispatch:
+ inputs:
+ frontend-branch:
+ description: "Frontend branch"
+ required: true
+ default: "main"
+
+ push:
+ branches: [main, master]
+ paths-ignore:
+ - ".cursor/**"
+ - ".husky/**"
+ - ".vscode/**"
+ - ".github/ISSUE_TEMPLATE/**"
+ - "test/**"
+
+ pull_request:
+ branches: [main, master]
+ paths-ignore:
+ - ".cursor/**"
+ - ".husky/**"
+ - ".vscode/**"
+ - ".github/ISSUE_TEMPLATE/**"
+ - "test/**"
+
+jobs:
+ test-comfyui-frontend:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout litegraph
+ uses: actions/checkout@v4
+ with:
+ path: "litegraph"
+
+ - name: Checkout ComfyUI
+ uses: actions/checkout@v4
+ with:
+ repository: "comfyanonymous/ComfyUI"
+ path: "ComfyUI"
+ ref: master
+
+ - name: Checkout ComfyUI_frontend
+ uses: actions/checkout@v4
+ with:
+ repository: "Comfy-Org/ComfyUI_frontend"
+ path: "ComfyUI_frontend"
+ ref: ${{ inputs.frontend-branch }}
+
+ - name: Checkout ComfyUI_devtools
+ uses: actions/checkout@v4
+ with:
+ repository: "Comfy-Org/ComfyUI_devtools"
+ path: "ComfyUI/custom_nodes/ComfyUI_devtools"
+
+ - uses: actions/setup-node@v4
+ with:
+ node-version: lts/*
+
+ - uses: actions/setup-python@v4
+ with:
+ python-version: "3.10"
+
+ - name: Install requirements
+ run: |
+ python -m pip install --upgrade pip
+ pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cpu
+ pip install -r requirements.txt
+ pip install wait-for-it
+ working-directory: ComfyUI
+
+ - name: Build litegraph
+ run: |
+ npm ci
+ npm run build
+ working-directory: litegraph
+
+ - name: Install updated litegraph in ComfyUI_frontend
+ run: |
+ npm ci
+ npm install ../litegraph
+ npm run build
+ working-directory: ComfyUI_frontend
+
+ - name: Start ComfyUI server
+ run: |
+ python main.py --cpu --multi-user --front-end-root ../ComfyUI_frontend/dist &
+ wait-for-it --service 127.0.0.1:8188 -t 600
+ working-directory: ComfyUI
+
+ - name: Run UI tests
+ run: |
+ npm run test:component
+ npm run test:unit
+ working-directory: ComfyUI_frontend
+
+ - name: Install Playwright Browsers
+ run: npx playwright install chromium --with-deps
+ working-directory: ComfyUI_frontend
+
+ - name: Run Playwright tests (chromium)
+ run: npx playwright test --project=chromium
+ working-directory: ComfyUI_frontend
+
+ - uses: actions/upload-artifact@v4
+ if: always()
+ with:
+ name: playwright-report
+ path: ComfyUI_frontend/playwright-report/
+ retention-days: 30
diff --git a/src/lib/litegraph/.github/workflows/test-lint-format.yml b/src/lib/litegraph/.github/workflows/test-lint-format.yml
new file mode 100644
index 0000000000..355639420e
--- /dev/null
+++ b/src/lib/litegraph/.github/workflows/test-lint-format.yml
@@ -0,0 +1,35 @@
+name: Unit Test, Lint, and Format
+
+on:
+ push:
+ branches: [main, master, "dev*"]
+ pull_request:
+ branches: [main, master, "dev*"]
+
+jobs:
+ test:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - uses: actions/setup-node@v3
+ with:
+ node-version: lts/*
+
+ - name: Build
+ run: |
+ npm ci
+ npm run build
+
+ - name: Run lint
+ run: |
+ npm run lint:ci
+
+ - name: Run format
+ run: |
+ npm run format
+
+ - name: Run vitest tests
+ run: |
+ npm test -- --reporter=verbose
diff --git a/src/lib/litegraph/.gitignore b/src/lib/litegraph/.gitignore
new file mode 100755
index 0000000000..5721fd8739
--- /dev/null
+++ b/src/lib/litegraph/.gitignore
@@ -0,0 +1,33 @@
+temp/
+temp/*
+coverage/
+
+# Editors
+*.bak
+.project
+
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+lerna-debug.log*
+
+node_modules
+dist
+dist-ssr
+*.local
+
+# Editor directories and files
+.vscode/*
+!.vscode/extensions.json
+*.code-workspace
+.idea
+.DS_Store
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?
diff --git a/src/lib/litegraph/.husky/pre-commit b/src/lib/litegraph/.husky/pre-commit
new file mode 100644
index 0000000000..a19f49ff7c
--- /dev/null
+++ b/src/lib/litegraph/.husky/pre-commit
@@ -0,0 +1,5 @@
+if [[ "$OS" == "Windows_NT" ]]; then
+ npx.cmd lint-staged
+else
+ npx lint-staged
+fi
diff --git a/src/lib/litegraph/.prettierrc b/src/lib/litegraph/.prettierrc
new file mode 100755
index 0000000000..c3be0e5439
--- /dev/null
+++ b/src/lib/litegraph/.prettierrc
@@ -0,0 +1,8 @@
+{
+ "singleQuote": false,
+ "semi": false,
+ "tabWidth": 2,
+ "trailingComma": "all",
+ "overrides": [{ "files": "*.ts", "options": { "requirePragma": true } }],
+ "arrowParens": "avoid"
+}
diff --git a/src/lib/litegraph/API.md b/src/lib/litegraph/API.md
new file mode 100644
index 0000000000..59d728c073
--- /dev/null
+++ b/src/lib/litegraph/API.md
@@ -0,0 +1,203 @@
+# API
+
+This document is intended to provide a brief introduction to new Litegraph APIs.
+
+
+
+
+
+# CanvasPointer API
+
+
+
+CanvasPointer replaces much of the original pointer handling code. It provides a standard click, double-click, and drag UX for users.
+
+
+
+
+
+## Default behaviour changes
+
+
+
+- Dragging multiple items no longer requires that the shift key be held down
+ - Clicking an item when multiple nodes / etc are selected will still deselect everything else
+- Clicking a connected link on an input no longer disconnects and reconnects it
+- Double-clicking requires that both clicks occur nearby
+- Provides much room for extension, configuration, and changes
+
+#### Bug fixes
+
+- Intermittent issue where clicking a node slightly displaces it
+- Alt-clicking to add a reroute creates two undo steps
+
+### Selecting multiple items
+
+- `Ctrl + drag` - Begin multi-select
+- `Ctrl + Shift + drag` - Add to selection
+ - `Ctrl + drag`, `Shift` - Alternate add to selection
+- `Ctrl + drag`, `Alt` - Remove from selection
+
+### Click "drift"
+
+A small amount of buffering is performed between down/up events to prevent accidental micro-drag events. If either of the two controls are exceeded, the event will be considered a drag event, not a click.
+
+- `buffterTime` is the maximum time that tiny movements can be ignored (Default: 150ms)
+- `maxClickDrift` controls how far a click can drift from its down event before it is considered a drag (Default: 6)
+
+### Double-click
+
+When double clicking, the double click callback is executed shortly after one normal click callback (if present). At present, dragging from the second click simply invalidates the event - nothing will happen.
+
+- `doubleClickTime` is the maximum time between two `down` events for them to be considered a double click (Default: 300ms)
+- Distance between the two events must be less than `3 * maxClickDrift`
+
+### Configuration
+
+All above configuration is via class static.
+
+```ts
+CanvasPointer.bufferTime = 150
+CanvasPointer.maxClickDrift = 6
+CanvasPointer.doubleClickTime = 300
+```
+
+
+
+
+
+
+
+## Implementing
+
+
+
+Clicking, double-clicking, and dragging can now all be configured during the initial `pointerdown` event, and the correct callback(s) will be executed.
+
+A click event can be as simple as:
+
+```ts
+if (node.isClickedInSpot(e.canvasX, e.canvasY))
+ this.pointer.onClick = () => node.gotClickInSpot()
+```
+
+Full usage can be seen in the old `processMouseDown` handler, which is still in place (several monkey patches in the wild).
+
+### Registering a click or drag event
+
+Example usage:
+
+```typescript
+const { pointer } = this
+// Click / double click - executed on pointerup
+pointer.onClick = e => node.executeClick(e)
+pointer.onDoubleClick = node.gotDoubleClick
+
+// Drag events - executed on pointermove
+pointer.onDragStart = e => {
+ node.isBeingDragged = true
+ canvas.startedDragging(e)
+}
+pointer.onDrag = () => {}
+// finally() is preferred where possible, as it is guaranteed to run
+pointer.onDragEnd = () => {}
+
+// Always run, regardless of outcome
+pointer.finally = () => (node.isBeingDragged = false)
+```
+
+## Widgets
+
+Adds `onPointerDown` callback to node widgets. A few benefits of the new API:
+
+- Simplified usage
+- Exposes callbacks like "double click", removing the need to time / measure multiple pointer events
+- Unified UX - same API as used in the rest of Litegraph
+- Honours the user's click speed and pointer accuracy settings
+
+#### Usage
+
+```ts
+// Callbacks for each pointer action can be configured ahead of time
+widget.onPointerDown = function (pointer, node, canvas) {
+ const e = pointer.eDown
+ const offsetFromNode = [e.canvasX - node.pos[0], e.canvasY - node.pos[1]]
+
+ // Click events - no overlap with drag events
+ pointer.onClick = upEvent => {
+ // Provides access to the whole lifecycle of events in every callback
+ console.log(pointer.eDown)
+ console.log(pointer.eMove ?? "Pointer didn't move")
+ console.log(pointer.eUp)
+ }
+ pointer.onDoubleClick = upEvent => this.customFunction(upEvent)
+
+ // Runs once before the first onDrag event
+ pointer.onDragStart = () => {}
+ // Receives every movement event
+ pointer.onDrag = moveEvent => {}
+ // The pointerup event of a drag
+ pointer.onDragEnd = upEvent => {}
+
+ // Semantics of a "finally" block (try/catch). Once set, the block always executes.
+ pointer.finally = () => {}
+
+ // Return true to cancel regular Litegraph handling of this click / drag
+ return true
+}
+```
+
+
+
+### TypeScript & JSDoc
+
+In-IDE typing is available for use in at least mainstream editors. It can be added to JS projects using the @comfyorg/litegraph npm package.
+
+```ts
+/** @import { IWidget } from './path/to/@comfyorg/litegraph/litegraph.d.ts' */
+/** @type IWidget */
+const widget = node.widgets[0]
+widget.onPointerDown = function (pointer, node, canvas) {}
+```
+
+#### VS Code
+
+
+
+## Hovering over
+
+Adds API for downstream consumers to handle custom cursors. A bitwise enum of items,
+
+```typescript
+type LGraphCanvasState = {
+ /** If `true`, pointer move events will set the canvas cursor style. */
+ shouldSetCursor: boolean,
+ /** Bit flags indicating what is currently below the pointer. */
+ hoveringOver: CanvasItem,
+ ...
+}
+
+// Disable litegraph cursors
+canvas.state.shouldSetCursor = false
+
+// Checking state - bit operators
+if (canvas.state.hoveringOver & CanvasItem.ResizeSe) element.style.cursor = 'se-resize'
+```
+
+
+
+# Removed public interfaces
+
+All are unused and incomplete. Have bugs beyond just typescript typing, and are (currently) not worth maintaining. If any of these features are desired down the track, they can be reimplemented.
+
+- Live mode
+- Subgraph
+- `dragged_node`
+
+## LiteGraph
+
+These features have not been maintained, and would require refactoring / rewrites. As code search revealed them to be unused, they are being removed.
+
+- addNodeMethod
+- compareObjects
+- auto_sort_node_types (option)
diff --git a/src/lib/litegraph/CLAUDE.md b/src/lib/litegraph/CLAUDE.md
new file mode 100644
index 0000000000..c04edd40c3
--- /dev/null
+++ b/src/lib/litegraph/CLAUDE.md
@@ -0,0 +1,62 @@
+- This codebase has extensive eslint autofix rules and IDEs are configured to use eslint as the format on save tool. Run ESLint instead of manually figuring out whitespace fixes or other trivial style concerns. Review the results and correct any remaining eslint errors.
+- Take advantage of `TypedArray` `subarray` when appropriate.
+- The `size` and `pos` properties of `Rectangle` share the same array buffer (`subarray`); they may be used to set the rectangles size and position.
+- Prefer single line `if` syntax over adding curly braces, when the statement has a very concise expression and concise, single line statement.
+- Do not replace `&&=` or `||=` with `=` when there is no reason to do so. If you do find a reason to remove either `&&=` or `||=`, leave a comment explaining why the removal occurred.
+- You are allowed to research code on https://developer.mozilla.org/ and https://stackoverflow.com without asking.
+- When adding features, always write vitest unit tests using cursor rules in @.cursor
+- When writing methods, prefer returning idiomatic JavaScript `undefined` over `null`.
+
+# Bash commands
+
+- `npm run typecheck` Run the typechecker
+- `npm run build` Build the project
+- `npm run lint:fix` Run ESLint
+
+# Code style
+
+- Always prefer best practices when writing code.
+- Write using concise, legible, and easily maintainable code.
+- Avoid repetition where possible, but not at the expense of code legibility.
+- Type assertions are an absolute last resort. In almost all cases, they are a crutch that leads to brittle code.
+
+# Workflow
+
+- Be sure to typecheck when you’re done making a series of code changes
+- Prefer running single tests, and not the whole test suite, for performance
+
+# Testing Guidelines
+
+## Avoiding Circular Dependencies in Tests
+
+**CRITICAL**: When writing tests for subgraph-related code, always import from the barrel export to avoid circular dependency issues:
+
+```typescript
+// ✅ CORRECT - Use barrel import
+import { LGraph, Subgraph, SubgraphNode } from "@/litegraph"
+
+// ❌ WRONG - Direct imports cause circular dependency
+import { LGraph } from "@/LGraph"
+import { Subgraph } from "@/subgraph/Subgraph"
+import { SubgraphNode } from "@/subgraph/SubgraphNode"
+```
+
+**Root cause**: `LGraph` and `Subgraph` have a circular dependency:
+- `LGraph.ts` imports `Subgraph` (creates instances with `new Subgraph()`)
+- `Subgraph.ts` extends `LGraph`
+
+The barrel export (`@/litegraph`) handles this properly, but direct imports cause module loading failures.
+
+## Test Setup for Subgraphs
+
+Use the provided test helpers for consistent setup:
+
+```typescript
+import { createTestSubgraph, createTestSubgraphNode } from "./fixtures/subgraphHelpers"
+
+function createTestSetup() {
+ const subgraph = createTestSubgraph()
+ const subgraphNode = createTestSubgraphNode(subgraph)
+ return { subgraph, subgraphNode }
+}
+```
diff --git a/src/lib/litegraph/CONTRIBUTING.md b/src/lib/litegraph/CONTRIBUTING.md
new file mode 100755
index 0000000000..45043616b4
--- /dev/null
+++ b/src/lib/litegraph/CONTRIBUTING.md
@@ -0,0 +1,9 @@
+# Contribution Rules
+There are some simple rules that everyone should follow:
+
+### Do not commit files from build folder
+> I usually have horrible merge conflicts when I upload the build version that take me too much time to solve, but I want to keep the build version in the repo, so I guess it would be better if only one of us does the built, which would be me.
+> https://github.com/jagenjo/litegraph.js/pull/155#issuecomment-656602861
+Those files will be updated by owner.
+
+
diff --git a/src/lib/litegraph/LICENSE b/src/lib/litegraph/LICENSE
new file mode 100755
index 0000000000..1b52aa11c1
--- /dev/null
+++ b/src/lib/litegraph/LICENSE
@@ -0,0 +1,19 @@
+Copyright (C) 2013 by Javi Agenjo
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/src/lib/litegraph/README.md b/src/lib/litegraph/README.md
new file mode 100755
index 0000000000..1d79f8c7ce
--- /dev/null
+++ b/src/lib/litegraph/README.md
@@ -0,0 +1,173 @@
+# @ComfyOrg/litegraph
+
+This is the litegraph version used in [ComfyUI_frontend](https://github.com/Comfy-Org/ComfyUI_frontend).
+
+It is a fork of the original `litegraph.js`. Some APIs may by unchanged, however it is largely incompatible with the original.
+
+Some early highlights:
+
+- Accumulated comfyUI custom changes (2024-01 ~ 2024-05) (https://github.com/Comfy-Org/litegraph.js/pull/1)
+- Type schema change for ComfyUI_frontend TS migration (https://github.com/Comfy-Org/litegraph.js/pull/3)
+- Zoom fix (https://github.com/Comfy-Org/litegraph.js/pull/7)
+- Emit search box triggering custom events ()
+- Truncate overflowing combo widget text ()
+- Sort node based on ID on graph serialization ()
+- Fix empty input not used when connecting links ()
+- Batch output connection move/disconnect ()
+- And now with hundreds more...
+
+# Install
+
+`npm i @comfyorg/litegraph`
+
+# litegraph.js
+
+A TypeScript library to create graphs in the browser similar to Unreal Blueprints.
+
+
+
+Description of the original litegraph.js
+
+A library in Javascript to create graphs in the browser similar to Unreal Blueprints. Nodes can be programmed easily and it includes an editor to construct and tests the graphs.
+
+It can be integrated easily in any existing web applications and graphs can be run without the need of the editor.
+
+
+
+
+
+## Features
+
+- Renders on Canvas2D (zoom in/out and panning, easy to render complex interfaces, can be used inside a WebGLTexture)
+- Easy to use editor (searchbox, keyboard shortcuts, multiple selection, context menu, ...)
+- Optimized to support hundreds of nodes per graph (on editor but also on execution)
+- Customizable theme (colors, shapes, background)
+- Callbacks to personalize every action/drawing/event of nodes
+- Graphs can be executed in NodeJS
+- Highly customizable nodes (color, shape, widgets, custom rendering)
+- Easy to integrate in any JS application (one single file, no dependencies)
+- Typescript support
+
+## Installation
+
+You can install it using npm
+
+```bash
+npm install @comfyorg/litegraph
+```
+
+## How to code a new Node type
+
+Here is an example of how to build a node that sums two inputs:
+
+```ts
+import { LiteGraph, LGraphNode } from "./litegraph"
+
+class MyAddNode extends LGraphNode {
+ // Name to show
+ title = "Sum"
+
+ constructor() {
+ this.addInput("A", "number")
+ this.addInput("B", "number")
+ this.addOutput("A+B", "number")
+ this.properties.precision = 1
+ }
+
+ // Function to call when the node is executed
+ onExecute() {
+ var A = this.getInputData(0)
+ if (A === undefined) A = 0
+ var B = this.getInputData(1)
+ if (B === undefined) B = 0
+ this.setOutputData(0, A + B)
+ }
+}
+
+// Register the node type
+LiteGraph.registerNodeType("basic/sum", MyAddNode)
+```
+
+## Server side
+
+It also works server-side using NodeJS although some nodes do not work in server (audio, graphics, input, etc).
+
+```ts
+import { LiteGraph, LGraph } from "./litegraph.js"
+
+const graph = new LGraph()
+
+const firstNode = LiteGraph.createNode("basic/sum")
+graph.add(firstNode)
+
+const secondNode = LiteGraph.createNode("basic/sum")
+graph.add(secondNode)
+
+firstNode.connect(0, secondNode, 1)
+
+graph.start()
+```
+
+## Projects using it
+
+### [ComfyUI](https://github.com/comfyanonymous/ComfyUI)
+
+
+
+### Projects using the original litegraph.js
+
+
+
+Click to expand
+
+### [webglstudio.org](http://webglstudio.org)
+
+
+
+### [MOI Elephant](http://moiscript.weebly.com/elephant-systegraveme-nodal.html)
+
+
+
+### Mynodes
+
+
+
+
+
+## Feedback
+
+Please [open an issue](https://github.com/Comfy-Org/litegraph.js/issues/) on the GitHub repo.
+
+# Development
+
+Litegraph has no runtime dependencies. The build tooling has been tested on Node.JS 20.18.x
+
+## Releasing
+
+Use GitHub actions to release normal versions.
+
+1. Run the `Release a New Version` action, selecting the version incrment type
+1. Merge the resultion PR
+1. A GitHub release is automatically published on merge
+
+### Pre-release
+
+The action directly translates `Version increment type` to the npm version command. `Pre-release ID (suffix)` is the option for the `--preid` argument.
+
+e.g. Use `prerelease` increment type to automatically bump the patch version and create a pre-release version. Subsequent runs of prerelease will update the prerelease version only.
+Use `patch` when ready to remove the pre-release suffix.
+
+## Contributors
+
+You can find the [current list of contributors](https://github.com/Comfy-Org/litegraph.js/graphs/contributors) on GitHub.
+
+### Contributors (pre-fork)
+
+- atlasan
+- kriffe
+- rappestad
+- InventivetalentDev
+- NateScarlet
+- coderofsalvation
+- ilyabesk
+- gausszhou
diff --git a/src/lib/litegraph/eslint.config.js b/src/lib/litegraph/eslint.config.js
new file mode 100644
index 0000000000..6acefd7915
--- /dev/null
+++ b/src/lib/litegraph/eslint.config.js
@@ -0,0 +1,310 @@
+import eslint from "@eslint/js"
+import stylistic from "@stylistic/eslint-plugin"
+import eslintPluginAntfu from "eslint-plugin-antfu"
+import jsdoc from "eslint-plugin-jsdoc"
+import eslintPluginSimpleImportSort from "eslint-plugin-simple-import-sort"
+import eslintPluginUnicorn from "eslint-plugin-unicorn"
+import unusedImports from "eslint-plugin-unused-imports"
+import globals from "globals"
+import tseslint from "typescript-eslint"
+
+const rules = Object.fromEntries(
+ Object.entries(eslintPluginAntfu.rules).map(([id]) => [`antfu/${id}`, "off"]),
+)
+const antfuLint = {
+ name: "antfu/without-if-newline-or-imports",
+ plugins: { antfu: eslintPluginAntfu },
+ rules,
+}
+
+const unicornRecommended = eslintPluginUnicorn.configs.recommended
+
+export default tseslint.config(
+ { ignores: [".*/**", "dist/**", "scripts/**"] },
+ { files: ["**/*.{js,mjs,ts,mts}"] },
+ eslint.configs.recommended,
+ ...tseslint.configs.recommendedTypeChecked,
+ stylistic.configs.customize({
+ quotes: "double",
+ braceStyle: "1tbs",
+ commaDangle: "always-multiline",
+ }),
+ {
+ languageOptions: {
+ globals: { ...globals.browser },
+ parserOptions: {
+ projectService: {
+ allowDefaultProject: [
+ "eslint.config.js",
+ "lint-staged.config.js",
+ "vite.config.mts",
+ ],
+ },
+ tsconfigRootDir: import.meta.dirname,
+ },
+ },
+ settings: {
+ jsdoc: {
+ mode: "typescript",
+ },
+ },
+ },
+ {
+ ignores: ["./dist/**/*"],
+ },
+
+ // Unicorn
+ unicornRecommended,
+ {
+ rules: {
+ // Temporarily disabled
+ // See https://github.com/Comfy-Org/litegraph.js/issues/629
+ "unicorn/no-lonely-if": "off",
+ "unicorn/no-this-assignment": "off",
+ "unicorn/no-useless-switch-case": "off",
+ "unicorn/no-zero-fractions": "off",
+ "unicorn/prefer-blob-reading-methods": "off",
+ "unicorn/prefer-default-parameters": "off",
+ "unicorn/prefer-math-min-max": "off",
+ "unicorn/prefer-query-selector": "off",
+ "unicorn/prefer-spread": "off",
+ "unicorn/prefer-structured-clone": "off",
+ "unicorn/prefer-switch": "off",
+ "unicorn/prefer-ternary": "off",
+
+ // Disable rules
+ "unicorn/consistent-function-scoping": "off",
+ "unicorn/explicit-length-check": "off",
+ "unicorn/filename-case": "off",
+ "unicorn/no-negated-condition": "off",
+ "unicorn/no-new-array": "off",
+ "unicorn/no-null": "off",
+ "unicorn/prefer-global-this": "off",
+ "unicorn/prefer-number-properties": "off",
+ "unicorn/prefer-string-raw": "off",
+ "unicorn/prefer-string-slice": "off",
+ "unicorn/prevent-abbreviations": "off",
+ "unicorn/require-number-to-fixed-digits-argument": "off",
+ "unicorn/switch-case-braces": "off",
+
+ // Node rules: dev dependency config, etc.
+ "unicorn/prefer-module": "error",
+ "unicorn/prefer-node-protocol": "error",
+ },
+ },
+
+ // JSDoc
+ jsdoc.configs["flat/contents-typescript-error"],
+ jsdoc.configs["flat/logical-typescript-error"],
+ jsdoc.configs["flat/stylistic-typescript-error"],
+ {
+ rules: {
+ "jsdoc/check-param-names": [
+ "error",
+ {
+ disableMissingParamChecks: true,
+ disableExtraPropertyReporting: true,
+ checkRestProperty: false,
+ checkDestructured: false,
+ },
+ ],
+ "jsdoc/check-tag-names": ["error", { definedTags: ["remarks"] }],
+ "jsdoc/multiline-blocks": "error",
+ // Disabling
+ "jsdoc/empty-tags": "off",
+ "jsdoc/lines-before-block": "off",
+ "jsdoc/match-description": "off",
+ "jsdoc/no-undefined-types": "off",
+ "jsdoc/text-escaping": "off",
+ "jsdoc/valid-types": "off",
+ "jsdoc/informative-docs": "off",
+ },
+ },
+
+ // Base, TypeScript, and Stylistic
+ {
+ rules: {
+ "prefer-template": "error",
+
+ // TODO: Update when TypeScript has been cleaned
+ // https://github.com/Comfy-Org/litegraph.js/issues/657
+ "@typescript-eslint/no-unnecessary-type-assertion": "off",
+ "prefer-spread": "off",
+ "no-empty": "off",
+ "no-prototype-builtins": "off",
+ "no-var": "error",
+ "no-fallthrough": "off",
+
+ "no-empty-pattern": ["error", { allowObjectPatternsAsParameters: true }],
+
+ "@typescript-eslint/ban-ts-comment": "off",
+ "@typescript-eslint/no-this-alias": "off",
+
+ "@typescript-eslint/no-explicit-any": "off",
+ "@typescript-eslint/no-unsafe-member-access": "off",
+ "@typescript-eslint/no-unsafe-assignment": "off",
+ "@typescript-eslint/no-unsafe-argument": "off",
+ "@typescript-eslint/no-unsafe-call": "off",
+ "@typescript-eslint/no-unsafe-return": "off",
+
+ "@typescript-eslint/no-base-to-string": "off",
+ "@typescript-eslint/restrict-plus-operands": "off",
+ "@typescript-eslint/no-implied-eval": "off",
+ "@typescript-eslint/unbound-method": "off",
+ "@typescript-eslint/no-unsafe-enum-comparison": "off",
+ "@typescript-eslint/no-for-in-array": "off",
+ "@typescript-eslint/only-throw-error": "off",
+ "@typescript-eslint/no-duplicate-type-constituents": "off",
+ "@typescript-eslint/no-empty-object-type": "off",
+
+ // "@typescript-eslint/prefer-readonly-parameter-types": "error",
+ // "@typescript-eslint/no-unsafe-function-type": "off",
+
+ "@stylistic/max-len": [
+ "off",
+ {
+ code: 100,
+ comments: 130,
+ ignoreStrings: true,
+ ignoreTemplateLiterals: true,
+ ignoreComments: true,
+ },
+ ],
+
+ // "@stylistic/multiline-comment-style": ["error", "starred-block"],
+ "@stylistic/curly-newline": [
+ "error",
+ { consistent: true, multiline: true },
+ ],
+ // "@stylistic/object-property-newline": ["error", { allowAllPropertiesOnSameLine: true }],
+ // "@stylistic/object-property-newline": "error",
+ "@stylistic/one-var-declaration-per-line": "error",
+
+ "@stylistic/array-bracket-newline": ["error", { multiline: true }],
+ "@stylistic/array-element-newline": [
+ "error",
+ { consistent: true, multiline: true },
+ ],
+
+ "@stylistic/function-paren-newline": ["error", "multiline-arguments"],
+
+ "@stylistic/array-bracket-spacing": "error",
+ "@stylistic/arrow-parens": "error",
+ "@stylistic/arrow-spacing": "error",
+ "@stylistic/block-spacing": "error",
+ "@stylistic/brace-style": "error",
+ "@stylistic/comma-dangle": "error",
+ "@stylistic/comma-spacing": "error",
+ "@stylistic/comma-style": "error",
+ "@stylistic/computed-property-spacing": "error",
+ "@stylistic/dot-location": "error",
+ "@stylistic/eol-last": "error",
+ "@stylistic/indent": ["error", 2, { VariableDeclarator: "first" }],
+ "@stylistic/indent-binary-ops": "error",
+ "@stylistic/key-spacing": "error",
+ "@stylistic/keyword-spacing": "error",
+ "@stylistic/lines-between-class-members": "error",
+ "@stylistic/max-statements-per-line": "error",
+ "@stylistic/member-delimiter-style": "error",
+ "@stylistic/multiline-ternary": "error",
+ "@stylistic/new-parens": "error",
+ "@stylistic/no-extra-parens": "error",
+ "@stylistic/no-floating-decimal": "error",
+ "@stylistic/no-mixed-operators": "error",
+ "@stylistic/no-mixed-spaces-and-tabs": "error",
+ "@stylistic/no-multi-spaces": "error",
+ "@stylistic/no-multiple-empty-lines": "error",
+ "@stylistic/no-tabs": "error",
+ "@stylistic/no-trailing-spaces": "error",
+ "@stylistic/no-whitespace-before-property": "error",
+ "@stylistic/object-curly-spacing": "error",
+ "@stylistic/operator-linebreak": [
+ "error",
+ "after",
+ { overrides: { "?": "before", ":": "before" } },
+ ],
+ "@stylistic/padded-blocks": "error",
+ "@stylistic/quote-props": "error",
+ "@stylistic/quotes": "error",
+ "@stylistic/rest-spread-spacing": "error",
+ "@stylistic/semi": "error",
+ "@stylistic/semi-spacing": "error",
+ "@stylistic/semi-style": ["error", "first"],
+ "@stylistic/space-before-blocks": "error",
+ "@stylistic/space-before-function-paren": "error",
+ "@stylistic/space-in-parens": "error",
+ "@stylistic/space-infix-ops": "error",
+ "@stylistic/space-unary-ops": "error",
+ "@stylistic/spaced-comment": "error",
+ "@stylistic/template-curly-spacing": "error",
+ "@stylistic/template-tag-spacing": "error",
+ "@stylistic/type-annotation-spacing": "error",
+ "@stylistic/type-generic-spacing": "error",
+ "@stylistic/type-named-tuple-spacing": "error",
+ "@stylistic/wrap-iife": "error",
+ "@stylistic/yield-star-spacing": "error",
+ },
+ },
+ {
+ rules: {
+ "@typescript-eslint/no-unused-vars": "error",
+ },
+ files: ["test/**/*.ts"],
+ },
+ {
+ plugins: {
+ "unused-imports": unusedImports,
+ },
+ rules: {
+ "@typescript-eslint/no-unused-vars": "off",
+ "unused-imports/no-unused-imports": "error",
+ "unused-imports/no-unused-vars": [
+ "error",
+ {
+ vars: "all",
+ varsIgnorePattern: "^_",
+ args: "after-used",
+ argsIgnorePattern: "^_",
+ },
+ ],
+ },
+ },
+
+ // Antfu
+ antfuLint,
+ {
+ rules: {
+ "antfu/consistent-chaining": "error",
+ "antfu/consistent-list-newline": "error",
+ "antfu/curly": "error",
+ "antfu/import-dedupe": "error",
+ "antfu/no-import-dist": "error",
+ "antfu/no-ts-export-equal": "error",
+ "antfu/top-level-function": "error",
+ },
+ },
+
+ // Sort imports
+ {
+ plugins: {
+ "simple-import-sort": eslintPluginSimpleImportSort,
+ },
+ rules: {
+ "simple-import-sort/imports": [
+ "error",
+ {
+ // The default grouping, but with type imports first as a separate group.
+ groups: [
+ ["^.*\\u0000$"],
+ ["^\\u0000"],
+ ["^node:"],
+ ["^@?\\w"],
+ ["^"],
+ ["^\\."],
+ ],
+ },
+ ],
+ "simple-import-sort/exports": "error",
+ },
+ },
+)
diff --git a/src/lib/litegraph/imgs/elephant.gif b/src/lib/litegraph/imgs/elephant.gif
new file mode 100755
index 0000000000..3bd28df3c0
Binary files /dev/null and b/src/lib/litegraph/imgs/elephant.gif differ
diff --git a/src/lib/litegraph/imgs/mynodes.png b/src/lib/litegraph/imgs/mynodes.png
new file mode 100755
index 0000000000..29ed2ff9cd
Binary files /dev/null and b/src/lib/litegraph/imgs/mynodes.png differ
diff --git a/src/lib/litegraph/imgs/node_graph_example.png b/src/lib/litegraph/imgs/node_graph_example.png
new file mode 100755
index 0000000000..1ff51fac9a
Binary files /dev/null and b/src/lib/litegraph/imgs/node_graph_example.png differ
diff --git a/src/lib/litegraph/imgs/webglstudio.gif b/src/lib/litegraph/imgs/webglstudio.gif
new file mode 100755
index 0000000000..7ba42947a7
Binary files /dev/null and b/src/lib/litegraph/imgs/webglstudio.gif differ
diff --git a/src/lib/litegraph/lint-staged.config.js b/src/lib/litegraph/lint-staged.config.js
new file mode 100644
index 0000000000..8dc7f8a743
--- /dev/null
+++ b/src/lib/litegraph/lint-staged.config.js
@@ -0,0 +1,17 @@
+export default {
+ "*.css": stagedFiles => `prettier --write ${stagedFiles.join(" ")}`,
+
+ "*.js": stagedFiles => prettierAndEslint(stagedFiles),
+
+ "*.{ts,mts}": stagedFiles => [
+ ...prettierAndEslint(stagedFiles),
+ "tsc",
+ ],
+}
+
+function prettierAndEslint(fileNames) {
+ return [
+ `prettier --write ${fileNames.join(" ")}`,
+ `eslint --fix ${fileNames.join(" ")}`,
+ ]
+}
diff --git a/src/lib/litegraph/package-lock.json b/src/lib/litegraph/package-lock.json
new file mode 100644
index 0000000000..a753121a72
--- /dev/null
+++ b/src/lib/litegraph/package-lock.json
@@ -0,0 +1,6046 @@
+{
+ "name": "@comfyorg/litegraph",
+ "version": "0.17.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "@comfyorg/litegraph",
+ "version": "0.17.0",
+ "license": "MIT",
+ "devDependencies": {
+ "@eslint/js": "^9.21.0",
+ "@stylistic/eslint-plugin": "^2.13.0",
+ "@types/eslint__js": "^8.42.3",
+ "@types/node": "^22.13.9",
+ "eslint": "^9.21.0",
+ "eslint-plugin-antfu": "^3.1.0",
+ "eslint-plugin-jsdoc": "^50.6.3",
+ "eslint-plugin-simple-import-sort": "^12.1.1",
+ "eslint-plugin-unicorn": "^57.0.0",
+ "eslint-plugin-unused-imports": "^4.1.4",
+ "globals": "^15.12.0",
+ "husky": "^9.1.7",
+ "jsdom": "^25.0.1",
+ "lint-staged": "^15.2.10",
+ "prettier": "^3.3.3",
+ "ts-node": "^10.9.2",
+ "typescript": "^5.8.3",
+ "typescript-eslint": "^8.26.0",
+ "vite": "^6.3.5",
+ "vite-plugin-dts": "^4.5.4",
+ "vitest": "^3.1.3"
+ }
+ },
+ "node_modules/@babel/code-frame": {
+ "version": "7.26.2",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz",
+ "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-validator-identifier": "^7.25.9",
+ "js-tokens": "^4.0.0",
+ "picocolors": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-string-parser": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
+ "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-identifier": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz",
+ "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/parser": {
+ "version": "7.27.2",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.2.tgz",
+ "integrity": "sha512-QYLs8299NA7WM/bZAdp+CviYYkVoYXlDW2rzliy3chxd1PQjej7JORuMJDJXJUb9g0TT+B99EwaVLKmX+sPXWw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.27.1"
+ },
+ "bin": {
+ "parser": "bin/babel-parser.js"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@babel/types": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.1.tgz",
+ "integrity": "sha512-+EzkxvLNfiUeKMgy/3luqfsCWFRXLb7U6wNQTk60tovuckwB15B191tJWvpp4HjiQWdJkCxO3Wbvc6jlk3Xb2Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-string-parser": "^7.27.1",
+ "@babel/helper-validator-identifier": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@cspotcode/source-map-support": {
+ "version": "0.8.1",
+ "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz",
+ "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/trace-mapping": "0.3.9"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": {
+ "version": "0.3.9",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz",
+ "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/resolve-uri": "^3.0.3",
+ "@jridgewell/sourcemap-codec": "^1.4.10"
+ }
+ },
+ "node_modules/@es-joy/jsdoccomment": {
+ "version": "0.49.0",
+ "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.49.0.tgz",
+ "integrity": "sha512-xjZTSFgECpb9Ohuk5yMX5RhUEbfeQcuOp8IF60e+wyzWEF0M5xeSgqsfLtvPEX8BIyOX9saZqzuGPmZ8oWc+5Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "comment-parser": "1.4.1",
+ "esquery": "^1.6.0",
+ "jsdoc-type-pratt-parser": "~4.1.0"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/@esbuild/aix-ppc64": {
+ "version": "0.25.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.0.tgz",
+ "integrity": "sha512-O7vun9Sf8DFjH2UtqK8Ku3LkquL9SZL8OLY1T5NZkA34+wG3OQF7cl4Ql8vdNzM6fzBbYfLaiRLIOZ+2FOCgBQ==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "aix"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-arm": {
+ "version": "0.25.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.0.tgz",
+ "integrity": "sha512-PTyWCYYiU0+1eJKmw21lWtC+d08JDZPQ5g+kFyxP0V+es6VPPSUhM6zk8iImp2jbV6GwjX4pap0JFbUQN65X1g==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-arm64": {
+ "version": "0.25.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.0.tgz",
+ "integrity": "sha512-grvv8WncGjDSyUBjN9yHXNt+cq0snxXbDxy5pJtzMKGmmpPxeAmAhWxXI+01lU5rwZomDgD3kJwulEnhTRUd6g==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-x64": {
+ "version": "0.25.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.0.tgz",
+ "integrity": "sha512-m/ix7SfKG5buCnxasr52+LI78SQ+wgdENi9CqyCXwjVR2X4Jkz+BpC3le3AoBPYTC9NHklwngVXvbJ9/Akhrfg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/darwin-arm64": {
+ "version": "0.25.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.0.tgz",
+ "integrity": "sha512-mVwdUb5SRkPayVadIOI78K7aAnPamoeFR2bT5nszFUZ9P8UpK4ratOdYbZZXYSqPKMHfS1wdHCJk1P1EZpRdvw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/darwin-x64": {
+ "version": "0.25.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.0.tgz",
+ "integrity": "sha512-DgDaYsPWFTS4S3nWpFcMn/33ZZwAAeAFKNHNa1QN0rI4pUjgqf0f7ONmXf6d22tqTY+H9FNdgeaAa+YIFUn2Rg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/freebsd-arm64": {
+ "version": "0.25.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.0.tgz",
+ "integrity": "sha512-VN4ocxy6dxefN1MepBx/iD1dH5K8qNtNe227I0mnTRjry8tj5MRk4zprLEdG8WPyAPb93/e4pSgi1SoHdgOa4w==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/freebsd-x64": {
+ "version": "0.25.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.0.tgz",
+ "integrity": "sha512-mrSgt7lCh07FY+hDD1TxiTyIHyttn6vnjesnPoVDNmDfOmggTLXRv8Id5fNZey1gl/V2dyVK1VXXqVsQIiAk+A==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-arm": {
+ "version": "0.25.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.0.tgz",
+ "integrity": "sha512-vkB3IYj2IDo3g9xX7HqhPYxVkNQe8qTK55fraQyTzTX/fxaDtXiEnavv9geOsonh2Fd2RMB+i5cbhu2zMNWJwg==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-arm64": {
+ "version": "0.25.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.0.tgz",
+ "integrity": "sha512-9QAQjTWNDM/Vk2bgBl17yWuZxZNQIF0OUUuPZRKoDtqF2k4EtYbpyiG5/Dk7nqeK6kIJWPYldkOcBqjXjrUlmg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-ia32": {
+ "version": "0.25.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.0.tgz",
+ "integrity": "sha512-43ET5bHbphBegyeqLb7I1eYn2P/JYGNmzzdidq/w0T8E2SsYL1U6un2NFROFRg1JZLTzdCoRomg8Rvf9M6W6Gg==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-loong64": {
+ "version": "0.25.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.0.tgz",
+ "integrity": "sha512-fC95c/xyNFueMhClxJmeRIj2yrSMdDfmqJnyOY4ZqsALkDrrKJfIg5NTMSzVBr5YW1jf+l7/cndBfP3MSDpoHw==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-mips64el": {
+ "version": "0.25.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.0.tgz",
+ "integrity": "sha512-nkAMFju7KDW73T1DdH7glcyIptm95a7Le8irTQNO/qtkoyypZAnjchQgooFUDQhNAy4iu08N79W4T4pMBwhPwQ==",
+ "cpu": [
+ "mips64el"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-ppc64": {
+ "version": "0.25.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.0.tgz",
+ "integrity": "sha512-NhyOejdhRGS8Iwv+KKR2zTq2PpysF9XqY+Zk77vQHqNbo/PwZCzB5/h7VGuREZm1fixhs4Q/qWRSi5zmAiO4Fw==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-riscv64": {
+ "version": "0.25.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.0.tgz",
+ "integrity": "sha512-5S/rbP5OY+GHLC5qXp1y/Mx//e92L1YDqkiBbO9TQOvuFXM+iDqUNG5XopAnXoRH3FjIUDkeGcY1cgNvnXp/kA==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-s390x": {
+ "version": "0.25.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.0.tgz",
+ "integrity": "sha512-XM2BFsEBz0Fw37V0zU4CXfcfuACMrppsMFKdYY2WuTS3yi8O1nFOhil/xhKTmE1nPmVyvQJjJivgDT+xh8pXJA==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-x64": {
+ "version": "0.25.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.0.tgz",
+ "integrity": "sha512-9yl91rHw/cpwMCNytUDxwj2XjFpxML0y9HAOH9pNVQDpQrBxHy01Dx+vaMu0N1CKa/RzBD2hB4u//nfc+Sd3Cw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-arm64": {
+ "version": "0.25.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.0.tgz",
+ "integrity": "sha512-RuG4PSMPFfrkH6UwCAqBzauBWTygTvb1nxWasEJooGSJ/NwRw7b2HOwyRTQIU97Hq37l3npXoZGYMy3b3xYvPw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-x64": {
+ "version": "0.25.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.0.tgz",
+ "integrity": "sha512-jl+qisSB5jk01N5f7sPCsBENCOlPiS/xptD5yxOx2oqQfyourJwIKLRA2yqWdifj3owQZCL2sn6o08dBzZGQzA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-arm64": {
+ "version": "0.25.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.0.tgz",
+ "integrity": "sha512-21sUNbq2r84YE+SJDfaQRvdgznTD8Xc0oc3p3iW/a1EVWeNj/SdUCbm5U0itZPQYRuRTW20fPMWMpcrciH2EJw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-x64": {
+ "version": "0.25.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.0.tgz",
+ "integrity": "sha512-2gwwriSMPcCFRlPlKx3zLQhfN/2WjJ2NSlg5TKLQOJdV0mSxIcYNTMhk3H3ulL/cak+Xj0lY1Ym9ysDV1igceg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/sunos-x64": {
+ "version": "0.25.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.0.tgz",
+ "integrity": "sha512-bxI7ThgLzPrPz484/S9jLlvUAHYMzy6I0XiU1ZMeAEOBcS0VePBFxh1JjTQt3Xiat5b6Oh4x7UC7IwKQKIJRIg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "sunos"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-arm64": {
+ "version": "0.25.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.0.tgz",
+ "integrity": "sha512-ZUAc2YK6JW89xTbXvftxdnYy3m4iHIkDtK3CLce8wg8M2L+YZhIvO1DKpxrd0Yr59AeNNkTiic9YLf6FTtXWMw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-ia32": {
+ "version": "0.25.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.0.tgz",
+ "integrity": "sha512-eSNxISBu8XweVEWG31/JzjkIGbGIJN/TrRoiSVZwZ6pkC6VX4Im/WV2cz559/TXLcYbcrDN8JtKgd9DJVIo8GA==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-x64": {
+ "version": "0.25.0",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.0.tgz",
+ "integrity": "sha512-ZENoHJBxA20C2zFzh6AI4fT6RraMzjYw4xKWemRTRmRVtN9c5DcH9r/f2ihEkMjOW5eGgrwCslG/+Y/3bL+DHQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@eslint-community/eslint-utils": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz",
+ "integrity": "sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==",
+ "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.19.2",
+ "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.2.tgz",
+ "integrity": "sha512-GNKqxfHG2ySmJOBSHg7LxeUx4xpuCoFjacmlCoYWEbaPXLwvfIjixRI12xCQZeULksQb23uiA8F40w5TojpV7w==",
+ "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/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/core": {
+ "version": "0.12.0",
+ "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.12.0.tgz",
+ "integrity": "sha512-cmrR6pytBuSMTaBweKoGMwu3EiHiEC+DoyupPmlZ0HxBJBtIxwe+j/E4XPIKNx+Q74c8lXKPwYawBf5glsTkHg==",
+ "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.0",
+ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.0.tgz",
+ "integrity": "sha512-yaVPAiNAalnCZedKLdR21GOGILMLKPyqSLWaAjQFvYA2i/ciDi8ArYVr69Anohb6cH2Ukhqti4aFnYyPm8wdwQ==",
+ "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/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/@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/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/@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.21.0",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.21.0.tgz",
+ "integrity": "sha512-BqStZ3HX8Yz6LvsF5ByXYrtigrV5AXADWLAGc7PH/1SxOb7/FIYYMszZZWiUou/GB9P2lXWk2SV4d+Z8h0nknw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "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.2.7",
+ "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.7.tgz",
+ "integrity": "sha512-JubJ5B2pJ4k4yGxaNLdbjrnk9d/iDz6/q8wOilpIowd6PJPgaxCuHBnBszq7Ce2TyMrywm5r4PnKm6V3iiZF+g==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@eslint/core": "^0.12.0",
+ "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.2",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.2.tgz",
+ "integrity": "sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18.18"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
+ "node_modules/@jridgewell/resolve-uri": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
+ "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/sourcemap-codec": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz",
+ "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@microsoft/api-extractor": {
+ "version": "7.52.8",
+ "resolved": "https://registry.npmjs.org/@microsoft/api-extractor/-/api-extractor-7.52.8.tgz",
+ "integrity": "sha512-cszYIcjiNscDoMB1CIKZ3My61+JOhpERGlGr54i6bocvGLrcL/wo9o+RNXMBrb7XgLtKaizZWUpqRduQuHQLdg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@microsoft/api-extractor-model": "7.30.6",
+ "@microsoft/tsdoc": "~0.15.1",
+ "@microsoft/tsdoc-config": "~0.17.1",
+ "@rushstack/node-core-library": "5.13.1",
+ "@rushstack/rig-package": "0.5.3",
+ "@rushstack/terminal": "0.15.3",
+ "@rushstack/ts-command-line": "5.0.1",
+ "lodash": "~4.17.15",
+ "minimatch": "~3.0.3",
+ "resolve": "~1.22.1",
+ "semver": "~7.5.4",
+ "source-map": "~0.6.1",
+ "typescript": "5.8.2"
+ },
+ "bin": {
+ "api-extractor": "bin/api-extractor"
+ }
+ },
+ "node_modules/@microsoft/api-extractor-model": {
+ "version": "7.30.6",
+ "resolved": "https://registry.npmjs.org/@microsoft/api-extractor-model/-/api-extractor-model-7.30.6.tgz",
+ "integrity": "sha512-znmFn69wf/AIrwHya3fxX6uB5etSIn6vg4Q4RB/tb5VDDs1rqREc+AvMC/p19MUN13CZ7+V/8pkYPTj7q8tftg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@microsoft/tsdoc": "~0.15.1",
+ "@microsoft/tsdoc-config": "~0.17.1",
+ "@rushstack/node-core-library": "5.13.1"
+ }
+ },
+ "node_modules/@microsoft/api-extractor/node_modules/lru-cache": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+ "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "yallist": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@microsoft/api-extractor/node_modules/minimatch": {
+ "version": "3.0.8",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.8.tgz",
+ "integrity": "sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/@microsoft/api-extractor/node_modules/semver": {
+ "version": "7.5.4",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
+ "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "lru-cache": "^6.0.0"
+ },
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@microsoft/api-extractor/node_modules/typescript": {
+ "version": "5.8.2",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz",
+ "integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=14.17"
+ }
+ },
+ "node_modules/@microsoft/tsdoc": {
+ "version": "0.15.1",
+ "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.15.1.tgz",
+ "integrity": "sha512-4aErSrCR/On/e5G2hDP0wjooqDdauzEbIq8hIkIe5pXV0rtWJZvdCEKL0ykZxex+IxIwBp0eGeV48hQN07dXtw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@microsoft/tsdoc-config": {
+ "version": "0.17.1",
+ "resolved": "https://registry.npmjs.org/@microsoft/tsdoc-config/-/tsdoc-config-0.17.1.tgz",
+ "integrity": "sha512-UtjIFe0C6oYgTnad4q1QP4qXwLhe6tIpNTRStJ2RZEPIkqQPREAwE5spzVxsdn9UaEMUqhh0AqSx3X4nWAKXWw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@microsoft/tsdoc": "0.15.1",
+ "ajv": "~8.12.0",
+ "jju": "~1.4.0",
+ "resolve": "~1.22.2"
+ }
+ },
+ "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/@pkgr/core": {
+ "version": "0.1.1",
+ "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz",
+ "integrity": "sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^12.20.0 || ^14.18.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/unts"
+ }
+ },
+ "node_modules/@rollup/pluginutils": {
+ "version": "5.1.4",
+ "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.4.tgz",
+ "integrity": "sha512-USm05zrsFxYLPdWWq+K3STlWiT/3ELn3RcV5hJMghpeAIhxfsUIg6mt12CBJBInWMV4VneoV7SfGv8xIwo2qNQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "^1.0.0",
+ "estree-walker": "^2.0.2",
+ "picomatch": "^4.0.2"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "peerDependencies": {
+ "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0"
+ },
+ "peerDependenciesMeta": {
+ "rollup": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@rollup/pluginutils/node_modules/picomatch": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz",
+ "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/@rollup/rollup-android-arm-eabi": {
+ "version": "4.40.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.40.2.tgz",
+ "integrity": "sha512-JkdNEq+DFxZfUwxvB58tHMHBHVgX23ew41g1OQinthJ+ryhdRk67O31S7sYw8u2lTjHUPFxwar07BBt1KHp/hg==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@rollup/rollup-android-arm64": {
+ "version": "4.40.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.40.2.tgz",
+ "integrity": "sha512-13unNoZ8NzUmnndhPTkWPWbX3vtHodYmy+I9kuLxN+F+l+x3LdVF7UCu8TWVMt1POHLh6oDHhnOA04n8oJZhBw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@rollup/rollup-darwin-arm64": {
+ "version": "4.40.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.40.2.tgz",
+ "integrity": "sha512-Gzf1Hn2Aoe8VZzevHostPX23U7N5+4D36WJNHK88NZHCJr7aVMG4fadqkIf72eqVPGjGc0HJHNuUaUcxiR+N/w==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@rollup/rollup-darwin-x64": {
+ "version": "4.40.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.40.2.tgz",
+ "integrity": "sha512-47N4hxa01a4x6XnJoskMKTS8XZ0CZMd8YTbINbi+w03A2w4j1RTlnGHOz/P0+Bg1LaVL6ufZyNprSg+fW5nYQQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@rollup/rollup-freebsd-arm64": {
+ "version": "4.40.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.40.2.tgz",
+ "integrity": "sha512-8t6aL4MD+rXSHHZUR1z19+9OFJ2rl1wGKvckN47XFRVO+QL/dUSpKA2SLRo4vMg7ELA8pzGpC+W9OEd1Z/ZqoQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-freebsd-x64": {
+ "version": "4.40.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.40.2.tgz",
+ "integrity": "sha512-C+AyHBzfpsOEYRFjztcYUFsH4S7UsE9cDtHCtma5BK8+ydOZYgMmWg1d/4KBytQspJCld8ZIujFMAdKG1xyr4Q==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
+ "version": "4.40.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.40.2.tgz",
+ "integrity": "sha512-de6TFZYIvJwRNjmW3+gaXiZ2DaWL5D5yGmSYzkdzjBDS3W+B9JQ48oZEsmMvemqjtAFzE16DIBLqd6IQQRuG9Q==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm-musleabihf": {
+ "version": "4.40.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.40.2.tgz",
+ "integrity": "sha512-urjaEZubdIkacKc930hUDOfQPysezKla/O9qV+O89enqsqUmQm8Xj8O/vh0gHg4LYfv7Y7UsE3QjzLQzDYN1qg==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm64-gnu": {
+ "version": "4.40.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.40.2.tgz",
+ "integrity": "sha512-KlE8IC0HFOC33taNt1zR8qNlBYHj31qGT1UqWqtvR/+NuCVhfufAq9fxO8BMFC22Wu0rxOwGVWxtCMvZVLmhQg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm64-musl": {
+ "version": "4.40.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.40.2.tgz",
+ "integrity": "sha512-j8CgxvfM0kbnhu4XgjnCWJQyyBOeBI1Zq91Z850aUddUmPeQvuAy6OiMdPS46gNFgy8gN1xkYyLgwLYZG3rBOg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-loongarch64-gnu": {
+ "version": "4.40.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.40.2.tgz",
+ "integrity": "sha512-Ybc/1qUampKuRF4tQXc7G7QY9YRyeVSykfK36Y5Qc5dmrIxwFhrOzqaVTNoZygqZ1ZieSWTibfFhQ5qK8jpWxw==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
+ "version": "4.40.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.40.2.tgz",
+ "integrity": "sha512-3FCIrnrt03CCsZqSYAOW/k9n625pjpuMzVfeI+ZBUSDT3MVIFDSPfSUgIl9FqUftxcUXInvFah79hE1c9abD+Q==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-riscv64-gnu": {
+ "version": "4.40.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.40.2.tgz",
+ "integrity": "sha512-QNU7BFHEvHMp2ESSY3SozIkBPaPBDTsfVNGx3Xhv+TdvWXFGOSH2NJvhD1zKAT6AyuuErJgbdvaJhYVhVqrWTg==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-riscv64-musl": {
+ "version": "4.40.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.40.2.tgz",
+ "integrity": "sha512-5W6vNYkhgfh7URiXTO1E9a0cy4fSgfE4+Hl5agb/U1sa0kjOLMLC1wObxwKxecE17j0URxuTrYZZME4/VH57Hg==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-s390x-gnu": {
+ "version": "4.40.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.40.2.tgz",
+ "integrity": "sha512-B7LKIz+0+p348JoAL4X/YxGx9zOx3sR+o6Hj15Y3aaApNfAshK8+mWZEf759DXfRLeL2vg5LYJBB7DdcleYCoQ==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-x64-gnu": {
+ "version": "4.40.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.40.2.tgz",
+ "integrity": "sha512-lG7Xa+BmBNwpjmVUbmyKxdQJ3Q6whHjMjzQplOs5Z+Gj7mxPtWakGHqzMqNER68G67kmCX9qX57aRsW5V0VOng==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-x64-musl": {
+ "version": "4.40.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.40.2.tgz",
+ "integrity": "sha512-tD46wKHd+KJvsmije4bUskNuvWKFcTOIM9tZ/RrmIvcXnbi0YK/cKS9FzFtAm7Oxi2EhV5N2OpfFB348vSQRXA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-arm64-msvc": {
+ "version": "4.40.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.40.2.tgz",
+ "integrity": "sha512-Bjv/HG8RRWLNkXwQQemdsWw4Mg+IJ29LK+bJPW2SCzPKOUaMmPEppQlu/Fqk1d7+DX3V7JbFdbkh/NMmurT6Pg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-ia32-msvc": {
+ "version": "4.40.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.40.2.tgz",
+ "integrity": "sha512-dt1llVSGEsGKvzeIO76HToiYPNPYPkmjhMHhP00T9S4rDern8P2ZWvWAQUEJ+R1UdMWJ/42i/QqJ2WV765GZcA==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-x64-msvc": {
+ "version": "4.40.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.40.2.tgz",
+ "integrity": "sha512-bwspbWB04XJpeElvsp+DCylKfF4trJDa2Y9Go8O6A7YLX2LIKGcNK/CYImJN6ZP4DcuOHB4Utl3iCbnR62DudA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rushstack/node-core-library": {
+ "version": "5.13.1",
+ "resolved": "https://registry.npmjs.org/@rushstack/node-core-library/-/node-core-library-5.13.1.tgz",
+ "integrity": "sha512-5yXhzPFGEkVc9Fu92wsNJ9jlvdwz4RNb2bMso+/+TH0nMm1jDDDsOIf4l8GAkPxGuwPw5DH24RliWVfSPhlW/Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ajv": "~8.13.0",
+ "ajv-draft-04": "~1.0.0",
+ "ajv-formats": "~3.0.1",
+ "fs-extra": "~11.3.0",
+ "import-lazy": "~4.0.0",
+ "jju": "~1.4.0",
+ "resolve": "~1.22.1",
+ "semver": "~7.5.4"
+ },
+ "peerDependencies": {
+ "@types/node": "*"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@rushstack/node-core-library/node_modules/ajv": {
+ "version": "8.13.0",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.13.0.tgz",
+ "integrity": "sha512-PRA911Blj99jR5RMeTunVbNXMF6Lp4vZXnk5GQjcnUWUTsrXtekg/pnmFFI2u/I36Y/2bITGS30GZCXei6uNkA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fast-deep-equal": "^3.1.3",
+ "json-schema-traverse": "^1.0.0",
+ "require-from-string": "^2.0.2",
+ "uri-js": "^4.4.1"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/@rushstack/node-core-library/node_modules/lru-cache": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+ "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "yallist": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@rushstack/node-core-library/node_modules/semver": {
+ "version": "7.5.4",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
+ "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "lru-cache": "^6.0.0"
+ },
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@rushstack/rig-package": {
+ "version": "0.5.3",
+ "resolved": "https://registry.npmjs.org/@rushstack/rig-package/-/rig-package-0.5.3.tgz",
+ "integrity": "sha512-olzSSjYrvCNxUFZowevC3uz8gvKr3WTpHQ7BkpjtRpA3wK+T0ybep/SRUMfr195gBzJm5gaXw0ZMgjIyHqJUow==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "resolve": "~1.22.1",
+ "strip-json-comments": "~3.1.1"
+ }
+ },
+ "node_modules/@rushstack/terminal": {
+ "version": "0.15.3",
+ "resolved": "https://registry.npmjs.org/@rushstack/terminal/-/terminal-0.15.3.tgz",
+ "integrity": "sha512-DGJ0B2Vm69468kZCJkPj3AH5nN+nR9SPmC0rFHtzsS4lBQ7/dgOwtwVxYP7W9JPDMuRBkJ4KHmWKr036eJsj9g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@rushstack/node-core-library": "5.13.1",
+ "supports-color": "~8.1.1"
+ },
+ "peerDependencies": {
+ "@types/node": "*"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@rushstack/terminal/node_modules/supports-color": {
+ "version": "8.1.1",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
+ "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/supports-color?sponsor=1"
+ }
+ },
+ "node_modules/@rushstack/ts-command-line": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/@rushstack/ts-command-line/-/ts-command-line-5.0.1.tgz",
+ "integrity": "sha512-bsbUucn41UXrQK7wgM8CNM/jagBytEyJqXw/umtI8d68vFm1Jwxh1OtLrlW7uGZgjCWiiPH6ooUNa1aVsuVr3Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@rushstack/terminal": "0.15.3",
+ "@types/argparse": "1.0.38",
+ "argparse": "~1.0.9",
+ "string-argv": "~0.3.1"
+ }
+ },
+ "node_modules/@stylistic/eslint-plugin": {
+ "version": "2.13.0",
+ "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin/-/eslint-plugin-2.13.0.tgz",
+ "integrity": "sha512-RnO1SaiCFHn666wNz2QfZEFxvmiNRqhzaMXHXxXXKt+MEP7aajlPxUSMIQpKAaJfverpovEYqjBOXDq6dDcaOQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/utils": "^8.13.0",
+ "eslint-visitor-keys": "^4.2.0",
+ "espree": "^10.3.0",
+ "estraverse": "^5.3.0",
+ "picomatch": "^4.0.2"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "peerDependencies": {
+ "eslint": ">=8.40.0"
+ }
+ },
+ "node_modules/@stylistic/eslint-plugin/node_modules/picomatch": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz",
+ "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/@tsconfig/node10": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz",
+ "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@tsconfig/node12": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz",
+ "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@tsconfig/node14": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz",
+ "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@tsconfig/node16": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz",
+ "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/argparse": {
+ "version": "1.0.38",
+ "resolved": "https://registry.npmjs.org/@types/argparse/-/argparse-1.0.38.tgz",
+ "integrity": "sha512-ebDJ9b0e702Yr7pWgB0jzm+CX4Srzz8RcXtLJDJB+BSccqMa36uyH/zUsSYao5+BD1ytv3k3rPYCq4mAE1hsXA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/eslint": {
+ "version": "9.6.1",
+ "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-9.6.1.tgz",
+ "integrity": "sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "*",
+ "@types/json-schema": "*"
+ }
+ },
+ "node_modules/@types/eslint__js": {
+ "version": "8.42.3",
+ "resolved": "https://registry.npmjs.org/@types/eslint__js/-/eslint__js-8.42.3.tgz",
+ "integrity": "sha512-alfG737uhmPdnvkrLdZLcEKJ/B8s9Y4hrZ+YAdzUeoArBlSUERA2E87ROfOaS4jd/C45fzOoZzidLc1IPwLqOw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/eslint": "*"
+ }
+ },
+ "node_modules/@types/estree": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz",
+ "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==",
+ "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/node": {
+ "version": "22.13.9",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.9.tgz",
+ "integrity": "sha512-acBjXdRJ3A6Pb3tqnw9HZmyR3Fiol3aGxRCK1x3d+6CDAMjl7I649wpSd+yNURCjbOUGu9tqtLKnTGxmK6CyGw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "undici-types": "~6.20.0"
+ }
+ },
+ "node_modules/@types/normalize-package-data": {
+ "version": "2.4.4",
+ "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz",
+ "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@typescript-eslint/eslint-plugin": {
+ "version": "8.26.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.26.0.tgz",
+ "integrity": "sha512-cLr1J6pe56zjKYajK6SSSre6nl1Gj6xDp1TY0trpgPzjVbgDwd09v2Ws37LABxzkicmUjhEeg/fAUjPJJB1v5Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/regexpp": "^4.10.0",
+ "@typescript-eslint/scope-manager": "8.26.0",
+ "@typescript-eslint/type-utils": "8.26.0",
+ "@typescript-eslint/utils": "8.26.0",
+ "@typescript-eslint/visitor-keys": "8.26.0",
+ "graphemer": "^1.4.0",
+ "ignore": "^5.3.1",
+ "natural-compare": "^1.4.0",
+ "ts-api-utils": "^2.0.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0",
+ "eslint": "^8.57.0 || ^9.0.0",
+ "typescript": ">=4.8.4 <5.9.0"
+ }
+ },
+ "node_modules/@typescript-eslint/parser": {
+ "version": "8.26.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.26.0.tgz",
+ "integrity": "sha512-mNtXP9LTVBy14ZF3o7JG69gRPBK/2QWtQd0j0oH26HcY/foyJJau6pNUez7QrM5UHnSvwlQcJXKsk0I99B9pOA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/scope-manager": "8.26.0",
+ "@typescript-eslint/types": "8.26.0",
+ "@typescript-eslint/typescript-estree": "8.26.0",
+ "@typescript-eslint/visitor-keys": "8.26.0",
+ "debug": "^4.3.4"
+ },
+ "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 <5.9.0"
+ }
+ },
+ "node_modules/@typescript-eslint/scope-manager": {
+ "version": "8.26.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.26.0.tgz",
+ "integrity": "sha512-E0ntLvsfPqnPwng8b8y4OGuzh/iIOm2z8U3S9zic2TeMLW61u5IH2Q1wu0oSTkfrSzwbDJIB/Lm8O3//8BWMPA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.26.0",
+ "@typescript-eslint/visitor-keys": "8.26.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/type-utils": {
+ "version": "8.26.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.26.0.tgz",
+ "integrity": "sha512-ruk0RNChLKz3zKGn2LwXuVoeBcUMh+jaqzN461uMMdxy5H9epZqIBtYj7UiPXRuOpaALXGbmRuZQhmwHhaS04Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/typescript-estree": "8.26.0",
+ "@typescript-eslint/utils": "8.26.0",
+ "debug": "^4.3.4",
+ "ts-api-utils": "^2.0.1"
+ },
+ "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 <5.9.0"
+ }
+ },
+ "node_modules/@typescript-eslint/types": {
+ "version": "8.26.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.26.0.tgz",
+ "integrity": "sha512-89B1eP3tnpr9A8L6PZlSjBvnJhWXtYfZhECqlBl1D9Lme9mHO6iWlsprBtVenQvY1HMhax1mWOjhtL3fh/u+pA==",
+ "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.26.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.26.0.tgz",
+ "integrity": "sha512-tiJ1Hvy/V/oMVRTbEOIeemA2XoylimlDQ03CgPPNaHYZbpsc78Hmngnt+WXZfJX1pjQ711V7g0H7cSJThGYfPQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.26.0",
+ "@typescript-eslint/visitor-keys": "8.26.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.0.1"
+ },
+ "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 <5.9.0"
+ }
+ },
+ "node_modules/@typescript-eslint/utils": {
+ "version": "8.26.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.26.0.tgz",
+ "integrity": "sha512-2L2tU3FVwhvU14LndnQCA2frYC8JnPDVKyQtWFPf8IYFMt/ykEN1bPolNhNbCVgOmdzTlWdusCTKA/9nKrf8Ig==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.4.0",
+ "@typescript-eslint/scope-manager": "8.26.0",
+ "@typescript-eslint/types": "8.26.0",
+ "@typescript-eslint/typescript-estree": "8.26.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 <5.9.0"
+ }
+ },
+ "node_modules/@typescript-eslint/visitor-keys": {
+ "version": "8.26.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.26.0.tgz",
+ "integrity": "sha512-2z8JQJWAzPdDd51dRQ/oqIJxe99/hoLIqmf8RMCAJQtYDc535W/Jt2+RTP4bP0aKeBG1F65yjIZuczOXCmbWwg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.26.0",
+ "eslint-visitor-keys": "^4.2.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@vitest/expect": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.1.3.tgz",
+ "integrity": "sha512-7FTQQuuLKmN1Ig/h+h/GO+44Q1IlglPlR2es4ab7Yvfx+Uk5xsv+Ykk+MEt/M2Yn/xGmzaLKxGw2lgy2bwuYqg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vitest/spy": "3.1.3",
+ "@vitest/utils": "3.1.3",
+ "chai": "^5.2.0",
+ "tinyrainbow": "^2.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/@vitest/mocker": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.1.3.tgz",
+ "integrity": "sha512-PJbLjonJK82uCWHjzgBJZuR7zmAOrSvKk1QBxrennDIgtH4uK0TB1PvYmc0XBCigxxtiAVPfWtAdy4lpz8SQGQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vitest/spy": "3.1.3",
+ "estree-walker": "^3.0.3",
+ "magic-string": "^0.30.17"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ },
+ "peerDependencies": {
+ "msw": "^2.4.9",
+ "vite": "^5.0.0 || ^6.0.0"
+ },
+ "peerDependenciesMeta": {
+ "msw": {
+ "optional": true
+ },
+ "vite": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@vitest/mocker/node_modules/estree-walker": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz",
+ "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "^1.0.0"
+ }
+ },
+ "node_modules/@vitest/pretty-format": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.1.3.tgz",
+ "integrity": "sha512-i6FDiBeJUGLDKADw2Gb01UtUNb12yyXAqC/mmRWuYl+m/U9GS7s8us5ONmGkGpUUo7/iAYzI2ePVfOZTYvUifA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "tinyrainbow": "^2.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/@vitest/runner": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.1.3.tgz",
+ "integrity": "sha512-Tae+ogtlNfFei5DggOsSUvkIaSuVywujMj6HzR97AHK6XK8i3BuVyIifWAm/sE3a15lF5RH9yQIrbXYuo0IFyA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vitest/utils": "3.1.3",
+ "pathe": "^2.0.3"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/@vitest/snapshot": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.1.3.tgz",
+ "integrity": "sha512-XVa5OPNTYUsyqG9skuUkFzAeFnEzDp8hQu7kZ0N25B1+6KjGm4hWLtURyBbsIAOekfWQ7Wuz/N/XXzgYO3deWQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vitest/pretty-format": "3.1.3",
+ "magic-string": "^0.30.17",
+ "pathe": "^2.0.3"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/@vitest/spy": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.1.3.tgz",
+ "integrity": "sha512-x6w+ctOEmEXdWaa6TO4ilb7l9DxPR5bwEb6hILKuxfU1NqWT2mpJD9NJN7t3OTfxmVlOMrvtoFJGdgyzZ605lQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "tinyspy": "^3.0.2"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/@vitest/utils": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.1.3.tgz",
+ "integrity": "sha512-2Ltrpht4OmHO9+c/nmHtF09HWiyWdworqnHIwjfvDyWjuwKbdkcS9AnhsDn+8E2RM4x++foD1/tNuLPVvWG1Rg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vitest/pretty-format": "3.1.3",
+ "loupe": "^3.1.3",
+ "tinyrainbow": "^2.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/@volar/language-core": {
+ "version": "2.4.13",
+ "resolved": "https://registry.npmjs.org/@volar/language-core/-/language-core-2.4.13.tgz",
+ "integrity": "sha512-MnQJ7eKchJx5Oz+YdbqyFUk8BN6jasdJv31n/7r6/WwlOOv7qzvot6B66887l2ST3bUW4Mewml54euzpJWA6bg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@volar/source-map": "2.4.13"
+ }
+ },
+ "node_modules/@volar/source-map": {
+ "version": "2.4.13",
+ "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.4.13.tgz",
+ "integrity": "sha512-l/EBcc2FkvHgz2ZxV+OZK3kMSroMr7nN3sZLF2/f6kWW66q8+tEL4giiYyFjt0BcubqJhBt6soYIrAPhg/Yr+Q==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@volar/typescript": {
+ "version": "2.4.13",
+ "resolved": "https://registry.npmjs.org/@volar/typescript/-/typescript-2.4.13.tgz",
+ "integrity": "sha512-Ukz4xv84swJPupZeoFsQoeJEOm7U9pqsEnaGGgt5ni3SCTa22m8oJP5Nng3Wed7Uw5RBELdLxxORX8YhJPyOgQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@volar/language-core": "2.4.13",
+ "path-browserify": "^1.0.1",
+ "vscode-uri": "^3.0.8"
+ }
+ },
+ "node_modules/@vue/compiler-core": {
+ "version": "3.5.14",
+ "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.14.tgz",
+ "integrity": "sha512-k7qMHMbKvoCXIxPhquKQVw3Twid3Kg4s7+oYURxLGRd56LiuHJVrvFKI4fm2AM3c8apqODPfVJGoh8nePbXMRA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.27.2",
+ "@vue/shared": "3.5.14",
+ "entities": "^4.5.0",
+ "estree-walker": "^2.0.2",
+ "source-map-js": "^1.2.1"
+ }
+ },
+ "node_modules/@vue/compiler-dom": {
+ "version": "3.5.14",
+ "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.14.tgz",
+ "integrity": "sha512-1aOCSqxGOea5I80U2hQJvXYpPm/aXo95xL/m/mMhgyPUsKe9jhjwWpziNAw7tYRnbz1I61rd9Mld4W9KmmRoug==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vue/compiler-core": "3.5.14",
+ "@vue/shared": "3.5.14"
+ }
+ },
+ "node_modules/@vue/compiler-vue2": {
+ "version": "2.7.16",
+ "resolved": "https://registry.npmjs.org/@vue/compiler-vue2/-/compiler-vue2-2.7.16.tgz",
+ "integrity": "sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "de-indent": "^1.0.2",
+ "he": "^1.2.0"
+ }
+ },
+ "node_modules/@vue/language-core": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-2.2.0.tgz",
+ "integrity": "sha512-O1ZZFaaBGkKbsRfnVH1ifOK1/1BUkyK+3SQsfnh6PmMmD4qJcTU8godCeA96jjDRTL6zgnK7YzCHfaUlH2r0Mw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@volar/language-core": "~2.4.11",
+ "@vue/compiler-dom": "^3.5.0",
+ "@vue/compiler-vue2": "^2.7.16",
+ "@vue/shared": "^3.5.0",
+ "alien-signals": "^0.4.9",
+ "minimatch": "^9.0.3",
+ "muggle-string": "^0.4.1",
+ "path-browserify": "^1.0.1"
+ },
+ "peerDependencies": {
+ "typescript": "*"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@vue/shared": {
+ "version": "3.5.14",
+ "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.14.tgz",
+ "integrity": "sha512-oXTwNxVfc9EtP1zzXAlSlgARLXNC84frFYkS0HHz0h3E4WZSP9sywqjqzGCP9Y34M8ipNmd380pVgmMuwELDyQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/acorn": {
+ "version": "8.14.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz",
+ "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==",
+ "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.3",
+ "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.3.tgz",
+ "integrity": "sha512-MxXdReSRhGO7VlFe1bRG/oI7/mdLV9B9JJT0N8vZOhF7gFRR5l3M8W9G8JxmKV+JC5mGqJ0QvqfSOLsCPa4nUw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "acorn": "^8.11.0"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/agent-base": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz",
+ "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "debug": "^4.3.4"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/ajv": {
+ "version": "8.12.0",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz",
+ "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fast-deep-equal": "^3.1.1",
+ "json-schema-traverse": "^1.0.0",
+ "require-from-string": "^2.0.2",
+ "uri-js": "^4.2.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/ajv-draft-04": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/ajv-draft-04/-/ajv-draft-04-1.0.0.tgz",
+ "integrity": "sha512-mv00Te6nmYbRp5DCwclxtt7yV/joXJPGS7nM+97GdxvuttCOfgI3K4U25zboyeX0O+myI8ERluxQe5wljMmVIw==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "ajv": "^8.5.0"
+ },
+ "peerDependenciesMeta": {
+ "ajv": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/ajv-formats": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz",
+ "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ajv": "^8.0.0"
+ },
+ "peerDependencies": {
+ "ajv": "^8.0.0"
+ },
+ "peerDependenciesMeta": {
+ "ajv": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/alien-signals": {
+ "version": "0.4.14",
+ "resolved": "https://registry.npmjs.org/alien-signals/-/alien-signals-0.4.14.tgz",
+ "integrity": "sha512-itUAVzhczTmP2U5yX67xVpsbbOiquusbWVyA9N+sy6+r6YVbFkahXvNCeEPWEOMhwDYwbVbGHFkVL03N9I5g+Q==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/ansi-escapes": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.0.0.tgz",
+ "integrity": "sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "environment": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "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,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+ }
+ },
+ "node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/are-docs-informative": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmjs.org/are-docs-informative/-/are-docs-informative-0.0.2.tgz",
+ "integrity": "sha512-ixiS0nLNNG5jNQzgZJNoUpBKdo9yTYZMGJ+QgT2jmjR7G7+QHRCc4v6LQ3NgE7EBJq+o0ams3waJwkrlBom8Ig==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/arg": {
+ "version": "4.1.3",
+ "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
+ "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/argparse": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
+ "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "sprintf-js": "~1.0.2"
+ }
+ },
+ "node_modules/assertion-error": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz",
+ "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/asynckit": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/brace-expansion": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+ "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "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/browserslist": {
+ "version": "4.24.4",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz",
+ "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "caniuse-lite": "^1.0.30001688",
+ "electron-to-chromium": "^1.5.73",
+ "node-releases": "^2.0.19",
+ "update-browserslist-db": "^1.1.1"
+ },
+ "bin": {
+ "browserslist": "cli.js"
+ },
+ "engines": {
+ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
+ }
+ },
+ "node_modules/builtin-modules": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-4.0.0.tgz",
+ "integrity": "sha512-p1n8zyCkt1BVrKNFymOHjcDSAl7oq/gUvfgULv2EblgpPVQlQr9yHnWjg9IJ2MhfwPqiYqMMrr01OY7yQoK2yA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18.20"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/cac": {
+ "version": "6.7.14",
+ "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz",
+ "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "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/caniuse-lite": {
+ "version": "1.0.30001701",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001701.tgz",
+ "integrity": "sha512-faRs/AW3jA9nTwmJBSO1PQ6L/EOgsB5HMQQq4iCu5zhPgVVgO/pZRHlmatwijZKetFw8/Pr4q6dEN8sJuq8qTw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "CC-BY-4.0"
+ },
+ "node_modules/chai": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/chai/-/chai-5.2.0.tgz",
+ "integrity": "sha512-mCuXncKXk5iCLhfhwTc0izo0gtEmpz5CtG2y8GiOINBlMVS6v8TMRc5TaLWKS6692m9+dVVfzgeVxR5UxWHTYw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "assertion-error": "^2.0.1",
+ "check-error": "^2.1.1",
+ "deep-eql": "^5.0.1",
+ "loupe": "^3.1.0",
+ "pathval": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "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/check-error": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz",
+ "integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 16"
+ }
+ },
+ "node_modules/ci-info": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.1.0.tgz",
+ "integrity": "sha512-HutrvTNsF48wnxkzERIXOe5/mlcfFcbfCmwcg6CJnizbSue78AbDt+1cgl26zwn61WFxhcPykPfZrbqjGmBb4A==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/sibiraj-s"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/clean-regexp": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/clean-regexp/-/clean-regexp-1.0.0.tgz",
+ "integrity": "sha512-GfisEZEJvzKrmGWkvfhgzcz/BllN1USeqD2V6tg14OAOgaCD2Z/PUEuxnAZ/nPvmaHRG7a8y77p1T/IRQ4D1Hw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "escape-string-regexp": "^1.0.5"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/clean-regexp/node_modules/escape-string-regexp": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
+ "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.8.0"
+ }
+ },
+ "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-truncate": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-4.0.0.tgz",
+ "integrity": "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "slice-ansi": "^5.0.0",
+ "string-width": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "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/colorette": {
+ "version": "2.0.20",
+ "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz",
+ "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/combined-stream": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+ "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "delayed-stream": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/commander": {
+ "version": "12.1.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz",
+ "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/comment-parser": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.4.1.tgz",
+ "integrity": "sha512-buhp5kePrmda3vhc5B9t7pUQXAb2Tnd0qgpkIhPhkHXxJpiPJ11H0ZEU0oBpJ2QztSbzG/ZxMj/CHsYJqRHmyg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 12.0.0"
+ }
+ },
+ "node_modules/compare-versions": {
+ "version": "6.1.1",
+ "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-6.1.1.tgz",
+ "integrity": "sha512-4hm4VPpIecmlg59CHXnRDnqGplJFrbLG4aFEl5vl6cK1u76ws3LLvX7ikFnTDl5vo39sjWD6AaDPYodJp/NNHg==",
+ "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/confbox": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.2.tgz",
+ "integrity": "sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/core-js-compat": {
+ "version": "3.40.0",
+ "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.40.0.tgz",
+ "integrity": "sha512-0XEDpr5y5mijvw8Lbc6E5AkjrHfp7eEoPlu36SWeAbcL8fn1G1ANe8DBlo2XoNN89oVpxWwOjYIPVzR4ZvsKCQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "browserslist": "^4.24.3"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/core-js"
+ }
+ },
+ "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/cssstyle": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.1.0.tgz",
+ "integrity": "sha512-h66W1URKpBS5YMI/V8PyXvTMFT8SupJ1IzoIV8IeBC/ji8WVmrO8dGlTi+2dh6whmdk6BiKJLD/ZBkhWbcg6nA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "rrweb-cssom": "^0.7.1"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/data-urls": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz",
+ "integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "whatwg-mimetype": "^4.0.0",
+ "whatwg-url": "^14.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/de-indent": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz",
+ "integrity": "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/debug": {
+ "version": "4.3.7",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
+ "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/decimal.js": {
+ "version": "10.4.3",
+ "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz",
+ "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/deep-eql": {
+ "version": "5.0.2",
+ "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz",
+ "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "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/delayed-stream": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+ "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/diff": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz",
+ "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.3.1"
+ }
+ },
+ "node_modules/electron-to-chromium": {
+ "version": "1.5.105",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.105.tgz",
+ "integrity": "sha512-ccp7LocdXx3yBhwiG0qTQ7XFrK48Ua2pxIxBdJO8cbddp/MvbBtPFzvnTchtyHQTsgqqczO8cdmAIbpMa0u2+g==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/emoji-regex": {
+ "version": "10.4.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz",
+ "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/entities": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
+ "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=0.12"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/entities?sponsor=1"
+ }
+ },
+ "node_modules/environment": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz",
+ "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/es-module-lexer": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz",
+ "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/esbuild": {
+ "version": "0.25.0",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.0.tgz",
+ "integrity": "sha512-BXq5mqc8ltbaN34cDqWuYKyNhX8D/Z0J1xdtdQ8UcIIIyJyz+ZMKUt58tF3SrZ85jcfN/PZYhjR5uDQAYNVbuw==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "optionalDependencies": {
+ "@esbuild/aix-ppc64": "0.25.0",
+ "@esbuild/android-arm": "0.25.0",
+ "@esbuild/android-arm64": "0.25.0",
+ "@esbuild/android-x64": "0.25.0",
+ "@esbuild/darwin-arm64": "0.25.0",
+ "@esbuild/darwin-x64": "0.25.0",
+ "@esbuild/freebsd-arm64": "0.25.0",
+ "@esbuild/freebsd-x64": "0.25.0",
+ "@esbuild/linux-arm": "0.25.0",
+ "@esbuild/linux-arm64": "0.25.0",
+ "@esbuild/linux-ia32": "0.25.0",
+ "@esbuild/linux-loong64": "0.25.0",
+ "@esbuild/linux-mips64el": "0.25.0",
+ "@esbuild/linux-ppc64": "0.25.0",
+ "@esbuild/linux-riscv64": "0.25.0",
+ "@esbuild/linux-s390x": "0.25.0",
+ "@esbuild/linux-x64": "0.25.0",
+ "@esbuild/netbsd-arm64": "0.25.0",
+ "@esbuild/netbsd-x64": "0.25.0",
+ "@esbuild/openbsd-arm64": "0.25.0",
+ "@esbuild/openbsd-x64": "0.25.0",
+ "@esbuild/sunos-x64": "0.25.0",
+ "@esbuild/win32-arm64": "0.25.0",
+ "@esbuild/win32-ia32": "0.25.0",
+ "@esbuild/win32-x64": "0.25.0"
+ }
+ },
+ "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.21.0",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.21.0.tgz",
+ "integrity": "sha512-KjeihdFqTPhOMXTt7StsDxriV4n66ueuF/jfPNC3j/lduHwr/ijDwJMsF+wyMJethgiKi5wniIE243vi07d3pg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.2.0",
+ "@eslint-community/regexpp": "^4.12.1",
+ "@eslint/config-array": "^0.19.2",
+ "@eslint/core": "^0.12.0",
+ "@eslint/eslintrc": "^3.3.0",
+ "@eslint/js": "9.21.0",
+ "@eslint/plugin-kit": "^0.2.7",
+ "@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.2.0",
+ "eslint-visitor-keys": "^4.2.0",
+ "espree": "^10.3.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-plugin-antfu": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-antfu/-/eslint-plugin-antfu-3.1.0.tgz",
+ "integrity": "sha512-BKlJcpIG8OGyU5JwQCdyTGaLuRgItheEYinhNroCx3bcuz2bCSYK0eNzJvPy2TY8yyz0uSSRxr5KHuQ1WOdOKg==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/antfu"
+ },
+ "peerDependencies": {
+ "eslint": "*"
+ }
+ },
+ "node_modules/eslint-plugin-jsdoc": {
+ "version": "50.6.3",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-50.6.3.tgz",
+ "integrity": "sha512-NxbJyt1M5zffPcYZ8Nb53/8nnbIScmiLAMdoe0/FAszwb7lcSiX3iYBTsuF7RV84dZZJC8r3NghomrUXsmWvxQ==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "@es-joy/jsdoccomment": "~0.49.0",
+ "are-docs-informative": "^0.0.2",
+ "comment-parser": "1.4.1",
+ "debug": "^4.3.6",
+ "escape-string-regexp": "^4.0.0",
+ "espree": "^10.1.0",
+ "esquery": "^1.6.0",
+ "parse-imports": "^2.1.1",
+ "semver": "^7.6.3",
+ "spdx-expression-parse": "^4.0.0",
+ "synckit": "^0.9.1"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "eslint": "^7.0.0 || ^8.0.0 || ^9.0.0"
+ }
+ },
+ "node_modules/eslint-plugin-simple-import-sort": {
+ "version": "12.1.1",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-simple-import-sort/-/eslint-plugin-simple-import-sort-12.1.1.tgz",
+ "integrity": "sha512-6nuzu4xwQtE3332Uz0to+TxDQYRLTKRESSc2hefVT48Zc8JthmN23Gx9lnYhu0FtkRSL1oxny3kJ2aveVhmOVA==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "eslint": ">=5.0.0"
+ }
+ },
+ "node_modules/eslint-plugin-unicorn": {
+ "version": "57.0.0",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-unicorn/-/eslint-plugin-unicorn-57.0.0.tgz",
+ "integrity": "sha512-zUYYa6zfNdTeG9BISWDlcLmz16c+2Ck2o5ZDHh0UzXJz3DEP7xjmlVDTzbyV0W+XksgZ0q37WEWzN2D2Ze+g9Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-validator-identifier": "^7.25.9",
+ "@eslint-community/eslint-utils": "^4.4.1",
+ "ci-info": "^4.1.0",
+ "clean-regexp": "^1.0.0",
+ "core-js-compat": "^3.40.0",
+ "esquery": "^1.6.0",
+ "globals": "^15.15.0",
+ "indent-string": "^5.0.0",
+ "is-builtin-module": "^4.0.0",
+ "jsesc": "^3.1.0",
+ "pluralize": "^8.0.0",
+ "read-package-up": "^11.0.0",
+ "regexp-tree": "^0.1.27",
+ "regjsparser": "^0.12.0",
+ "semver": "^7.7.1",
+ "strip-indent": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=18.18"
+ },
+ "funding": {
+ "url": "https://github.com/sindresorhus/eslint-plugin-unicorn?sponsor=1"
+ },
+ "peerDependencies": {
+ "eslint": ">=9.20.0"
+ }
+ },
+ "node_modules/eslint-plugin-unused-imports": {
+ "version": "4.1.4",
+ "resolved": "https://registry.npmjs.org/eslint-plugin-unused-imports/-/eslint-plugin-unused-imports-4.1.4.tgz",
+ "integrity": "sha512-YptD6IzQjDardkl0POxnnRBhU1OEePMV0nd6siHaRBbd+lyh6NAhFEobiznKU7kTsSsDeSD62Pe7kAM1b7dAZQ==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "@typescript-eslint/eslint-plugin": "^8.0.0-0 || ^7.0.0 || ^6.0.0 || ^5.0.0",
+ "eslint": "^9.0.0 || ^8.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@typescript-eslint/eslint-plugin": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/eslint-scope": {
+ "version": "8.2.0",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.2.0.tgz",
+ "integrity": "sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==",
+ "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.0",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz",
+ "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==",
+ "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/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/eslint/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/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.3.0",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz",
+ "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "acorn": "^8.14.0",
+ "acorn-jsx": "^5.3.2",
+ "eslint-visitor-keys": "^4.2.0"
+ },
+ "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/estree-walker": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
+ "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "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/eventemitter3": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz",
+ "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/execa": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz",
+ "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cross-spawn": "^7.0.3",
+ "get-stream": "^8.0.1",
+ "human-signals": "^5.0.0",
+ "is-stream": "^3.0.0",
+ "merge-stream": "^2.0.0",
+ "npm-run-path": "^5.1.0",
+ "onetime": "^6.0.0",
+ "signal-exit": "^4.1.0",
+ "strip-final-newline": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=16.17"
+ },
+ "funding": {
+ "url": "https://github.com/sindresorhus/execa?sponsor=1"
+ }
+ },
+ "node_modules/expect-type": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.2.1.tgz",
+ "integrity": "sha512-/kP8CAwxzLVEeFrMm4kMmy4CCDlpipyA7MYLVrdJIkV0fYF0UaigQHRsxHiuY/GEea+bh4KSv3TIlgr+2UL6bw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=12.0.0"
+ }
+ },
+ "node_modules/exsolve": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.5.tgz",
+ "integrity": "sha512-pz5dvkYYKQ1AHVrgOzBKWeP4u4FRb3a6DNK2ucr0OoNwYIU4QWsJ+NM36LLzORT+z845MzKHHhpXiUF5nvQoJg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fast-glob": {
+ "version": "3.3.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/find-up-simple": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/find-up-simple/-/find-up-simple-1.0.0.tgz",
+ "integrity": "sha512-q7Us7kcjj2VMePAa02hDAF6d+MzsdsAWEwYyOpwUtlerRBkOEPBCRZrAV4XfcSN8fHAgaD0hP7miwoay6DCprw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "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.1",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz",
+ "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/form-data": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz",
+ "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.8",
+ "mime-types": "^2.1.12"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/fs-extra": {
+ "version": "11.3.0",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.0.tgz",
+ "integrity": "sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "graceful-fs": "^4.2.0",
+ "jsonfile": "^6.0.1",
+ "universalify": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=14.14"
+ }
+ },
+ "node_modules/fsevents": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+ "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/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-stream": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz",
+ "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "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": "15.15.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz",
+ "integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/graceful-fs": {
+ "version": "4.2.11",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
+ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "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-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/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/hosted-git-info": {
+ "version": "7.0.2",
+ "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.2.tgz",
+ "integrity": "sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "lru-cache": "^10.0.1"
+ },
+ "engines": {
+ "node": "^16.14.0 || >=18.0.0"
+ }
+ },
+ "node_modules/html-encoding-sniffer": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz",
+ "integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "whatwg-encoding": "^3.1.1"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "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.5",
+ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz",
+ "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "agent-base": "^7.0.2",
+ "debug": "4"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/human-signals": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz",
+ "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=16.17.0"
+ }
+ },
+ "node_modules/husky": {
+ "version": "9.1.7",
+ "resolved": "https://registry.npmjs.org/husky/-/husky-9.1.7.tgz",
+ "integrity": "sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "husky": "bin.js"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/typicode"
+ }
+ },
+ "node_modules/iconv-lite": {
+ "version": "0.6.3",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
+ "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "safer-buffer": ">= 2.1.2 < 3.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "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": ">= 4"
+ }
+ },
+ "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": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/import-lazy": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-4.0.0.tgz",
+ "integrity": "sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "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",
+ "engines": {
+ "node": ">=0.8.19"
+ }
+ },
+ "node_modules/indent-string": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-5.0.0.tgz",
+ "integrity": "sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/index-to-position": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/index-to-position/-/index-to-position-0.1.2.tgz",
+ "integrity": "sha512-MWDKS3AS1bGCHLBA2VLImJz42f7bJh8wQsTGCzI3j519/CASStoDONUBVz2I/VID0MpiX3SGSnbOD2xUalbE5g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/is-builtin-module": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/is-builtin-module/-/is-builtin-module-4.0.0.tgz",
+ "integrity": "sha512-rWP3AMAalQSesXO8gleROyL2iKU73SX5Er66losQn9rWOWL4Gef0a/xOEOVqjWGMuR2vHG3FJ8UUmT700O8oFg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "builtin-modules": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=18.20"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "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": {
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "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.10.0"
+ }
+ },
+ "node_modules/is-fullwidth-code-point": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz",
+ "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "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": ">=0.10.0"
+ }
+ },
+ "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": "MIT",
+ "engines": {
+ "node": ">=0.12.0"
+ }
+ },
+ "node_modules/is-potential-custom-element-name": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz",
+ "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/is-stream": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz",
+ "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "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/jju": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/jju/-/jju-1.4.0.tgz",
+ "integrity": "sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/js-tokens": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/js-yaml": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
+ "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "argparse": "^2.0.1"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
+ "node_modules/js-yaml/node_modules/argparse": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+ "dev": true,
+ "license": "Python-2.0"
+ },
+ "node_modules/jsdoc-type-pratt-parser": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-4.1.0.tgz",
+ "integrity": "sha512-Hicd6JK5Njt2QB6XYFS7ok9e37O8AYk3jTcppG4YVQnYjOemymvTcmc7OWsmq/Qqj5TdRFO5/x/tIPmBeRtGHg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.0.0"
+ }
+ },
+ "node_modules/jsdom": {
+ "version": "25.0.1",
+ "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-25.0.1.tgz",
+ "integrity": "sha512-8i7LzZj7BF8uplX+ZyOlIz86V6TAsSs+np6m1kpW9u0JWi4z/1t+FzcK1aek+ybTnAC4KhBL4uXCNT0wcUIeCw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cssstyle": "^4.1.0",
+ "data-urls": "^5.0.0",
+ "decimal.js": "^10.4.3",
+ "form-data": "^4.0.0",
+ "html-encoding-sniffer": "^4.0.0",
+ "http-proxy-agent": "^7.0.2",
+ "https-proxy-agent": "^7.0.5",
+ "is-potential-custom-element-name": "^1.0.1",
+ "nwsapi": "^2.2.12",
+ "parse5": "^7.1.2",
+ "rrweb-cssom": "^0.7.1",
+ "saxes": "^6.0.0",
+ "symbol-tree": "^3.2.4",
+ "tough-cookie": "^5.0.0",
+ "w3c-xmlserializer": "^5.0.0",
+ "webidl-conversions": "^7.0.0",
+ "whatwg-encoding": "^3.1.1",
+ "whatwg-mimetype": "^4.0.0",
+ "whatwg-url": "^14.0.0",
+ "ws": "^8.18.0",
+ "xml-name-validator": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "canvas": "^2.11.2"
+ },
+ "peerDependenciesMeta": {
+ "canvas": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/jsesc": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
+ "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "jsesc": "bin/jsesc"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "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": "1.0.0",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
+ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
+ "dev": true,
+ "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/jsonfile": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
+ "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "universalify": "^2.0.0"
+ },
+ "optionalDependencies": {
+ "graceful-fs": "^4.1.6"
+ }
+ },
+ "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/kolorist": {
+ "version": "1.8.0",
+ "resolved": "https://registry.npmjs.org/kolorist/-/kolorist-1.8.0.tgz",
+ "integrity": "sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "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/lilconfig": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.2.tgz",
+ "integrity": "sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/antonk52"
+ }
+ },
+ "node_modules/lint-staged": {
+ "version": "15.2.10",
+ "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.2.10.tgz",
+ "integrity": "sha512-5dY5t743e1byO19P9I4b3x8HJwalIznL5E1FWYnU6OWw33KxNBSLAc6Cy7F2PsFEO8FKnLwjwm5hx7aMF0jzZg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "chalk": "~5.3.0",
+ "commander": "~12.1.0",
+ "debug": "~4.3.6",
+ "execa": "~8.0.1",
+ "lilconfig": "~3.1.2",
+ "listr2": "~8.2.4",
+ "micromatch": "~4.0.8",
+ "pidtree": "~0.6.0",
+ "string-argv": "~0.3.2",
+ "yaml": "~2.5.0"
+ },
+ "bin": {
+ "lint-staged": "bin/lint-staged.js"
+ },
+ "engines": {
+ "node": ">=18.12.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/lint-staged"
+ }
+ },
+ "node_modules/lint-staged/node_modules/chalk": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz",
+ "integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^12.17.0 || ^14.13 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/listr2": {
+ "version": "8.2.5",
+ "resolved": "https://registry.npmjs.org/listr2/-/listr2-8.2.5.tgz",
+ "integrity": "sha512-iyAZCeyD+c1gPyE9qpFu8af0Y+MRtmKOncdGoA2S5EY8iFq99dmmvkNnHiWo+pj0s7yH7l3KPIgee77tKpXPWQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cli-truncate": "^4.0.0",
+ "colorette": "^2.0.20",
+ "eventemitter3": "^5.0.1",
+ "log-update": "^6.1.0",
+ "rfdc": "^1.4.1",
+ "wrap-ansi": "^9.0.0"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/local-pkg": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-1.1.1.tgz",
+ "integrity": "sha512-WunYko2W1NcdfAFpuLUoucsgULmgDBRkdxHxWQ7mK0cQqwPiy8E1enjuRBrhLtZkB5iScJ1XIPdhVEFK8aOLSg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mlly": "^1.7.4",
+ "pkg-types": "^2.0.1",
+ "quansync": "^0.2.8"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/antfu"
+ }
+ },
+ "node_modules/locate-path": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
+ "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-locate": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/lodash": {
+ "version": "4.17.21",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
+ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "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-update": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/log-update/-/log-update-6.1.0.tgz",
+ "integrity": "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-escapes": "^7.0.0",
+ "cli-cursor": "^5.0.0",
+ "slice-ansi": "^7.1.0",
+ "strip-ansi": "^7.1.0",
+ "wrap-ansi": "^9.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/log-update/node_modules/ansi-styles": {
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
+ "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/log-update/node_modules/is-fullwidth-code-point": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.0.0.tgz",
+ "integrity": "sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "get-east-asian-width": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/log-update/node_modules/slice-ansi": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.0.tgz",
+ "integrity": "sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^6.2.1",
+ "is-fullwidth-code-point": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/slice-ansi?sponsor=1"
+ }
+ },
+ "node_modules/loupe": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.3.tgz",
+ "integrity": "sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/lru-cache": {
+ "version": "10.4.3",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
+ "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/magic-string": {
+ "version": "0.30.17",
+ "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz",
+ "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/sourcemap-codec": "^1.5.0"
+ }
+ },
+ "node_modules/make-error": {
+ "version": "1.3.6",
+ "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
+ "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/merge-stream": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
+ "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "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.52.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mime-types": {
+ "version": "2.1.35",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mime-db": "1.52.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mimic-fn": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz",
+ "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/mimic-function": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz",
+ "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/min-indent": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz",
+ "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/minimatch": {
+ "version": "9.0.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
+ "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/minimatch/node_modules/brace-expansion": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+ "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/mlly": {
+ "version": "1.7.4",
+ "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.7.4.tgz",
+ "integrity": "sha512-qmdSIPC4bDJXgZTCR7XosJiNKySV7O215tsPtDN9iEO/7q/76b/ijtgRu/+epFXSJhijtTCCGp3DWS549P3xKw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "acorn": "^8.14.0",
+ "pathe": "^2.0.1",
+ "pkg-types": "^1.3.0",
+ "ufo": "^1.5.4"
+ }
+ },
+ "node_modules/mlly/node_modules/confbox": {
+ "version": "0.1.8",
+ "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz",
+ "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/mlly/node_modules/pkg-types": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz",
+ "integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "confbox": "^0.1.8",
+ "mlly": "^1.7.4",
+ "pathe": "^2.0.1"
+ }
+ },
+ "node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/muggle-string": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/muggle-string/-/muggle-string-0.4.1.tgz",
+ "integrity": "sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/nanoid": {
+ "version": "3.3.8",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz",
+ "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "bin": {
+ "nanoid": "bin/nanoid.cjs"
+ },
+ "engines": {
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+ }
+ },
+ "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/node-releases": {
+ "version": "2.0.19",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz",
+ "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/normalize-package-data": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-6.0.2.tgz",
+ "integrity": "sha512-V6gygoYb/5EmNI+MEGrWkC+e6+Rr7mTmfHrxDbLzxQogBkgzo76rkok0Am6thgSF7Mv2nLOajAJj5vDJZEFn7g==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "hosted-git-info": "^7.0.0",
+ "semver": "^7.3.5",
+ "validate-npm-package-license": "^3.0.4"
+ },
+ "engines": {
+ "node": "^16.14.0 || >=18.0.0"
+ }
+ },
+ "node_modules/npm-run-path": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz",
+ "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "path-key": "^4.0.0"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/npm-run-path/node_modules/path-key": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz",
+ "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/nwsapi": {
+ "version": "2.2.13",
+ "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.13.tgz",
+ "integrity": "sha512-cTGB9ptp9dY9A5VbMSe7fQBcl/tt22Vcqdq8+eN93rblOuE0aCFu4aZ2vMwct/2t+lFnosm8RkQW1I0Omb1UtQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/onetime": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz",
+ "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mimic-fn": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "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/p-limit": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
+ "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "yocto-queue": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/p-locate": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
+ "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-limit": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "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/parse-imports": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/parse-imports/-/parse-imports-2.2.1.tgz",
+ "integrity": "sha512-OL/zLggRp8mFhKL0rNORUTR4yBYujK/uU+xZL+/0Rgm2QE4nLO9v8PzEweSJEbMGKmDRjJE4R3IMJlL2di4JeQ==",
+ "dev": true,
+ "license": "Apache-2.0 AND MIT",
+ "dependencies": {
+ "es-module-lexer": "^1.5.3",
+ "slashes": "^3.0.12"
+ },
+ "engines": {
+ "node": ">= 18"
+ }
+ },
+ "node_modules/parse-json": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-8.1.0.tgz",
+ "integrity": "sha512-rum1bPifK5SSar35Z6EKZuYPJx85pkNaFrxBK3mwdfSJ1/WKbYrjoW/zTPSjRRamfmVX1ACBIdFAO0VRErW/EA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.22.13",
+ "index-to-position": "^0.1.2",
+ "type-fest": "^4.7.1"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/parse5": {
+ "version": "7.2.1",
+ "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.2.1.tgz",
+ "integrity": "sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "entities": "^4.5.0"
+ },
+ "funding": {
+ "url": "https://github.com/inikulin/parse5?sponsor=1"
+ }
+ },
+ "node_modules/path-browserify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz",
+ "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/path-exists": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-key": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "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/pathe": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz",
+ "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/pathval": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.0.tgz",
+ "integrity": "sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 14.16"
+ }
+ },
+ "node_modules/picocolors": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
+ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
+ "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/pidtree": {
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz",
+ "integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "pidtree": "bin/pidtree.js"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/pkg-types": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.1.0.tgz",
+ "integrity": "sha512-wmJwA+8ihJixSoHKxZJRBQG1oY8Yr9pGLzRmSsNms0iNWyHHAlZCa7mmKiFR10YPZuz/2k169JiS/inOjBCZ2A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "confbox": "^0.2.1",
+ "exsolve": "^1.0.1",
+ "pathe": "^2.0.3"
+ }
+ },
+ "node_modules/pluralize": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz",
+ "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/postcss": {
+ "version": "8.5.3",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz",
+ "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "nanoid": "^3.3.8",
+ "picocolors": "^1.1.1",
+ "source-map-js": "^1.2.1"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "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.3.3",
+ "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz",
+ "integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "prettier": "bin/prettier.cjs"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/prettier/prettier?sponsor=1"
+ }
+ },
+ "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/quansync": {
+ "version": "0.2.10",
+ "resolved": "https://registry.npmjs.org/quansync/-/quansync-0.2.10.tgz",
+ "integrity": "sha512-t41VRkMYbkHyCYmOvx/6URnN80H7k4X0lLdBMGsz+maAwrJQYB1djpV6vHrQIBE0WBSGqhtEHrK9U3DWWH8v7A==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://github.com/sponsors/antfu"
+ },
+ {
+ "type": "individual",
+ "url": "https://github.com/sponsors/sxzz"
+ }
+ ],
+ "license": "MIT"
+ },
+ "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/read-package-up": {
+ "version": "11.0.0",
+ "resolved": "https://registry.npmjs.org/read-package-up/-/read-package-up-11.0.0.tgz",
+ "integrity": "sha512-MbgfoNPANMdb4oRBNg5eqLbB2t2r+o5Ua1pNt8BqGp4I0FJZhuVSOj3PaBPni4azWuSzEdNn2evevzVmEk1ohQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "find-up-simple": "^1.0.0",
+ "read-pkg": "^9.0.0",
+ "type-fest": "^4.6.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/read-pkg": {
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-9.0.1.tgz",
+ "integrity": "sha512-9viLL4/n1BJUCT1NXVTdS1jtm80yDEgR5T4yCelII49Mbj0v1rZdKqj7zCiYdbB0CuCgdrvHcNogAKTFPBocFA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/normalize-package-data": "^2.4.3",
+ "normalize-package-data": "^6.0.0",
+ "parse-json": "^8.0.0",
+ "type-fest": "^4.6.0",
+ "unicorn-magic": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/regexp-tree": {
+ "version": "0.1.27",
+ "resolved": "https://registry.npmjs.org/regexp-tree/-/regexp-tree-0.1.27.tgz",
+ "integrity": "sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "regexp-tree": "bin/regexp-tree"
+ }
+ },
+ "node_modules/regjsparser": {
+ "version": "0.12.0",
+ "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.12.0.tgz",
+ "integrity": "sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "jsesc": "~3.0.2"
+ },
+ "bin": {
+ "regjsparser": "bin/parser"
+ }
+ },
+ "node_modules/regjsparser/node_modules/jsesc": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz",
+ "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "jsesc": "bin/jsesc"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/require-from-string": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
+ "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/resolve": {
+ "version": "1.22.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",
+ "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "onetime": "^7.0.0",
+ "signal-exit": "^4.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/restore-cursor/node_modules/onetime": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz",
+ "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mimic-function": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "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/rfdc": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz",
+ "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/rollup": {
+ "version": "4.40.2",
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.40.2.tgz",
+ "integrity": "sha512-tfUOg6DTP4rhQ3VjOO6B4wyrJnGOX85requAXvqYTHsOgb2TFJdZ3aWpT8W2kPoypSGP7dZUyzxJ9ee4buM5Fg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "1.0.7"
+ },
+ "bin": {
+ "rollup": "dist/bin/rollup"
+ },
+ "engines": {
+ "node": ">=18.0.0",
+ "npm": ">=8.0.0"
+ },
+ "optionalDependencies": {
+ "@rollup/rollup-android-arm-eabi": "4.40.2",
+ "@rollup/rollup-android-arm64": "4.40.2",
+ "@rollup/rollup-darwin-arm64": "4.40.2",
+ "@rollup/rollup-darwin-x64": "4.40.2",
+ "@rollup/rollup-freebsd-arm64": "4.40.2",
+ "@rollup/rollup-freebsd-x64": "4.40.2",
+ "@rollup/rollup-linux-arm-gnueabihf": "4.40.2",
+ "@rollup/rollup-linux-arm-musleabihf": "4.40.2",
+ "@rollup/rollup-linux-arm64-gnu": "4.40.2",
+ "@rollup/rollup-linux-arm64-musl": "4.40.2",
+ "@rollup/rollup-linux-loongarch64-gnu": "4.40.2",
+ "@rollup/rollup-linux-powerpc64le-gnu": "4.40.2",
+ "@rollup/rollup-linux-riscv64-gnu": "4.40.2",
+ "@rollup/rollup-linux-riscv64-musl": "4.40.2",
+ "@rollup/rollup-linux-s390x-gnu": "4.40.2",
+ "@rollup/rollup-linux-x64-gnu": "4.40.2",
+ "@rollup/rollup-linux-x64-musl": "4.40.2",
+ "@rollup/rollup-win32-arm64-msvc": "4.40.2",
+ "@rollup/rollup-win32-ia32-msvc": "4.40.2",
+ "@rollup/rollup-win32-x64-msvc": "4.40.2",
+ "fsevents": "~2.3.2"
+ }
+ },
+ "node_modules/rrweb-cssom": {
+ "version": "0.7.1",
+ "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.7.1.tgz",
+ "integrity": "sha512-TrEMa7JGdVm0UThDJSx7ddw5nVm3UJS9o9CCIZ72B1vSyEZoziDqBYP3XIoi/12lKrJR8rE3jeFHMok2F/Mnsg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "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/safer-buffer": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/saxes": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz",
+ "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "xmlchars": "^2.2.0"
+ },
+ "engines": {
+ "node": ">=v12.22.7"
+ }
+ },
+ "node_modules/semver": {
+ "version": "7.7.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz",
+ "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/shebang-command": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+ "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "shebang-regex": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/shebang-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/siginfo": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz",
+ "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/signal-exit": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
+ "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/slashes": {
+ "version": "3.0.12",
+ "resolved": "https://registry.npmjs.org/slashes/-/slashes-3.0.12.tgz",
+ "integrity": "sha512-Q9VME8WyGkc7pJf6QEkj3wE+2CnvZMI+XJhwdTPR8Z/kWQRXi7boAWLDibRPyHRTUTPx5FaU7MsyrjI3yLB4HA==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/slice-ansi": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz",
+ "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^6.0.0",
+ "is-fullwidth-code-point": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/slice-ansi?sponsor=1"
+ }
+ },
+ "node_modules/slice-ansi/node_modules/ansi-styles": {
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
+ "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/source-map-js": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
+ "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/spdx-correct": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz",
+ "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "spdx-expression-parse": "^3.0.0",
+ "spdx-license-ids": "^3.0.0"
+ }
+ },
+ "node_modules/spdx-correct/node_modules/spdx-expression-parse": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz",
+ "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "spdx-exceptions": "^2.1.0",
+ "spdx-license-ids": "^3.0.0"
+ }
+ },
+ "node_modules/spdx-exceptions": {
+ "version": "2.5.0",
+ "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz",
+ "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==",
+ "dev": true,
+ "license": "CC-BY-3.0"
+ },
+ "node_modules/spdx-expression-parse": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-4.0.0.tgz",
+ "integrity": "sha512-Clya5JIij/7C6bRR22+tnGXbc4VKlibKSVj2iHvVeX5iMW7s1SIQlqu699JkODJJIhh/pUu8L0/VLh8xflD+LQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "spdx-exceptions": "^2.1.0",
+ "spdx-license-ids": "^3.0.0"
+ }
+ },
+ "node_modules/spdx-license-ids": {
+ "version": "3.0.21",
+ "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.21.tgz",
+ "integrity": "sha512-Bvg/8F5XephndSK3JffaRqdT+gyhfqIPwDHpX80tJrF8QQRYMo8sNMeaZ2Dp5+jhwKnUmIOyFFQfHRkjJm5nXg==",
+ "dev": true,
+ "license": "CC0-1.0"
+ },
+ "node_modules/sprintf-js": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
+ "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==",
+ "dev": true,
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/stackback": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz",
+ "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/std-env": {
+ "version": "3.9.0",
+ "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.9.0.tgz",
+ "integrity": "sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/string-argv": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz",
+ "integrity": "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.6.19"
+ }
+ },
+ "node_modules/string-width": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz",
+ "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^10.3.0",
+ "get-east-asian-width": "^1.0.0",
+ "strip-ansi": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/strip-ansi": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
+ "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+ }
+ },
+ "node_modules/strip-final-newline": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz",
+ "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/strip-indent": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-4.0.0.tgz",
+ "integrity": "sha512-mnVSV2l+Zv6BLpSD/8V87CW/y9EmmbYzGCIavsnsI6/nwn26DwffM/yztm30Z/I2DY9wdS3vXVCMnHDgZaVNoA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "min-indent": "^1.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/strip-json-comments": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "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/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/symbol-tree": {
+ "version": "3.2.4",
+ "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz",
+ "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/synckit": {
+ "version": "0.9.2",
+ "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.9.2.tgz",
+ "integrity": "sha512-vrozgXDQwYO72vHjUb/HnFbQx1exDjoKzqx23aXEg2a9VIg2TSFZ8FmeZpTjUCFMYw7mpX4BE2SFu8wI7asYsw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@pkgr/core": "^0.1.0",
+ "tslib": "^2.6.2"
+ },
+ "engines": {
+ "node": "^14.18.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/unts"
+ }
+ },
+ "node_modules/tinybench": {
+ "version": "2.9.0",
+ "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz",
+ "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/tinyexec": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz",
+ "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/tinyglobby": {
+ "version": "0.2.13",
+ "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.13.tgz",
+ "integrity": "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fdir": "^6.4.4",
+ "picomatch": "^4.0.2"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/SuperchupuDev"
+ }
+ },
+ "node_modules/tinyglobby/node_modules/fdir": {
+ "version": "6.4.4",
+ "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz",
+ "integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "picomatch": "^3 || ^4"
+ },
+ "peerDependenciesMeta": {
+ "picomatch": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/tinyglobby/node_modules/picomatch": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz",
+ "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/tinypool": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.0.2.tgz",
+ "integrity": "sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.0.0 || >=20.0.0"
+ }
+ },
+ "node_modules/tinyrainbow": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-2.0.0.tgz",
+ "integrity": "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/tinyspy": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-3.0.2.tgz",
+ "integrity": "sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/tldts": {
+ "version": "6.1.58",
+ "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.58.tgz",
+ "integrity": "sha512-MQJrJhjHOYGYb8DobR6Y4AdDbd4TYkyQ+KBDVc5ODzs1cbrvPpfN1IemYi9jfipJ/vR1YWvrDli0hg1y19VRoA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "tldts-core": "^6.1.58"
+ },
+ "bin": {
+ "tldts": "bin/cli.js"
+ }
+ },
+ "node_modules/tldts-core": {
+ "version": "6.1.58",
+ "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.58.tgz",
+ "integrity": "sha512-dR936xmhBm7AeqHIhCWwK765gZ7dFyL+IqLSFAjJbFlUXGMLCb8i2PzlzaOuWBuplBTaBYseSb565nk/ZEM0Bg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "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/tough-cookie": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.0.0.tgz",
+ "integrity": "sha512-FRKsF7cz96xIIeMZ82ehjC3xW2E+O2+v11udrDYewUbszngYhsGa8z6YUMMzO9QJZzzyd0nGGXnML/TReX6W8Q==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "tldts": "^6.1.32"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/tr46": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.0.0.tgz",
+ "integrity": "sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "punycode": "^2.3.1"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/ts-api-utils": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.0.1.tgz",
+ "integrity": "sha512-dnlgjFSVetynI8nzgJ+qF62efpglpWRk8isUEWZGWlJYySCTD6aKvbUDu+zbPeDakk3bg5H4XpitHukgfL1m9w==",
+ "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",
+ "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@cspotcode/source-map-support": "^0.8.0",
+ "@tsconfig/node10": "^1.0.7",
+ "@tsconfig/node12": "^1.0.7",
+ "@tsconfig/node14": "^1.0.0",
+ "@tsconfig/node16": "^1.0.2",
+ "acorn": "^8.4.1",
+ "acorn-walk": "^8.1.1",
+ "arg": "^4.1.0",
+ "create-require": "^1.1.0",
+ "diff": "^4.0.1",
+ "make-error": "^1.1.1",
+ "v8-compile-cache-lib": "^3.0.1",
+ "yn": "3.1.1"
+ },
+ "bin": {
+ "ts-node": "dist/bin.js",
+ "ts-node-cwd": "dist/bin-cwd.js",
+ "ts-node-esm": "dist/bin-esm.js",
+ "ts-node-script": "dist/bin-script.js",
+ "ts-node-transpile-only": "dist/bin-transpile.js",
+ "ts-script": "dist/bin-script-deprecated.js"
+ },
+ "peerDependencies": {
+ "@swc/core": ">=1.2.50",
+ "@swc/wasm": ">=1.2.50",
+ "@types/node": "*",
+ "typescript": ">=2.7"
+ },
+ "peerDependenciesMeta": {
+ "@swc/core": {
+ "optional": true
+ },
+ "@swc/wasm": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/tslib": {
+ "version": "2.8.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
+ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
+ "dev": true,
+ "license": "0BSD"
+ },
+ "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/type-fest": {
+ "version": "4.35.0",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.35.0.tgz",
+ "integrity": "sha512-2/AwEFQDFEy30iOLjrvHDIH7e4HEWH+f1Yl1bI5XMqzuoCUqwYCdxachgsgv0og/JdVZUhbfjcJAoHj5L1753A==",
+ "dev": true,
+ "license": "(MIT OR CC0-1.0)",
+ "engines": {
+ "node": ">=16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/typescript": {
+ "version": "5.8.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz",
+ "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=14.17"
+ }
+ },
+ "node_modules/typescript-eslint": {
+ "version": "8.26.0",
+ "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.26.0.tgz",
+ "integrity": "sha512-PtVz9nAnuNJuAVeUFvwztjuUgSnJInODAUx47VDwWPXzd5vismPOtPtt83tzNXyOjVQbPRp786D6WFW/M2koIA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/eslint-plugin": "8.26.0",
+ "@typescript-eslint/parser": "8.26.0",
+ "@typescript-eslint/utils": "8.26.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 <5.9.0"
+ }
+ },
+ "node_modules/ufo": {
+ "version": "1.6.1",
+ "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.1.tgz",
+ "integrity": "sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/undici-types": {
+ "version": "6.20.0",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz",
+ "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/unicorn-magic": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz",
+ "integrity": "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/universalify": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
+ "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 10.0.0"
+ }
+ },
+ "node_modules/update-browserslist-db": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz",
+ "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "escalade": "^3.2.0",
+ "picocolors": "^1.1.1"
+ },
+ "bin": {
+ "update-browserslist-db": "cli.js"
+ },
+ "peerDependencies": {
+ "browserslist": ">= 4.21.0"
+ }
+ },
+ "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/v8-compile-cache-lib": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
+ "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/validate-npm-package-license": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
+ "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "spdx-correct": "^3.0.0",
+ "spdx-expression-parse": "^3.0.0"
+ }
+ },
+ "node_modules/validate-npm-package-license/node_modules/spdx-expression-parse": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz",
+ "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "spdx-exceptions": "^2.1.0",
+ "spdx-license-ids": "^3.0.0"
+ }
+ },
+ "node_modules/vite": {
+ "version": "6.3.5",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz",
+ "integrity": "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "esbuild": "^0.25.0",
+ "fdir": "^6.4.4",
+ "picomatch": "^4.0.2",
+ "postcss": "^8.5.3",
+ "rollup": "^4.34.9",
+ "tinyglobby": "^0.2.13"
+ },
+ "bin": {
+ "vite": "bin/vite.js"
+ },
+ "engines": {
+ "node": "^18.0.0 || ^20.0.0 || >=22.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/vitejs/vite?sponsor=1"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.3"
+ },
+ "peerDependencies": {
+ "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0",
+ "jiti": ">=1.21.0",
+ "less": "*",
+ "lightningcss": "^1.21.0",
+ "sass": "*",
+ "sass-embedded": "*",
+ "stylus": "*",
+ "sugarss": "*",
+ "terser": "^5.16.0",
+ "tsx": "^4.8.1",
+ "yaml": "^2.4.2"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ },
+ "jiti": {
+ "optional": true
+ },
+ "less": {
+ "optional": true
+ },
+ "lightningcss": {
+ "optional": true
+ },
+ "sass": {
+ "optional": true
+ },
+ "sass-embedded": {
+ "optional": true
+ },
+ "stylus": {
+ "optional": true
+ },
+ "sugarss": {
+ "optional": true
+ },
+ "terser": {
+ "optional": true
+ },
+ "tsx": {
+ "optional": true
+ },
+ "yaml": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/vite-node": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.1.3.tgz",
+ "integrity": "sha512-uHV4plJ2IxCl4u1up1FQRrqclylKAogbtBfOTwcuJ28xFi+89PZ57BRh+naIRvH70HPwxy5QHYzg1OrEaC7AbA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cac": "^6.7.14",
+ "debug": "^4.4.0",
+ "es-module-lexer": "^1.7.0",
+ "pathe": "^2.0.3",
+ "vite": "^5.0.0 || ^6.0.0"
+ },
+ "bin": {
+ "vite-node": "vite-node.mjs"
+ },
+ "engines": {
+ "node": "^18.0.0 || ^20.0.0 || >=22.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ }
+ },
+ "node_modules/vite-node/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/vite-plugin-dts": {
+ "version": "4.5.4",
+ "resolved": "https://registry.npmjs.org/vite-plugin-dts/-/vite-plugin-dts-4.5.4.tgz",
+ "integrity": "sha512-d4sOM8M/8z7vRXHHq/ebbblfaxENjogAAekcfcDCCwAyvGqnPrc7f4NZbvItS+g4WTgerW0xDwSz5qz11JT3vg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@microsoft/api-extractor": "^7.50.1",
+ "@rollup/pluginutils": "^5.1.4",
+ "@volar/typescript": "^2.4.11",
+ "@vue/language-core": "2.2.0",
+ "compare-versions": "^6.1.1",
+ "debug": "^4.4.0",
+ "kolorist": "^1.8.0",
+ "local-pkg": "^1.0.0",
+ "magic-string": "^0.30.17"
+ },
+ "peerDependencies": {
+ "typescript": "*",
+ "vite": "*"
+ },
+ "peerDependenciesMeta": {
+ "vite": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/vite-plugin-dts/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/vite/node_modules/fdir": {
+ "version": "6.4.4",
+ "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz",
+ "integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "picomatch": "^3 || ^4"
+ },
+ "peerDependenciesMeta": {
+ "picomatch": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/vite/node_modules/picomatch": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz",
+ "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/vitest": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.1.3.tgz",
+ "integrity": "sha512-188iM4hAHQ0km23TN/adso1q5hhwKqUpv+Sd6p5sOuh6FhQnRNW3IsiIpvxqahtBabsJ2SLZgmGSpcYK4wQYJw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vitest/expect": "3.1.3",
+ "@vitest/mocker": "3.1.3",
+ "@vitest/pretty-format": "^3.1.3",
+ "@vitest/runner": "3.1.3",
+ "@vitest/snapshot": "3.1.3",
+ "@vitest/spy": "3.1.3",
+ "@vitest/utils": "3.1.3",
+ "chai": "^5.2.0",
+ "debug": "^4.4.0",
+ "expect-type": "^1.2.1",
+ "magic-string": "^0.30.17",
+ "pathe": "^2.0.3",
+ "std-env": "^3.9.0",
+ "tinybench": "^2.9.0",
+ "tinyexec": "^0.3.2",
+ "tinyglobby": "^0.2.13",
+ "tinypool": "^1.0.2",
+ "tinyrainbow": "^2.0.0",
+ "vite": "^5.0.0 || ^6.0.0",
+ "vite-node": "3.1.3",
+ "why-is-node-running": "^2.3.0"
+ },
+ "bin": {
+ "vitest": "vitest.mjs"
+ },
+ "engines": {
+ "node": "^18.0.0 || ^20.0.0 || >=22.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/vitest"
+ },
+ "peerDependencies": {
+ "@edge-runtime/vm": "*",
+ "@types/debug": "^4.1.12",
+ "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0",
+ "@vitest/browser": "3.1.3",
+ "@vitest/ui": "3.1.3",
+ "happy-dom": "*",
+ "jsdom": "*"
+ },
+ "peerDependenciesMeta": {
+ "@edge-runtime/vm": {
+ "optional": true
+ },
+ "@types/debug": {
+ "optional": true
+ },
+ "@types/node": {
+ "optional": true
+ },
+ "@vitest/browser": {
+ "optional": true
+ },
+ "@vitest/ui": {
+ "optional": true
+ },
+ "happy-dom": {
+ "optional": true
+ },
+ "jsdom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/vitest/node_modules/debug": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
+ "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/vscode-uri": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.1.0.tgz",
+ "integrity": "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/w3c-xmlserializer": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz",
+ "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "xml-name-validator": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/webidl-conversions": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
+ "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/whatwg-encoding": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz",
+ "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "iconv-lite": "0.6.3"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/whatwg-mimetype": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz",
+ "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/whatwg-url": {
+ "version": "14.0.0",
+ "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.0.0.tgz",
+ "integrity": "sha512-1lfMEm2IEr7RIV+f4lUNPOqfFL+pO+Xw3fJSqmjX9AbXcXcYOkCe1P6+9VBZB6n94af16NfZf+sSk0JCBZC9aw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "tr46": "^5.0.0",
+ "webidl-conversions": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "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/why-is-node-running": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz",
+ "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "siginfo": "^2.0.0",
+ "stackback": "0.0.2"
+ },
+ "bin": {
+ "why-is-node-running": "cli.js"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/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/wrap-ansi": {
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.0.tgz",
+ "integrity": "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^6.2.1",
+ "string-width": "^7.0.0",
+ "strip-ansi": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/wrap-ansi/node_modules/ansi-styles": {
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
+ "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/ws": {
+ "version": "8.18.0",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz",
+ "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10.0.0"
+ },
+ "peerDependencies": {
+ "bufferutil": "^4.0.1",
+ "utf-8-validate": ">=5.0.2"
+ },
+ "peerDependenciesMeta": {
+ "bufferutil": {
+ "optional": true
+ },
+ "utf-8-validate": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/xml-name-validator": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz",
+ "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/xmlchars": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz",
+ "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/yallist": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/yaml": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.5.1.tgz",
+ "integrity": "sha512-bLQOjaX/ADgQ20isPJRvF0iRUHIxVhYvr53Of7wGcWlO2jvtUlH5m87DsmulFVxRpNLOnI4tB6p/oh8D7kpn9Q==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "yaml": "bin.mjs"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/yn": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
+ "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/yocto-queue": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
+ "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ }
+ }
+}
diff --git a/src/lib/litegraph/package.json b/src/lib/litegraph/package.json
new file mode 100644
index 0000000000..f2bdc2ba96
--- /dev/null
+++ b/src/lib/litegraph/package.json
@@ -0,0 +1,72 @@
+{
+ "name": "@comfyorg/litegraph",
+ "version": "0.17.0",
+ "type": "module",
+ "description": "A graph node editor similar to PD or UDK Blueprints. It works in an HTML5 Canvas and allows to export graphs to be included in applications.",
+ "main": "./dist/litegraph.umd.js",
+ "module": "./dist/litegraph.es.js",
+ "types": "./dist/litegraph.d.ts",
+ "style": "./dist/css/litegraph.css",
+ "exports": {
+ ".": {
+ "types": "./dist/litegraph.d.ts",
+ "import": "./dist/litegraph.es.js",
+ "require": "./dist/litegraph.umd.js"
+ },
+ "./style.css": "./dist/css/litegraph.css",
+ "./dist/*": "./dist/*.d.ts"
+ },
+ "directories": {
+ "doc": "doc"
+ },
+ "private": false,
+ "scripts": {
+ "build": "tsc && vite build",
+ "dev": "vite",
+ "preview": "vite preview",
+ "watch": "vite build --watch",
+ "test": "vitest",
+ "lint": "eslint",
+ "lint:fix": "eslint --fix",
+ "lint:ci": "eslint src",
+ "format": "prettier --check './src/*.{js,ts,tsx,vue,mts}'",
+ "format:fix": "prettier --write './src/*.{js,ts,tsx,vue,mts}'",
+ "typecheck": "tsc"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git@github.com:Comfy-Org/litegraph.js.git"
+ },
+ "author": "comfyorg",
+ "license": "MIT",
+ "files": [
+ "dist"
+ ],
+ "bugs": {
+ "url": "https://github.com/Comfy-Org/litegraph.js/issues"
+ },
+ "homepage": "https://github.com/Comfy-Org/litegraph.js",
+ "devDependencies": {
+ "@eslint/js": "^9.21.0",
+ "@stylistic/eslint-plugin": "^2.13.0",
+ "@types/eslint__js": "^8.42.3",
+ "@types/node": "^22.13.9",
+ "eslint": "^9.21.0",
+ "eslint-plugin-antfu": "^3.1.0",
+ "eslint-plugin-jsdoc": "^50.6.3",
+ "eslint-plugin-simple-import-sort": "^12.1.1",
+ "eslint-plugin-unicorn": "^57.0.0",
+ "eslint-plugin-unused-imports": "^4.1.4",
+ "globals": "^15.12.0",
+ "husky": "^9.1.7",
+ "jsdom": "^25.0.1",
+ "lint-staged": "^15.2.10",
+ "prettier": "^3.3.3",
+ "ts-node": "^10.9.2",
+ "typescript": "^5.8.3",
+ "typescript-eslint": "^8.26.0",
+ "vite": "^6.3.5",
+ "vite-plugin-dts": "^4.5.4",
+ "vitest": "^3.1.3"
+ }
+}
diff --git a/src/lib/litegraph/public/css/litegraph.css b/src/lib/litegraph/public/css/litegraph.css
new file mode 100644
index 0000000000..ec23023ebf
--- /dev/null
+++ b/src/lib/litegraph/public/css/litegraph.css
@@ -0,0 +1,638 @@
+/* this CSS contains only the basic CSS needed to run the app and use it */
+
+.lgraphcanvas {
+ /*cursor: crosshair;*/
+ user-select: none;
+ -moz-user-select: none;
+ -webkit-user-select: none;
+ outline: none;
+ font-family: Tahoma, sans-serif;
+}
+
+.lgraphcanvas * {
+ box-sizing: border-box;
+}
+
+.litegraph.litecontextmenu {
+ font-family: Tahoma, sans-serif;
+ position: fixed;
+ top: 100px;
+ left: 100px;
+ min-width: 100px;
+ color: #aaf;
+ padding: 0;
+ box-shadow: 0 0 10px black !important;
+ background-color: #2e2e2e !important;
+ z-index: 10;
+ max-height: -webkit-fill-available;
+ overflow-y: auto;
+}
+
+/* Enable scrolling overflow in Firefox */
+@supports not (max-height: -webkit-fill-available) {
+ .litegraph.litecontextmenu {
+ max-height: 80vh;
+ overflow-y: scroll;
+ }
+}
+
+.litegraph.litecontextmenu.dark {
+ background-color: #000 !important;
+}
+
+.litegraph.litecontextmenu .litemenu-title img {
+ margin-top: 2px;
+ margin-left: 2px;
+ margin-right: 4px;
+}
+
+.litegraph.litecontextmenu .litemenu-entry {
+ margin: 2px;
+ padding: 2px;
+}
+
+.litegraph.litecontextmenu .litemenu-entry.submenu {
+ background-color: #2e2e2e !important;
+}
+
+.litegraph.litecontextmenu.dark .litemenu-entry.submenu {
+ background-color: #000 !important;
+}
+
+.litegraph .litemenubar ul {
+ font-family: Tahoma, sans-serif;
+ margin: 0;
+ padding: 0;
+}
+
+.litegraph .litemenubar li {
+ font-size: 14px;
+ color: #999;
+ display: inline-block;
+ min-width: 50px;
+ padding-left: 10px;
+ padding-right: 10px;
+ user-select: none;
+ -moz-user-select: none;
+ -webkit-user-select: none;
+ cursor: pointer;
+}
+
+.litegraph .litemenubar li:hover {
+ background-color: #777;
+ color: #eee;
+}
+
+.litegraph .litegraph .litemenubar-panel {
+ position: absolute;
+ top: 5px;
+ left: 5px;
+ min-width: 100px;
+ background-color: #444;
+ box-shadow: 0 0 3px black;
+ padding: 4px;
+ border-bottom: 2px solid #aaf;
+ z-index: 10;
+}
+
+.litegraph .litemenu-entry,
+.litemenu-title {
+ font-size: 12px;
+ color: #aaa;
+ padding: 0 0 0 4px;
+ margin: 2px;
+ padding-left: 2px;
+ -moz-user-select: none;
+ -webkit-user-select: none;
+ user-select: none;
+ cursor: pointer;
+}
+
+.litegraph .litemenu-entry .icon {
+ display: inline-block;
+ width: 12px;
+ height: 12px;
+ margin: 2px;
+ vertical-align: top;
+}
+
+.litegraph .litemenu-entry.checked .icon {
+ background-color: #aaf;
+}
+
+.litegraph .litemenu-entry .more {
+ float: right;
+ padding-right: 5px;
+}
+
+.litegraph .litemenu-entry.disabled {
+ opacity: 0.5;
+ cursor: default;
+}
+
+.litegraph .litemenu-entry.separator {
+ display: block;
+ border-top: 1px solid #333;
+ border-bottom: 1px solid #666;
+ width: 100%;
+ height: 0px;
+ margin: 3px 0 2px 0;
+ background-color: transparent;
+ padding: 0 !important;
+ cursor: default !important;
+}
+
+.litegraph .litemenu-entry.has_submenu {
+ border-right: 2px solid cyan;
+}
+
+.litegraph .litemenu-title {
+ color: #dde;
+ background-color: #111;
+ margin: 0;
+ padding: 2px;
+ cursor: default;
+}
+
+.litegraph .litemenu-entry:hover:not(.disabled):not(.separator) {
+ background-color: #444 !important;
+ color: #eee;
+ transition: all 0.2s;
+}
+
+.litegraph .litemenu-entry .property_name {
+ display: inline-block;
+ text-align: left;
+ min-width: 80px;
+ min-height: 1.2em;
+}
+
+.litegraph .litemenu-entry .property_value {
+ display: inline-block;
+ background-color: rgba(0, 0, 0, 0.5);
+ text-align: right;
+ min-width: 80px;
+ min-height: 1.2em;
+ vertical-align: middle;
+ padding-right: 10px;
+}
+
+.litegraph.litesearchbox {
+ font-family: Tahoma, sans-serif;
+ position: absolute;
+ background-color: rgba(0, 0, 0, 0.5);
+ padding-top: 4px;
+}
+
+.litegraph.litesearchbox input,
+.litegraph.litesearchbox select {
+ margin-top: 3px;
+ min-width: 60px;
+ min-height: 1.5em;
+ background-color: black;
+ border: 0;
+ color: white;
+ padding-left: 10px;
+ margin-right: 5px;
+ max-width: 300px;
+}
+
+.litegraph.litesearchbox .name {
+ display: inline-block;
+ min-width: 60px;
+ min-height: 1.5em;
+ padding-left: 10px;
+}
+
+.litegraph.litesearchbox .helper {
+ overflow: auto;
+ max-height: 200px;
+ margin-top: 2px;
+}
+
+.litegraph.lite-search-item {
+ font-family: Tahoma, sans-serif;
+ background-color: rgba(0, 0, 0, 0.5);
+ color: white;
+ padding-top: 2px;
+}
+
+.litegraph.lite-search-item.not_in_filter {
+ /*background-color: rgba(50, 50, 50, 0.5);*/
+ /*color: #999;*/
+ color: #b99;
+ font-style: italic;
+}
+
+.litegraph.lite-search-item.generic_type {
+ /*background-color: rgba(50, 50, 50, 0.5);*/
+ /*color: #DD9;*/
+ color: #999;
+ font-style: italic;
+}
+
+.litegraph.lite-search-item:hover,
+.litegraph.lite-search-item.selected {
+ cursor: pointer;
+ background-color: white;
+ color: black;
+}
+
+.litegraph.lite-search-item-type {
+ display: inline-block;
+ background: rgba(0, 0, 0, 0.2);
+ margin-left: 5px;
+ font-size: 14px;
+ padding: 2px 5px;
+ position: relative;
+ top: -2px;
+ opacity: 0.8;
+ border-radius: 4px;
+}
+
+/* DIALOGs ******/
+
+.litegraph .dialog {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ margin-top: -150px;
+ margin-left: -200px;
+
+ background-color: #2a2a2a;
+
+ min-width: 400px;
+ min-height: 200px;
+ box-shadow: 0 0 4px #111;
+ border-radius: 6px;
+}
+
+.litegraph .dialog.settings {
+ left: 10px;
+ top: 10px;
+ height: calc(100% - 20px);
+ margin: auto;
+ max-width: 50%;
+}
+
+.litegraph .dialog.centered {
+ top: 50px;
+ left: 50%;
+ position: absolute;
+ transform: translateX(-50%);
+ min-width: 600px;
+ min-height: 300px;
+ height: calc(100% - 100px);
+ margin: auto;
+}
+
+.litegraph .dialog .close {
+ float: right;
+ margin: 4px;
+ margin-right: 10px;
+ cursor: pointer;
+ font-size: 1.4em;
+}
+
+.litegraph .dialog .close:hover {
+ color: white;
+}
+
+.litegraph .dialog .dialog-header {
+ color: #aaa;
+ border-bottom: 1px solid #161616;
+}
+
+.litegraph .dialog .dialog-header {
+ height: 40px;
+}
+.litegraph .dialog .dialog-footer {
+ height: 50px;
+ padding: 10px;
+ margin: 0;
+ border-top: 1px solid #1a1a1a;
+}
+
+.litegraph .dialog .dialog-header .dialog-title {
+ font: 20px "Arial";
+ margin: 4px;
+ padding: 4px 10px;
+ display: inline-block;
+}
+
+.litegraph .dialog .dialog-content,
+.litegraph .dialog .dialog-alt-content {
+ height: calc(100% - 90px);
+ width: 100%;
+ min-height: 100px;
+ display: inline-block;
+ color: #aaa;
+ /*background-color: black;*/
+ overflow: auto;
+}
+
+.litegraph .dialog .dialog-content h3 {
+ margin: 10px;
+}
+
+.litegraph .dialog .dialog-content .connections {
+ flex-direction: row;
+}
+
+.litegraph .dialog .dialog-content .connections .connections_side {
+ width: calc(50% - 5px);
+ min-height: 100px;
+ background-color: black;
+ display: flex;
+}
+
+.litegraph .dialog .node_type {
+ font-size: 1.2em;
+ display: block;
+ margin: 10px;
+}
+
+.litegraph .dialog .node_desc {
+ opacity: 0.5;
+ display: block;
+ margin: 10px;
+}
+
+.litegraph .dialog .separator {
+ display: block;
+ width: calc(100% - 4px);
+ height: 1px;
+ border-top: 1px solid #000;
+ border-bottom: 1px solid #333;
+ margin: 10px 2px;
+ padding: 0;
+}
+
+.litegraph .dialog .property {
+ margin-bottom: 2px;
+ padding: 4px;
+}
+
+.litegraph .dialog .property:hover {
+ background: #545454;
+}
+
+.litegraph .dialog .property_name {
+ color: #737373;
+ display: inline-block;
+ text-align: left;
+ vertical-align: top;
+ width: 160px;
+ padding-left: 4px;
+ overflow: hidden;
+ margin-right: 6px;
+}
+
+.litegraph .dialog .property:hover .property_name {
+ color: white;
+}
+
+.litegraph .dialog .property_value {
+ display: inline-block;
+ text-align: right;
+ color: #aaa;
+ background-color: #1a1a1a;
+ /*width: calc( 100% - 122px );*/
+ max-width: calc(100% - 162px);
+ min-width: 200px;
+ max-height: 300px;
+ min-height: 20px;
+ padding: 4px;
+ padding-right: 12px;
+ overflow: hidden;
+ cursor: pointer;
+ border-radius: 3px;
+}
+
+.litegraph .dialog .property_value:hover {
+ color: white;
+}
+
+.litegraph .dialog .property.boolean .property_value {
+ padding-right: 30px;
+ color: #a88;
+ /*width: auto;
+ float: right;*/
+}
+
+.litegraph .dialog .property.boolean.bool-on .property_name {
+ color: #8a8;
+}
+.litegraph .dialog .property.boolean.bool-on .property_value {
+ color: #8a8;
+}
+
+.litegraph .dialog .btn {
+ border: 0;
+ border-radius: 4px;
+ padding: 4px 20px;
+ margin-left: 0px;
+ background-color: #060606;
+ color: #8e8e8e;
+}
+
+.litegraph .dialog .btn:hover {
+ background-color: #111;
+ color: #fff;
+}
+
+.litegraph .dialog .btn.delete:hover {
+ background-color: #f33;
+ color: black;
+}
+
+.litegraph .bullet_icon {
+ margin-left: 10px;
+ border-radius: 10px;
+ width: 12px;
+ height: 12px;
+ background-color: #666;
+ display: inline-block;
+ margin-top: 2px;
+ margin-right: 4px;
+ transition: background-color 0.1s ease 0s;
+ -moz-transition: background-color 0.1s ease 0s;
+}
+
+.litegraph .bullet_icon:hover {
+ background-color: #698;
+ cursor: pointer;
+}
+
+/* OLD */
+
+.graphcontextmenu {
+ padding: 4px;
+ min-width: 100px;
+}
+
+.graphcontextmenu-title {
+ color: #dde;
+ background-color: #222;
+ margin: 0;
+ padding: 2px;
+ cursor: default;
+}
+
+.graphmenu-entry {
+ box-sizing: border-box;
+ margin: 2px;
+ padding-left: 20px;
+ user-select: none;
+ -moz-user-select: none;
+ -webkit-user-select: none;
+ transition: all linear 0.3s;
+}
+
+.graphmenu-entry.event,
+.litemenu-entry.event {
+ border-left: 8px solid orange;
+ padding-left: 12px;
+}
+
+.graphmenu-entry.disabled {
+ opacity: 0.3;
+}
+
+.graphmenu-entry.submenu {
+ border-right: 2px solid #eee;
+}
+
+.graphmenu-entry:hover {
+ background-color: #555;
+}
+
+.graphmenu-entry.separator {
+ background-color: #111;
+ border-bottom: 1px solid #666;
+ height: 1px;
+ width: calc(100% - 20px);
+ -moz-width: calc(100% - 20px);
+ -webkit-width: calc(100% - 20px);
+}
+
+.graphmenu-entry .property_name {
+ display: inline-block;
+ text-align: left;
+ min-width: 80px;
+ min-height: 1.2em;
+}
+
+.graphmenu-entry .property_value,
+.litemenu-entry .property_value {
+ display: inline-block;
+ background-color: rgba(0, 0, 0, 0.5);
+ text-align: right;
+ min-width: 80px;
+ min-height: 1.2em;
+ vertical-align: middle;
+ padding-right: 10px;
+}
+
+.graphdialog {
+ position: absolute;
+ top: 10px;
+ left: 10px;
+ min-height: 2em;
+ background-color: #333;
+ font-size: 1.2em;
+ box-shadow: 0 0 10px black !important;
+ z-index: 10;
+}
+
+.graphdialog.rounded {
+ border-radius: 12px;
+ padding-right: 2px;
+}
+
+.graphdialog .name {
+ display: inline-block;
+ min-width: 60px;
+ min-height: 1.5em;
+ padding-left: 10px;
+}
+
+.graphdialog input,
+.graphdialog textarea,
+.graphdialog select {
+ margin: 3px;
+ min-width: 60px;
+ min-height: 1.5em;
+ background-color: black;
+ border: 0;
+ color: white;
+ padding-left: 10px;
+ outline: none;
+}
+
+.graphdialog textarea {
+ min-height: 150px;
+}
+
+.graphdialog button {
+ margin-top: 3px;
+ vertical-align: top;
+ background-color: #999;
+ border: 0;
+}
+
+.graphdialog button.rounded,
+.graphdialog input.rounded {
+ border-radius: 0 12px 12px 0;
+}
+
+.graphdialog .helper {
+ overflow: auto;
+ max-height: 200px;
+}
+
+.graphdialog .help-item {
+ padding-left: 10px;
+}
+
+.graphdialog .help-item:hover,
+.graphdialog .help-item.selected {
+ cursor: pointer;
+ background-color: white;
+ color: black;
+}
+
+.litegraph .dialog {
+ min-height: 0;
+}
+.litegraph .dialog .dialog-content {
+ display: block;
+}
+.litegraph .graphdialog {
+ display: flex;
+ align-items: center;
+ border-radius: 20px;
+ padding: 4px 10px;
+ position: fixed;
+}
+.litegraph .graphdialog .name {
+ padding: 0;
+ min-height: 0;
+ font-size: 16px;
+ vertical-align: middle;
+}
+.litegraph .graphdialog .value {
+ font-size: 16px;
+ min-height: 0;
+ margin: 0 10px;
+ padding: 2px 5px;
+}
+.litegraph .graphdialog input[type="checkbox"] {
+ width: 16px;
+ height: 16px;
+}
+.litegraph .graphdialog button {
+ padding: 4px 18px;
+ border-radius: 20px;
+ cursor: pointer;
+}
diff --git a/src/lib/litegraph/src/CanvasPointer.ts b/src/lib/litegraph/src/CanvasPointer.ts
new file mode 100644
index 0000000000..eba3a02003
--- /dev/null
+++ b/src/lib/litegraph/src/CanvasPointer.ts
@@ -0,0 +1,290 @@
+import type { CompassCorners } from "./interfaces"
+import type { CanvasPointerEvent } from "./types/events"
+
+import { dist2 } from "./measure"
+
+/**
+ * Allows click and drag actions to be declared ahead of time during a pointerdown event.
+ *
+ * By default, it retains the most recent event of each type until it is reset (on pointerup).
+ * - {@link eDown}
+ * - {@link eMove}
+ * - {@link eUp}
+ *
+ * Depending on whether the user clicks or drags the pointer, only the appropriate callbacks are called:
+ * - {@link onClick}
+ * - {@link onDoubleClick}
+ * - {@link onDragStart}
+ * - {@link onDrag}
+ * - {@link onDragEnd}
+ * - {@link finally}
+ * @see
+ * - {@link LGraphCanvas.processMouseDown}
+ * - {@link LGraphCanvas.processMouseMove}
+ * - {@link LGraphCanvas.processMouseUp}
+ */
+export class CanvasPointer {
+ /** Maximum time in milliseconds to ignore click drift */
+ static bufferTime = 150
+
+ /** Maximum gap between pointerup and pointerdown events to be considered as a double click */
+ static doubleClickTime = 300
+
+ /** Maximum offset from click location */
+ static get maxClickDrift() {
+ return this.#maxClickDrift
+ }
+
+ static set maxClickDrift(value) {
+ this.#maxClickDrift = value
+ this.#maxClickDrift2 = value * value
+ }
+
+ static #maxClickDrift = 6
+ /** {@link maxClickDrift} squared. Used to calculate click drift without `sqrt`. */
+ static #maxClickDrift2 = this.#maxClickDrift ** 2
+
+ /** The element this PointerState should capture input against when dragging. */
+ element: Element
+ /** Pointer ID used by drag capture. */
+ pointerId?: number
+
+ /** Set to true when if the pointer moves far enough after a down event, before the corresponding up event is fired. */
+ dragStarted: boolean = false
+
+ /** The {@link eUp} from the last successful click */
+ eLastDown?: CanvasPointerEvent
+
+ /** Used downstream for touch event support. */
+ isDouble: boolean = false
+ /** Used downstream for touch event support. */
+ isDown: boolean = false
+
+ /** The resize handle currently being hovered or dragged */
+ resizeDirection?: CompassCorners
+
+ /**
+ * If `true`, {@link eDown}, {@link eMove}, and {@link eUp} will be set to
+ * `undefined` when {@link reset} is called.
+ *
+ * Default: `true`
+ */
+ clearEventsOnReset: boolean = true
+
+ /** The last pointerdown event for the primary button */
+ eDown?: CanvasPointerEvent
+ /** The last pointermove event for the primary button */
+ eMove?: CanvasPointerEvent
+ /** The last pointerup event for the primary button */
+ eUp?: CanvasPointerEvent
+
+ /**
+ * If set, as soon as the mouse moves outside the click drift threshold, this action is run once.
+ * @param pointer [DEPRECATED] This parameter will be removed in a future release.
+ * @param eMove The pointermove event of this ongoing drag action.
+ *
+ * It is possible for no `pointermove` events to occur, but still be far from
+ * the original `pointerdown` event. In this case, {@link eMove} will be null, and
+ * {@link onDragEnd} will be called immediately after {@link onDragStart}.
+ */
+ onDragStart?(pointer: this, eMove?: CanvasPointerEvent): unknown
+
+ /**
+ * Called on pointermove whilst dragging.
+ * @param eMove The pointermove event of this ongoing drag action
+ */
+ onDrag?(eMove: CanvasPointerEvent): unknown
+
+ /**
+ * Called on pointerup after dragging (i.e. not called if clicked).
+ * @param upEvent The pointerup or pointermove event that triggered this callback
+ */
+ onDragEnd?(upEvent: CanvasPointerEvent): unknown
+
+ /**
+ * Callback that will be run once, the next time a pointerup event appears to be a normal click.
+ * @param upEvent The pointerup or pointermove event that triggered this callback
+ */
+ onClick?(upEvent: CanvasPointerEvent): unknown
+
+ /**
+ * Callback that will be run once, the next time a pointerup event appears to be a normal click.
+ * @param upEvent The pointerup or pointermove event that triggered this callback
+ */
+ onDoubleClick?(upEvent: CanvasPointerEvent): unknown
+
+ /**
+ * Run-once callback, called at the end of any click or drag, whether or not it was successful in any way.
+ *
+ * The setter of this callback will call the existing value before replacing it.
+ * Therefore, simply setting this value twice will execute the first callback.
+ */
+ get finally() {
+ return this.#finally
+ }
+
+ set finally(value) {
+ try {
+ this.#finally?.()
+ } finally {
+ this.#finally = value
+ }
+ }
+
+ #finally?: () => unknown
+
+ constructor(element: Element) {
+ this.element = element
+ }
+
+ /**
+ * Callback for `pointerdown` events. To be used as the event handler (or called by it).
+ * @param e The `pointerdown` event
+ */
+ down(e: CanvasPointerEvent): void {
+ this.reset()
+ this.eDown = e
+ this.pointerId = e.pointerId
+ this.element.setPointerCapture(e.pointerId)
+ }
+
+ /**
+ * Callback for `pointermove` events. To be used as the event handler (or called by it).
+ * @param e The `pointermove` event
+ */
+ move(e: CanvasPointerEvent): void {
+ const { eDown } = this
+ if (!eDown) return
+
+ // No buttons down, but eDown exists - clean up & leave
+ if (!e.buttons) {
+ this.reset()
+ return
+ }
+
+ // Primary button released - treat as pointerup.
+ if (!(e.buttons & eDown.buttons)) {
+ this.#completeClick(e)
+ this.reset()
+ return
+ }
+ this.eMove = e
+ this.onDrag?.(e)
+
+ // Dragging, but no callback to run
+ if (this.dragStarted) return
+
+ const longerThanBufferTime = e.timeStamp - eDown.timeStamp > CanvasPointer.bufferTime
+ if (longerThanBufferTime || !this.#hasSamePosition(e, eDown)) {
+ this.#setDragStarted(e)
+ }
+ }
+
+ /**
+ * Callback for `pointerup` events. To be used as the event handler (or called by it).
+ * @param e The `pointerup` event
+ */
+ up(e: CanvasPointerEvent): boolean {
+ if (e.button !== this.eDown?.button) return false
+
+ this.#completeClick(e)
+ const { dragStarted } = this
+ this.reset()
+ return !dragStarted
+ }
+
+ #completeClick(e: CanvasPointerEvent): void {
+ const { eDown } = this
+ if (!eDown) return
+
+ this.eUp = e
+
+ if (this.dragStarted) {
+ // A move event already started drag
+ this.onDragEnd?.(e)
+ } else if (!this.#hasSamePosition(e, eDown)) {
+ // Teleport without a move event (e.g. tab out, move, tab back)
+ this.#setDragStarted()
+ this.onDragEnd?.(e)
+ } else if (this.onDoubleClick && this.#isDoubleClick()) {
+ // Double-click event
+ this.onDoubleClick(e)
+ this.eLastDown = undefined
+ } else {
+ // Normal click event
+ this.onClick?.(e)
+ this.eLastDown = eDown
+ }
+ }
+
+ /**
+ * Checks if two events occurred near each other - not further apart than the maximum click drift.
+ * @param a The first event to compare
+ * @param b The second event to compare
+ * @param tolerance2 The maximum distance (squared) before the positions are considered different
+ * @returns `true` if the two events were no more than {@link maxClickDrift} apart, otherwise `false`
+ */
+ #hasSamePosition(
+ a: PointerEvent,
+ b: PointerEvent,
+ tolerance2 = CanvasPointer.#maxClickDrift2,
+ ): boolean {
+ const drift = dist2(a.clientX, a.clientY, b.clientX, b.clientY)
+ return drift <= tolerance2
+ }
+
+ /**
+ * Checks whether the pointer is currently past the max click drift threshold.
+ * @returns `true` if the latest pointer event is past the the click drift threshold
+ */
+ #isDoubleClick(): boolean {
+ const { eDown, eLastDown } = this
+ if (!eDown || !eLastDown) return false
+
+ // Use thrice the drift distance for double-click gap
+ const tolerance2 = (3 * CanvasPointer.#maxClickDrift) ** 2
+ const diff = eDown.timeStamp - eLastDown.timeStamp
+ return diff > 0 &&
+ diff < CanvasPointer.doubleClickTime &&
+ this.#hasSamePosition(eDown, eLastDown, tolerance2)
+ }
+
+ #setDragStarted(eMove?: CanvasPointerEvent): void {
+ this.dragStarted = true
+ this.onDragStart?.(this, eMove)
+ delete this.onDragStart
+ }
+
+ /**
+ * Resets the state of this {@link CanvasPointer} instance.
+ *
+ * The {@link finally} callback is first executed, then all callbacks and intra-click
+ * state is cleared.
+ */
+ reset(): void {
+ // The setter executes the callback before clearing it
+ this.finally = undefined
+ delete this.onClick
+ delete this.onDoubleClick
+ delete this.onDragStart
+ delete this.onDrag
+ delete this.onDragEnd
+
+ this.isDown = false
+ this.isDouble = false
+ this.dragStarted = false
+ this.resizeDirection = undefined
+
+ if (this.clearEventsOnReset) {
+ this.eDown = undefined
+ this.eMove = undefined
+ this.eUp = undefined
+ }
+
+ const { element, pointerId } = this
+ this.pointerId = undefined
+ if (typeof pointerId === "number" && element.hasPointerCapture(pointerId)) {
+ element.releasePointerCapture(pointerId)
+ }
+ }
+}
diff --git a/src/lib/litegraph/src/ContextMenu.ts b/src/lib/litegraph/src/ContextMenu.ts
new file mode 100644
index 0000000000..b6b7b55f13
--- /dev/null
+++ b/src/lib/litegraph/src/ContextMenu.ts
@@ -0,0 +1,387 @@
+import type { ContextMenuDivElement, IContextMenuOptions, IContextMenuValue } from "./interfaces"
+
+import { LiteGraph } from "./litegraph"
+
+// TODO: Replace this pattern with something more modern.
+export interface ContextMenu {
+ constructor: new (...args: ConstructorParameters>) => ContextMenu
+}
+
+/**
+ * ContextMenu from LiteGUI
+ */
+// eslint-disable-next-line @typescript-eslint/no-unsafe-declaration-merging
+export class ContextMenu {
+ options: IContextMenuOptions
+ parentMenu?: ContextMenu
+ root: ContextMenuDivElement
+ current_submenu?: ContextMenu
+ lock?: boolean
+
+ controller: AbortController = new AbortController()
+
+ /**
+ * @todo Interface for values requires functionality change - currently accepts
+ * an array of strings, functions, objects, nulls, or undefined.
+ * @param values (allows object { title: "Nice text", callback: function ... })
+ * @param options [optional] Some options:\
+ * - title: title to show on top of the menu
+ * - callback: function to call when an option is clicked, it receives the item information
+ * - ignore_item_callbacks: ignores the callback inside the item, it just calls the options.callback
+ * - event: you can pass a MouseEvent, this way the ContextMenu appears in that position
+ */
+ constructor(values: readonly (string | IContextMenuValue | null)[], options: IContextMenuOptions) {
+ options ||= {}
+ this.options = options
+
+ // to link a menu with its parent
+ const parent = options.parentMenu
+ if (parent) {
+ if (!(parent instanceof ContextMenu)) {
+ console.error("parentMenu must be of class ContextMenu, ignoring it")
+ options.parentMenu = undefined
+ } else {
+ this.parentMenu = parent
+ this.parentMenu.lock = true
+ this.parentMenu.current_submenu = this
+ }
+ if (parent.options?.className === "dark") {
+ options.className = "dark"
+ }
+ }
+
+ // use strings because comparing classes between windows doesnt work
+ const eventClass = options.event
+ ? options.event.constructor.name
+ : null
+ if (
+ eventClass !== "MouseEvent" &&
+ eventClass !== "CustomEvent" &&
+ eventClass !== "PointerEvent"
+ ) {
+ console.error(`Event passed to ContextMenu is not of type MouseEvent or CustomEvent. Ignoring it. (${eventClass})`)
+ options.event = undefined
+ }
+
+ const root: ContextMenuDivElement = document.createElement("div")
+ let classes = "litegraph litecontextmenu litemenubar-panel"
+ if (options.className) classes += ` ${options.className}`
+ root.className = classes
+ root.style.minWidth = "100"
+ root.style.minHeight = "100"
+
+ // Close the context menu when a click occurs outside this context menu or its submenus
+ const { signal } = this.controller
+ const eventOptions = { capture: true, signal }
+
+ if (!this.parentMenu) {
+ document.addEventListener("pointerdown", (e) => {
+ if (e.target instanceof Node && !this.containsNode(e.target)) {
+ this.close()
+ }
+ }, eventOptions)
+ }
+
+ // this prevents the default context browser menu to open in case this menu was created when pressing right button
+ root.addEventListener("pointerup", e => e.preventDefault(), eventOptions)
+
+ // Right button
+ root.addEventListener(
+ "contextmenu",
+ (e) => {
+ if (e.button === 2) e.preventDefault()
+ },
+ eventOptions,
+ )
+
+ root.addEventListener(
+ "pointerdown",
+ (e) => {
+ if (e.button == 2) {
+ this.close()
+ e.preventDefault()
+ }
+ },
+ eventOptions,
+ )
+
+ this.root = root
+
+ // title
+ if (options.title) {
+ const element = document.createElement("div")
+ element.className = "litemenu-title"
+ element.innerHTML = options.title
+ root.append(element)
+ }
+
+ // entries
+ for (let i = 0; i < values.length; i++) {
+ const value = values[i]
+ let name = Array.isArray(values) ? value : String(i)
+
+ if (typeof name !== "string") {
+ name = name != null
+ ? (name.content === undefined ? String(name) : name.content)
+ : name
+ }
+
+ this.addItem(name, value, options)
+ }
+
+ // insert before checking position
+ const ownerDocument = (options.event?.target as Node | null | undefined)?.ownerDocument
+ const root_document = ownerDocument || document
+
+ if (root_document.fullscreenElement)
+ root_document.fullscreenElement.append(root)
+ else
+ root_document.body.append(root)
+
+ // compute best position
+ let left = options.left || 0
+ let top = options.top || 0
+ if (options.event) {
+ left = options.event.clientX - 10
+ top = options.event.clientY - 10
+ if (options.title) top -= 20
+
+ if (parent) {
+ const rect = parent.root.getBoundingClientRect()
+ left = rect.left + rect.width
+ }
+
+ const body_rect = document.body.getBoundingClientRect()
+ const root_rect = root.getBoundingClientRect()
+ if (body_rect.height == 0)
+ console.error("document.body height is 0. That is dangerous, set html,body { height: 100%; }")
+
+ if (body_rect.width && left > body_rect.width - root_rect.width - 10)
+ left = body_rect.width - root_rect.width - 10
+ if (body_rect.height && top > body_rect.height - root_rect.height - 10)
+ top = body_rect.height - root_rect.height - 10
+ }
+
+ root.style.left = `${left}px`
+ root.style.top = `${top}px`
+
+ if (LiteGraph.context_menu_scaling && options.scale) {
+ root.style.transform = `scale(${Math.round(options.scale * 4) * 0.25})`
+ }
+ }
+
+ /**
+ * Checks if {@link node} is inside this context menu or any of its submenus
+ * @param node The {@link Node} to check
+ * @param visited A set of visited menus to avoid circular references
+ * @returns `true` if {@link node} is inside this context menu or any of its submenus
+ */
+ containsNode(node: Node, visited: Set = new Set()): boolean {
+ if (visited.has(this)) return false
+ visited.add(this)
+
+ return this.current_submenu?.containsNode(node, visited) || this.root.contains(node)
+ }
+
+ addItem(
+ name: string | null,
+ value: string | IContextMenuValue | null,
+ options: IContextMenuOptions,
+ ): HTMLElement {
+ options ||= {}
+
+ const element: ContextMenuDivElement = document.createElement("div")
+ element.className = "litemenu-entry submenu"
+
+ let disabled = false
+
+ if (value === null) {
+ element.classList.add("separator")
+ } else {
+ const innerHtml = name === null ? "" : String(name)
+ if (typeof value === "string") {
+ element.innerHTML = innerHtml
+ } else {
+ element.innerHTML = value?.title ?? innerHtml
+
+ if (value.disabled) {
+ disabled = true
+ element.classList.add("disabled")
+ element.setAttribute("aria-disabled", "true")
+ }
+ if (value.submenu || value.has_submenu) {
+ element.classList.add("has_submenu")
+ element.setAttribute("aria-haspopup", "true")
+ element.setAttribute("aria-expanded", "false")
+ }
+ if (value.className) element.className += ` ${value.className}`
+ }
+ element.value = value
+ element.setAttribute("role", "menuitem")
+
+ if (typeof value === "function") {
+ element.dataset["value"] = String(name)
+ element.onclick_callback = value
+ } else {
+ element.dataset["value"] = String(value)
+ }
+ }
+
+ this.root.append(element)
+ if (!disabled) element.addEventListener("click", inner_onclick)
+ if (!disabled && options.autoopen)
+ element.addEventListener("pointerenter", inner_over)
+
+ const setAriaExpanded = () => {
+ const entries = this.root.querySelectorAll("div.litemenu-entry.has_submenu")
+ if (entries) {
+ for (const entry of entries) {
+ entry.setAttribute("aria-expanded", "false")
+ }
+ }
+ element.setAttribute("aria-expanded", "true")
+ }
+
+ function inner_over(this: ContextMenuDivElement, e: MouseEvent) {
+ const value = this.value
+ if (!value || !(value as IContextMenuValue).has_submenu) return
+
+ // if it is a submenu, autoopen like the item was clicked
+ inner_onclick.call(this, e)
+ setAriaExpanded()
+ }
+
+ // menu option clicked
+ const that = this
+ function inner_onclick(this: ContextMenuDivElement, e: MouseEvent) {
+ const value = this.value
+ let close_parent = true
+
+ that.current_submenu?.close(e)
+ if (
+ (value as IContextMenuValue)?.has_submenu ||
+ (value as IContextMenuValue)?.submenu
+ ) {
+ setAriaExpanded()
+ }
+
+ // global callback
+ if (options.callback) {
+ const r = options.callback.call(
+ this,
+ value,
+ options,
+ e,
+ that,
+ options.node,
+ )
+ if (r === true) close_parent = false
+ }
+
+ // special cases
+ if (typeof value === "object") {
+ if (
+ value.callback &&
+ !options.ignore_item_callbacks &&
+ value.disabled !== true
+ ) {
+ // item callback
+ const r = value.callback.call(
+ this,
+ value,
+ options,
+ e,
+ that,
+ options.extra,
+ )
+ if (r === true) close_parent = false
+ }
+ if (value.submenu) {
+ if (!value.submenu.options) throw "ContextMenu submenu needs options"
+
+ new that.constructor(value.submenu.options, {
+ callback: value.submenu.callback,
+ event: e,
+ parentMenu: that,
+ ignore_item_callbacks: value.submenu.ignore_item_callbacks,
+ title: value.submenu.title,
+ extra: value.submenu.extra,
+ autoopen: options.autoopen,
+ })
+ close_parent = false
+ }
+ }
+
+ if (close_parent && !that.lock) that.close()
+ }
+
+ return element
+ }
+
+ close(e?: MouseEvent, ignore_parent_menu?: boolean): void {
+ this.controller.abort()
+ this.root.remove()
+ if (this.parentMenu && !ignore_parent_menu) {
+ this.parentMenu.lock = false
+ this.parentMenu.current_submenu = undefined
+ if (e === undefined) {
+ this.parentMenu.close()
+ } else if (e && !ContextMenu.isCursorOverElement(e, this.parentMenu.root)) {
+ ContextMenu.trigger(
+ this.parentMenu.root,
+ `${LiteGraph.pointerevents_method}leave`,
+ e,
+ )
+ }
+ }
+ this.current_submenu?.close(e, true)
+ }
+
+ /** @deprecated Likely unused, however code search was inconclusive (too many results to check by hand). */
+ // this code is used to trigger events easily (used in the context menu mouseleave
+ static trigger(
+ element: HTMLDivElement,
+ event_name: string,
+ params: MouseEvent,
+ ): CustomEvent {
+ const evt = document.createEvent("CustomEvent")
+ evt.initCustomEvent(event_name, true, true, params)
+ if (element.dispatchEvent) element.dispatchEvent(evt)
+ // else nothing seems bound here so nothing to do
+ return evt
+ }
+
+ // returns the top most menu
+ getTopMenu(): ContextMenu {
+ return this.options.parentMenu
+ ? this.options.parentMenu.getTopMenu()
+ : this
+ }
+
+ getFirstEvent(): MouseEvent | undefined {
+ return this.options.parentMenu
+ ? this.options.parentMenu.getFirstEvent()
+ : this.options.event
+ }
+
+ /** @deprecated Unused. */
+ static isCursorOverElement(
+ event: MouseEvent,
+ element: HTMLDivElement,
+ ): boolean {
+ const left = event.clientX
+ const top = event.clientY
+ const rect = element.getBoundingClientRect()
+ if (!rect) return false
+
+ if (
+ top > rect.top &&
+ top < rect.top + rect.height &&
+ left > rect.left &&
+ left < rect.left + rect.width
+ ) {
+ return true
+ }
+ return false
+ }
+}
diff --git a/src/lib/litegraph/src/CurveEditor.ts b/src/lib/litegraph/src/CurveEditor.ts
new file mode 100644
index 0000000000..f9395e71e5
--- /dev/null
+++ b/src/lib/litegraph/src/CurveEditor.ts
@@ -0,0 +1,195 @@
+import type { Point, Rect } from "./interfaces"
+
+import { clamp, LGraphCanvas } from "./litegraph"
+import { distance } from "./measure"
+
+// used by some widgets to render a curve editor
+
+export class CurveEditor {
+ points: Point[]
+ selected: number
+ nearest: number
+ size: Rect | null
+ must_update: boolean
+ margin: number
+ _nearest?: number
+
+ constructor(points: Point[]) {
+ this.points = points
+ this.selected = -1
+ this.nearest = -1
+ // stores last size used
+ this.size = null
+ this.must_update = true
+ this.margin = 5
+ }
+
+ static sampleCurve(f: number, points: Point[]): number | undefined {
+ if (!points) return
+
+ for (let i = 0; i < points.length - 1; ++i) {
+ const p = points[i]
+ const pn = points[i + 1]
+ if (pn[0] < f) continue
+
+ const r = pn[0] - p[0]
+ if (Math.abs(r) < 0.000_01) return p[1]
+
+ const local_f = (f - p[0]) / r
+ return p[1] * (1.0 - local_f) + pn[1] * local_f
+ }
+ return 0
+ }
+
+ draw(
+ ctx: CanvasRenderingContext2D,
+ size: Rect,
+ graphcanvas?: LGraphCanvas,
+ background_color?: string,
+ line_color?: string,
+ inactive = false,
+ ): void {
+ const points = this.points
+ if (!points) return
+
+ this.size = size
+ const w = size[0] - this.margin * 2
+ const h = size[1] - this.margin * 2
+
+ line_color = line_color || "#666"
+
+ ctx.save()
+ ctx.translate(this.margin, this.margin)
+
+ if (background_color) {
+ ctx.fillStyle = "#111"
+ ctx.fillRect(0, 0, w, h)
+ ctx.fillStyle = "#222"
+ ctx.fillRect(w * 0.5, 0, 1, h)
+ ctx.strokeStyle = "#333"
+ ctx.strokeRect(0, 0, w, h)
+ }
+ ctx.strokeStyle = line_color
+ if (inactive) ctx.globalAlpha = 0.5
+ ctx.beginPath()
+ for (const p of points) {
+ ctx.lineTo(p[0] * w, (1.0 - p[1]) * h)
+ }
+ ctx.stroke()
+ ctx.globalAlpha = 1
+ if (!inactive) {
+ for (const [i, p] of points.entries()) {
+ ctx.fillStyle = this.selected == i
+ ? "#FFF"
+ : (this.nearest == i ? "#DDD" : "#AAA")
+ ctx.beginPath()
+ ctx.arc(p[0] * w, (1.0 - p[1]) * h, 2, 0, Math.PI * 2)
+ ctx.fill()
+ }
+ }
+ ctx.restore()
+ }
+
+ // localpos is mouse in curve editor space
+ onMouseDown(localpos: Point, graphcanvas: LGraphCanvas): boolean | undefined {
+ const points = this.points
+ if (!points) return
+ if (localpos[1] < 0) return
+
+ // this.captureInput(true);
+ if (this.size == null) throw new Error("CurveEditor.size was null or undefined.")
+ const w = this.size[0] - this.margin * 2
+ const h = this.size[1] - this.margin * 2
+ const x = localpos[0] - this.margin
+ const y = localpos[1] - this.margin
+ const pos: Point = [x, y]
+ const max_dist = 30 / graphcanvas.ds.scale
+ // search closer one
+ this.selected = this.getCloserPoint(pos, max_dist)
+ // create one
+ if (this.selected == -1) {
+ const point: Point = [x / w, 1 - y / h]
+ points.push(point)
+ points.sort(function (a, b) {
+ return a[0] - b[0]
+ })
+ this.selected = points.indexOf(point)
+ this.must_update = true
+ }
+ if (this.selected != -1) return true
+ }
+
+ onMouseMove(localpos: Point, graphcanvas: LGraphCanvas): void {
+ const points = this.points
+ if (!points) return
+
+ const s = this.selected
+ if (s < 0) return
+
+ if (this.size == null) throw new Error("CurveEditor.size was null or undefined.")
+ const x = (localpos[0] - this.margin) / (this.size[0] - this.margin * 2)
+ const y = (localpos[1] - this.margin) / (this.size[1] - this.margin * 2)
+ const curvepos: Point = [
+ localpos[0] - this.margin,
+ localpos[1] - this.margin,
+ ]
+ const max_dist = 30 / graphcanvas.ds.scale
+ this._nearest = this.getCloserPoint(curvepos, max_dist)
+ const point = points[s]
+ if (point) {
+ const is_edge_point = s == 0 || s == points.length - 1
+ if (
+ !is_edge_point &&
+ (localpos[0] < -10 ||
+ localpos[0] > this.size[0] + 10 ||
+ localpos[1] < -10 ||
+ localpos[1] > this.size[1] + 10)
+ ) {
+ points.splice(s, 1)
+ this.selected = -1
+ return
+ }
+ // not edges
+ if (!is_edge_point) point[0] = clamp(x, 0, 1)
+ else point[0] = s == 0 ? 0 : 1
+ point[1] = 1.0 - clamp(y, 0, 1)
+ points.sort(function (a, b) {
+ return a[0] - b[0]
+ })
+ this.selected = points.indexOf(point)
+ this.must_update = true
+ }
+ }
+
+ // Former params: localpos, graphcanvas
+ onMouseUp(): boolean {
+ this.selected = -1
+ return false
+ }
+
+ getCloserPoint(pos: Point, max_dist: number): number {
+ const points = this.points
+ if (!points) return -1
+
+ max_dist = max_dist || 30
+ if (this.size == null) throw new Error("CurveEditor.size was null or undefined.")
+ const w = this.size[0] - this.margin * 2
+ const h = this.size[1] - this.margin * 2
+ const num = points.length
+ const p2: Point = [0, 0]
+ let min_dist = 1_000_000
+ let closest = -1
+
+ for (let i = 0; i < num; ++i) {
+ const p = points[i]
+ p2[0] = p[0] * w
+ p2[1] = (1.0 - p[1]) * h
+ const dist = distance(pos, p2)
+ if (dist > min_dist || dist > max_dist) continue
+
+ closest = i
+ min_dist = dist
+ }
+ return closest
+ }
+}
diff --git a/src/lib/litegraph/src/DragAndScale.ts b/src/lib/litegraph/src/DragAndScale.ts
new file mode 100644
index 0000000000..8a56988604
--- /dev/null
+++ b/src/lib/litegraph/src/DragAndScale.ts
@@ -0,0 +1,311 @@
+import type { Point, ReadOnlyRect, Rect } from "./interfaces"
+
+import { EaseFunction, Rectangle } from "./litegraph"
+
+export interface DragAndScaleState {
+ /**
+ * The offset from the top-left of the current canvas viewport to `[0, 0]` in graph space.
+ * Or said another way, the inverse offset of the viewport.
+ */
+ offset: [number, number]
+ /** The scale of the graph. */
+ scale: number
+}
+
+export type AnimationOptions = {
+ /** Duration of the animation in milliseconds. */
+ duration?: number
+ /** Relative target zoom level. 1 means the view is fit exactly on the bounding box. */
+ zoom?: number
+ /** The animation easing function (curve) */
+ easing?: EaseFunction
+}
+
+export class DragAndScale {
+ /**
+ * The state of this DragAndScale instance.
+ *
+ * Implemented as a POCO that can be proxied without side-effects.
+ */
+ state: DragAndScaleState
+ lastState: DragAndScaleState = {
+ offset: [0, 0],
+ scale: 0,
+ }
+
+ /** Maximum scale (zoom in) */
+ max_scale: number
+ /** Minimum scale (zoom out) */
+ min_scale: number
+ enabled: boolean
+ last_mouse: Point
+ element: HTMLCanvasElement
+ visible_area: Rectangle
+ dragging?: boolean
+ viewport?: Rect
+
+ onredraw?(das: DragAndScale): void
+ onChanged?(scale: number, offset: Point): void
+
+ get offset(): [number, number] {
+ return this.state.offset
+ }
+
+ set offset(value: Point) {
+ this.state.offset[0] = value[0]
+ this.state.offset[1] = value[1]
+ }
+
+ get scale(): number {
+ return this.state.scale
+ }
+
+ set scale(value: number) {
+ this.state.scale = value
+ }
+
+ constructor(element: HTMLCanvasElement) {
+ this.state = {
+ offset: [0, 0],
+ scale: 1,
+ }
+ this.max_scale = 10
+ this.min_scale = 0.1
+ this.enabled = true
+ this.last_mouse = [0, 0]
+ this.visible_area = new Rectangle()
+
+ this.element = element
+ }
+
+ /**
+ * Returns `true` if the current state has changed from the previous state.
+ * @returns `true` if the current state has changed from the previous state, otherwise `false`.
+ */
+ #stateHasChanged(): boolean {
+ const current = this.state
+ const previous = this.lastState
+
+ return current.scale !== previous.scale ||
+ current.offset[0] !== previous.offset[0] ||
+ current.offset[1] !== previous.offset[1]
+ }
+
+ computeVisibleArea(viewport: Rect | undefined): void {
+ const { scale, offset, visible_area } = this
+
+ if (this.#stateHasChanged()) {
+ this.onChanged?.(scale, offset)
+ copyState(this.state, this.lastState)
+ }
+
+ if (!this.element) {
+ visible_area[0] = visible_area[1] = visible_area[2] = visible_area[3] = 0
+ return
+ }
+ let { width, height } = this.element
+ let startx = -offset[0]
+ let starty = -offset[1]
+ if (viewport) {
+ startx += viewport[0] / scale
+ starty += viewport[1] / scale
+ width = viewport[2]
+ height = viewport[3]
+ }
+ const endx = startx + width / scale
+ const endy = starty + height / scale
+ visible_area[0] = startx
+ visible_area[1] = starty
+ visible_area.resizeBottomRight(endx, endy)
+ }
+
+ toCanvasContext(ctx: CanvasRenderingContext2D): void {
+ ctx.scale(this.scale, this.scale)
+ ctx.translate(this.offset[0], this.offset[1])
+ }
+
+ convertOffsetToCanvas(pos: Point): Point {
+ return [
+ (pos[0] + this.offset[0]) * this.scale,
+ (pos[1] + this.offset[1]) * this.scale,
+ ]
+ }
+
+ convertCanvasToOffset(pos: Point, out?: Point): Point {
+ out = out || [0, 0]
+ out[0] = pos[0] / this.scale - this.offset[0]
+ out[1] = pos[1] / this.scale - this.offset[1]
+ return out
+ }
+
+ /** @deprecated Has not been kept up to date */
+ mouseDrag(x: number, y: number): void {
+ this.offset[0] += x / this.scale
+ this.offset[1] += y / this.scale
+
+ this.onredraw?.(this)
+ }
+
+ changeScale(value: number, zooming_center?: Point, roundToScaleOne = true): void {
+ if (value < this.min_scale) {
+ value = this.min_scale
+ } else if (value > this.max_scale) {
+ value = this.max_scale
+ }
+ if (value == this.scale) return
+
+ const rect = this.element.getBoundingClientRect()
+ if (!rect) return
+
+ zooming_center = zooming_center ?? [rect.width * 0.5, rect.height * 0.5]
+
+ const normalizedCenter: Point = [
+ zooming_center[0] - rect.x,
+ zooming_center[1] - rect.y,
+ ]
+ const center = this.convertCanvasToOffset(normalizedCenter)
+ this.scale = value
+ if (roundToScaleOne && Math.abs(this.scale - 1) < 0.01) this.scale = 1
+ const new_center = this.convertCanvasToOffset(normalizedCenter)
+ const delta_offset = [
+ new_center[0] - center[0],
+ new_center[1] - center[1],
+ ]
+
+ this.offset[0] += delta_offset[0]
+ this.offset[1] += delta_offset[1]
+
+ this.onredraw?.(this)
+ }
+
+ changeDeltaScale(value: number, zooming_center?: Point): void {
+ this.changeScale(this.scale * value, zooming_center)
+ }
+
+ /**
+ * Fits the view to the specified bounds.
+ * @param bounds The bounds to fit the view to, defined by a rectangle.
+ */
+ fitToBounds(bounds: ReadOnlyRect, { zoom = 0.75 }: { zoom?: number } = {}): void {
+ const cw = this.element.width / window.devicePixelRatio
+ const ch = this.element.height / window.devicePixelRatio
+ let targetScale = this.scale
+
+ if (zoom > 0) {
+ const targetScaleX = (zoom * cw) / Math.max(bounds[2], 300)
+ const targetScaleY = (zoom * ch) / Math.max(bounds[3], 300)
+
+ // Choose the smaller scale to ensure the node fits into the viewport
+ // Ensure we don't go over the max scale
+ targetScale = Math.min(targetScaleX, targetScaleY, this.max_scale)
+ }
+
+ const scaledWidth = cw / targetScale
+ const scaledHeight = ch / targetScale
+
+ // Calculate the target position to center the bounds in the viewport
+ const targetX = -bounds[0] - (bounds[2] * 0.5) + (scaledWidth * 0.5)
+ const targetY = -bounds[1] - (bounds[3] * 0.5) + (scaledHeight * 0.5)
+
+ // Apply the changes immediately
+ this.offset[0] = targetX
+ this.offset[1] = targetY
+ this.scale = targetScale
+ }
+
+ /**
+ * Starts an animation to fit the view around the specified selection of nodes.
+ * @param bounds The bounds to animate the view to, defined by a rectangle.
+ */
+ animateToBounds(
+ bounds: ReadOnlyRect,
+ setDirty: () => void,
+ {
+ duration = 350,
+ zoom = 0.75,
+ easing = EaseFunction.EASE_IN_OUT_QUAD,
+ }: AnimationOptions = {},
+ ) {
+ if (!(duration > 0)) throw new RangeError("Duration must be greater than 0")
+
+ const easeFunctions = {
+ linear: (t: number) => t,
+ easeInQuad: (t: number) => t * t,
+ easeOutQuad: (t: number) => t * (2 - t),
+ easeInOutQuad: (t: number) => (t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t),
+ }
+ const easeFunction = easeFunctions[easing] ?? easeFunctions.linear
+
+ const startTimestamp = performance.now()
+ const cw = this.element.width / window.devicePixelRatio
+ const ch = this.element.height / window.devicePixelRatio
+ const startX = this.offset[0]
+ const startY = this.offset[1]
+ const startX2 = startX - (cw / this.scale)
+ const startY2 = startY - (ch / this.scale)
+ const startScale = this.scale
+ let targetScale = startScale
+
+ if (zoom > 0) {
+ const targetScaleX = (zoom * cw) / Math.max(bounds[2], 300)
+ const targetScaleY = (zoom * ch) / Math.max(bounds[3], 300)
+
+ // Choose the smaller scale to ensure the node fits into the viewport
+ // Ensure we don't go over the max scale
+ targetScale = Math.min(targetScaleX, targetScaleY, this.max_scale)
+ }
+ const scaledWidth = cw / targetScale
+ const scaledHeight = ch / targetScale
+
+ const targetX = -bounds[0] - (bounds[2] * 0.5) + (scaledWidth * 0.5)
+ const targetY = -bounds[1] - (bounds[3] * 0.5) + (scaledHeight * 0.5)
+ const targetX2 = targetX - scaledWidth
+ const targetY2 = targetY - scaledHeight
+
+ const animate = (timestamp: number) => {
+ const elapsed = timestamp - startTimestamp
+ const progress = Math.min(elapsed / duration, 1)
+ const easedProgress = easeFunction(progress)
+
+ const currentX = startX + ((targetX - startX) * easedProgress)
+ const currentY = startY + ((targetY - startY) * easedProgress)
+ this.offset[0] = currentX
+ this.offset[1] = currentY
+
+ if (zoom > 0) {
+ const currentX2 = startX2 + ((targetX2 - startX2) * easedProgress)
+ const currentY2 = startY2 + ((targetY2 - startY2) * easedProgress)
+ const currentWidth = Math.abs(currentX2 - currentX)
+ const currentHeight = Math.abs(currentY2 - currentY)
+
+ this.scale = Math.min(cw / currentWidth, ch / currentHeight)
+ }
+
+ setDirty()
+
+ if (progress < 1) {
+ animationId = requestAnimationFrame(animate)
+ } else {
+ cancelAnimationFrame(animationId)
+ }
+ }
+ let animationId = requestAnimationFrame(animate)
+ }
+
+ reset(): void {
+ this.scale = 1
+ this.offset[0] = 0
+ this.offset[1] = 0
+ }
+}
+
+/**
+ * Copies the values of one state into another, preserving references.
+ * @param from The state to copy values from.
+ * @param to The state to copy values into.
+ */
+function copyState(from: DragAndScaleState, to: DragAndScaleState): void {
+ to.scale = from.scale
+ to.offset[0] = from.offset[0]
+ to.offset[1] = from.offset[1]
+}
diff --git a/src/lib/litegraph/src/LGraph.ts b/src/lib/litegraph/src/LGraph.ts
new file mode 100644
index 0000000000..10eaf43e88
--- /dev/null
+++ b/src/lib/litegraph/src/LGraph.ts
@@ -0,0 +1,1936 @@
+import type { DragAndScaleState } from "./DragAndScale"
+import type { LGraphEventMap } from "./infrastructure/LGraphEventMap"
+import type {
+ Dictionary,
+ IContextMenuValue,
+ LinkNetwork,
+ LinkSegment,
+ MethodNames,
+ OptionalProps,
+ Point,
+ Positionable,
+} from "./interfaces"
+import type {
+ ExportedSubgraph,
+ ISerialisedGraph,
+ ISerialisedNode,
+ Serialisable,
+ SerialisableGraph,
+ SerialisableReroute,
+} from "./types/serialisation"
+import type { UUID } from "@/utils/uuid"
+
+import { SUBGRAPH_INPUT_ID, SUBGRAPH_OUTPUT_ID } from "@/constants"
+import { createUuidv4, zeroUuid } from "@/utils/uuid"
+
+import { CustomEventTarget } from "./infrastructure/CustomEventTarget"
+import { LGraphCanvas } from "./LGraphCanvas"
+import { LGraphGroup } from "./LGraphGroup"
+import { LGraphNode, type NodeId } from "./LGraphNode"
+import { LiteGraph, SubgraphNode } from "./litegraph"
+import { type LinkId, LLink } from "./LLink"
+import { MapProxyHandler } from "./MapProxyHandler"
+import { alignOutsideContainer, alignToContainer, createBounds } from "./measure"
+import { Reroute, type RerouteId } from "./Reroute"
+import { stringOrEmpty } from "./strings"
+import { type GraphOrSubgraph, Subgraph } from "./subgraph/Subgraph"
+import { SubgraphInput } from "./subgraph/SubgraphInput"
+import { SubgraphOutput } from "./subgraph/SubgraphOutput"
+import { findUsedSubgraphIds, getBoundaryLinks, groupResolvedByOutput, mapSubgraphInputsAndLinks, mapSubgraphOutputsAndLinks, multiClone, splitPositionables } from "./subgraph/subgraphUtils"
+import { Alignment, LGraphEventMode } from "./types/globalEnums"
+import { getAllNestedItems } from "./utils/collections"
+
+export interface LGraphState {
+ lastGroupId: number
+ lastNodeId: number
+ lastLinkId: number
+ lastRerouteId: number
+}
+
+type ParamsArray, K extends MethodNames> =
+ Parameters[1] extends undefined
+ ? Parameters | Parameters[0]
+ : Parameters
+
+/** Configuration used by {@link LGraph} `config`. */
+export interface LGraphConfig {
+ /** @deprecated Legacy config - unused */
+ align_to_grid?: any
+ links_ontop?: any
+}
+
+export interface LGraphExtra extends Dictionary {
+ reroutes?: SerialisableReroute[]
+ linkExtensions?: { id: number, parentId: number | undefined }[]
+ ds?: DragAndScaleState
+}
+
+export interface BaseLGraph {
+ /** The root graph. */
+ readonly rootGraph: LGraph
+}
+
+/**
+ * LGraph is the class that contain a full graph. We instantiate one and add nodes to it, and then we can run the execution loop.
+ * supported callbacks:
+ * + onNodeAdded: when a new node is added to the graph
+ * + onNodeRemoved: when a node inside this graph is removed
+ */
+export class LGraph implements LinkNetwork, BaseLGraph, Serialisable {
+ static serialisedSchemaVersion = 1 as const
+
+ static STATUS_STOPPED = 1
+ static STATUS_RUNNING = 2
+
+ /** List of LGraph properties that are manually handled by {@link LGraph.configure}. */
+ static readonly ConfigureProperties = new Set([
+ "nodes",
+ "groups",
+ "links",
+ "state",
+ "reroutes",
+ "floatingLinks",
+ "id",
+ "subgraphs",
+ "definitions",
+ "inputs",
+ "outputs",
+ "widgets",
+ "inputNode",
+ "outputNode",
+ "extra",
+ ])
+
+ id: UUID = zeroUuid
+ revision: number = 0
+
+ _version: number = -1
+ /** The backing store for links. Keys are wrapped in String() */
+ _links: Map = new Map()
+ /**
+ * Indexed property access is deprecated.
+ * Backwards compatibility with a Proxy has been added, but will eventually be removed.
+ *
+ * Use {@link Map} methods:
+ * ```
+ * const linkId = 123
+ * const link = graph.links.get(linkId)
+ * // Deprecated: const link = graph.links[linkId]
+ * ```
+ */
+ links: Map & Record
+ list_of_graphcanvas: LGraphCanvas[] | null
+ status: number = LGraph.STATUS_STOPPED
+
+ state: LGraphState = {
+ lastGroupId: 0,
+ lastNodeId: 0,
+ lastLinkId: 0,
+ lastRerouteId: 0,
+ }
+
+ readonly events = new CustomEventTarget()
+ readonly _subgraphs: Map = new Map()
+
+ _nodes: (LGraphNode | SubgraphNode)[] = []
+ _nodes_by_id: Record = {}
+ _nodes_in_order: LGraphNode[] = []
+ _nodes_executable: LGraphNode[] | null = null
+ _groups: LGraphGroup[] = []
+ iteration: number = 0
+ globaltime: number = 0
+ /** @deprecated Unused */
+ runningtime: number = 0
+ fixedtime: number = 0
+ fixedtime_lapse: number = 0.01
+ elapsed_time: number = 0.01
+ last_update_time: number = 0
+ starttime: number = 0
+ catch_errors: boolean = true
+ execution_timer_id?: number | null
+ errors_in_execution?: boolean
+ /** @deprecated Unused */
+ execution_time!: number
+ _last_trigger_time?: number
+ filter?: string
+ /** Must contain serialisable values, e.g. primitive types */
+ config: LGraphConfig = {}
+ vars: Dictionary = {}
+ nodes_executing: boolean[] = []
+ nodes_actioning: (string | boolean)[] = []
+ nodes_executedAction: string[] = []
+ extra: LGraphExtra = {}
+
+ /** @deprecated Deserialising a workflow sets this unused property. */
+ version?: number
+
+ /** @returns Whether the graph has no items */
+ get empty(): boolean {
+ return this._nodes.length + this._groups.length + this.reroutes.size === 0
+ }
+
+ /** @returns All items on the canvas that can be selected */
+ *positionableItems(): Generator {
+ for (const node of this._nodes) yield node
+ for (const group of this._groups) yield group
+ for (const reroute of this.reroutes.values()) yield reroute
+ return
+ }
+
+ /** Internal only. Not required for serialisation; calculated on deserialise. */
+ #lastFloatingLinkId: number = 0
+
+ #floatingLinks: Map = new Map()
+ get floatingLinks(): ReadonlyMap {
+ return this.#floatingLinks
+ }
+
+ #reroutes = new Map()
+ /** All reroutes in this graph. */
+ public get reroutes(): Map {
+ return this.#reroutes
+ }
+
+ get rootGraph(): LGraph {
+ return this
+ }
+
+ get isRootGraph(): boolean {
+ return this.rootGraph === this
+ }
+
+ /** @deprecated See {@link state}.{@link LGraphState.lastNodeId lastNodeId} */
+ get last_node_id() {
+ return this.state.lastNodeId
+ }
+
+ set last_node_id(value) {
+ this.state.lastNodeId = value
+ }
+
+ /** @deprecated See {@link state}.{@link LGraphState.lastLinkId lastLinkId} */
+ get last_link_id() {
+ return this.state.lastLinkId
+ }
+
+ set last_link_id(value) {
+ this.state.lastLinkId = value
+ }
+
+ onAfterStep?(): void
+ onBeforeStep?(): void
+ onPlayEvent?(): void
+ onStopEvent?(): void
+ onAfterExecute?(): void
+ onExecuteStep?(): void
+ onNodeAdded?(node: LGraphNode): void
+ onNodeRemoved?(node: LGraphNode): void
+ onTrigger?(action: string, param: unknown): void
+ onBeforeChange?(graph: LGraph, info?: LGraphNode): void
+ onAfterChange?(graph: LGraph, info?: LGraphNode | null): void
+ onConnectionChange?(node: LGraphNode): void
+ on_change?(graph: LGraph): void
+ onSerialize?(data: ISerialisedGraph | SerialisableGraph): void
+ onConfigure?(data: ISerialisedGraph | SerialisableGraph): void
+ onGetNodeMenuOptions?(options: (IContextMenuValue | null)[], node: LGraphNode): void
+
+ private _input_nodes?: LGraphNode[]
+
+ /**
+ * See {@link LGraph}
+ * @param o data from previous serialization [optional]
+ */
+ constructor(o?: ISerialisedGraph | SerialisableGraph) {
+ if (LiteGraph.debug) console.log("Graph created")
+
+ /** @see MapProxyHandler */
+ const links = this._links
+ MapProxyHandler.bindAllMethods(links)
+ const handler = new MapProxyHandler()
+ this.links = new Proxy(links, handler) as Map & Record
+
+ this.list_of_graphcanvas = null
+ this.clear()
+
+ if (o) this.configure(o)
+ }
+
+ /**
+ * Removes all nodes from this graph
+ */
+ clear(): void {
+ this.stop()
+ this.status = LGraph.STATUS_STOPPED
+
+ this.id = zeroUuid
+ this.revision = 0
+
+ this.state = {
+ lastGroupId: 0,
+ lastNodeId: 0,
+ lastLinkId: 0,
+ lastRerouteId: 0,
+ }
+
+ // used to detect changes
+ this._version = -1
+ this._subgraphs.clear()
+
+ // safe clear
+ if (this._nodes) {
+ for (const _node of this._nodes) {
+ _node.onRemoved?.()
+ }
+ }
+
+ // nodes
+ this._nodes = []
+ this._nodes_by_id = {}
+ // nodes sorted in execution order
+ this._nodes_in_order = []
+ // nodes that contain onExecute sorted in execution order
+ this._nodes_executable = null
+
+ this._links.clear()
+ this.reroutes.clear()
+ this.#floatingLinks.clear()
+
+ this.#lastFloatingLinkId = 0
+
+ // other scene stuff
+ this._groups = []
+
+ // iterations
+ this.iteration = 0
+
+ // custom data
+ this.config = {}
+ this.vars = {}
+ // to store custom data
+ this.extra = {}
+
+ // timing
+ this.globaltime = 0
+ this.runningtime = 0
+ this.fixedtime = 0
+ this.fixedtime_lapse = 0.01
+ this.elapsed_time = 0.01
+ this.last_update_time = 0
+ this.starttime = 0
+
+ this.catch_errors = true
+
+ this.nodes_executing = []
+ this.nodes_actioning = []
+ this.nodes_executedAction = []
+
+ // notify canvas to redraw
+ this.change()
+
+ this.canvasAction(c => c.clear())
+ }
+
+ get subgraphs(): Map {
+ return this.rootGraph._subgraphs
+ }
+
+ get nodes() {
+ return this._nodes
+ }
+
+ get groups() {
+ return this._groups
+ }
+
+ /**
+ * Attach Canvas to this graph
+ */
+ attachCanvas(canvas: LGraphCanvas): void {
+ if (!(canvas instanceof LGraphCanvas)) {
+ throw new TypeError("attachCanvas expects an LGraphCanvas instance")
+ }
+
+ this.primaryCanvas = canvas
+
+ this.list_of_graphcanvas ??= []
+ if (!this.list_of_graphcanvas.includes(canvas)) {
+ this.list_of_graphcanvas.push(canvas)
+ }
+
+ if (canvas.graph === this) return
+
+ canvas.graph?.detachCanvas(canvas)
+ canvas.graph = this
+ canvas.subgraph = undefined
+ }
+
+ /**
+ * Detach Canvas from this graph
+ */
+ detachCanvas(canvas: LGraphCanvas): void {
+ canvas.graph = null
+ const canvases = this.list_of_graphcanvas
+ if (canvases) {
+ const pos = canvases.indexOf(canvas)
+ if (pos !== -1) canvases.splice(pos, 1)
+ }
+ }
+
+ /**
+ * @deprecated Will be removed in 0.9
+ * Starts running this graph every interval milliseconds.
+ * @param interval amount of milliseconds between executions, if 0 then it renders to the monitor refresh rate
+ */
+ start(interval?: number): void {
+ if (this.status == LGraph.STATUS_RUNNING) return
+ this.status = LGraph.STATUS_RUNNING
+
+ this.onPlayEvent?.()
+ this.sendEventToAllNodes("onStart")
+
+ // launch
+ this.starttime = LiteGraph.getTime()
+ this.last_update_time = this.starttime
+ interval ||= 0
+
+ // execute once per frame
+ if (
+ interval == 0 &&
+ typeof window != "undefined" &&
+ window.requestAnimationFrame
+ ) {
+ const on_frame = () => {
+ if (this.execution_timer_id != -1) return
+
+ window.requestAnimationFrame(on_frame)
+ this.onBeforeStep?.()
+ this.runStep(1, !this.catch_errors)
+ this.onAfterStep?.()
+ }
+ this.execution_timer_id = -1
+ on_frame()
+ } else {
+ // execute every 'interval' ms
+ this.execution_timer_id = setInterval(() => {
+ // execute
+ this.onBeforeStep?.()
+ this.runStep(1, !this.catch_errors)
+ this.onAfterStep?.()
+ }, interval)
+ }
+ }
+
+ /**
+ * @deprecated Will be removed in 0.9
+ * Stops the execution loop of the graph
+ */
+ stop(): void {
+ if (this.status == LGraph.STATUS_STOPPED) return
+
+ this.status = LGraph.STATUS_STOPPED
+
+ this.onStopEvent?.()
+
+ if (this.execution_timer_id != null) {
+ if (this.execution_timer_id != -1) {
+ clearInterval(this.execution_timer_id)
+ }
+ this.execution_timer_id = null
+ }
+
+ this.sendEventToAllNodes("onStop")
+ }
+
+ /**
+ * Run N steps (cycles) of the graph
+ * @param num number of steps to run, default is 1
+ * @param do_not_catch_errors [optional] if you want to try/catch errors
+ * @param limit max number of nodes to execute (used to execute from start to a node)
+ */
+ runStep(num: number, do_not_catch_errors: boolean, limit?: number): void {
+ num = num || 1
+
+ const start = LiteGraph.getTime()
+ this.globaltime = 0.001 * (start - this.starttime)
+
+ const nodes = this._nodes_executable || this._nodes
+ if (!nodes) return
+
+ limit = limit || nodes.length
+
+ if (do_not_catch_errors) {
+ // iterations
+ for (let i = 0; i < num; i++) {
+ for (let j = 0; j < limit; ++j) {
+ const node = nodes[j]
+ // FIXME: Looks like copy/paste broken logic - checks for "on", executes "do"
+ if (node.mode == LGraphEventMode.ALWAYS && node.onExecute) {
+ // wrap node.onExecute();
+ node.doExecute?.()
+ }
+ }
+
+ this.fixedtime += this.fixedtime_lapse
+ this.onExecuteStep?.()
+ }
+
+ this.onAfterExecute?.()
+ } else {
+ try {
+ // iterations
+ for (let i = 0; i < num; i++) {
+ for (let j = 0; j < limit; ++j) {
+ const node = nodes[j]
+ if (node.mode == LGraphEventMode.ALWAYS) {
+ node.onExecute?.()
+ }
+ }
+
+ this.fixedtime += this.fixedtime_lapse
+ this.onExecuteStep?.()
+ }
+
+ this.onAfterExecute?.()
+ this.errors_in_execution = false
+ } catch (error) {
+ this.errors_in_execution = true
+ if (LiteGraph.throw_errors) throw error
+
+ if (LiteGraph.debug) console.log("Error during execution:", error)
+ this.stop()
+ }
+ }
+
+ const now = LiteGraph.getTime()
+ let elapsed = now - start
+ if (elapsed == 0) elapsed = 1
+
+ this.execution_time = 0.001 * elapsed
+ this.globaltime += 0.001 * elapsed
+ this.iteration += 1
+ this.elapsed_time = (now - this.last_update_time) * 0.001
+ this.last_update_time = now
+ this.nodes_executing = []
+ this.nodes_actioning = []
+ this.nodes_executedAction = []
+ }
+
+ /**
+ * Updates the graph execution order according to relevance of the nodes (nodes with only outputs have more relevance than
+ * nodes with only inputs.
+ */
+ updateExecutionOrder(): void {
+ this._nodes_in_order = this.computeExecutionOrder(false)
+ this._nodes_executable = []
+ for (const node of this._nodes_in_order) {
+ if (node.onExecute) {
+ this._nodes_executable.push(node)
+ }
+ }
+ }
+
+ // This is more internal, it computes the executable nodes in order and returns it
+ computeExecutionOrder(
+ only_onExecute: boolean,
+ set_level?: boolean,
+ ): LGraphNode[] {
+ const L: LGraphNode[] = []
+ const S: LGraphNode[] = []
+ const M: Dictionary = {}
+ // to avoid repeating links
+ const visited_links: Record = {}
+ const remaining_links: Record = {}
+
+ // search for the nodes without inputs (starting nodes)
+ for (const node of this._nodes) {
+ if (only_onExecute && !node.onExecute) {
+ continue
+ }
+
+ // add to pending nodes
+ M[node.id] = node
+
+ // num of input connections
+ let num = 0
+ if (node.inputs) {
+ for (const input of node.inputs) {
+ if (input?.link != null) {
+ num += 1
+ }
+ }
+ }
+
+ if (num == 0) {
+ // is a starting node
+ S.push(node)
+ if (set_level) node._level = 1
+ } else {
+ // num of input links
+ if (set_level) node._level = 0
+ remaining_links[node.id] = num
+ }
+ }
+
+ while (true) {
+ // get an starting node
+ const node = S.shift()
+ if (node === undefined) break
+
+ // add to ordered list
+ L.push(node)
+ // remove from the pending nodes
+ delete M[node.id]
+
+ if (!node.outputs) continue
+
+ // for every output
+ for (const output of node.outputs) {
+ // not connected
+ // TODO: Confirm functionality, clean condition
+ if (output?.links == null || output.links.length == 0)
+ continue
+
+ // for every connection
+ for (const link_id of output.links) {
+ const link = this._links.get(link_id)
+ if (!link) continue
+
+ // already visited link (ignore it)
+ if (visited_links[link.id]) continue
+
+ const target_node = this.getNodeById(link.target_id)
+ if (target_node == null) {
+ visited_links[link.id] = true
+ continue
+ }
+
+ if (set_level) {
+ node._level ??= 0
+ if (!target_node._level || target_node._level <= node._level) {
+ target_node._level = node._level + 1
+ }
+ }
+
+ // mark as visited
+ visited_links[link.id] = true
+ // reduce the number of links remaining
+ remaining_links[target_node.id] -= 1
+
+ // if no more links, then add to starters array
+ if (remaining_links[target_node.id] == 0) S.push(target_node)
+ }
+ }
+ }
+
+ // the remaining ones (loops)
+ for (const i in M) {
+ L.push(M[i])
+ }
+
+ if (L.length != this._nodes.length && LiteGraph.debug)
+ console.warn("something went wrong, nodes missing")
+
+ /** Ensure type is set */
+ type OrderedLGraphNode = LGraphNode & { order: number }
+
+ /** Sets the order property of each provided node to its index in {@link nodes}. */
+ function setOrder(nodes: LGraphNode[]): asserts nodes is OrderedLGraphNode[] {
+ const l = nodes.length
+ for (let i = 0; i < l; ++i) {
+ nodes[i].order = i
+ }
+ }
+
+ // save order number in the node
+ setOrder(L)
+
+ // sort now by priority
+ L.sort(function (A, B) {
+ // @ts-expect-error ctor props
+ const Ap = A.constructor.priority || A.priority || 0
+ // @ts-expect-error ctor props
+ const Bp = B.constructor.priority || B.priority || 0
+ // if same priority, sort by order
+
+ return Ap == Bp
+ ? A.order - B.order
+ : Ap - Bp
+ })
+
+ // save order number in the node, again...
+ setOrder(L)
+
+ return L
+ }
+
+ /**
+ * Positions every node in a more readable manner
+ */
+ arrange(margin?: number, layout?: string): void {
+ margin = margin || 100
+
+ const nodes = this.computeExecutionOrder(false, true)
+ const columns: LGraphNode[][] = []
+ for (const node of nodes) {
+ const col = node._level || 1
+ columns[col] ||= []
+ columns[col].push(node)
+ }
+
+ let x = margin
+
+ for (const column of columns) {
+ if (!column) continue
+
+ let max_size = 100
+ let y = margin + LiteGraph.NODE_TITLE_HEIGHT
+ for (const node of column) {
+ node.pos[0] = layout == LiteGraph.VERTICAL_LAYOUT ? y : x
+ node.pos[1] = layout == LiteGraph.VERTICAL_LAYOUT ? x : y
+ const max_size_index = layout == LiteGraph.VERTICAL_LAYOUT ? 1 : 0
+ if (node.size[max_size_index] > max_size) {
+ max_size = node.size[max_size_index]
+ }
+ const node_size_index = layout == LiteGraph.VERTICAL_LAYOUT ? 0 : 1
+ y += node.size[node_size_index] + margin + LiteGraph.NODE_TITLE_HEIGHT
+ }
+ x += max_size + margin
+ }
+
+ this.setDirtyCanvas(true, true)
+ }
+
+ /**
+ * Returns the amount of time the graph has been running in milliseconds
+ * @returns number of milliseconds the graph has been running
+ */
+ getTime(): number {
+ return this.globaltime
+ }
+
+ /**
+ * Returns the amount of time accumulated using the fixedtime_lapse var.
+ * This is used in context where the time increments should be constant
+ * @returns number of milliseconds the graph has been running
+ */
+ getFixedTime(): number {
+ return this.fixedtime
+ }
+
+ /**
+ * Returns the amount of time it took to compute the latest iteration.
+ * Take into account that this number could be not correct
+ * if the nodes are using graphical actions
+ * @returns number of milliseconds it took the last cycle
+ */
+ getElapsedTime(): number {
+ return this.elapsed_time
+ }
+
+ /**
+ * @deprecated Will be removed in 0.9
+ * Sends an event to all the nodes, useful to trigger stuff
+ * @param eventname the name of the event (function to be called)
+ * @param params parameters in array format
+ */
+ sendEventToAllNodes(
+ eventname: string,
+ params?: object | object[],
+ mode?: LGraphEventMode,
+ ): void {
+ mode = mode || LGraphEventMode.ALWAYS
+
+ const nodes = this._nodes_in_order || this._nodes
+ if (!nodes) return
+
+ for (const node of nodes) {
+ // @ts-expect-error deprecated
+ if (!node[eventname] || node.mode != mode) continue
+ if (params === undefined) {
+ // @ts-expect-error deprecated
+ node[eventname]()
+ } else if (params && params.constructor === Array) {
+ // @ts-expect-error deprecated
+ node[eventname].apply(node, params)
+ } else {
+ // @ts-expect-error deprecated
+ node[eventname](params)
+ }
+ }
+ }
+
+ /**
+ * Runs an action on every canvas registered to this graph.
+ * @param action Action to run for every canvas
+ */
+ canvasAction(action: (canvas: LGraphCanvas) => void): void {
+ const canvases = this.list_of_graphcanvas
+ if (!canvases) return
+ for (const canvas of canvases) action(canvas)
+ }
+
+ /** @deprecated See {@link LGraph.canvasAction} */
+ sendActionToCanvas>(
+ action: T,
+ params?: ParamsArray,
+ ): void {
+ const { list_of_graphcanvas } = this
+ if (!list_of_graphcanvas) return
+
+ for (const c of list_of_graphcanvas) {
+ c[action]?.apply(c, params)
+ }
+ }
+
+ /**
+ * Adds a new node instance to this graph
+ * @param node the instance of the node
+ */
+ add(
+ node: LGraphNode | LGraphGroup,
+ skip_compute_order?: boolean,
+ ): LGraphNode | null | undefined {
+ if (!node) return
+ const { state } = this
+
+ // Ensure created items are snapped
+ if (LiteGraph.alwaysSnapToGrid) {
+ const snapTo = this.getSnapToGridSize()
+ if (snapTo) node.snapToGrid(snapTo)
+ }
+
+ // LEGACY: This was changed from constructor === LGraphGroup
+ // groups
+ if (node instanceof LGraphGroup) {
+ // Assign group ID
+ if (node.id == null || node.id === -1) node.id = ++state.lastGroupId
+ if (node.id > state.lastGroupId) state.lastGroupId = node.id
+
+ this._groups.push(node)
+ this.setDirtyCanvas(true)
+ this.change()
+ node.graph = this
+ this._version++
+ return
+ }
+
+ // nodes
+ if (node.id != -1 && this._nodes_by_id[node.id] != null) {
+ console.warn(
+ "LiteGraph: there is already a node with this ID, changing it",
+ )
+ node.id = LiteGraph.use_uuids
+ ? LiteGraph.uuidv4()
+ : ++state.lastNodeId
+ }
+
+ if (this._nodes.length >= LiteGraph.MAX_NUMBER_OF_NODES) {
+ throw "LiteGraph: max number of nodes in a graph reached"
+ }
+
+ // give him an id
+ if (LiteGraph.use_uuids) {
+ if (node.id == null || node.id == -1)
+ node.id = LiteGraph.uuidv4()
+ } else {
+ if (node.id == null || node.id == -1) {
+ node.id = ++state.lastNodeId
+ } else if (typeof node.id === "number" && state.lastNodeId < node.id) {
+ state.lastNodeId = node.id
+ }
+ }
+
+ node.graph = this
+ this._version++
+
+ this._nodes.push(node)
+ this._nodes_by_id[node.id] = node
+
+ node.onAdded?.(this)
+
+ if (this.config.align_to_grid) node.alignToGrid()
+
+ if (!skip_compute_order) this.updateExecutionOrder()
+
+ this.onNodeAdded?.(node)
+
+ this.setDirtyCanvas(true)
+ this.change()
+
+ // to chain actions
+ return node
+ }
+
+ /**
+ * Removes a node from the graph
+ * @param node the instance of the node
+ */
+ remove(node: LGraphNode | LGraphGroup): void {
+ // LEGACY: This was changed from constructor === LiteGraph.LGraphGroup
+ if (node instanceof LGraphGroup) {
+ this.canvasAction(c => c.deselect(node))
+
+ const index = this._groups.indexOf(node)
+ if (index != -1) {
+ this._groups.splice(index, 1)
+ }
+ node.graph = undefined
+ this._version++
+ this.setDirtyCanvas(true, true)
+ this.change()
+ return
+ }
+
+ // not found
+ if (this._nodes_by_id[node.id] == null) {
+ console.warn("LiteGraph: node not found", node)
+ return
+ }
+ // cannot be removed
+ if (node.ignore_remove) {
+ console.warn("LiteGraph: node cannot be removed", node)
+ return
+ }
+
+ // sure? - almost sure is wrong
+ this.beforeChange()
+
+ const { inputs, outputs } = node
+
+ // disconnect inputs
+ if (inputs) {
+ for (const [i, slot] of inputs.entries()) {
+ if (slot.link != null) node.disconnectInput(i, true)
+ }
+ }
+
+ // disconnect outputs
+ if (outputs) {
+ for (const [i, slot] of outputs.entries()) {
+ if (slot.links?.length) node.disconnectOutput(i)
+ }
+ }
+
+ // Floating links
+ for (const link of this.floatingLinks.values()) {
+ if (link.origin_id === node.id || link.target_id === node.id) {
+ this.removeFloatingLink(link)
+ }
+ }
+
+ // callback
+ node.onRemoved?.()
+
+ node.graph = null
+ this._version++
+
+ // remove from canvas render
+ const { list_of_graphcanvas } = this
+ if (list_of_graphcanvas) {
+ for (const canvas of list_of_graphcanvas) {
+ if (canvas.selected_nodes[node.id])
+ delete canvas.selected_nodes[node.id]
+
+ canvas.deselect(node)
+ }
+ }
+
+ // remove from containers
+ const pos = this._nodes.indexOf(node)
+ if (pos != -1) this._nodes.splice(pos, 1)
+
+ delete this._nodes_by_id[node.id]
+
+ this.onNodeRemoved?.(node)
+
+ // close panels
+ this.canvasAction(c => c.checkPanels())
+
+ this.setDirtyCanvas(true, true)
+ // sure? - almost sure is wrong
+ this.afterChange()
+ this.change()
+
+ this.updateExecutionOrder()
+ }
+
+ /**
+ * Returns a node by its id.
+ */
+ getNodeById(id: NodeId | null | undefined): LGraphNode | null {
+ return id != null
+ ? this._nodes_by_id[id]
+ : null
+ }
+
+ /**
+ * Returns a list of nodes that matches a class
+ * @param classObject the class itself (not an string)
+ * @returns a list with all the nodes of this type
+ */
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
+ findNodesByClass(classObject: Function, result?: LGraphNode[]): LGraphNode[] {
+ result = result || []
+ result.length = 0
+ const { _nodes } = this
+ for (const node of _nodes) {
+ if (node.constructor === classObject)
+ result.push(node)
+ }
+ return result
+ }
+
+ /**
+ * Returns a list of nodes that matches a type
+ * @param type the name of the node type
+ * @returns a list with all the nodes of this type
+ */
+ findNodesByType(type: string, result: LGraphNode[]): LGraphNode[] {
+ const matchType = type.toLowerCase()
+ result = result || []
+ result.length = 0
+ const { _nodes } = this
+ for (const node of _nodes) {
+ if (node.type?.toLowerCase() == matchType)
+ result.push(node)
+ }
+ return result
+ }
+
+ /**
+ * Returns the first node that matches a name in its title
+ * @param title the name of the node to search
+ * @returns the node or null
+ */
+ findNodeByTitle(title: string): LGraphNode | null {
+ const { _nodes } = this
+ for (const node of _nodes) {
+ if (node.title == title)
+ return node
+ }
+ return null
+ }
+
+ /**
+ * Returns a list of nodes that matches a name
+ * @param title the name of the node to search
+ * @returns a list with all the nodes with this name
+ */
+ findNodesByTitle(title: string): LGraphNode[] {
+ const result: LGraphNode[] = []
+ const { _nodes } = this
+ for (const node of _nodes) {
+ if (node.title == title)
+ result.push(node)
+ }
+ return result
+ }
+
+ /**
+ * Returns the top-most node in this position of the canvas
+ * @param x the x coordinate in canvas space
+ * @param y the y coordinate in canvas space
+ * @param nodeList a list with all the nodes to search from, by default is all the nodes in the graph
+ * @returns the node at this position or null
+ */
+ getNodeOnPos(
+ x: number,
+ y: number,
+ nodeList?: LGraphNode[],
+ ): LGraphNode | null {
+ const nodes = nodeList || this._nodes
+ let i = nodes.length
+ while (--i >= 0) {
+ const node = nodes[i]
+ if (node.isPointInside(x, y)) return node
+ }
+ return null
+ }
+
+ /**
+ * Returns the top-most group in that position
+ * @param x The x coordinate in canvas space
+ * @param y The y coordinate in canvas space
+ * @returns The group or null
+ */
+ getGroupOnPos(x: number, y: number): LGraphGroup | undefined {
+ return this._groups.toReversed().find(g => g.isPointInside(x, y))
+ }
+
+ /**
+ * Returns the top-most group with a titlebar in the provided position.
+ * @param x The x coordinate in canvas space
+ * @param y The y coordinate in canvas space
+ * @returns The group or null
+ */
+ getGroupTitlebarOnPos(x: number, y: number): LGraphGroup | undefined {
+ return this._groups.toReversed().find(g => g.isPointInTitlebar(x, y))
+ }
+
+ /**
+ * Finds a reroute a the given graph point
+ * @param x X co-ordinate in graph space
+ * @param y Y co-ordinate in graph space
+ * @returns The first reroute under the given co-ordinates, or undefined
+ */
+ getRerouteOnPos(x: number, y: number, reroutes?: Iterable): Reroute | undefined {
+ for (const reroute of reroutes ?? this.reroutes.values()) {
+ if (reroute.containsPoint([x, y])) return reroute
+ }
+ }
+
+ /**
+ * Snaps the provided items to a grid.
+ *
+ * Item positions are reounded to the nearest multiple of {@link LiteGraph.CANVAS_GRID_SIZE}.
+ *
+ * When {@link LiteGraph.alwaysSnapToGrid} is enabled
+ * and the grid size is falsy, a default of 1 is used.
+ * @param items The items to be snapped to the grid
+ * @todo Currently only snaps nodes.
+ */
+ snapToGrid(items: Set): void {
+ const snapTo = this.getSnapToGridSize()
+ if (!snapTo) return
+
+ for (const item of getAllNestedItems(items)) {
+ if (!item.pinned) item.snapToGrid(snapTo)
+ }
+ }
+
+ /**
+ * Finds the size of the grid that items should be snapped to when moved.
+ * @returns The size of the grid that items should be snapped to
+ */
+ getSnapToGridSize(): number {
+ // Default to 1 when always snapping
+ return LiteGraph.alwaysSnapToGrid
+ ? LiteGraph.CANVAS_GRID_SIZE || 1
+ : LiteGraph.CANVAS_GRID_SIZE
+ }
+
+ /**
+ * @deprecated Will be removed in 0.9
+ * Checks that the node type matches the node type registered,
+ * used when replacing a nodetype by a newer version during execution
+ * this replaces the ones using the old version with the new version
+ */
+ checkNodeTypes() {
+ const { _nodes } = this
+ for (const [i, node] of _nodes.entries()) {
+ const ctor = LiteGraph.registered_node_types[node.type]
+ if (node.constructor == ctor) continue
+
+ console.log("node being replaced by newer version:", node.type)
+ const newnode = LiteGraph.createNode(node.type)
+ if (!newnode) continue
+ _nodes[i] = newnode
+ newnode.configure(node.serialize())
+ newnode.graph = this
+ this._nodes_by_id[newnode.id] = newnode
+
+ if (node.inputs) newnode.inputs = [...node.inputs]
+ if (node.outputs) newnode.outputs = [...node.outputs]
+ }
+ this.updateExecutionOrder()
+ }
+
+ // ********** GLOBALS *****************
+ trigger(action: string, param: unknown) {
+ this.onTrigger?.(action, param)
+ }
+
+ /** @todo Clean up - never implemented. */
+ triggerInput(name: string, value: any): void {
+ const nodes = this.findNodesByTitle(name)
+ for (const node of nodes) {
+ // @ts-expect-error
+ node.onTrigger(value)
+ }
+ }
+
+ /** @todo Clean up - never implemented. */
+ setCallback(name: string, func: any): void {
+ const nodes = this.findNodesByTitle(name)
+ for (const node of nodes) {
+ // @ts-expect-error
+ node.setTrigger(func)
+ }
+ }
+
+ // used for undo, called before any change is made to the graph
+ beforeChange(info?: LGraphNode): void {
+ this.onBeforeChange?.(this, info)
+ this.canvasAction(c => c.onBeforeChange?.(this))
+ }
+
+ // used to resend actions, called after any change is made to the graph
+ afterChange(info?: LGraphNode | null): void {
+ this.onAfterChange?.(this, info)
+ this.canvasAction(c => c.onAfterChange?.(this))
+ }
+
+ /**
+ * clears the triggered slot animation in all links (stop visual animation)
+ */
+ clearTriggeredSlots(): void {
+ for (const link_info of this._links.values()) {
+ if (!link_info) continue
+
+ if (link_info._last_time) link_info._last_time = 0
+ }
+ }
+
+ /* Called when something visually changed (not the graph!) */
+ change(): void {
+ if (LiteGraph.debug) {
+ console.log("Graph changed")
+ }
+ this.canvasAction(c => c.setDirty(true, true))
+ this.on_change?.(this)
+ }
+
+ setDirtyCanvas(fg: boolean, bg?: boolean): void {
+ this.canvasAction(c => c.setDirty(fg, bg))
+ }
+
+ addFloatingLink(link: LLink): LLink {
+ if (link.id === -1) {
+ link.id = ++this.#lastFloatingLinkId
+ }
+ this.#floatingLinks.set(link.id, link)
+
+ const slot = link.target_id !== -1
+ ? this.getNodeById(link.target_id)?.inputs?.[link.target_slot]
+ : this.getNodeById(link.origin_id)?.outputs?.[link.origin_slot]
+ if (slot) {
+ slot._floatingLinks ??= new Set()
+ slot._floatingLinks.add(link)
+ } else {
+ console.warn(`Adding invalid floating link: target/slot: [${link.target_id}/${link.target_slot}] origin/slot: [${link.origin_id}/${link.origin_slot}]`)
+ }
+
+ const reroutes = LLink.getReroutes(this, link)
+ for (const reroute of reroutes) {
+ reroute.floatingLinkIds.add(link.id)
+ }
+ return link
+ }
+
+ removeFloatingLink(link: LLink): void {
+ this.#floatingLinks.delete(link.id)
+
+ const slot = link.target_id !== -1
+ ? this.getNodeById(link.target_id)?.inputs?.[link.target_slot]
+ : this.getNodeById(link.origin_id)?.outputs?.[link.origin_slot]
+ if (slot) {
+ slot._floatingLinks?.delete(link)
+ }
+
+ const reroutes = LLink.getReroutes(this, link)
+ for (const reroute of reroutes) {
+ reroute.floatingLinkIds.delete(link.id)
+ if (reroute.floatingLinkIds.size === 0) {
+ delete reroute.floating
+ }
+
+ if (reroute.totalLinks === 0) this.removeReroute(reroute.id)
+ }
+ }
+
+ /**
+ * Finds the link with the provided ID.
+ * @param id ID of link to find
+ * @returns The link with the provided {@link id}, otherwise `undefined`. Always returns `undefined` if `id` is nullish.
+ */
+ getLink(id: null | undefined): undefined
+ getLink(id: LinkId | null | undefined): LLink | undefined
+ getLink(id: LinkId | null | undefined): LLink | undefined {
+ return id == null ? undefined : this._links.get(id)
+ }
+
+ /**
+ * Finds the reroute with the provided ID.
+ * @param id ID of reroute to find
+ * @returns The reroute with the provided {@link id}, otherwise `undefined`. Always returns `undefined` if `id` is nullish.
+ */
+ getReroute(id: null | undefined): undefined
+ getReroute(id: RerouteId | null | undefined): Reroute | undefined
+ getReroute(id: RerouteId | null | undefined): Reroute | undefined {
+ return id == null ? undefined : this.reroutes.get(id)
+ }
+
+ /**
+ * Configures a reroute on the graph where ID is already known (probably deserialisation).
+ * Creates the object if it does not exist.
+ * @param serialisedReroute See {@link SerialisableReroute}
+ */
+ setReroute({ id, parentId, pos, linkIds, floating }: OptionalProps): Reroute {
+ id ??= ++this.state.lastRerouteId
+ if (id > this.state.lastRerouteId) this.state.lastRerouteId = id
+
+ const reroute = this.reroutes.get(id) ?? new Reroute(id, this)
+ reroute.update(parentId, pos, linkIds, floating)
+ this.reroutes.set(id, reroute)
+ return reroute
+ }
+
+ /**
+ * Creates a new reroute and adds it to the graph.
+ * @param pos Position in graph space
+ * @param before The existing link segment (reroute, link) that will be after this reroute,
+ * going from the node output to input.
+ * @returns The newly created reroute - typically ignored.
+ */
+ createReroute(pos: Point, before: LinkSegment): Reroute {
+ const rerouteId = ++this.state.lastRerouteId
+ const linkIds = before instanceof Reroute
+ ? before.linkIds
+ : [before.id]
+ const floatingLinkIds = before instanceof Reroute
+ ? before.floatingLinkIds
+ : [before.id]
+ const reroute = new Reroute(rerouteId, this, pos, before.parentId, linkIds, floatingLinkIds)
+ this.reroutes.set(rerouteId, reroute)
+ for (const linkId of linkIds) {
+ const link = this._links.get(linkId)
+ if (!link) continue
+ if (link.parentId === before.parentId) link.parentId = rerouteId
+
+ const reroutes = LLink.getReroutes(this, link)
+ for (const x of reroutes.filter(x => x.parentId === before.parentId)) {
+ x.parentId = rerouteId
+ }
+ }
+
+ for (const linkId of floatingLinkIds) {
+ const link = this.floatingLinks.get(linkId)
+ if (!link) continue
+ if (link.parentId === before.parentId) link.parentId = rerouteId
+
+ const reroutes = LLink.getReroutes(this, link)
+ for (const x of reroutes.filter(x => x.parentId === before.parentId)) {
+ x.parentId = rerouteId
+ }
+ }
+
+ return reroute
+ }
+
+ /**
+ * Removes a reroute from the graph
+ * @param id ID of reroute to remove
+ */
+ removeReroute(id: RerouteId): void {
+ const { reroutes } = this
+ const reroute = reroutes.get(id)
+ if (!reroute) return
+
+ this.canvasAction(c => c.deselect(reroute))
+
+ // Extract reroute from the reroute chain
+ const { parentId, linkIds, floatingLinkIds } = reroute
+ for (const reroute of reroutes.values()) {
+ if (reroute.parentId === id) reroute.parentId = parentId
+ }
+
+ for (const linkId of linkIds) {
+ const link = this._links.get(linkId)
+ if (link && link.parentId === id) link.parentId = parentId
+ }
+
+ for (const linkId of floatingLinkIds) {
+ const link = this.floatingLinks.get(linkId)
+ if (!link) {
+ console.warn(`Removed reroute had floating link ID that did not exist [${linkId}]`)
+ continue
+ }
+
+ // A floating link is a unique branch; if there is no parent reroute, or
+ // the parent reroute has any other links, remove this floating link.
+ const floatingReroutes = LLink.getReroutes(this, link)
+ const lastReroute = floatingReroutes.at(-1)
+ const secondLastReroute = floatingReroutes.at(-2)
+
+ if (reroute !== lastReroute) {
+ continue
+ } else if (secondLastReroute?.totalLinks !== 1) {
+ this.removeFloatingLink(link)
+ } else if (link.parentId === id) {
+ link.parentId = parentId
+ secondLastReroute.floating = reroute.floating
+ }
+ }
+
+ reroutes.delete(id)
+ // This does not belong here; it should be handled by the caller, or run by a remove-many API.
+ // https://github.com/Comfy-Org/litegraph.js/issues/898
+ this.setDirtyCanvas(false, true)
+ }
+
+ /**
+ * Destroys a link
+ */
+ removeLink(link_id: LinkId): void {
+ const link = this._links.get(link_id)
+ if (!link) return
+
+ const node = this.getNodeById(link.target_id)
+ node?.disconnectInput(link.target_slot, false)
+
+ link.disconnect(this)
+ }
+
+ /**
+ * Creates a new subgraph definition, and adds it to the graph.
+ * @param data Exported data (typically serialised) to configure the new subgraph with
+ * @returns The newly created subgraph definition.
+ */
+ createSubgraph(data: ExportedSubgraph): Subgraph {
+ const { id } = data
+
+ const subgraph = new Subgraph(this.rootGraph, data)
+ this.subgraphs.set(id, subgraph)
+
+ // FE: Create node defs
+ this.rootGraph.events.dispatch("subgraph-created", { subgraph, data })
+ return subgraph
+ }
+
+ convertToSubgraph(items: Set): { subgraph: Subgraph, node: SubgraphNode } {
+ if (items.size === 0) throw new Error("Cannot convert to subgraph: nothing to convert")
+ const { state, revision, config } = this
+
+ const { boundaryLinks, boundaryFloatingLinks, internalLinks, boundaryInputLinks, boundaryOutputLinks } = getBoundaryLinks(this, items)
+ const { nodes, reroutes, groups } = splitPositionables(items)
+
+ const boundingRect = createBounds(items)
+ if (!boundingRect) throw new Error("Failed to create bounding rect for subgraph")
+
+ const resolvedInputLinks = boundaryInputLinks.map(x => x.resolve(this))
+ const resolvedOutputLinks = boundaryOutputLinks.map(x => x.resolve(this))
+
+ const clonedNodes = multiClone(nodes)
+
+ // Inputs, outputs, and links
+ const links = internalLinks.map(x => x.asSerialisable())
+ const inputs = mapSubgraphInputsAndLinks(resolvedInputLinks, links)
+ const outputs = mapSubgraphOutputsAndLinks(resolvedOutputLinks, links)
+
+ // Prepare subgraph data
+ const data = {
+ id: createUuidv4(),
+ name: "New Subgraph",
+ inputNode: {
+ id: SUBGRAPH_INPUT_ID,
+ bounding: [0, 0, 75, 100],
+ },
+ outputNode: {
+ id: SUBGRAPH_OUTPUT_ID,
+ bounding: [0, 0, 75, 100],
+ },
+ inputs,
+ outputs,
+ widgets: [],
+ version: LGraph.serialisedSchemaVersion,
+ state,
+ revision,
+ config,
+ links,
+ nodes: clonedNodes,
+ reroutes: structuredClone([...reroutes].map(reroute => reroute.asSerialisable())),
+ groups: structuredClone([...groups].map(group => group.serialize())),
+ } satisfies ExportedSubgraph
+
+ const subgraph = this.createSubgraph(data)
+ subgraph.configure(data)
+
+ // Position the subgraph input nodes
+ subgraph.inputNode.arrange()
+ subgraph.outputNode.arrange()
+ const { boundingRect: inputRect } = subgraph.inputNode
+ const { boundingRect: outputRect } = subgraph.outputNode
+ alignOutsideContainer(inputRect, Alignment.MidLeft, boundingRect, [50, 0])
+ alignOutsideContainer(outputRect, Alignment.MidRight, boundingRect, [50, 0])
+
+ // Remove items converted to subgraph
+ for (const resolved of resolvedInputLinks) resolved.inputNode?.disconnectInput(resolved.inputNode.inputs.indexOf(resolved.input!), true)
+ for (const resolved of resolvedOutputLinks) resolved.outputNode?.disconnectOutput(resolved.outputNode.outputs.indexOf(resolved.output!), resolved.inputNode)
+
+ for (const node of nodes) this.remove(node)
+ for (const reroute of reroutes) this.removeReroute(reroute.id)
+ for (const group of groups) this.remove(group)
+
+ this.rootGraph.events.dispatch("convert-to-subgraph", {
+ subgraph,
+ bounds: boundingRect,
+ exportedSubgraph: data,
+ boundaryLinks,
+ resolvedInputLinks,
+ resolvedOutputLinks,
+ boundaryFloatingLinks,
+ internalLinks,
+ })
+
+ // Create subgraph node object
+ const subgraphNode = LiteGraph.createNode(subgraph.id, subgraph.name, {
+ inputs: structuredClone(inputs),
+ outputs: structuredClone(outputs),
+ })
+ if (!subgraphNode) throw new Error("Failed to create subgraph node")
+
+ // Resize to inputs/outputs
+ subgraphNode.setSize(subgraphNode.computeSize())
+
+ // Center the subgraph node
+ alignToContainer(subgraphNode._posSize, Alignment.Centre | Alignment.Middle, boundingRect)
+
+ // Add the subgraph node to the graph
+ this.add(subgraphNode)
+
+ // Group matching input links
+ const groupedByOutput = groupResolvedByOutput(resolvedInputLinks)
+
+ // Reconnect input links in parent graph
+ let i = 0
+ for (const [, connections] of groupedByOutput.entries()) {
+ const [firstResolved, ...others] = connections
+ const { output, outputNode, link, subgraphInput } = firstResolved
+
+ // Special handling: Subgraph input node
+ i++
+ if (link.origin_id === SUBGRAPH_INPUT_ID) {
+ link.target_id = subgraphNode.id
+ link.target_slot = i - 1
+ if (subgraphInput instanceof SubgraphInput) {
+ subgraphInput.connect(subgraphNode.findInputSlotByType(link.type, true, true), subgraphNode, link.parentId)
+ } else {
+ throw new TypeError("Subgraph input node is not a SubgraphInput")
+ }
+ console.debug("Reconnect input links in parent graph", { ...link }, this.links.get(link.id), this.links.get(link.id) === link)
+
+ for (const resolved of others) {
+ resolved.link.disconnect(this)
+ }
+ continue
+ }
+
+ if (!output || !outputNode) {
+ console.warn("Convert to Subgraph reconnect: Failed to resolve input link", connections[0])
+ continue
+ }
+
+ const input = subgraphNode.findInputSlotByType(link.type, true, true)
+ outputNode.connectSlots(
+ output,
+ subgraphNode,
+ input,
+ link.parentId,
+ )
+ }
+
+ // Group matching links
+ const outputsGroupedByOutput = groupResolvedByOutput(resolvedOutputLinks)
+
+ // Reconnect output links in parent graph
+ i = 0
+ for (const [, connections] of outputsGroupedByOutput.entries()) {
+ // Special handling: Subgraph output node
+ i++
+ for (const connection of connections) {
+ const { input, inputNode, link, subgraphOutput } = connection
+ if (link.target_id === SUBGRAPH_OUTPUT_ID) {
+ link.origin_id = subgraphNode.id
+ link.origin_slot = i - 1
+ this.links.set(link.id, link)
+ if (subgraphOutput instanceof SubgraphOutput) {
+ subgraphOutput.connect(subgraphNode.findOutputSlotByType(link.type, true, true), subgraphNode, link.parentId)
+ } else {
+ throw new TypeError("Subgraph input node is not a SubgraphInput")
+ }
+ continue
+ }
+
+ if (!input || !inputNode) {
+ console.warn("Convert to Subgraph reconnect: Failed to resolve output link", connection)
+ continue
+ }
+
+ const output = subgraphNode.outputs[i - 1]
+ subgraphNode.connectSlots(
+ output,
+ inputNode,
+ input,
+ link.parentId,
+ )
+ }
+ }
+
+ return { subgraph, node: subgraphNode as SubgraphNode }
+ }
+
+ /**
+ * Resolve a path of subgraph node IDs into a list of subgraph nodes.
+ * Not intended to be run from subgraphs.
+ * @param nodeIds An ordered list of node IDs, from the root graph to the most nested subgraph node
+ * @returns An ordered list of nested subgraph nodes.
+ */
+ resolveSubgraphIdPath(nodeIds: readonly NodeId[]): SubgraphNode[] {
+ const result: SubgraphNode[] = []
+ let currentGraph: GraphOrSubgraph = this.rootGraph
+
+ for (const nodeId of nodeIds) {
+ const node: LGraphNode | null = currentGraph.getNodeById(nodeId)
+ if (!node) throw new Error(`Node [${nodeId}] not found. ID Path: ${nodeIds.join(":")}`)
+ if (!node.isSubgraphNode()) throw new Error(`Node [${nodeId}] is not a SubgraphNode. ID Path: ${nodeIds.join(":")}`)
+
+ result.push(node)
+ currentGraph = node.subgraph
+ }
+
+ return result
+ }
+
+ /**
+ * Creates a Object containing all the info about this graph, it can be serialized
+ * @deprecated Use {@link asSerialisable}, which returns the newer schema version.
+ * @returns value of the node
+ */
+ serialize(option?: { sortNodes: boolean }): ISerialisedGraph {
+ const { config, state, groups, nodes, reroutes, extra, floatingLinks, definitions } = this.asSerialisable(option)
+ const linkArray = [...this._links.values()]
+ const links = linkArray.map(x => x.serialize())
+
+ if (reroutes?.length) {
+ // Link parent IDs cannot go in 0.4 schema arrays
+ extra.linkExtensions = linkArray
+ .filter(x => x.parentId !== undefined)
+ .map(x => ({ id: x.id, parentId: x.parentId }))
+ }
+
+ extra.reroutes = reroutes?.length ? reroutes : undefined
+ return {
+ id: this.id,
+ revision: this.revision,
+ last_node_id: state.lastNodeId,
+ last_link_id: state.lastLinkId,
+ nodes,
+ links,
+ floatingLinks,
+ groups,
+ definitions,
+ config,
+ extra,
+ version: LiteGraph.VERSION,
+ }
+ }
+
+ /** @returns The drag and scale state of the first attached canvas, otherwise `undefined`. */
+ #getDragAndScale(): DragAndScaleState | undefined {
+ const ds = this.list_of_graphcanvas?.at(0)?.ds
+ if (ds) return { scale: ds.scale, offset: ds.offset }
+ }
+
+ /**
+ * Prepares a shallow copy of this object for immediate serialisation or structuredCloning.
+ * The return value should be discarded immediately.
+ * @param options Serialise options = currently `sortNodes: boolean`, whether to sort nodes by ID.
+ * @returns A shallow copy of parts of this graph, with shallow copies of its serialisable objects.
+ * Mutating the properties of the return object may result in changes to your graph.
+ * It is intended for use with {@link structuredClone} or {@link JSON.stringify}.
+ */
+ asSerialisable(options?: { sortNodes: boolean }): SerialisableGraph & Required> {
+ const { id, revision, config, state } = this
+
+ const nodeList = !LiteGraph.use_uuids && options?.sortNodes
+ // @ts-expect-error If LiteGraph.use_uuids is false, ids are numbers.
+ ? [...this._nodes].sort((a, b) => a.id - b.id)
+ : this._nodes
+
+ const nodes = nodeList.map(node => node.serialize())
+ const groups = this._groups.map(x => x.serialize())
+
+ const links = this._links.size ? [...this._links.values()].map(x => x.asSerialisable()) : undefined
+ const floatingLinks = this.floatingLinks.size ? [...this.floatingLinks.values()].map(x => x.asSerialisable()) : undefined
+ const reroutes = this.reroutes.size ? [...this.reroutes.values()].map(x => x.asSerialisable()) : undefined
+
+ // Save scale and offset
+ const extra = { ...this.extra }
+ if (LiteGraph.saveViewportWithGraph) extra.ds = this.#getDragAndScale()
+ if (!extra.ds) delete extra.ds
+
+ const data: ReturnType = {
+ id,
+ revision,
+ version: LGraph.serialisedSchemaVersion,
+ config,
+ state,
+ groups,
+ nodes,
+ links,
+ floatingLinks,
+ reroutes,
+ extra,
+ }
+
+ if (this.isRootGraph && this._subgraphs.size) {
+ const usedSubgraphIds = findUsedSubgraphIds(this, this._subgraphs)
+ const usedSubgraphs = [...this._subgraphs.values()]
+ .filter(subgraph => usedSubgraphIds.has(subgraph.id))
+ .map(x => x.asSerialisable())
+
+ if (usedSubgraphs.length > 0) {
+ data.definitions = { subgraphs: usedSubgraphs }
+ }
+ }
+
+ this.onSerialize?.(data)
+ return data
+ }
+
+ protected _configureBase(data: ISerialisedGraph | SerialisableGraph): void {
+ const { id, extra } = data
+
+ // Create a new graph ID if none is provided
+ if (id) {
+ this.id = id
+ } else if (this.id === zeroUuid) {
+ this.id = createUuidv4()
+ }
+
+ // Extra
+ this.extra = extra ? structuredClone(extra) : {}
+
+ // Ensure auto-generated serialisation data is removed from extra
+ delete this.extra.linkExtensions
+ }
+
+ /**
+ * Configure a graph from a JSON string
+ * @param data The deserialised object to configure this graph from
+ * @param keep_old If `true`, the graph will not be cleared prior to
+ * adding the configuration.
+ */
+ configure(
+ data: ISerialisedGraph | SerialisableGraph,
+ keep_old?: boolean,
+ ): boolean | undefined {
+ const options: LGraphEventMap["configuring"] = {
+ data,
+ clearGraph: !keep_old,
+ }
+ const mayContinue = this.events.dispatch("configuring", options)
+ if (!mayContinue) return
+
+ try {
+ // TODO: Finish typing configure()
+ if (!data) return
+ if (options.clearGraph) this.clear()
+
+ this._configureBase(data)
+
+ let reroutes: SerialisableReroute[] | undefined
+
+ // TODO: Determine whether this should this fall back to 0.4.
+ if (data.version === 0.4) {
+ const { extra } = data
+ // Deprecated - old schema version, links are arrays
+ if (Array.isArray(data.links)) {
+ for (const linkData of data.links) {
+ const link = LLink.createFromArray(linkData)
+ this._links.set(link.id, link)
+ }
+ }
+ // #region `extra` embeds for v0.4
+
+ // LLink parentIds
+ if (Array.isArray(extra?.linkExtensions)) {
+ for (const linkEx of extra.linkExtensions) {
+ const link = this._links.get(linkEx.id)
+ if (link) link.parentId = linkEx.parentId
+ }
+ }
+
+ // Reroutes
+ reroutes = extra?.reroutes
+
+ // #endregion `extra` embeds for v0.4
+ } else {
+ // New schema - one version so far, no check required.
+
+ // State
+ if (data.state) {
+ const { lastGroupId, lastLinkId, lastNodeId, lastRerouteId } = data.state
+ const { state } = this
+ if (lastGroupId != null) state.lastGroupId = lastGroupId
+ if (lastLinkId != null) state.lastLinkId = lastLinkId
+ if (lastNodeId != null) state.lastNodeId = lastNodeId
+ if (lastRerouteId != null) state.lastRerouteId = lastRerouteId
+ }
+
+ // Links
+ if (Array.isArray(data.links)) {
+ for (const linkData of data.links) {
+ const link = LLink.create(linkData)
+ this._links.set(link.id, link)
+ }
+ }
+
+ reroutes = data.reroutes
+ }
+
+ // Reroutes
+ if (Array.isArray(reroutes)) {
+ for (const rerouteData of reroutes) {
+ this.setReroute(rerouteData)
+ }
+ }
+
+ const nodesData = data.nodes
+
+ // copy all stored fields
+ for (const i in data) {
+ if (LGraph.ConfigureProperties.has(i)) continue
+
+ // @ts-expect-error #574 Legacy property assignment
+ this[i] = data[i]
+ }
+
+ // Subgraph definitions
+ const subgraphs = data.definitions?.subgraphs
+ if (subgraphs) {
+ for (const subgraph of subgraphs) this.createSubgraph(subgraph)
+ for (const subgraph of subgraphs) this.subgraphs.get(subgraph.id)?.configure(subgraph)
+ }
+
+ let error = false
+ const nodeDataMap = new Map()
+
+ // create nodes
+ this._nodes = []
+ if (nodesData) {
+ for (const n_info of nodesData) {
+ // stored info
+ let node = LiteGraph.createNode(String(n_info.type), n_info.title)
+ if (!node) {
+ if (LiteGraph.debug) console.log("Node not found or has errors:", n_info.type)
+
+ // in case of error we create a replacement node to avoid losing info
+ node = new LGraphNode("")
+ node.last_serialization = n_info
+ node.has_errors = true
+ error = true
+ // continue;
+ }
+
+ // id it or it will create a new id
+ node.id = n_info.id
+ // add before configure, otherwise configure cannot create links
+ this.add(node, true)
+ nodeDataMap.set(node.id, n_info)
+ }
+
+ // configure nodes afterwards so they can reach each other
+ for (const [id, nodeData] of nodeDataMap) {
+ this.getNodeById(id)?.configure(nodeData)
+ }
+ }
+
+ // Floating links
+ if (Array.isArray(data.floatingLinks)) {
+ for (const linkData of data.floatingLinks) {
+ const floatingLink = LLink.create(linkData)
+ this.addFloatingLink(floatingLink)
+
+ if (floatingLink.id > this.#lastFloatingLinkId) this.#lastFloatingLinkId = floatingLink.id
+ }
+ }
+
+ // Drop broken reroutes
+ for (const reroute of this.reroutes.values()) {
+ // Drop broken links, and ignore reroutes with no valid links
+ if (!reroute.validateLinks(this._links, this.floatingLinks)) {
+ this.reroutes.delete(reroute.id)
+ }
+ }
+
+ // groups
+ this._groups.length = 0
+ const groupData = data.groups
+ if (groupData) {
+ for (const data of groupData) {
+ // TODO: Search/remove these global object refs
+ const group = new LiteGraph.LGraphGroup()
+ group.configure(data)
+ this.add(group)
+ }
+ }
+
+ this.updateExecutionOrder()
+
+ this.onConfigure?.(data)
+ this._version++
+
+ // Ensure the primary canvas is set to the correct graph
+ const { primaryCanvas } = this
+ const subgraphId = primaryCanvas?.subgraph?.id
+ if (subgraphId) {
+ const subgraph = this.subgraphs.get(subgraphId)
+ if (subgraph) {
+ primaryCanvas.setGraph(subgraph)
+ } else {
+ primaryCanvas.setGraph(this)
+ }
+ }
+
+ this.setDirtyCanvas(true, true)
+ return error
+ } finally {
+ this.events.dispatch("configured")
+ }
+ }
+
+ #canvas?: LGraphCanvas
+ get primaryCanvas(): LGraphCanvas | undefined {
+ return this.rootGraph.#canvas
+ }
+
+ set primaryCanvas(canvas: LGraphCanvas) {
+ this.rootGraph.#canvas = canvas
+ }
+
+ load(url: string | Blob | URL | File, callback: () => void) {
+ const that = this
+
+ // from file
+ if (url instanceof Blob || url instanceof File) {
+ const reader = new FileReader()
+ reader.addEventListener("load", function (event) {
+ const result = stringOrEmpty(event.target?.result)
+ const data = JSON.parse(result)
+ that.configure(data)
+ callback?.()
+ })
+
+ reader.readAsText(url)
+ return
+ }
+
+ // is a string, then an URL
+ const req = new XMLHttpRequest()
+ req.open("GET", url, true)
+ req.send(null)
+ req.addEventListener("load", function () {
+ if (req.status !== 200) {
+ console.error("Error loading graph:", req.status, req.response)
+ return
+ }
+ const data = JSON.parse(req.response)
+ that.configure(data)
+ callback?.()
+ })
+ req.addEventListener("error", (err) => {
+ console.error("Error loading graph:", err)
+ })
+ }
+}
diff --git a/src/lib/litegraph/src/LGraphBadge.ts b/src/lib/litegraph/src/LGraphBadge.ts
new file mode 100644
index 0000000000..8e613c2af9
--- /dev/null
+++ b/src/lib/litegraph/src/LGraphBadge.ts
@@ -0,0 +1,126 @@
+import { LGraphIcon, type LGraphIconOptions } from "./LGraphIcon"
+
+export enum BadgePosition {
+ TopLeft = "top-left",
+ TopRight = "top-right",
+}
+
+export interface LGraphBadgeOptions {
+ text: string
+ fgColor?: string
+ bgColor?: string
+ fontSize?: number
+ padding?: number
+ height?: number
+ cornerRadius?: number
+ iconOptions?: LGraphIconOptions
+ xOffset?: number
+ yOffset?: number
+}
+
+export class LGraphBadge {
+ text: string
+ fgColor: string
+ bgColor: string
+ fontSize: number
+ padding: number
+ height: number
+ cornerRadius: number
+ icon?: LGraphIcon
+ xOffset: number
+ yOffset: number
+
+ constructor({
+ text,
+ fgColor = "white",
+ bgColor = "#0F1F0F",
+ fontSize = 12,
+ padding = 6,
+ height = 20,
+ cornerRadius = 5,
+ iconOptions,
+ xOffset = 0,
+ yOffset = 0,
+ }: LGraphBadgeOptions) {
+ this.text = text
+ this.fgColor = fgColor
+ this.bgColor = bgColor
+ this.fontSize = fontSize
+ this.padding = padding
+ this.height = height
+ this.cornerRadius = cornerRadius
+ if (iconOptions) {
+ this.icon = new LGraphIcon(iconOptions)
+ }
+ this.xOffset = xOffset
+ this.yOffset = yOffset
+ }
+
+ get visible() {
+ return (this.text?.length ?? 0) > 0 || !!this.icon
+ }
+
+ getWidth(ctx: CanvasRenderingContext2D) {
+ if (!this.visible) return 0
+ const { font } = ctx
+ let iconWidth = 0
+ if (this.icon) {
+ ctx.font = `${this.icon.fontSize}px '${this.icon.fontFamily}'`
+ iconWidth = ctx.measureText(this.icon.unicode).width + this.padding
+ }
+ ctx.font = `${this.fontSize}px sans-serif`
+ const textWidth = this.text ? ctx.measureText(this.text).width : 0
+ ctx.font = font
+ return iconWidth + textWidth + this.padding * 2
+ }
+
+ draw(
+ ctx: CanvasRenderingContext2D,
+ x: number,
+ y: number,
+ ): void {
+ if (!this.visible) return
+
+ x += this.xOffset
+ y += this.yOffset
+
+ const { font, fillStyle, textBaseline, textAlign } = ctx
+
+ ctx.font = `${this.fontSize}px sans-serif`
+ const badgeWidth = this.getWidth(ctx)
+ const badgeX = 0
+
+ // Draw badge background
+ ctx.fillStyle = this.bgColor
+ ctx.beginPath()
+ if (ctx.roundRect) {
+ ctx.roundRect(x + badgeX, y, badgeWidth, this.height, this.cornerRadius)
+ } else {
+ // Fallback for browsers that don't support roundRect
+ ctx.rect(x + badgeX, y, badgeWidth, this.height)
+ }
+ ctx.fill()
+
+ let drawX = x + badgeX + this.padding
+ const centerY = y + this.height / 2
+
+ // Draw icon if present
+ if (this.icon) {
+ this.icon.draw(ctx, drawX, centerY)
+ drawX += this.icon.fontSize + this.padding / 2 + 4
+ }
+
+ // Draw badge text
+ if (this.text) {
+ ctx.fillStyle = this.fgColor
+ ctx.textBaseline = "middle"
+ ctx.textAlign = "left"
+ ctx.fillText(this.text, drawX, centerY + 1)
+ }
+
+ ctx.font = font
+ ctx.fillStyle = fillStyle
+ ctx.textBaseline = textBaseline
+ ctx.textAlign = textAlign
+ }
+}
diff --git a/src/lib/litegraph/src/LGraphButton.ts b/src/lib/litegraph/src/LGraphButton.ts
new file mode 100644
index 0000000000..1ee27c6f82
--- /dev/null
+++ b/src/lib/litegraph/src/LGraphButton.ts
@@ -0,0 +1,89 @@
+import { Rectangle } from "./infrastructure/Rectangle"
+import { LGraphBadge, type LGraphBadgeOptions } from "./LGraphBadge"
+
+export interface LGraphButtonOptions extends LGraphBadgeOptions {
+ name?: string // To identify the button
+}
+
+export class LGraphButton extends LGraphBadge {
+ name?: string
+ _last_area: Rectangle = new Rectangle()
+
+ constructor(options: LGraphButtonOptions) {
+ super(options)
+ this.name = options.name
+ }
+
+ override getWidth(ctx: CanvasRenderingContext2D): number {
+ if (!this.visible) return 0
+
+ const { font } = ctx
+ ctx.font = `${this.fontSize}px 'PrimeIcons'`
+
+ // For icon buttons, just measure the text width without padding
+ const textWidth = this.text ? ctx.measureText(this.text).width : 0
+
+ ctx.font = font
+ return textWidth
+ }
+
+ /**
+ * @internal
+ *
+ * Draws the button and updates its last rendered area for hit detection.
+ * @param ctx The canvas rendering context.
+ * @param x The x-coordinate to draw the button at.
+ * @param y The y-coordinate to draw the button at.
+ */
+ override draw(ctx: CanvasRenderingContext2D, x: number, y: number): void {
+ if (!this.visible) {
+ return
+ }
+
+ const width = this.getWidth(ctx)
+
+ // Update the hit area
+ this._last_area[0] = x + this.xOffset
+ this._last_area[1] = y + this.yOffset
+ this._last_area[2] = width
+ this._last_area[3] = this.height
+
+ // Custom drawing for buttons - no background, just icon/text
+ const adjustedX = x + this.xOffset
+ const adjustedY = y + this.yOffset
+
+ const { font, fillStyle, textBaseline, textAlign } = ctx
+
+ // Use the same color as the title text (usually white)
+ const titleTextColor = ctx.fillStyle || "white"
+
+ // Draw as icon-only without background
+ ctx.font = `${this.fontSize}px 'PrimeIcons'`
+ ctx.fillStyle = titleTextColor
+ ctx.textBaseline = "middle"
+ ctx.textAlign = "center"
+
+ const centerX = adjustedX + width / 2
+ const centerY = adjustedY + this.height / 2
+
+ if (this.text) {
+ ctx.fillText(this.text, centerX, centerY)
+ }
+
+ // Restore context
+ ctx.font = font
+ ctx.fillStyle = fillStyle
+ ctx.textBaseline = textBaseline
+ ctx.textAlign = textAlign
+ }
+
+ /**
+ * Checks if a point is inside the button's last rendered area.
+ * @param x The x-coordinate of the point.
+ * @param y The y-coordinate of the point.
+ * @returns `true` if the point is inside the button, otherwise `false`.
+ */
+ isPointInside(x: number, y: number): boolean {
+ return this._last_area.containsPoint([x, y])
+ }
+}
diff --git a/src/lib/litegraph/src/LGraphCanvas.ts b/src/lib/litegraph/src/LGraphCanvas.ts
new file mode 100644
index 0000000000..d5cc1d150f
--- /dev/null
+++ b/src/lib/litegraph/src/LGraphCanvas.ts
@@ -0,0 +1,7812 @@
+import type { ContextMenu } from "./ContextMenu"
+import type { CustomEventDispatcher, ICustomEventTarget } from "./infrastructure/CustomEventTarget"
+import type { LGraphCanvasEventMap } from "./infrastructure/LGraphCanvasEventMap"
+import type {
+ CanvasColour,
+ ColorOption,
+ ConnectingLink,
+ ContextMenuDivElement,
+ DefaultConnectionColors,
+ Dictionary,
+ Direction,
+ IBoundaryNodes,
+ IColorable,
+ IContextMenuOptions,
+ IContextMenuValue,
+ INodeInputSlot,
+ INodeOutputSlot,
+ INodeSlot,
+ INodeSlotContextItem,
+ ISlotType,
+ LinkSegment,
+ NullableProperties,
+ Point,
+ Positionable,
+ ReadOnlyPoint,
+ ReadOnlyRect,
+ Rect,
+ Size,
+} from "./interfaces"
+import type { LGraph } from "./LGraph"
+import type {
+ CanvasPointerEvent,
+ CanvasPointerExtensions,
+} from "./types/events"
+import type { ClipboardItems, SubgraphIO } from "./types/serialisation"
+import type { NeverNever } from "./types/utility"
+import type { PickNevers } from "./types/utility"
+import type { IBaseWidget } from "./types/widgets"
+import type { UUID } from "./utils/uuid"
+
+import { LinkConnector } from "@/canvas/LinkConnector"
+
+import { isOverNodeInput, isOverNodeOutput } from "./canvas/measureSlots"
+import { CanvasPointer } from "./CanvasPointer"
+import { type AnimationOptions, DragAndScale } from "./DragAndScale"
+import { strokeShape } from "./draw"
+import { NullGraphError } from "./infrastructure/NullGraphError"
+import { LGraphGroup } from "./LGraphGroup"
+import { LGraphNode, type NodeId, type NodeProperty } from "./LGraphNode"
+import { createUuidv4, LiteGraph, Rectangle, SubgraphNode } from "./litegraph"
+import { type LinkId, LLink } from "./LLink"
+import {
+ containsRect,
+ createBounds,
+ distance,
+ findPointOnCurve,
+ isInRect,
+ isInRectangle,
+ isPointInRect,
+ overlapBounding,
+ snapPoint,
+} from "./measure"
+import { NodeInputSlot } from "./node/NodeInputSlot"
+import { Reroute, type RerouteId } from "./Reroute"
+import { stringOrEmpty } from "./strings"
+import { Subgraph } from "./subgraph/Subgraph"
+import { SubgraphInputNode } from "./subgraph/SubgraphInputNode"
+import { SubgraphIONodeBase } from "./subgraph/SubgraphIONodeBase"
+import { SubgraphOutputNode } from "./subgraph/SubgraphOutputNode"
+import {
+ CanvasItem,
+ LGraphEventMode,
+ LinkDirection,
+ LinkMarkerShape,
+ LinkRenderType,
+ RenderShape,
+ TitleMode,
+} from "./types/globalEnums"
+import { alignNodes, distributeNodes, getBoundaryNodes } from "./utils/arrange"
+import { findFirstNode, getAllNestedItems } from "./utils/collections"
+import { BaseWidget } from "./widgets/BaseWidget"
+import { toConcreteWidget } from "./widgets/widgetMap"
+
+interface IShowSearchOptions {
+ node_to?: LGraphNode | null
+ node_from?: LGraphNode | null
+ slot_from: number | INodeOutputSlot | INodeInputSlot | null | undefined
+ type_filter_in?: ISlotType
+ type_filter_out?: ISlotType | false
+
+ // TODO check for registered_slot_[in/out]_types not empty // this will be checked for functionality enabled : filter on slot type, in and out
+ do_type_filter?: boolean
+ show_general_if_none_on_typefilter?: boolean
+ show_general_after_typefiltered?: boolean
+ hide_on_mouse_leave?: boolean
+ show_all_if_empty?: boolean
+ show_all_on_open?: boolean
+}
+
+interface ICreateNodeOptions {
+ /** input */
+ nodeFrom?: SubgraphInputNode | LGraphNode | null
+ /** input */
+ slotFrom?: number | INodeOutputSlot | INodeInputSlot | SubgraphIO | null
+ /** output */
+ nodeTo?: SubgraphOutputNode | LGraphNode | null
+ /** output */
+ slotTo?: number | INodeOutputSlot | INodeInputSlot | SubgraphIO | null
+ /** pass the event coords */
+
+ /** Create the connection from a reroute */
+ afterRerouteId?: RerouteId
+
+ // FIXME: Should not be optional
+ /** choose a nodetype to add, AUTO to set at first good */
+ nodeType?: string
+ e?: CanvasPointerEvent
+ allow_searchbox?: boolean
+}
+
+interface ICreateDefaultNodeOptions extends ICreateNodeOptions {
+ /** Position of new node */
+ position: Point
+ /** adjust x,y */
+ posAdd?: Point
+ /** alpha, adjust the position x,y based on the new node size w,h */
+ posSizeFix?: Point
+}
+
+interface HasShowSearchCallback {
+ /** See {@link LGraphCanvas.showSearchBox} */
+ showSearchBox: (
+ event: MouseEvent,
+ options?: IShowSearchOptions,
+ ) => HTMLDivElement | void
+}
+
+interface ICloseable {
+ close(): void
+}
+
+interface IDialogExtensions extends ICloseable {
+ modified(): void
+ is_modified: boolean
+}
+
+interface IDialog extends HTMLDivElement, IDialogExtensions {}
+type PromptDialog = Omit
+
+interface IDialogOptions {
+ position?: Point
+ event?: MouseEvent
+ checkForInput?: boolean
+ closeOnLeave?: boolean
+ onclose?(): void
+}
+
+/** @inheritdoc {@link LGraphCanvas.state} */
+export interface LGraphCanvasState {
+ /** {@link Positionable} items are being dragged on the canvas. */
+ draggingItems: boolean
+ /** The canvas itself is being dragged. */
+ draggingCanvas: boolean
+ /** The canvas is read-only, preventing changes to nodes, disconnecting links, moving items, etc. */
+ readOnly: boolean
+
+ /** Bit flags indicating what is currently below the pointer. */
+ hoveringOver: CanvasItem
+ /** If `true`, pointer move events will set the canvas cursor style. */
+ shouldSetCursor: boolean
+
+ /**
+ * Dirty flag indicating that {@link selectedItems} has changed.
+ * Downstream consumers may reset to false once actioned.
+ */
+ selectionChanged: boolean
+}
+
+/**
+ * The items created by a clipboard paste operation.
+ * Includes maps of original copied IDs to newly created items.
+ */
+interface ClipboardPasteResult {
+ /** All successfully created items */
+ created: Positionable[]
+ /** Map: original node IDs to newly created nodes */
+ nodes: Map
+ /** Map: original link IDs to new link IDs */
+ links: Map
+ /** Map: original reroute IDs to newly created reroutes */
+ reroutes: Map
+ /** Map: original subgraph IDs to newly created subgraphs */
+ subgraphs: Map
+}
+
+/** Options for {@link LGraphCanvas.pasteFromClipboard}. */
+interface IPasteFromClipboardOptions {
+ /** If `true`, always attempt to connect inputs of pasted nodes - including to nodes that were not pasted. */
+ connectInputs?: boolean
+ /** The position to paste the items at. */
+ position?: Point
+}
+
+interface ICreatePanelOptions {
+ closable?: any
+ window?: any
+ onOpen?: () => void
+ onClose?: () => void
+ width?: any
+ height?: any
+}
+
+const cursors = {
+ NE: "nesw-resize",
+ SE: "nwse-resize",
+ SW: "nesw-resize",
+ NW: "nwse-resize",
+} as const
+
+/**
+ * This class is in charge of rendering one graph inside a canvas. And provides all the interaction required.
+ * Valid callbacks are: onNodeSelected, onNodeDeselected, onShowNodePanel, onNodeDblClicked
+ */
+export class LGraphCanvas implements CustomEventDispatcher {
+ // Optimised buffers used during rendering
+ static #temp = new Float32Array(4)
+ static #temp_vec2 = new Float32Array(2)
+ static #tmp_area = new Float32Array(4)
+ static #margin_area = new Float32Array(4)
+ static #link_bounding = new Float32Array(4)
+ static #lTempA: Point = new Float32Array(2)
+ static #lTempB: Point = new Float32Array(2)
+ static #lTempC: Point = new Float32Array(2)
+
+ static DEFAULT_BACKGROUND_IMAGE = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAABkCAIAAAD/gAIDAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAQBJREFUeNrs1rEKwjAUhlETUkj3vP9rdmr1Ysammk2w5wdxuLgcMHyptfawuZX4pJSWZTnfnu/lnIe/jNNxHHGNn//HNbbv+4dr6V+11uF527arU7+u63qfa/bnmh8sWLBgwYJlqRf8MEptXPBXJXa37BSl3ixYsGDBMliwFLyCV/DeLIMFCxYsWLBMwSt4Be/NggXLYMGCBUvBK3iNruC9WbBgwYJlsGApeAWv4L1ZBgsWLFiwYJmCV/AK3psFC5bBggULloJX8BpdwXuzYMGCBctgwVLwCl7Be7MMFixYsGDBsu8FH1FaSmExVfAxBa/gvVmwYMGCZbBg/W4vAQYA5tRF9QYlv/QAAAAASUVORK5CYII="
+
+ static DEFAULT_EVENT_LINK_COLOR = "#A86"
+
+ /** Link type to colour dictionary. */
+ static link_type_colors: Dictionary = {
+ "-1": LGraphCanvas.DEFAULT_EVENT_LINK_COLOR,
+ "number": "#AAA",
+ "node": "#DCA",
+ }
+
+ static gradients: Record = {}
+
+ static search_limit = -1
+ static node_colors: Record = {
+ red: { color: "#322", bgcolor: "#533", groupcolor: "#A88" },
+ brown: { color: "#332922", bgcolor: "#593930", groupcolor: "#b06634" },
+ green: { color: "#232", bgcolor: "#353", groupcolor: "#8A8" },
+ blue: { color: "#223", bgcolor: "#335", groupcolor: "#88A" },
+ pale_blue: {
+ color: "#2a363b",
+ bgcolor: "#3f5159",
+ groupcolor: "#3f789e",
+ },
+ cyan: { color: "#233", bgcolor: "#355", groupcolor: "#8AA" },
+ purple: { color: "#323", bgcolor: "#535", groupcolor: "#a1309b" },
+ yellow: { color: "#432", bgcolor: "#653", groupcolor: "#b58b2a" },
+ black: { color: "#222", bgcolor: "#000", groupcolor: "#444" },
+ }
+
+ /**
+ * @internal Exclusively a workaround for design limitation in {@link LGraphNode.computeSize}.
+ */
+ static _measureText?: (text: string, fontStyle?: string) => number
+
+ /**
+ * The state of this canvas, e.g. whether it is being dragged, or read-only.
+ *
+ * Implemented as a POCO that can be proxied without side-effects.
+ */
+ state: LGraphCanvasState = {
+ draggingItems: false,
+ draggingCanvas: false,
+ readOnly: false,
+ hoveringOver: CanvasItem.Nothing,
+ shouldSetCursor: true,
+ selectionChanged: false,
+ }
+
+ #subgraph?: Subgraph
+ get subgraph(): Subgraph | undefined {
+ return this.#subgraph
+ }
+
+ set subgraph(value: Subgraph | undefined) {
+ if (value !== this.#subgraph) {
+ this.#subgraph = value
+ if (value) this.dispatch("litegraph:set-graph", { oldGraph: this.#subgraph, newGraph: value })
+ }
+ }
+
+ /** Dispatches a custom event on the canvas. */
+ dispatch>(type: T, detail: LGraphCanvasEventMap[T]): boolean
+ dispatch>(type: T): boolean
+ dispatch(type: T, detail?: LGraphCanvasEventMap[T]) {
+ const event = new CustomEvent(type as string, { detail, bubbles: true })
+ return this.canvas.dispatchEvent(event)
+ }
+
+ dispatchEvent(type: TEvent, detail: LGraphCanvasEventMap[TEvent]) {
+ this.canvas.dispatchEvent(new CustomEvent(type, { detail }))
+ }
+
+ #updateCursorStyle() {
+ if (!this.state.shouldSetCursor) return
+
+ const crosshairItems =
+ CanvasItem.Node |
+ CanvasItem.RerouteSlot |
+ CanvasItem.SubgraphIoNode |
+ CanvasItem.SubgraphIoSlot
+
+ let cursor = "default"
+ if (this.state.draggingCanvas) {
+ cursor = "grabbing"
+ } else if (this.state.readOnly) {
+ cursor = "grab"
+ } else if (this.pointer.resizeDirection) {
+ cursor = cursors[this.pointer.resizeDirection] ?? cursors.SE
+ } else if (this.state.hoveringOver & crosshairItems) {
+ cursor = "crosshair"
+ } else if (this.state.hoveringOver & CanvasItem.Reroute) {
+ cursor = "grab"
+ }
+
+ this.canvas.style.cursor = cursor
+ }
+
+ // Whether the canvas was previously being dragged prior to pressing space key.
+ // null if space key is not pressed.
+ private _previously_dragging_canvas: boolean | null = null
+
+ // #region Legacy accessors
+ /** @deprecated @inheritdoc {@link LGraphCanvasState.readOnly} */
+ get read_only(): boolean {
+ return this.state.readOnly
+ }
+
+ set read_only(value: boolean) {
+ this.state.readOnly = value
+ this.#updateCursorStyle()
+ }
+
+ get isDragging(): boolean {
+ return this.state.draggingItems
+ }
+
+ set isDragging(value: boolean) {
+ this.state.draggingItems = value
+ }
+
+ get hoveringOver(): CanvasItem {
+ return this.state.hoveringOver
+ }
+
+ set hoveringOver(value: CanvasItem) {
+ this.state.hoveringOver = value
+ this.#updateCursorStyle()
+ }
+
+ /** @deprecated Replace all references with {@link pointer}.{@link CanvasPointer.isDown isDown}. */
+ get pointer_is_down() {
+ return this.pointer.isDown
+ }
+
+ /** @deprecated Replace all references with {@link pointer}.{@link CanvasPointer.isDouble isDouble}. */
+ get pointer_is_double() {
+ return this.pointer.isDouble
+ }
+
+ /** @deprecated @inheritdoc {@link LGraphCanvasState.draggingCanvas} */
+ get dragging_canvas(): boolean {
+ return this.state.draggingCanvas
+ }
+
+ set dragging_canvas(value: boolean) {
+ this.state.draggingCanvas = value
+ this.#updateCursorStyle()
+ }
+
+ /**
+ * @deprecated Use {@link LGraphNode.titleFontStyle} instead.
+ */
+ get title_text_font(): string {
+ return `${LiteGraph.NODE_TEXT_SIZE}px ${LiteGraph.NODE_FONT}`
+ }
+ // #endregion Legacy accessors
+
+ get inner_text_font(): string {
+ return `normal ${LiteGraph.NODE_SUBTEXT_SIZE}px ${LiteGraph.NODE_FONT}`
+ }
+
+ #maximumFrameGap = 0
+ /** Maximum frames per second to render. 0: unlimited. Default: 0 */
+ public get maximumFps() {
+ return this.#maximumFrameGap > Number.EPSILON ? this.#maximumFrameGap / 1000 : 0
+ }
+
+ public set maximumFps(value) {
+ this.#maximumFrameGap = value > Number.EPSILON ? 1000 / value : 0
+ }
+
+ /**
+ * @deprecated Use {@link LiteGraphGlobal.ROUND_RADIUS} instead.
+ */
+ get round_radius() {
+ return LiteGraph.ROUND_RADIUS
+ }
+
+ /**
+ * @deprecated Use {@link LiteGraphGlobal.ROUND_RADIUS} instead.
+ */
+ set round_radius(value: number) {
+ LiteGraph.ROUND_RADIUS = value
+ }
+
+ /**
+ * Render low quality when zoomed out.
+ */
+ get low_quality(): boolean {
+ return this.ds.scale < this.low_quality_zoom_threshold
+ }
+
+ options: {
+ skip_events?: any
+ viewport?: any
+ skip_render?: any
+ autoresize?: any
+ }
+
+ background_image: string
+ readonly ds: DragAndScale
+ readonly pointer: CanvasPointer
+ zoom_modify_alpha: boolean
+ zoom_speed: number
+ node_title_color: string
+ default_link_color: string
+ default_connection_color: {
+ input_off: string
+ input_on: string
+ output_off: string
+ output_on: string
+ }
+
+ default_connection_color_byType: Dictionary
+ default_connection_color_byTypeOff: Dictionary
+
+ /** Gets link colours. Extremely basic impl. until the legacy object dictionaries are removed. */
+ colourGetter: DefaultConnectionColors = {
+ getConnectedColor: (type: string) =>
+ this.default_connection_color_byType[type] ||
+ this.default_connection_color.output_on,
+ getDisconnectedColor: (type: string) =>
+ this.default_connection_color_byTypeOff[type] ||
+ this.default_connection_color_byType[type] ||
+ this.default_connection_color.output_off,
+ }
+
+ highquality_render: boolean
+ use_gradients: boolean
+ editor_alpha: number
+ pause_rendering: boolean
+ clear_background: boolean
+ clear_background_color: string
+ render_only_selected: boolean
+ show_info: boolean
+ allow_dragcanvas: boolean
+ allow_dragnodes: boolean
+ allow_interaction: boolean
+ multi_select: boolean
+ allow_searchbox: boolean
+ allow_reconnect_links: boolean
+ align_to_grid: boolean
+ drag_mode: boolean
+ dragging_rectangle: Rect | null
+ filter?: string | null
+ set_canvas_dirty_on_mouse_event: boolean
+ always_render_background: boolean
+ render_shadows: boolean
+ render_canvas_border: boolean
+ render_connections_shadows: boolean
+ render_connections_border: boolean
+ render_curved_connections: boolean
+ render_connection_arrows: boolean
+ render_collapsed_slots: boolean
+ render_execution_order: boolean
+ render_link_tooltip: boolean
+
+ /** Shape of the markers shown at the midpoint of links. Default: Circle */
+ linkMarkerShape: LinkMarkerShape = LinkMarkerShape.Circle
+ links_render_mode: number
+ /** Zoom threshold for low quality rendering. Zoom below this threshold will render low quality. */
+ low_quality_zoom_threshold: number = 0.6
+ /** mouse in canvas coordinates, where 0,0 is the top-left corner of the blue rectangle */
+ readonly mouse: Point
+ /** mouse in graph coordinates, where 0,0 is the top-left corner of the blue rectangle */
+ readonly graph_mouse: Point
+ /** @deprecated LEGACY: REMOVE THIS, USE {@link graph_mouse} INSTEAD */
+ canvas_mouse: Point
+ /** to personalize the search box */
+ onSearchBox?: (helper: Element, str: string, canvas: LGraphCanvas) => any
+ onSearchBoxSelection?: (name: any, event: any, canvas: LGraphCanvas) => void
+ onMouse?: (e: CanvasPointerEvent) => boolean
+ /** to render background objects (behind nodes and connections) in the canvas affected by transform */
+ onDrawBackground?: (ctx: CanvasRenderingContext2D, visible_area: any) => void
+ /** to render foreground objects (above nodes and connections) in the canvas affected by transform */
+ onDrawForeground?: (arg0: CanvasRenderingContext2D, arg1: any) => void
+ connections_width: number
+ /** The current node being drawn by {@link drawNode}. This should NOT be used to determine the currently selected node. See {@link selectedItems} */
+ current_node: LGraphNode | null
+ /** used for widgets */
+ node_widget?: [LGraphNode, IBaseWidget] | null
+ /** The link to draw a tooltip for. */
+ over_link_center?: LinkSegment
+ last_mouse_position: Point
+ /** The visible area of this canvas. Tightly coupled with {@link ds}. */
+ visible_area: Rectangle
+ /** Contains all links and reroutes that were rendered. Repopulated every render cycle. */
+ renderedPaths: Set = new Set()
+ /** @deprecated Replaced by {@link renderedPaths}, but length is set to 0 by some extensions. */
+ visible_links: LLink[] = []
+ /** @deprecated This array is populated and cleared to support legacy extensions. The contents are ignored by Litegraph. */
+ connecting_links: ConnectingLink[] | null
+ linkConnector = new LinkConnector(links => this.connecting_links = links)
+ /** The viewport of this canvas. Tightly coupled with {@link ds}. */
+ readonly viewport?: Rect
+ autoresize: boolean
+ static active_canvas: LGraphCanvas
+ frame = 0
+ last_draw_time = 0
+ render_time = 0
+ fps = 0
+ /** @deprecated See {@link LGraphCanvas.selectedItems} */
+ selected_nodes: Dictionary = {}
+ /** All selected nodes, groups, and reroutes */
+ selectedItems: Set = new Set()
+ /** The group currently being resized. */
+ resizingGroup: LGraphGroup | null = null
+ /** @deprecated See {@link LGraphCanvas.selectedItems} */
+ selected_group: LGraphGroup | null = null
+ /** The nodes that are currently visible on the canvas. */
+ visible_nodes: LGraphNode[] = []
+ /**
+ * The IDs of the nodes that are currently visible on the canvas. More
+ * performant than {@link visible_nodes} for visibility checks.
+ */
+ #visible_node_ids: Set = new Set()
+ node_over?: LGraphNode
+ node_capturing_input?: LGraphNode | null
+ highlighted_links: Dictionary = {}
+
+ #visibleReroutes: Set = new Set()
+
+ dirty_canvas: boolean = true
+ dirty_bgcanvas: boolean = true
+ /** A map of nodes that require selective-redraw */
+ dirty_nodes = new Map()
+ dirty_area?: Rect | null
+ /** @deprecated Unused */
+ node_in_panel?: LGraphNode | null
+ last_mouse: ReadOnlyPoint = [0, 0]
+ last_mouseclick: number = 0
+ graph: LGraph | Subgraph | null
+ get _graph(): LGraph | Subgraph {
+ if (!this.graph) throw new NullGraphError()
+ return this.graph
+ }
+
+ canvas: HTMLCanvasElement & ICustomEventTarget
+ bgcanvas: HTMLCanvasElement
+ ctx: CanvasRenderingContext2D
+ _events_binded?: boolean
+ _mousedown_callback?(e: PointerEvent): void
+ _mousewheel_callback?(e: WheelEvent): void
+ _mousemove_callback?(e: PointerEvent): void
+ _mouseup_callback?(e: PointerEvent): void
+ _mouseout_callback?(e: PointerEvent): void
+ _mousecancel_callback?(e: PointerEvent): void
+ _key_callback?(e: KeyboardEvent): void
+ bgctx?: CanvasRenderingContext2D | null
+ is_rendering?: boolean
+ /** @deprecated Panels */
+ block_click?: boolean
+ /** @deprecated Panels */
+ last_click_position?: Point | null
+ resizing_node?: LGraphNode | null
+ /** @deprecated See {@link LGraphCanvas.resizingGroup} */
+ selected_group_resizing?: boolean
+ /** @deprecated See {@link pointer}.{@link CanvasPointer.dragStarted dragStarted} */
+ last_mouse_dragging?: boolean
+ onMouseDown?: (arg0: CanvasPointerEvent) => void
+ _highlight_pos?: Point
+ _highlight_input?: INodeInputSlot
+ // TODO: Check if panels are used
+ /** @deprecated Panels */
+ node_panel?: any
+ /** @deprecated Panels */
+ options_panel?: any
+ _bg_img?: HTMLImageElement
+ _pattern?: CanvasPattern
+ _pattern_img?: HTMLImageElement
+ // TODO: This looks like another panel thing
+ prompt_box?: PromptDialog | null
+ search_box?: HTMLDivElement
+ /** @deprecated Panels */
+ SELECTED_NODE?: LGraphNode
+ /** @deprecated Panels */
+ NODEPANEL_IS_OPEN?: boolean
+
+ /** Once per frame check of snap to grid value. @todo Update on change. */
+ #snapToGrid?: number
+ /** Set on keydown, keyup. @todo */
+ #shiftDown: boolean = false
+
+ /** If true, enable drag zoom. Ctrl+Shift+Drag Up/Down: zoom canvas. */
+ dragZoomEnabled: boolean = false
+ /** The start position of the drag zoom. */
+ #dragZoomStart: { pos: Point, scale: number } | null = null
+
+ getMenuOptions?(): IContextMenuValue[]
+ getExtraMenuOptions?(
+ canvas: LGraphCanvas,
+ options: IContextMenuValue[],
+ ): IContextMenuValue[]
+ static active_node: LGraphNode
+ /** called before modifying the graph */
+ onBeforeChange?(graph: LGraph): void
+ /** called after modifying the graph */
+ onAfterChange?(graph: LGraph): void
+ onClear?: () => void
+ /** called after moving a node @deprecated Does not handle multi-node move, and can return the wrong node. */
+ onNodeMoved?: (node_dragged: LGraphNode | undefined) => void
+ /** @deprecated Called with the deprecated {@link selected_nodes} when the selection changes. Replacement not yet impl. */
+ onSelectionChange?: (selected: Dictionary) => void
+ /** called when rendering a tooltip */
+ onDrawLinkTooltip?: (
+ ctx: CanvasRenderingContext2D,
+ link: LLink | null,
+ canvas?: LGraphCanvas,
+ ) => boolean
+
+ /** to render foreground objects not affected by transform (for GUIs) */
+ onDrawOverlay?: (ctx: CanvasRenderingContext2D) => void
+ onRenderBackground?: (
+ canvas: HTMLCanvasElement,
+ ctx: CanvasRenderingContext2D,
+ ) => boolean
+
+ onNodeDblClicked?: (n: LGraphNode) => void
+ onShowNodePanel?: (n: LGraphNode) => void
+ onNodeSelected?: (node: LGraphNode) => void
+ onNodeDeselected?: (node: LGraphNode) => void
+ onRender?: (canvas: HTMLCanvasElement, ctx: CanvasRenderingContext2D) => void
+
+ /**
+ * Creates a new instance of LGraphCanvas.
+ * @param canvas The canvas HTML element (or its id) to use, or null / undefined to leave blank.
+ * @param graph The graph that owns this canvas.
+ * @param options
+ */
+ constructor(
+ canvas: HTMLCanvasElement,
+ graph: LGraph,
+ options?: LGraphCanvas["options"],
+ ) {
+ options ||= {}
+ this.options = options
+
+ // if(graph === undefined)
+ // throw ("No graph assigned");
+ this.background_image = LGraphCanvas.DEFAULT_BACKGROUND_IMAGE
+
+ this.ds = new DragAndScale(canvas)
+ this.pointer = new CanvasPointer(canvas)
+
+ this.linkConnector.events.addEventListener("link-created", () => this.#dirty())
+
+ // @deprecated Workaround: Keep until connecting_links is removed.
+ this.linkConnector.events.addEventListener("reset", () => {
+ this.connecting_links = null
+ this.dirty_bgcanvas = true
+ })
+
+ // Dropped a link on the canvas
+ this.linkConnector.events.addEventListener("dropped-on-canvas", (customEvent) => {
+ if (!this.connecting_links) return
+
+ const e = customEvent.detail
+ this.emitEvent({
+ subType: "empty-release",
+ originalEvent: e,
+ linkReleaseContext: { links: this.connecting_links },
+ })
+
+ const firstLink = this.linkConnector.renderLinks[0]
+
+ // No longer in use
+ // add menu when releasing link in empty space
+ if (LiteGraph.release_link_on_empty_shows_menu) {
+ const linkReleaseContext = this.linkConnector.state.connectingTo === "input"
+ ? {
+ node_from: firstLink.node as LGraphNode,
+ slot_from: firstLink.fromSlot as INodeOutputSlot,
+ type_filter_in: firstLink.fromSlot.type,
+ }
+ : {
+ node_to: firstLink.node as LGraphNode,
+ slot_to: firstLink.fromSlot as INodeInputSlot,
+ type_filter_out: firstLink.fromSlot.type,
+ }
+
+ const afterRerouteId = firstLink.fromReroute?.id
+
+ if ("shiftKey" in e && e.shiftKey) {
+ if (this.allow_searchbox) {
+ this.showSearchBox(e as unknown as MouseEvent, linkReleaseContext as IShowSearchOptions)
+ }
+ } else if (this.linkConnector.state.connectingTo === "input") {
+ this.showConnectionMenu({ nodeFrom: firstLink.node as LGraphNode, slotFrom: firstLink.fromSlot as INodeOutputSlot, e, afterRerouteId })
+ } else {
+ this.showConnectionMenu({ nodeTo: firstLink.node as LGraphNode, slotTo: firstLink.fromSlot as INodeInputSlot, e, afterRerouteId })
+ }
+ }
+ })
+
+ // otherwise it generates ugly patterns when scaling down too much
+ this.zoom_modify_alpha = true
+ // in range (1.01, 2.5). Less than 1 will invert the zoom direction
+ this.zoom_speed = 1.1
+
+ this.node_title_color = LiteGraph.NODE_TITLE_COLOR
+ this.default_link_color = LiteGraph.LINK_COLOR
+ this.default_connection_color = {
+ input_off: "#778",
+ input_on: "#7F7",
+ output_off: "#778",
+ output_on: "#7F7",
+ }
+ this.default_connection_color_byType = {
+ /* number: "#7F7",
+ string: "#77F",
+ boolean: "#F77", */
+ }
+ this.default_connection_color_byTypeOff = {
+ /* number: "#474",
+ string: "#447",
+ boolean: "#744", */
+ }
+
+ this.highquality_render = true
+ // set to true to render titlebar with gradients
+ this.use_gradients = false
+ // used for transition
+ this.editor_alpha = 1
+ this.pause_rendering = false
+ this.clear_background = true
+ this.clear_background_color = "#222"
+
+ this.render_only_selected = true
+ this.show_info = true
+ this.allow_dragcanvas = true
+ this.allow_dragnodes = true
+ // allow to control widgets, buttons, collapse, etc
+ this.allow_interaction = true
+ // allow selecting multi nodes without pressing extra keys
+ this.multi_select = false
+ this.allow_searchbox = true
+ // allows to change a connection with having to redo it again
+ this.allow_reconnect_links = true
+ // snap to grid
+ this.align_to_grid = false
+
+ this.drag_mode = false
+ this.dragging_rectangle = null
+
+ // allows to filter to only accept some type of nodes in a graph
+ this.filter = null
+
+ // forces to redraw the canvas on mouse events (except move)
+ this.set_canvas_dirty_on_mouse_event = true
+ this.always_render_background = false
+ this.render_shadows = true
+ this.render_canvas_border = true
+ // too much cpu
+ this.render_connections_shadows = false
+ this.render_connections_border = true
+ this.render_curved_connections = false
+ this.render_connection_arrows = false
+ this.render_collapsed_slots = true
+ this.render_execution_order = false
+ this.render_link_tooltip = true
+
+ this.links_render_mode = LinkRenderType.SPLINE_LINK
+
+ this.mouse = [0, 0]
+ this.graph_mouse = [0, 0]
+ this.canvas_mouse = this.graph_mouse
+
+ this.connections_width = 3
+
+ this.current_node = null
+ this.node_widget = null
+ this.last_mouse_position = [0, 0]
+ this.visible_area = this.ds.visible_area
+ // Explicitly null-checked
+ this.connecting_links = null
+
+ // to constraint render area to a portion of the canvas
+ this.viewport = options.viewport || null
+
+ // link canvas and graph
+ this.graph = graph
+ graph?.attachCanvas(this)
+
+ // TypeScript strict workaround: cannot use method to initialize properties.
+ this.canvas = undefined!
+ this.bgcanvas = undefined!
+ this.ctx = undefined!
+
+ this.setCanvas(canvas, options.skip_events)
+ this.clear()
+
+ LGraphCanvas._measureText = (text: string, fontStyle = this.inner_text_font) => {
+ const { ctx } = this
+ const { font } = ctx
+ try {
+ ctx.font = fontStyle
+ return ctx.measureText(text).width
+ } finally {
+ ctx.font = font
+ }
+ }
+
+ if (!options.skip_render) {
+ this.startRendering()
+ }
+
+ this.autoresize = options.autoresize
+ }
+
+ static onGroupAdd(info: unknown, entry: unknown, mouse_event: MouseEvent): void {
+ const canvas = LGraphCanvas.active_canvas
+
+ const group = new LiteGraph.LGraphGroup()
+ group.pos = canvas.convertEventToCanvasOffset(mouse_event)
+ if (!canvas.graph) throw new NullGraphError()
+ canvas.graph.add(group)
+ }
+
+ /**
+ * @deprecated Functionality moved to {@link getBoundaryNodes}. The new function returns null on failure, instead of an object with all null properties.
+ * Determines the furthest nodes in each direction
+ * @param nodes the nodes to from which boundary nodes will be extracted
+ * @returns
+ */
+ static getBoundaryNodes(
+ nodes: LGraphNode[] | Dictionary,
+ ): NullableProperties {
+ const _nodes = Array.isArray(nodes) ? nodes : Object.values(nodes)
+ return (
+ getBoundaryNodes(_nodes) ?? {
+ top: null,
+ right: null,
+ bottom: null,
+ left: null,
+ }
+ )
+ }
+
+ /**
+ * @deprecated Functionality moved to {@link alignNodes}. The new function does not set dirty canvas.
+ * @param nodes a list of nodes
+ * @param direction Direction to align the nodes
+ * @param align_to Node to align to (if null, align to the furthest node in the given direction)
+ */
+ static alignNodes(
+ nodes: Dictionary,
+ direction: Direction,
+ align_to?: LGraphNode,
+ ): void {
+ alignNodes(Object.values(nodes), direction, align_to)
+ LGraphCanvas.active_canvas.setDirty(true, true)
+ }
+
+ static onNodeAlign(
+ value: IContextMenuValue,
+ options: IContextMenuOptions,
+ event: MouseEvent,
+ prev_menu: ContextMenu,
+ node: LGraphNode,
+ ): void {
+ new LiteGraph.ContextMenu(["Top", "Bottom", "Left", "Right"], {
+ event,
+ callback: inner_clicked,
+ parentMenu: prev_menu,
+ })
+
+ function inner_clicked(value: string) {
+ alignNodes(
+ Object.values(LGraphCanvas.active_canvas.selected_nodes),
+ value.toLowerCase() as Direction,
+ node,
+ )
+ LGraphCanvas.active_canvas.setDirty(true, true)
+ }
+ }
+
+ static onGroupAlign(
+ value: IContextMenuValue,
+ options: IContextMenuOptions,
+ event: MouseEvent,
+ prev_menu: ContextMenu,
+ ): void {
+ new LiteGraph.ContextMenu(["Top", "Bottom", "Left", "Right"], {
+ event,
+ callback: inner_clicked,
+ parentMenu: prev_menu,
+ })
+
+ function inner_clicked(value: string) {
+ alignNodes(
+ Object.values(LGraphCanvas.active_canvas.selected_nodes),
+ value.toLowerCase() as Direction,
+ )
+ LGraphCanvas.active_canvas.setDirty(true, true)
+ }
+ }
+
+ static createDistributeMenu(
+ value: IContextMenuValue,
+ options: IContextMenuOptions,
+ event: MouseEvent,
+ prev_menu: ContextMenu,
+ ): void {
+ new LiteGraph.ContextMenu(["Vertically", "Horizontally"], {
+ event,
+ callback: inner_clicked,
+ parentMenu: prev_menu,
+ })
+
+ function inner_clicked(value: string) {
+ const canvas = LGraphCanvas.active_canvas
+ distributeNodes(Object.values(canvas.selected_nodes), value === "Horizontally")
+ canvas.setDirty(true, true)
+ }
+ }
+
+ static onMenuAdd(
+ value: unknown,
+ options: unknown,
+ e: MouseEvent,
+ prev_menu?: ContextMenu,
+ callback?: (node: LGraphNode | null) => void,
+ ): boolean | undefined {
+ const canvas = LGraphCanvas.active_canvas
+ const ref_window = canvas.getCanvasWindow()
+ const { graph } = canvas
+ if (!graph) return
+
+ inner_onMenuAdded("", prev_menu)
+ return false
+
+ type AddNodeMenu = Omit, "callback"> & {
+ callback: (
+ value: { value: string },
+ event: Event,
+ mouseEvent: MouseEvent,
+ contextMenu: ContextMenu
+ ) => void
+ }
+
+ function inner_onMenuAdded(base_category: string, prev_menu?: ContextMenu): void {
+ if (!graph) return
+
+ const categories = LiteGraph
+ .getNodeTypesCategories(canvas.filter || graph.filter)
+ .filter(category => category.startsWith(base_category))
+ const entries: AddNodeMenu[] = []
+
+ for (const category of categories) {
+ if (!category) continue
+
+ const base_category_regex = new RegExp(`^(${base_category})`)
+ const category_name = category
+ .replace(base_category_regex, "")
+ .split("/", 1)[0]
+ const category_path =
+ base_category === ""
+ ? `${category_name}/`
+ : `${base_category}${category_name}/`
+
+ let name = category_name
+ // in case it has a namespace like "shader::math/rand" it hides the namespace
+ if (name.includes("::")) name = name.split("::", 2)[1]
+
+ const index = entries.findIndex(entry => entry.value === category_path)
+ if (index === -1) {
+ entries.push({
+ value: category_path,
+ content: name,
+ has_submenu: true,
+ callback: function (value, event, mouseEvent, contextMenu) {
+ inner_onMenuAdded(value.value, contextMenu)
+ },
+ })
+ }
+ }
+
+ const nodes = LiteGraph.getNodeTypesInCategory(
+ base_category.slice(0, -1),
+ canvas.filter || graph.filter,
+ )
+
+ for (const node of nodes) {
+ if (node.skip_list) continue
+
+ const entry: AddNodeMenu = {
+ value: node.type,
+ content: node.title,
+ has_submenu: false,
+ callback: function (value, event, mouseEvent, contextMenu) {
+ if (!canvas.graph) throw new NullGraphError()
+
+ const first_event = contextMenu.getFirstEvent()
+ canvas.graph.beforeChange()
+ const node = LiteGraph.createNode(value.value)
+ if (node) {
+ if (!first_event) throw new TypeError("Context menu event was null. This should not occur in normal usage.")
+ node.pos = canvas.convertEventToCanvasOffset(first_event)
+ canvas.graph.add(node)
+ } else {
+ console.warn("Failed to create node of type:", value.value)
+ }
+
+ callback?.(node)
+ canvas.graph.afterChange()
+ },
+ }
+
+ entries.push(entry)
+ }
+
+ // @ts-expect-error Remove param ref_window - unused
+ new LiteGraph.ContextMenu(entries, { event: e, parentMenu: prev_menu }, ref_window)
+ }
+ }
+
+ static onMenuCollapseAll() {}
+ static onMenuNodeEdit() {}
+
+ /** @param _options Parameter is never used */
+ static showMenuNodeOptionalOutputs(
+ v: unknown,
+ /** Unused - immediately overwritten */
+ _options: INodeOutputSlot[],
+ e: MouseEvent,
+ prev_menu: ContextMenu,
+ node: LGraphNode,
+ ): boolean | undefined {
+ if (!node) return
+
+ const canvas = LGraphCanvas.active_canvas
+
+ let entries: (IContextMenuValue | null)[] = []
+
+ if (LiteGraph.do_add_triggers_slots && node.findOutputSlot("onExecuted") == -1) {
+ entries.push({ content: "On Executed", value: ["onExecuted", LiteGraph.EVENT, { nameLocked: true }], className: "event" })
+ }
+ // add callback for modifing the menu elements onMenuNodeOutputs
+ const retEntries = node.onMenuNodeOutputs?.(entries)
+ if (retEntries) entries = retEntries
+
+ if (!entries.length) return
+
+ new LiteGraph.ContextMenu(
+ entries,
+ {
+ event: e,
+ callback: inner_clicked,
+ parentMenu: prev_menu,
+ node,
+ },
+ )
+
+ function inner_clicked(this: ContextMenuDivElement, v: IContextMenuValue, e: any, prev: any) {
+ if (!node) return
+
+ // TODO: This is a static method, so the below "that" appears broken.
+ if (v.callback) v.callback.call(this, node, v, e, prev)
+
+ if (!v.value) return
+
+ const value = v.value[1]
+
+ if (value &&
+ (typeof value === "object" || Array.isArray(value))) {
+ // submenu why?
+ const entries = []
+ for (const i in value) {
+ entries.push({ content: i, value: value[i] })
+ }
+ new LiteGraph.ContextMenu(entries, {
+ event: e,
+ callback: inner_clicked,
+ parentMenu: prev_menu,
+ node,
+ })
+ return false
+ }
+
+ const { graph } = node
+ if (!graph) throw new NullGraphError()
+
+ graph.beforeChange()
+ node.addOutput(v.value[0], v.value[1], v.value[2])
+
+ // a callback to the node when adding a slot
+ node.onNodeOutputAdd?.(v.value)
+ canvas.setDirty(true, true)
+ graph.afterChange()
+ }
+
+ return false
+ }
+
+ /** @param value Parameter is never used */
+ static onShowMenuNodeProperties(
+ value: NodeProperty | undefined,
+ options: unknown,
+ e: MouseEvent,
+ prev_menu: ContextMenu,
+ node: LGraphNode,
+ ): boolean | undefined {
+ if (!node || !node.properties) return
+
+ const canvas = LGraphCanvas.active_canvas
+ const ref_window = canvas.getCanvasWindow()
+
+ const entries: IContextMenuValue[] = []
+ for (const i in node.properties) {
+ value = node.properties[i] !== undefined ? node.properties[i] : " "
+ if (typeof value == "object")
+ value = JSON.stringify(value)
+ const info = node.getPropertyInfo(i)
+ if (info.type == "enum" || info.type == "combo")
+ value = LGraphCanvas.getPropertyPrintableValue(value, info.values)
+
+ // value could contain invalid html characters, clean that
+ value = LGraphCanvas.decodeHTML(stringOrEmpty(value))
+ entries.push({
+ content:
+ `${info.label || i}` +
+ `${value}`,
+ value: i,
+ })
+ }
+ if (!entries.length) {
+ return
+ }
+
+ new LiteGraph.ContextMenu(
+ entries,
+ {
+ event: e,
+ callback: inner_clicked,
+ parentMenu: prev_menu,
+ allow_html: true,
+ node,
+ },
+ // @ts-expect-error Unused
+ ref_window,
+ )
+
+ function inner_clicked(this: ContextMenuDivElement, v: { value: any }) {
+ if (!node) return
+
+ const rect = this.getBoundingClientRect()
+ canvas.showEditPropertyValue(node, v.value, {
+ position: [rect.left, rect.top],
+ })
+ }
+
+ return false
+ }
+
+ /** @deprecated */
+ static decodeHTML(str: string): string {
+ const e = document.createElement("div")
+ e.textContent = str
+ return e.innerHTML
+ }
+
+ static onMenuResizeNode(
+ value: IContextMenuValue,
+ options: IContextMenuOptions,
+ e: MouseEvent,
+ menu: ContextMenu,
+ node: LGraphNode,
+ ): void {
+ if (!node) return
+
+ const fApplyMultiNode = function (node: LGraphNode) {
+ node.setSize(node.computeSize())
+ }
+
+ const canvas = LGraphCanvas.active_canvas
+ if (!canvas.selected_nodes || Object.keys(canvas.selected_nodes).length <= 1) {
+ fApplyMultiNode(node)
+ } else {
+ for (const i in canvas.selected_nodes) {
+ fApplyMultiNode(canvas.selected_nodes[i])
+ }
+ }
+
+ canvas.setDirty(true, true)
+ }
+
+ // TODO refactor :: this is used fot title but not for properties!
+ static onShowPropertyEditor(
+ item: { property: keyof LGraphNode, type: string },
+ options: IContextMenuOptions,
+ e: MouseEvent,
+ menu: ContextMenu,
+ node: LGraphNode,
+ ): void {
+ const property = item.property || "title"
+ const value = node[property]
+
+ const title = document.createElement("span")
+ title.className = "name"
+ title.textContent = property
+
+ const input = document.createElement("input")
+ Object.assign(input, { type: "text", className: "value", autofocus: true })
+
+ const button = document.createElement("button")
+ button.textContent = "OK"
+
+ // TODO refactor :: use createDialog ?
+ const dialog = Object.assign(document.createElement("div"), {
+ is_modified: false,
+ className: "graphdialog",
+ close: () => dialog.remove(),
+ })
+ dialog.append(title, input, button)
+
+ input.value = String(value)
+ input.addEventListener("blur", function () {
+ this.focus()
+ })
+ input.addEventListener("keydown", (e: KeyboardEvent) => {
+ dialog.is_modified = true
+ if (e.key == "Escape") {
+ // ESC
+ dialog.close()
+ } else if (e.key == "Enter") {
+ // save
+ inner()
+ } else if (!e.target || !("localName" in e.target) || e.target.localName != "textarea") {
+ return
+ }
+ e.preventDefault()
+ e.stopPropagation()
+ })
+
+ const canvas = LGraphCanvas.active_canvas
+ const canvasEl = canvas.canvas
+
+ const rect = canvasEl.getBoundingClientRect()
+ const offsetx = rect ? -20 - rect.left : -20
+ const offsety = rect ? -20 - rect.top : -20
+
+ if (e) {
+ dialog.style.left = `${e.clientX + offsetx}px`
+ dialog.style.top = `${e.clientY + offsety}px`
+ } else {
+ dialog.style.left = `${canvasEl.width * 0.5 + offsetx}px`
+ dialog.style.top = `${canvasEl.height * 0.5 + offsety}px`
+ }
+
+ button.addEventListener("click", inner)
+
+ if (canvasEl.parentNode == null) throw new TypeError("canvasEl.parentNode was null")
+ canvasEl.parentNode.append(dialog)
+
+ input.focus()
+
+ let dialogCloseTimer: number
+ dialog.addEventListener("mouseleave", function () {
+ if (LiteGraph.dialog_close_on_mouse_leave) {
+ if (!dialog.is_modified && LiteGraph.dialog_close_on_mouse_leave) {
+ dialogCloseTimer = setTimeout(
+ dialog.close,
+ LiteGraph.dialog_close_on_mouse_leave_delay,
+ )
+ }
+ }
+ })
+ dialog.addEventListener("mouseenter", function () {
+ if (LiteGraph.dialog_close_on_mouse_leave) {
+ if (dialogCloseTimer) clearTimeout(dialogCloseTimer)
+ }
+ })
+
+ function inner() {
+ if (input) setValue(input.value)
+ }
+
+ function setValue(value: NodeProperty) {
+ if (item.type == "Number") {
+ value = Number(value)
+ } else if (item.type == "Boolean") {
+ value = Boolean(value)
+ }
+ // @ts-expect-error Requires refactor.
+ node[property] = value
+ dialog.remove()
+ canvas.setDirty(true, true)
+ }
+ }
+
+ static getPropertyPrintableValue(value: unknown, values: unknown[] | object | undefined): string | undefined {
+ if (!values) return String(value)
+
+ if (Array.isArray(values)) {
+ return String(value)
+ }
+
+ if (typeof values === "object") {
+ let desc_value = ""
+ for (const k in values) {
+ // @ts-expect-error deprecated #578
+ if (values[k] != value) continue
+
+ desc_value = k
+ break
+ }
+ return `${String(value)} (${desc_value})`
+ }
+ }
+
+ static onMenuNodeCollapse(
+ value: IContextMenuValue,
+ options: IContextMenuOptions,
+ e: MouseEvent,
+ menu: ContextMenu,
+ node: LGraphNode,
+ ): void {
+ if (!node.graph) throw new NullGraphError()
+
+ node.graph.beforeChange()
+
+ const fApplyMultiNode = function (node: LGraphNode) {
+ node.collapse()
+ }
+
+ const graphcanvas = LGraphCanvas.active_canvas
+ if (!graphcanvas.selected_nodes || Object.keys(graphcanvas.selected_nodes).length <= 1) {
+ fApplyMultiNode(node)
+ } else {
+ for (const i in graphcanvas.selected_nodes) {
+ fApplyMultiNode(graphcanvas.selected_nodes[i])
+ }
+ }
+
+ node.graph.afterChange()
+ }
+
+ static onMenuToggleAdvanced(
+ value: IContextMenuValue,
+ options: IContextMenuOptions,
+ e: MouseEvent,
+ menu: ContextMenu,
+ node: LGraphNode,
+ ): void {
+ if (!node.graph) throw new NullGraphError()
+
+ node.graph.beforeChange()
+ const fApplyMultiNode = function (node: LGraphNode) {
+ node.toggleAdvanced()
+ }
+
+ const graphcanvas = LGraphCanvas.active_canvas
+ if (!graphcanvas.selected_nodes || Object.keys(graphcanvas.selected_nodes).length <= 1) {
+ fApplyMultiNode(node)
+ } else {
+ for (const i in graphcanvas.selected_nodes) {
+ fApplyMultiNode(graphcanvas.selected_nodes[i])
+ }
+ }
+ node.graph.afterChange()
+ }
+
+ static onMenuNodeMode(
+ value: IContextMenuValue,
+ options: IContextMenuOptions,
+ e: MouseEvent,
+ menu: ContextMenu,
+ node: LGraphNode,
+ ): boolean {
+ new LiteGraph.ContextMenu(
+ LiteGraph.NODE_MODES,
+ { event: e, callback: inner_clicked, parentMenu: menu, node },
+ )
+
+ function inner_clicked(v: string) {
+ if (!node) return
+
+ const kV = Object.values(LiteGraph.NODE_MODES).indexOf(v)
+ const fApplyMultiNode = function (node: LGraphNode) {
+ if (kV !== -1 && LiteGraph.NODE_MODES[kV]) {
+ node.changeMode(kV)
+ } else {
+ console.warn(`unexpected mode: ${v}`)
+ node.changeMode(LGraphEventMode.ALWAYS)
+ }
+ }
+
+ const graphcanvas = LGraphCanvas.active_canvas
+ if (!graphcanvas.selected_nodes || Object.keys(graphcanvas.selected_nodes).length <= 1) {
+ fApplyMultiNode(node)
+ } else {
+ for (const i in graphcanvas.selected_nodes) {
+ fApplyMultiNode(graphcanvas.selected_nodes[i])
+ }
+ }
+ }
+
+ return false
+ }
+
+ /** @param value Parameter is never used */
+ static onMenuNodeColors(
+ value: IContextMenuValue,
+ options: IContextMenuOptions,
+ e: MouseEvent,
+ menu: ContextMenu,
+ node: LGraphNode,
+ ): boolean {
+ if (!node) throw "no node for color"
+
+ const values: IContextMenuValue[] = []
+ values.push({
+ value: null,
+ content: "No color",
+ })
+
+ for (const i in LGraphCanvas.node_colors) {
+ const color = LGraphCanvas.node_colors[i]
+ value = {
+ value: i,
+ content: `${i}`,
+ }
+ values.push(value)
+ }
+ new LiteGraph.ContextMenu(values, {
+ event: e,
+ callback: inner_clicked,
+ parentMenu: menu,
+ node,
+ })
+
+ function inner_clicked(v: IContextMenuValue) {
+ if (!node) return
+
+ const fApplyColor = function (item: IColorable) {
+ const colorOption = v.value ? LGraphCanvas.node_colors[v.value] : null
+ item.setColorOption(colorOption)
+ }
+
+ const canvas = LGraphCanvas.active_canvas
+ if (!canvas.selected_nodes || Object.keys(canvas.selected_nodes).length <= 1) {
+ fApplyColor(node)
+ } else {
+ for (const i in canvas.selected_nodes) {
+ fApplyColor(canvas.selected_nodes[i])
+ }
+ }
+ canvas.setDirty(true, true)
+ }
+
+ return false
+ }
+
+ static onMenuNodeShapes(
+ value: IContextMenuValue,
+ options: IContextMenuOptions,
+ e: MouseEvent,
+ menu?: ContextMenu,
+ node?: LGraphNode,
+ ): boolean {
+ if (!node) throw "no node passed"
+
+ new LiteGraph.ContextMenu(LiteGraph.VALID_SHAPES, {
+ event: e,
+ callback: inner_clicked,
+ parentMenu: menu,
+ node,
+ })
+
+ function inner_clicked(v: typeof LiteGraph.VALID_SHAPES[number]) {
+ if (!node) return
+ if (!node.graph) throw new NullGraphError()
+
+ node.graph.beforeChange()
+
+ const fApplyMultiNode = function (node: LGraphNode) {
+ node.shape = v
+ }
+
+ const canvas = LGraphCanvas.active_canvas
+ if (!canvas.selected_nodes || Object.keys(canvas.selected_nodes).length <= 1) {
+ fApplyMultiNode(node)
+ } else {
+ for (const i in canvas.selected_nodes) {
+ fApplyMultiNode(canvas.selected_nodes[i])
+ }
+ }
+
+ node.graph.afterChange()
+ canvas.setDirty(true)
+ }
+
+ return false
+ }
+
+ static onMenuNodeRemove(): void {
+ LGraphCanvas.active_canvas.deleteSelected()
+ }
+
+ static onMenuNodeClone(
+ value: IContextMenuValue,
+ options: IContextMenuOptions,
+ e: MouseEvent,
+ menu: ContextMenu,
+ node: LGraphNode,
+ ): void {
+ const { graph } = node
+ if (!graph) throw new NullGraphError()
+ graph.beforeChange()
+
+ const newSelected = new Set()
+
+ const fApplyMultiNode = function (node: LGraphNode, newNodes: Set): void {
+ if (node.clonable === false) return
+
+ const newnode = node.clone()
+ if (!newnode) return
+
+ newnode.pos = [node.pos[0] + 5, node.pos[1] + 5]
+ if (!node.graph) throw new NullGraphError()
+
+ node.graph.add(newnode)
+ newNodes.add(newnode)
+ }
+
+ const canvas = LGraphCanvas.active_canvas
+ if (!canvas.selected_nodes || Object.keys(canvas.selected_nodes).length <= 1) {
+ fApplyMultiNode(node, newSelected)
+ } else {
+ for (const i in canvas.selected_nodes) {
+ fApplyMultiNode(canvas.selected_nodes[i], newSelected)
+ }
+ }
+
+ if (newSelected.size) {
+ canvas.selectNodes([...newSelected])
+ }
+
+ graph.afterChange()
+
+ canvas.setDirty(true, true)
+ }
+
+ /**
+ * clears all the data inside
+ *
+ */
+ clear(): void {
+ this.frame = 0
+ this.last_draw_time = 0
+ this.render_time = 0
+ this.fps = 0
+
+ // this.scale = 1;
+ // this.offset = [0,0];
+ this.dragging_rectangle = null
+
+ this.selected_nodes = {}
+ this.selected_group = null
+ this.selectedItems.clear()
+ this.state.selectionChanged = true
+ this.onSelectionChange?.(this.selected_nodes)
+
+ this.visible_nodes = []
+ this.node_over = undefined
+ this.node_capturing_input = null
+ this.connecting_links = null
+ this.highlighted_links = {}
+
+ this.dragging_canvas = false
+
+ this.#dirty()
+ this.dirty_area = null
+
+ this.node_in_panel = null
+ this.node_widget = null
+
+ this.last_mouse = [0, 0]
+ this.last_mouseclick = 0
+ this.pointer.reset()
+ this.visible_area.set([0, 0, 0, 0])
+
+ this.onClear?.()
+ }
+
+ /**
+ * Assigns a new graph to this canvas.
+ */
+ setGraph(newGraph: LGraph | Subgraph): void {
+ const { graph } = this
+ if (newGraph === graph) return
+
+ this.clear()
+ newGraph.attachCanvas(this)
+
+ this.dispatch("litegraph:set-graph", { newGraph, oldGraph: graph })
+ this.#dirty()
+ }
+
+ openSubgraph(subgraph: Subgraph): void {
+ const { graph } = this
+ if (!graph) throw new NullGraphError()
+
+ const options = { bubbles: true, detail: { subgraph, closingGraph: graph }, cancelable: true }
+ const mayContinue = this.canvas.dispatchEvent(new CustomEvent("subgraph-opening", options))
+ if (!mayContinue) return
+
+ this.clear()
+ this.subgraph = subgraph
+ this.setGraph(subgraph)
+
+ this.canvas.dispatchEvent(new CustomEvent("subgraph-opened", options))
+ }
+
+ /**
+ * @returns the visually active graph (in case there are more in the stack)
+ */
+ getCurrentGraph(): LGraph | null {
+ return this.graph
+ }
+
+ /**
+ * Finds the canvas if required, throwing on failure.
+ * @param canvas Canvas element, or its element ID
+ * @returns The canvas element
+ * @throws If {@link canvas} is an element ID that does not belong to a valid HTML canvas element
+ */
+ #validateCanvas(
+ canvas: string | HTMLCanvasElement,
+ ): HTMLCanvasElement & { data?: LGraphCanvas } {
+ if (typeof canvas === "string") {
+ const el = document.getElementById(canvas)
+ if (!(el instanceof HTMLCanvasElement)) throw "Error validating LiteGraph canvas: Canvas element not found"
+ return el
+ }
+ return canvas
+ }
+
+ /**
+ * Sets the current HTML canvas element.
+ * Calls bindEvents to add input event listeners, and (re)creates the background canvas.
+ * @param canvas The canvas element to assign, or its HTML element ID. If null or undefined, the current reference is cleared.
+ * @param skip_events If true, events on the previous canvas will not be removed. Has no effect on the first invocation.
+ */
+ setCanvas(canvas: string | HTMLCanvasElement, skip_events?: boolean) {
+ const element = this.#validateCanvas(canvas)
+ if (element === this.canvas) return
+ // maybe detach events from old_canvas
+ if (!element && this.canvas && !skip_events) this.unbindEvents()
+
+ this.canvas = element
+ this.ds.element = element
+ this.pointer.element = element
+
+ if (!element) return
+
+ // TODO: classList.add
+ element.className += " lgraphcanvas"
+ element.data = this
+
+ // Background canvas: To render objects behind nodes (background, links, groups)
+ this.bgcanvas = document.createElement("canvas")
+ this.bgcanvas.width = this.canvas.width
+ this.bgcanvas.height = this.canvas.height
+
+ const ctx = element.getContext?.("2d")
+ if (ctx == null) {
+ if (element.localName != "canvas") {
+ throw `Element supplied for LGraphCanvas must be a