diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 000000000..b51aad169 --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,42 @@ +# Copyright 2025 The MathWorks, Inc. + +# Light weight image only to test builds of matlab-proxy + +ARG PYTHON_VERSION=3.11 +ARG NODE_VERSION=20 +ARG NVM_VERSION="0.40.3" + +FROM ubuntu:24.04 + +ARG NVM_VERSION +ARG PYTHON_VERSION +ARG NODE_VERSION + +ENV DEBIAN_FRONTEND=noninteractive +# Install build dependencies, install UV (for python) and NVM (for nodejs) +RUN apt-get update && \ + apt-get install -y --no-install-recommends \ + git \ + wget \ + curl \ + unzip \ + ca-certificates && \ + rm -rf /var/lib/apt/lists/* + +USER ubuntu + +# Updates the default shell to bash instead of sh +SHELL ["/bin/bash", "-c"] + +WORKDIR /home/ubuntu +# Set up development environment for ubuntu user with UV and NVM +RUN curl -LsSf https://astral.sh/uv/install.sh | sh && \ + source ${HOME}/.local/bin/env && \ + uv python install ${PYTHON_VERSION} && \ + curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v${NVM_VERSION}/install.sh | bash && \ + export NVM_DIR="/home/ubuntu/.nvm" && [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" && \ + nvm install ${NODE_VERSION} + +# Now the matlab-proxy directory can be mounted and tested for build + + diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 000000000..59362dd20 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,29 @@ +{ + "name": "Only build Jupyter MATLAB Proxy", + "build": { + "dockerfile": "Dockerfile", + "context": ".", + "args": { + "PYTHON_VERSION" : "3.11", + "NVM_VERSION" : "0.40.3", + "NODE_VERSION" : "24" + } + }, + "containerEnv": { + "NPM_CONFIG_REGISTRY": "${localEnv:NPM_CONFIG_REGISTRY}" + }, + "customizations": { + "vscode": { + "extensions": [ + "ms-python.python" + ], + "settings": { + "python.terminal.activateEnvInCurrentTerminal": true, + "python.defaultInterpreterPath": ".venv/bin/python" + } + } + }, + "postCreateCommand": "uv venv --clear && . .venv/bin/activate && uv pip install . --cache-dir ./__uvcache__", + "waitFor": "postCreateCommand" + +} \ No newline at end of file diff --git a/.devcontainer/with-matlab/Dockerfile b/.devcontainer/with-matlab/Dockerfile new file mode 100644 index 000000000..1ba308e28 --- /dev/null +++ b/.devcontainer/with-matlab/Dockerfile @@ -0,0 +1,89 @@ +# Copyright 2025 The MathWorks, Inc. + +# Full image, with MATLAB and MATLAB Proxy + +ARG PYTHON_VERSION=3.11 +ARG NODE_VERSION=20 +ARG NVM_VERSION="0.40.3" +ARG MATLAB_RELEASE="R2025b" +ARG UBUNTU_VERSION="24.04" +# Default installation directory for MATLAB +ARG MATLAB_INSTALL_LOCATION="/opt/matlab" +ARG MATLAB_PRODUCT_LIST="MATLAB" + +FROM ubuntu:${UBUNTU_VERSION} + +ARG NVM_VERSION +ARG PYTHON_VERSION +ARG NODE_VERSION +ARG MATLAB_RELEASE +ARG UBUNTU_VERSION +ARG MATLAB_INSTALL_LOCATION +ARG MATLAB_PRODUCT_LIST + +ENV DEBIAN_FRONTEND=noninteractive +# Install build dependencies, install UV (for python) and NVM (for nodejs) +RUN apt-get update && \ + apt-get install -y --no-install-recommends \ + git \ + wget \ + curl \ + unzip \ + xvfb \ + fluxbox \ + ca-certificates && \ + rm -rf /var/lib/apt/lists/* + +USER ubuntu + +# Updates the default shell to bash instead of sh +SHELL ["/bin/bash", "-c"] + +WORKDIR /home/ubuntu +# Set up development environment for ubuntu user with UV and NVM +RUN curl -LsSf https://astral.sh/uv/install.sh | sh && \ + source ${HOME}/.local/bin/env && \ + uv python install ${PYTHON_VERSION} && \ + curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v${NVM_VERSION}/install.sh | bash && \ + export NVM_DIR="/home/ubuntu/.nvm" && [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" && \ + nvm install ${NODE_VERSION} + +# Now the matlab-proxy directory can be mounted and tested for build + +USER root + +# Install MATLAB dependencies and MATLAB using MPM +ARG MATLAB_DEPS_URL="https://raw.githubusercontent.com/mathworks-ref-arch/container-images/main/matlab-deps/${MATLAB_RELEASE}/ubuntu${UBUNTU_VERSION}/base-dependencies.txt" +ARG MATLAB_DEPENDENCIES="matlab-deps-${MATLAB_RELEASE}-base-dependencies.txt" +ARG ADDITIONAL_PACKAGES="wget curl unzip ca-certificates xvfb git vim fluxbox gettext" +RUN export DEBIAN_FRONTEND=noninteractive && apt-get update \ + && apt-get install --no-install-recommends -y ${ADDITIONAL_PACKAGES}\ + && wget $(echo ${MATLAB_DEPS_URL} | tr "[:upper:]" "[:lower:]") -O ${MATLAB_DEPENDENCIES} \ + && xargs -a ${MATLAB_DEPENDENCIES} -r apt-get install --no-install-recommends -y \ + && apt-get clean \ + && apt-get -y autoremove \ + && rm -rf /var/lib/apt/lists/* ${MATLAB_DEPENDENCIES} + +WORKDIR /matlab-install +ARG MSH_MANAGED_INSTALL_ROOT=/usr/local/MathWorks/ServiceHost/ +ARG MSH_DOWNLOAD_LOCATION=/tmp/Downloads/MathWorks/ServiceHost +# Dont need to set HOME to install Support packages as jupyter images set HOME to NB_USER in all images, even for ROOT. +RUN echo "Installing MATLAB using MPM..." +RUN wget -q https://www.mathworks.com/mpm/glnxa64/mpm && \ + chmod +x mpm \ + && ./mpm install --release=${MATLAB_RELEASE} --destination=${MATLAB_INSTALL_LOCATION} \ + --products ${MATLAB_PRODUCT_LIST} \ + || (echo "MPM Installation Failure. See below for more information:" && cat /tmp/mathworks_root.log && false)\ + && rm -f mpm /tmp/mathworks_root.log \ + && ln -s ${MATLAB_INSTALL_LOCATION}/bin/matlab /usr/local/bin/matlab \ + && git clone https://github.com/mathworks-ref-arch/administer-mathworks-service-host.git \ + && cd /matlab-install/administer-mathworks-service-host/admin-scripts/linux/admin-controlled-installation \ + && ./download_msh.sh --destination ${MSH_DOWNLOAD_LOCATION} \ + && ./install_msh.sh --source ${MSH_DOWNLOAD_LOCATION} --destination ${MSH_MANAGED_INSTALL_ROOT} --no-update-environment \ + && ./cleanup_default_msh_installation_location.sh --for-all-users \ + && cd / && rm -rf /matlab-install ${MSH_DOWNLOAD_LOCATION} + +ENV MATHWORKS_SERVICE_HOST_MANAGED_INSTALL_ROOT=${MSH_MANAGED_INSTALL_ROOT} + +USER ubuntu +WORKDIR /home/ubuntu diff --git a/.devcontainer/with-matlab/devcontainer.json b/.devcontainer/with-matlab/devcontainer.json new file mode 100644 index 000000000..877de2676 --- /dev/null +++ b/.devcontainer/with-matlab/devcontainer.json @@ -0,0 +1,34 @@ +{ + "name": "MATLAB & Jupyter MATLAB Proxy", + "build": { + "dockerfile": "Dockerfile", + "context": ".", + "args": { + "PYTHON_VERSION" : "3.11", + "NVM_VERSION" : "0.40.3", + "NODE_VERSION" : "24", + "MATLAB_RELEASE" : "R2025b", + "UBUNTU_VERSION" : "24.04", + "MATLAB_PRODUCT_LIST" : "MATLAB" + } + }, + "containerEnv": { + "NPM_CONFIG_REGISTRY": "${localEnv:NPM_CONFIG_REGISTRY}", + "MWI_SESSION_NAME": "DevContainer" + }, + "customizations": { + "vscode": { + "extensions": [ + "ms-python.python", + "MathWorks.language-matlab" + ], + "settings": { + "MATLAB.signIn": true, + "python.terminal.activateEnvInCurrentTerminal": true, + "python.defaultInterpreterPath": ".venv/bin/python" + } + } + }, + "postCreateCommand": "uv venv --clear && . .venv/bin/activate && uv pip install . jupyterlab --cache-dir ./__uvcache__", + "waitFor": "postCreateCommand" +} \ No newline at end of file diff --git a/.githooks/pre-commit b/.githooks/pre-commit new file mode 100755 index 000000000..b3dcb86dc --- /dev/null +++ b/.githooks/pre-commit @@ -0,0 +1,37 @@ +#!/bin/sh +# Copyright 2025 The MathWorks, Inc. +# Pre-commit hook to run all scripts in the scripts folder +# Called by "git commit" with no arguments. + +# Path to the scripts directory +# Get the directory where this script is located +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +SCRIPTS_DIR="$SCRIPT_DIR/scripts" + +# Check if the scripts directory exists +if [ ! -d "$SCRIPTS_DIR" ]; then + echo "Error: Scripts directory not found at $SCRIPTS_DIR" + exit 1 +fi + +echo "Running pre-commit scripts from $SCRIPTS_DIR" + +# Find all executable scripts in the scripts directory and sort them alphabetically +for script in $(find "$SCRIPTS_DIR" -type f -executable | sort); do + script_name=$(basename "$script") + echo "Running $script_name..." + + # Execute the script + sh "$script" + + # Check the exit code + if [ $? -ne 0 ]; then + echo "Error: $script_name failed. Commit aborted." + exit 1 + fi + + echo "$script_name completed successfully." +done + +echo "All pre-commit scripts completed successfully." +exit 0 diff --git a/.githooks/scripts/sanitize-npm-registry.sh b/.githooks/scripts/sanitize-npm-registry.sh new file mode 100755 index 000000000..ac6aa0227 --- /dev/null +++ b/.githooks/scripts/sanitize-npm-registry.sh @@ -0,0 +1,32 @@ +#!/bin/bash +# Copyright 2025 The MathWorks, Inc. +# Pre-commit hook to replace MathWorks NPM registry URLs with public NPM registry + +# Find all package.json, .npmrc, and other relevant files +echo "Executing pre-commit hook: sanitize-npm-registry" +FILES=$( git diff --cached --name-only --diff-filter=ACM | grep -E '(.*.json)') + +if [ -z "$FILES" ]; then + exit 0 +fi + +# Replace the MathWorks NPM registry URL with the public NPM registry +for FILE in $FILES; do + # Skip if file doesn't exist (it may have been deleted) + [ -f "$FILE" ] || continue + + echo "Sanitizing NPM registry URL in $FILE" + + # Replace the URL in the file + sed -i.bak 's|https://.*/artifactory/api/npm/npm-repos/|https://registry.npmjs.org/|g' "$FILE" + + # Remove backup file + rm -f "${FILE}.bak" + + # Stage the modified file + git add "$FILE" + + echo "Sanitization complete for $FILE" +done + +exit 0 diff --git a/.github/workflows/publish-jupyter-matlab-proxy.yml b/.github/workflows/publish-jupyter-matlab-proxy.yml index c437da220..ea081ccc2 100644 --- a/.github/workflows/publish-jupyter-matlab-proxy.yml +++ b/.github/workflows/publish-jupyter-matlab-proxy.yml @@ -30,10 +30,10 @@ jobs: with: ref: ${{ github.sha }} - - name: Set up Python 3.8 - uses: actions/setup-python@v4 + - name: Set up Python 3.10 + uses: actions/setup-python@v5 with: - python-version: 3.8 + python-version: '3.10' - name: Install Python build dependencies run: | diff --git a/.github/workflows/publish-tljh-matlab.yml b/.github/workflows/publish-tljh-matlab.yml index 6cc8ff78b..00ae320c8 100644 --- a/.github/workflows/publish-tljh-matlab.yml +++ b/.github/workflows/publish-tljh-matlab.yml @@ -1,4 +1,4 @@ -# Copyright 2024 The MathWorks, Inc. +# Copyright 2024-2025 The MathWorks, Inc. name: Upload Python Package for tljh-matlab on: @@ -22,7 +22,7 @@ jobs: - uses: actions/checkout@v4 - name: Set up Python - uses: actions/setup-python@v3 + uses: actions/setup-python@v5 with: python-version: '3.x' diff --git a/.github/workflows/run-e2e-tests.yml b/.github/workflows/run-e2e-tests.yml index 9e463417d..5a60a1424 100644 --- a/.github/workflows/run-e2e-tests.yml +++ b/.github/workflows/run-e2e-tests.yml @@ -1,4 +1,4 @@ -# Copyright 2023-2024 The MathWorks, Inc. +# Copyright 2023-2025 The MathWorks, Inc. name: End-to-End Tests for MATLAB Integration for Jupyter on: @@ -6,22 +6,25 @@ on: jobs: playwright_e2e_tests: + timeout-minutes: 15 runs-on: ubuntu-latest defaults: run: working-directory: tests/e2e env: - NODE_VERSION: 18 - PYTHON_VERSION: 3.8 + NODE_VERSION: 24 + PYTHON_VERSION: '3.10' + steps: - name: Checkout uses: actions/checkout@v4 - - uses: actions/setup-node@v4 + - name: Set up Node ${{ env.NODE_VERSION }} + uses: actions/setup-node@v6 with: node-version: ${{ env.NODE_VERSION }} - - name: Set up Python + - name: Set up Python ${{ env.PYTHON_VERSION }} uses: actions/setup-python@v5 with: python-version: ${{ env.PYTHON_VERSION }} @@ -101,4 +104,4 @@ jobs: with: name: e2e_test_results path: ./tests/e2e/zipped-e2e-test-results.zip - retention-days: 5 + retention-days: 5 \ No newline at end of file diff --git a/.github/workflows/run-integration-tests.yml b/.github/workflows/run-integration-tests.yml index 98826e369..08b36b510 100644 --- a/.github/workflows/run-integration-tests.yml +++ b/.github/workflows/run-integration-tests.yml @@ -1,4 +1,4 @@ -# Copyright 2023-2024 The MathWorks, Inc. +# Copyright 2023-2025 The MathWorks, Inc. name: Integration testing MATLAB Integration for Jupyter @@ -8,12 +8,12 @@ on: jobs: python_integration_tests: + timeout-minutes: 15 strategy: fail-fast: false matrix: - #TODO: Add test coverage for macOs os: [ubuntu-latest, windows-latest] - python-version: ["3.8", "3.11"] + python-version: ["3.10", "3.13"] # The minimum matlab-release is set to 21b as 20b and 21a are not supported by matlab-actions/setup-matlab. # See https://github.com/matlab-actions/setup-matlab/issues/76 @@ -32,7 +32,7 @@ jobs: products: MATLAB Symbolic_Math_Toolbox - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} @@ -69,4 +69,4 @@ jobs: name: MATLAB Proxy Integration Test Log File ${{ matrix.os }} matlab-${{ matrix.matlab-release }} python-${{ matrix.python-version }} path: | tests/integration/integ_logs.log - licensing-screenshot-failed.png + licensing-screenshot-failed.png \ No newline at end of file diff --git a/.github/workflows/run-unit-tests.yml b/.github/workflows/run-unit-tests.yml index dcb5f57ed..1a42fcf21 100644 --- a/.github/workflows/run-unit-tests.yml +++ b/.github/workflows/run-unit-tests.yml @@ -1,4 +1,4 @@ -# Copyright 2020-2024 The MathWorks, Inc. +# Copyright 2020-2025 The MathWorks, Inc. name: Unit Testing MATLAB Integration for Jupyter @@ -10,6 +10,7 @@ on: jobs: matlab_unit_tests: + timeout-minutes: 15 strategy: fail-fast: false matrix: @@ -60,14 +61,15 @@ jobs: select-by-folder: tests/matlab-tests python_unit_tests: + timeout-minutes: 15 env: - code-cov-py: "3.11" + code-cov-py: "3.13" code-cov-os: "ubuntu-latest" strategy: fail-fast: false matrix: - os: [ubuntu-latest, windows-latest, macos-latest] - python-version: ["3.8", "3.11"] + os: [ubuntu-latest, windows-latest] + python-version: ["3.10", "3.13"] runs-on: ${{ matrix.os }} steps: @@ -75,7 +77,7 @@ jobs: uses: actions/checkout@v3 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} @@ -108,22 +110,23 @@ jobs: retention-days: 5 lezer_unit_tests: + timeout-minutes: 15 strategy: fail-fast: false matrix: - os: [ubuntu-latest, windows-latest, macos-latest] + os: [ubuntu-latest, windows-latest] runs-on: ${{ matrix.os }} defaults: run: working-directory: src/jupyter_matlab_labextension/src/lezer-matlab/test/ env: - NODE_VERSION: 18 + NODE_VERSION: 24 steps: - name: Checkout uses: actions/checkout@v4 - - uses: actions/setup-node@v4 + - uses: actions/setup-node@v6 with: node-version: ${{ env.NODE_VERSION }} @@ -134,6 +137,7 @@ jobs: run: npm test upload_code_coverage: + timeout-minutes: 15 name: "Upload Code Coverage using codecov" needs: [python_unit_tests] if: success() diff --git a/.github/workflows/test-tljh-matlab.yml b/.github/workflows/test-tljh-matlab.yml index c1e751a7b..ebc015971 100644 --- a/.github/workflows/test-tljh-matlab.yml +++ b/.github/workflows/test-tljh-matlab.yml @@ -19,9 +19,9 @@ jobs: steps: - uses: actions/checkout@v4 - name: Set up Python 3.10 - uses: actions/setup-python@v3 + uses: actions/setup-python@v5 with: - python-version: "3.10" + python-version: '3.10' - name: Install dependencies run: | diff --git a/.gitignore b/.gitignore index adc185492..d4ca2d4e6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,9 @@ *.egg-info .eggs __pycache__ +__uvcache__ +.ipynb_checkpoints/ +*.ipynb build dist .venv diff --git a/README.md b/README.md index 5007df57a..d4432d6e7 100644 --- a/README.md +++ b/README.md @@ -45,7 +45,7 @@ From your Jupyter notebook or JupyterLab, you can also open the MATLAB developme - Windows® (supported from [v0.6.0](https://github.com/mathworks/jupyter-matlab-proxy/releases/tag/v0.6.0)). - Windows Subsystem for Linux (WSL 2) [Installation Guide](https://github.com/mathworks/jupyter-matlab-proxy/blob/main/install_guides/wsl2/README.md). -* Python versions: 3.8 | 3.9 | 3.10 | 3.11 +* Python versions: 3.10 | 3.11 | 3.12 | 3.13 * MATLAB R2020b or later, installed and on the system PATH. ```bash @@ -171,6 +171,8 @@ This opens a Jupyter notebook that supports MATLAB. - **Licensing:** When you execute MATLAB code in a notebook for the first time, enter your MATLAB license information in the dialog box that appears. For details, see [Licensing](https://github.com/mathworks/matlab-proxy/blob/main/MATLAB-Licensing-Info.md). The MATLAB session can take a few minutes to start. +- **Sharing MATLAB across notebooks:** By default, multiple notebooks running on a Jupyter server share the underlying MATLAB process, so executing code in one notebook affects the workspace in others. To use a dedicated MATLAB for your kernel instead, use the magic `%%matlab new_session`. For details, see [Magic Commands for MATLAB Kernel](https://github.com/mathworks/jupyter-matlab-proxy/blob/main/src/jupyter_matlab_kernel/magics/README.md). To learn more about the kernel architecture, see [MATLAB Kernel for Jupyter](https://github.com/mathworks/jupyter-matlab-proxy/blob/main/src/jupyter_matlab_kernel/README.md). + - **MATLAB Kernel:** The MATLAB kernel supports tab completion and rich outputs: * Inline static plot images * LaTeX representation for symbolic expressions @@ -181,7 +183,7 @@ This opens a Jupyter notebook that supports MATLAB. For a technical overview of the MATLAB kernel, see [MATLAB Kernel for Jupyter](https://github.com/mathworks/jupyter-matlab-proxy/blob/main/src/jupyter_matlab_kernel/README.md). -- **Multiple notebooks:** Multiple notebooks running on a Jupyter server share the underlying MATLAB process, so executing code in one notebook affects the workspace in others. If you work in several notebooks simultaneously, be aware they share a workspace. For details, see [MATLAB Kernel for Jupyter](https://github.com/mathworks/jupyter-matlab-proxy/blob/main/src/jupyter_matlab_kernel/README.md). + - **Local functions:** With MATLAB R2022b and later, you can define a local function at the end of the cell where you want to call it:

diff --git a/img/kernel-architecture-dedicated.png b/img/kernel-architecture-dedicated.png new file mode 100644 index 000000000..cb9a1fcb4 Binary files /dev/null and b/img/kernel-architecture-dedicated.png differ diff --git a/img/kernel-architecture.png b/img/kernel-architecture.png deleted file mode 100644 index 367fb7962..000000000 Binary files a/img/kernel-architecture.png and /dev/null differ diff --git a/pyproject.toml b/pyproject.toml index 4054bdbcd..2b6370d48 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,11 +6,11 @@ build-backend = "hatchling.build" [project] name = "jupyter-matlab-proxy" -version = "0.17.2" +version = "0.18.0" description = "MATLAB Integration for Jupyter" readme = "README.md" license = { file = "LICENSE.md" } -requires-python = ">=3.8" +requires-python = ">=3.10, <3.14" authors = [ { name = "The MathWorks Inc.", email = "jupyter-support@mathworks.com" }, ] @@ -43,7 +43,7 @@ dependencies = [ "ipykernel>=6.0.3", "jupyter-client", "jupyter-server-proxy>=4.1.0", - "matlab-proxy>=0.26.0", + "matlab-proxy>=0.30.0", "psutil", "requests", ] @@ -74,7 +74,7 @@ features = [ ] [[tool.hatch.envs.hatch-test.matrix]] -python = ["3.9", "3.10", "3.11", "3.12", "3.13"] +python = ["3.10", "3.11", "3.12", "3.13"] [project.entry-points.jupyter_serverproxy_servers] matlab = "jupyter_matlab_proxy:setup_matlab" diff --git a/src/jupyter_matlab_kernel/README.md b/src/jupyter_matlab_kernel/README.md index aee316bf2..55aae4a4a 100644 --- a/src/jupyter_matlab_kernel/README.md +++ b/src/jupyter_matlab_kernel/README.md @@ -12,16 +12,23 @@ After installing the MATLAB Integration for Jupyter, your Jupyter environment sh ## Technical Overview +Start a Jupyter notebook to create a MATLAB kernel. When you run MATLAB code in a notebook for the first time, you see a licensing screen to enter your MATLAB license details. If a MATLAB process is not already running, one would be started automatically. -|

| -|--| -|The diagram above illustrates that multiple Jupyter notebooks communicate with a shared MATLAB process, through the Jupyter notebook server.| +

-Start a Jupyter notebook to create a MATLAB kernel. When you run MATLAB code in a notebook for the first time, you see a licensing screen to enter your MATLAB license details. If a MATLAB process is not already running, Jupyter will start one. +### Shared MATLAB Workspace (Default Behavior) -Multiple notebooks share the same MATLAB workspace. MATLAB processes commands from multiple notebooks in on a first-in, first-out basis. +By default, multiple notebooks share the same MATLAB workspace. MATLAB processes commands from multiple notebooks on a first-in, first-out basis. -You can use kernel interrupts to stop MATLAB from processing a request. Remember that if cells from multiple notebooks are being run at the same time, the execution request you interrupt may not be from the notebook where you initated the interrupt. +You can use kernel interrupts to stop MATLAB from processing a request. Remember that if cells from multiple notebooks are being run at the same time, the execution request you interrupt may not be from the notebook where you initiated the interrupt. + +### Dedicated MATLAB Workspace (Optional Behavior) + +You can now create a dedicated MATLAB session for your notebook by using the magic command `%%matlab new_session` in a cell. This starts a separate MATLAB process exclusively for that notebook, providing an isolated workspace that is not shared with other notebooks. + +This is useful when you need to avoid conflicts with other notebooks or require an independent execution environment. + +Once created, all subsequent MATLAB code in that notebook will execute in the dedicated session. Each dedicated session operates independently with its own workspace and execution queue. ## Limitations @@ -33,6 +40,6 @@ To request an enhancement or technical support, [create a GitHub issue](https:// ---- -Copyright 2023-2024 The MathWorks, Inc. +Copyright 2023-2025 The MathWorks, Inc. ---- diff --git a/src/jupyter_matlab_kernel/__main__.py b/src/jupyter_matlab_kernel/__main__.py index 0d0b5884e..65a401bb5 100644 --- a/src/jupyter_matlab_kernel/__main__.py +++ b/src/jupyter_matlab_kernel/__main__.py @@ -1,4 +1,4 @@ -# Copyright 2023-2024 The MathWorks, Inc. +# Copyright 2023-2025 The MathWorks, Inc. # Use ipykernel infrastructure to launch the MATLAB Kernel. if __name__ == "__main__": diff --git a/src/jupyter_matlab_kernel/base_kernel.py b/src/jupyter_matlab_kernel/base_kernel.py index 7afa7612b..253a48ed7 100644 --- a/src/jupyter_matlab_kernel/base_kernel.py +++ b/src/jupyter_matlab_kernel/base_kernel.py @@ -13,7 +13,6 @@ import time from logging import Logger from pathlib import Path -from typing import Optional import aiohttp import aiohttp.client_exceptions @@ -26,8 +25,12 @@ MagicExecutionEngine, get_completion_result_for_magics, ) +from jupyter_matlab_kernel.mwi_comm_helpers import MWICommHelper from jupyter_matlab_kernel.mwi_exceptions import MATLABConnectionError +from jupyter_matlab_kernel.comms import LabExtensionCommunication + + _MATLAB_STARTUP_TIMEOUT = mwi_settings.get_process_startup_timeout() @@ -139,7 +142,34 @@ def __init__(self, *args, **kwargs): self.magic_engine = MagicExecutionEngine(self.log) # Communication helper for interaction with backend MATLAB proxy - self.mwi_comm_helper = None + self.mwi_comm_helper: Optional[MWICommHelper] = None + + # Used to detect if this Kernel has been assigned a MATLAB-proxy server or not + self.is_matlab_assigned = False + + # Flag indicating whether this kernel is using a shared MATLAB instance + self.is_shared_matlab: bool = True + + # Keeps track of MATLAB version information for the MATLAB assigned to this Kernel + self.matlab_version = None + + # Keeps track of MATLAB root path information for the MATLAB assigned to this Kernel + self.matlab_root_path = None + + # Keeps track of the MATLAB licensing mode information for the MATLAB assigned to this Kernel + self.licensing_mode = None + + self.labext_comm = LabExtensionCommunication(self) + + # Custom handling of comm messages for jupyterlab extension communication. + # https://jupyter-client.readthedocs.io/en/latest/messaging.html#custom-messages + + # Override only comm handlers to keep implementation clean by separating + # JupyterLab extension communication logic from core kernel functionality. + # Other handlers (interrupt_request, execute_request, etc.) remain in base class. + self.shell_handlers["comm_open"] = self.labext_comm.comm_open + self.shell_handlers["comm_msg"] = self.labext_comm.comm_msg + self.shell_handlers["comm_close"] = self.labext_comm.comm_close # ipykernel Interface API # https://ipython.readthedocs.io/en/stable/development/wrapperkernels.html @@ -151,8 +181,9 @@ async def interrupt_request(self, stream, ident, parent): """ self.log.debug("Received interrupt request from Jupyter") try: - # Send interrupt request to MATLAB - await self.mwi_comm_helper.send_interrupt_request_to_matlab() + if self.is_matlab_assigned and self.mwi_comm_helper: + # Send interrupt request to MATLAB + await self.mwi_comm_helper.send_interrupt_request_to_matlab() # Set the response to interrupt request. content = {"status": "ok"} @@ -170,29 +201,6 @@ async def interrupt_request(self, stream, ident, parent): self.session.send(stream, "interrupt_reply", content, parent, ident=ident) - def modify_kernel(self, states_to_modify): - """ - Used to modify MATLAB Kernel state - Args: - states_to_modify (dict): A key value pair of all the states to be modified. - - """ - self.log.debug(f"Modifying the kernel with {states_to_modify}") - for key, value in states_to_modify.items(): - if hasattr(self, key): - self.log.debug(f"set the value of {key} to {value}") - setattr(self, key, value) - - def handle_magic_output(self, output, outputs=None): - if output["type"] == "modify_kernel": - self.modify_kernel(output) - else: - self.display_output(output) - if outputs is not None and not self.startup_checks_completed: - # Outputs are cleared after startup_check. - # Storing the magic outputs to display them after startup_check completes. - outputs.append(output) - async def do_execute( self, code, @@ -208,18 +216,19 @@ async def do_execute( https://jupyter-client.readthedocs.io/en/stable/messaging.html#execute """ self.log.debug(f"Received execution request from Jupyter with code:\n{code}") + try: - accumulated_magic_outputs = [] performed_startup_checks = False - - for output in self.magic_engine.process_before_cell_execution( - code, self.execution_count - ): - self.handle_magic_output(output, accumulated_magic_outputs) + accumulated_magic_outputs = await self._perform_before_cell_execution(code) skip_cell_execution = self.magic_engine.skip_cell_execution() self.log.debug(f"Skipping cell execution is set to {skip_cell_execution}") + # Start a shared matlab-proxy (default) if not already started + if not self.is_matlab_assigned and not skip_cell_execution: + await self.start_matlab_proxy_and_comm_helper() + self.is_matlab_assigned = True + # Complete one-time startup checks before sending request to MATLAB. # Blocking call, returns after MATLAB is started. if not skip_cell_execution: @@ -261,9 +270,8 @@ async def do_execute( ) # Display all the outputs produced during the execution of code. - for idx in range(len(outputs)): - data = outputs[idx] - self.log.debug(f"Displaying output {idx+1}:\n{data}") + for idx, data in enumerate(outputs): + self.log.debug(f"Displaying output {idx + 1}:\n{data}") # Ignore empty values returned from MATLAB. if not data: @@ -272,7 +280,7 @@ async def do_execute( # Execute post execution of MAGICs for output in self.magic_engine.process_after_cell_execution(): - self.handle_magic_output(output) + await self._handle_magic_output(output) except Exception as e: self.log.error( @@ -404,6 +412,119 @@ async def do_history( # Helper functions + def _get_kernel_info(self): + return { + "is_shared_matlab": self.is_shared_matlab, + "matlab_version": self.matlab_version, + "matlab_root_path": self.matlab_root_path, + "licensing_mode": self.licensing_mode, + } + + def _modify_kernel(self, states_to_modify): + """ + Used to modify MATLAB Kernel state + Args: + states_to_modify (dict): A key value pair of all the states to be modified. + + """ + self.log.info(f"Modifying the kernel with {states_to_modify}") + for key, value in states_to_modify.items(): + if hasattr(self, key): + self.log.debug(f"set the value of {key} to {value}") + setattr(self, key, value) + else: + self.log.warning(f"Attribute with name: {key} not found in kernel") + + async def _handle_magic_output(self, output): + """ + Handle the output from magic commands. + + Args: + output (dict): The output from a magic command. + + Returns: + dict or None: Returns the output if startup checks are not completed, + otherwise returns None. + + This method processes the output from magic commands. It handles kernel + modifications, stores outputs before startup checks are completed, and + displays outputs after startup checks are done. + """ + if output["type"] == "modify_kernel": + self.log.debug("Handling modify_kernel output") + self._modify_kernel(output) + elif output["type"] == "callback": + self.log.debug("Handling callback output") + await self._invoke_callback_function(output.get("callback_function")) + else: + self.display_output(output) + + if not self.startup_checks_completed: + # Outputs are cleared after startup_check. + # Storing the magic outputs to display them after startup_check completes. + return output + return None + + async def _invoke_callback_function(self, callback_fx): + """ + Handles the invocation of callback function supplied by the magic command. Kernel injects + itself as a parameter. + + Args: + callback_fx: Function to be called. Currently only supports calling async or async generator functions. + """ + if callback_fx: + import inspect + + if inspect.isasyncgenfunction(callback_fx): + async for result in callback_fx(self): + self.display_output(result) + else: + result = await callback_fx(self) + if result: + self.display_output(result) + self.log.debug(f"Callback function {callback_fx} executed successfully") + return None + + async def start_matlab_proxy_and_comm_helper(self): + """ + Start MATLAB proxy and communication helper. + + This method is intended to be overridden by subclasses to perform + any necessary setup for matlab-proxy startup. The default implementation + does nothing. + + Returns: + None + + Raises: + NotImplementedError: Always raised as this method must be implemented by subclasses. + """ + raise NotImplementedError("Subclasses should implement this method") + + async def _perform_before_cell_execution(self, code) -> list: + """ + Perform actions before cell execution and handle magic outputs. + + This method processes magic commands before cell execution and accumulates + their outputs. + + Args: + code (str): The code to be executed. + + Returns: + list: A list of accumulated magic outputs. + """ + accumulated_magic_outputs = [] + for magic_output in self.magic_engine.process_before_cell_execution( + code, self.execution_count + ): + output = await self._handle_magic_output(magic_output) + if output: + accumulated_magic_outputs.append(output) + + return accumulated_magic_outputs + def display_output(self, out): """ Common function to send execution outputs to Jupyter UI. @@ -458,11 +579,8 @@ async def perform_startup_checks( self.log.error(f"Found a startup error: {self.startup_error}") raise self.startup_error - ( - is_matlab_licensed, - matlab_status, - matlab_proxy_has_error, - ) = await self.mwi_comm_helper.fetch_matlab_proxy_status() + # Query matlab-proxy for its current status + matlab_proxy_status = await self.mwi_comm_helper.fetch_matlab_proxy_status() # Display iframe containing matlab-proxy to show login window if MATLAB # is not licensed using matlab-proxy. The iframe is removed after MATLAB @@ -472,7 +590,7 @@ async def perform_startup_checks( # as src for iframe to avoid hardcoding any hostname/domain information. This is done to # ensure the kernel works in Jupyter deployments. VS Code however does not work the same way # as other browser based Jupyter clients. - if not is_matlab_licensed: + if not matlab_proxy_status.is_matlab_licensed: if not jupyter_base_url: # happens for non-jupyter environments (like VSCode), we expect licensing to # be completed before hand @@ -505,22 +623,13 @@ async def perform_startup_checks( ) # Wait until MATLAB is started before sending requests. - await self.poll_for_matlab_startup( - is_matlab_licensed, matlab_status, matlab_proxy_has_error - ) + await self.poll_for_matlab_startup(matlab_proxy_status) - async def poll_for_matlab_startup( - self, is_matlab_licensed, matlab_status, matlab_proxy_has_error - ): - """Wait until MATLAB has started or time has run out" + async def poll_for_matlab_startup(self, matlab_proxy_status): + """Wait until MATLAB has started or time has run out Args: - is_matlab_licensed (bool): A flag indicating whether MATLAB is - licensed and eligible to start. - matlab_status (str): A string representing the current status - of the MATLAB startup process. - matlab_proxy_has_error (bool): A flag indicating whether there - is an error in the MATLAB proxy process during startup. + matlab_proxy_status: The status object from matlab-proxy Raises: MATLABConnectionError: If an error occurs while attempting to @@ -530,11 +639,12 @@ async def poll_for_matlab_startup( self.log.debug("Waiting until MATLAB is started") timeout = 0 while ( - matlab_status != "up" + matlab_proxy_status + and matlab_proxy_status.matlab_status != "up" and timeout != _MATLAB_STARTUP_TIMEOUT - and not matlab_proxy_has_error + and not matlab_proxy_status.matlab_proxy_has_error ): - if is_matlab_licensed: + if matlab_proxy_status.is_matlab_licensed: if timeout == 0: self.log.debug("Licensing completed. Clearing output area") self.display_output( @@ -551,11 +661,7 @@ async def poll_for_matlab_startup( ) timeout += 1 time.sleep(1) - ( - is_matlab_licensed, - matlab_status, - matlab_proxy_has_error, - ) = await self.mwi_comm_helper.fetch_matlab_proxy_status() + matlab_proxy_status = await self.mwi_comm_helper.fetch_matlab_proxy_status() # If MATLAB is not available after 15 seconds of licensing information # being available either through user input or through matlab-proxy cache, @@ -566,10 +672,15 @@ async def poll_for_matlab_startup( ) raise MATLABConnectionError - if matlab_proxy_has_error: + if not matlab_proxy_status or matlab_proxy_status.matlab_proxy_has_error: self.log.error("matlab-proxy encountered error.") raise MATLABConnectionError + # Update the kernel state with information from matlab proxy server + self.licensing_mode = matlab_proxy_status.licensing_mode + self.matlab_version = matlab_proxy_status.matlab_version + self.matlab_root_path = await self.mwi_comm_helper.fetch_matlab_root_path() + self.log.debug("MATLAB is running, startup checks completed.") def _extract_kernel_id_from_sys_args(self, args) -> str: diff --git a/src/jupyter_matlab_kernel/comms/__init__.py b/src/jupyter_matlab_kernel/comms/__init__.py new file mode 100644 index 000000000..c5f51be7c --- /dev/null +++ b/src/jupyter_matlab_kernel/comms/__init__.py @@ -0,0 +1,3 @@ +# Copyright 2025 The MathWorks, Inc. + +from .labextension import LabExtensionCommunication diff --git a/src/jupyter_matlab_kernel/comms/labextension/__init__.py b/src/jupyter_matlab_kernel/comms/labextension/__init__.py new file mode 100644 index 000000000..c5f51be7c --- /dev/null +++ b/src/jupyter_matlab_kernel/comms/labextension/__init__.py @@ -0,0 +1,3 @@ +# Copyright 2025 The MathWorks, Inc. + +from .labextension import LabExtensionCommunication diff --git a/src/jupyter_matlab_kernel/comms/labextension/labextension.py b/src/jupyter_matlab_kernel/comms/labextension/labextension.py new file mode 100644 index 000000000..aba744b85 --- /dev/null +++ b/src/jupyter_matlab_kernel/comms/labextension/labextension.py @@ -0,0 +1,55 @@ +# Copyright 2025 The MathWorks, Inc. + +from ipykernel.comm import Comm + + +class LabExtensionCommunication: + def __init__(self, kernel): + self.comms = {} + self.kernel = kernel + self.log = kernel.log + + def comm_open(self, stream, ident, msg): + """Handler to execute when labextension sends a message with 'comm_open' type .""" + + # As per jupyter messaging protocol https://jupyter-client.readthedocs.io/en/latest/messaging.html#custom-messages + # 'content' will be present in msg, 'comm_id' and 'target_name' will be present in content. + + content = msg["content"] + comm_id = content["comm_id"] + target_name = content["target_name"] + self.log.debug( + f"Received comm_open message with id: {comm_id} and target_name: {target_name}" + ) + comm = Comm(comm_id=comm_id, primary=False, target_name=target_name) + self.comms[comm_id] = comm + self.log.debug( + f"Successfully created communication channel with labextension on: {comm_id}" + ) + + async def comm_msg(self, stream, ident, msg): + """Handler to execute when labextension sends a message with 'comm_msg' type.""" + # As per jupyter messaging protocol https://jupyter-client.readthedocs.io/en/latest/messaging.html#custom-messages + # 'content' will be present in msg, 'comm_id' and 'data' will be present in content. + payload = msg["content"]["data"] + action_type, action_data = payload["action"], payload["data"] + + self.log.debug( + f"Received action_type:{action_type} with data:{action_data} from the lab extension" + ) + + def comm_close(self, stream, ident, msg): + """Handler to execute when labextension sends a message with 'comm_close' type.""" + + # As per jupyter messaging protocol https://jupyter-client.readthedocs.io/en/latest/messaging.html#custom-messages + # 'content' will be present in msg, 'comm_id' and 'data' will be present in content. + content = msg["content"] + comm_id = content["comm_id"] + comm = self.comms.get(comm_id) + + if comm: + self.log.info(f"Comm closed with id: {comm_id}") + del self.comms[comm_id] + + else: + self.log.debug(f"Attempted to close unknown comm_id: {comm_id}") diff --git a/src/jupyter_matlab_kernel/jsp_kernel.py b/src/jupyter_matlab_kernel/jsp_kernel.py index cb1afcebc..8efbc73a2 100644 --- a/src/jupyter_matlab_kernel/jsp_kernel.py +++ b/src/jupyter_matlab_kernel/jsp_kernel.py @@ -1,4 +1,4 @@ -# Copyright 2024 The MathWorks, Inc. +# Copyright 2024-2025 The MathWorks, Inc. """This module contains derived class implementation of MATLABKernel that uses Jupyter Server to manage interactions with matlab-proxy & MATLAB. @@ -186,19 +186,24 @@ def __init__(self, *args, **kwargs): async def do_shutdown(self, restart): self.log.debug("Received shutdown request from Jupyter") - try: - await self.mwi_comm_helper.send_shutdown_request_to_matlab() - await self.mwi_comm_helper.disconnect() - except ( - MATLABConnectionError, - aiohttp.client_exceptions.ClientResponseError, - ) as e: - self.log.error( - f"Exception occurred while sending shutdown request to MATLAB:\n{e}" - ) + if self.is_matlab_assigned: + try: + await self.mwi_comm_helper.send_shutdown_request_to_matlab() + await self.mwi_comm_helper.disconnect() + except ( + MATLABConnectionError, + aiohttp.client_exceptions.ClientResponseError, + ) as e: + self.log.error( + f"Exception occurred while sending shutdown request to MATLAB:\n{e}" + ) return super().do_shutdown(restart) async def perform_startup_checks(self): """Overriding base function to provide a different iframe source""" await super().perform_startup_checks(self.jupyter_base_url, "matlab") + + async def start_matlab_proxy_and_comm_helper(self): + """Default implementation assumes that matlab is assigned""" + self.is_matlab_assigned = True diff --git a/src/jupyter_matlab_kernel/magic_execution_engine.py b/src/jupyter_matlab_kernel/magic_execution_engine.py index e7cd176a1..9169aadf1 100644 --- a/src/jupyter_matlab_kernel/magic_execution_engine.py +++ b/src/jupyter_matlab_kernel/magic_execution_engine.py @@ -1,4 +1,4 @@ -# Copyright 2024 The MathWorks, Inc. +# Copyright 2024-2025 The MathWorks, Inc. import importlib import re @@ -144,7 +144,7 @@ def get_magics_from_cell(cell_code): magic_dict = magic_matches.groupdict() params = magic_dict["params"].strip() if params: - magic_dict["params"] = re.split("\s+", params) + magic_dict["params"] = re.split(r"\s+", params) # transform ? parameter into a help magic, such that %%file? becomes %%help file if params.startswith("?"): # Ignore additional parameters after the ? parameter @@ -181,7 +181,7 @@ def magic_executor(magics_for_execution, magic_execution_function): for output_from_method in magic_method(): if output_from_method: if "type" in output_from_method: - yield output_from_method + yield (output_from_method) else: raise MagicError( f"Invalid result returned by a Magic command. Contact Magic Author to fix. \n Error: {output_from_method}\n Does not contain a key called type." diff --git a/src/jupyter_matlab_kernel/magics/README.md b/src/jupyter_matlab_kernel/magics/README.md index 117bfe433..038f8f8d6 100644 --- a/src/jupyter_matlab_kernel/magics/README.md +++ b/src/jupyter_matlab_kernel/magics/README.md @@ -15,6 +15,8 @@ This table lists the predefined magic commands you can use: |---|---|---|---|---| |`?` and `help`| Display documentation of given magic command.|Name of magic command.||`%%lsmagic?` or `%%help lsmagic`| |`lsmagic`|List predefined magic commands.|||`%%lsmagic`| +|`matlab new_session`|Starts a new MATLAB dedicated to the kernel instead of being shared across kernels.

Note: To change from a shared MATLAB to a dedicated MATLAB after you have already run MATLAB code in a notebook, you must first restart the kernel.|||`%%matlab new_session`| +|`matlab info`|Print a summary of the MATLAB session currently being used for the kernel. The summary includes the MATLAB version, root path, licensing mode, and whether the MATLAB is shared or dedicated to a kernel. |||`%%matlab info`| |`time`|Display time taken to execute a cell.|||`%%time`| |`file`|Save contents of cell as a file in the notebook folder. You can use this command to define and save new functions. For details, see the section below on how to [Create New Functions Using the %%file Magic Command](#create-new-functions-using-the-the-file-magic-command)|Name of saved file.|The file magic command will save the contents of the cell, but not execute them in MATLAB.|`%%file myfile.m`| @@ -63,6 +65,6 @@ Note: to use your function in MATLAB, remember to add the Jupyter notebook folde --- -Copyright 2024 The MathWorks, Inc. +Copyright 2024-2025 The MathWorks, Inc. --- diff --git a/src/jupyter_matlab_kernel/magics/base/matlab_magic.py b/src/jupyter_matlab_kernel/magics/base/matlab_magic.py index 510310896..8015399f2 100644 --- a/src/jupyter_matlab_kernel/magics/base/matlab_magic.py +++ b/src/jupyter_matlab_kernel/magics/base/matlab_magic.py @@ -1,4 +1,4 @@ -# Copyright 2024 The MathWorks, Inc. +# Copyright 2024-2025 The MathWorks, Inc. from jupyter_matlab_kernel import mwi_logger @@ -83,6 +83,11 @@ def before_cell_execute(self): "murl": new_url, "headers": new_headers, } + 4. To invoke the callback function: + { + "type": "callback", + "callback_function": callback_function_name, + } default: Empty dict ({}). """ yield {} diff --git a/src/jupyter_matlab_kernel/magics/matlab.py b/src/jupyter_matlab_kernel/magics/matlab.py new file mode 100644 index 000000000..5a207cbf2 --- /dev/null +++ b/src/jupyter_matlab_kernel/magics/matlab.py @@ -0,0 +1,219 @@ +# Copyright 2025 The MathWorks, Inc. + +from jupyter_matlab_kernel.magics.base.matlab_magic import MATLABMagic +from jupyter_matlab_kernel.mpm_kernel import MATLABKernelUsingMPM +from jupyter_matlab_kernel.mwi_exceptions import MagicError + +# Module constants +LICENSING_MODES = { + "mhlm": "Online Licensing", + "nlm": "Network License Manager", + "existing_license": "Existing License", +} +CMD_NEW_SESSION = "new_session" +CMD_INFO = "info" +EXISTING_NEW_SESSION_ERROR = "This kernel is already using a dedicated MATLAB.\n" +DEDICATED_SESSION_CONFIRMATION_MSG = ( + "A dedicated MATLAB session has been started for this kernel.\n" +) + + +async def handle_new_matlab_session(kernel: MATLABKernelUsingMPM): + """ + Handles the creation of a new dedicated MATLAB session for the kernel. + Args: + kernel: The kernel instance. + + Yields: + dict: Result dictionary containing execution status and confirmation message. + """ + # Validations + if kernel.is_matlab_assigned: + if not kernel.is_shared_matlab: + # No-op if already in an isolated MATLAB session + yield { + "type": "execute_result", + "mimetype": ["text/plain", "text/html"], + "value": [ + EXISTING_NEW_SESSION_ERROR, + f"
{EXISTING_NEW_SESSION_ERROR}
", + ], + } + return + + else: + # Shared MATLAB session is already assigned + kernel.log.warning( + "Cannot start a new MATLAB session while an existing session is active." + ) + raise MagicError( + "This notebook is currently linked to Default MATLAB session." + "To proceed, restart the kernel and run this magic command before any other MATLAB commands." + ) + # Starting new dedicated MATLAB session + try: + kernel.is_shared_matlab = False + await kernel.start_matlab_proxy_and_comm_helper() + kernel.is_matlab_assigned = True + + # Raises MATLABConnectionError if matlab-proxy failed to start in previous step + await kernel.perform_startup_checks() + kernel.startup_checks_completed = True + kernel.display_output({"type": "clear_output", "content": {"wait": False}}) + except Exception as ex: + _reset_kernel_state(kernel) + + # Try and cleanup the matlab-proxy process if it was started + await kernel.cleanup_matlab_proxy() + + # Raising here so that matlab magic output can display the error + raise MagicError(str(ex)) from ex + + yield { + "type": "execute_result", + "mimetype": ["text/plain", "text/html"], + "value": [ + DEDICATED_SESSION_CONFIRMATION_MSG, + f"
{DEDICATED_SESSION_CONFIRMATION_MSG}
", + ], + } + + +def _reset_kernel_state(kernel: MATLABKernelUsingMPM): + """ + Resets the kernel to its initial state for MATLAB session management. + + Args: + kernel (MATLABKernelUsingMPM): The MATLAB kernel instance whose state + needs to be reset. + """ + kernel.is_shared_matlab = True + kernel.is_matlab_assigned = False + kernel.startup_checks_completed = False + + +async def get_kernel_info(kernel): + """ + Provides information about the current MATLAB kernel state related to MATLAB. + + :param kernel: kernel object containing MATLAB information + """ + output = _format_info(kernel._get_kernel_info()) + yield { + "type": "execute_result", + "mimetype": ["text/plain", "text/html"], + "value": [ + output, + f"
{output}
", + ], + } + + +def _format_info(info) -> str: + """ + Formats MATLAB information into a formatted string. + + Args: + info: Dictionary containing MATLAB information. + + Returns: + str: Formatted string with MATLAB information. + """ + info_text = f'MATLAB Version: {info.get("matlab_version")}\n' + info_text += f'MATLAB Root Path: {info.get("matlab_root_path")}\n' + info_text += f'Licensing Mode: {LICENSING_MODES.get(info.get("licensing_mode"), "Unknown")}\n' + info_text += f'MATLAB Shared With Other Notebooks: {info.get("is_shared_matlab")}\n' + return info_text + + +class matlab(MATLABMagic): + info_about_magic = f""" + Starts a new MATLAB that is dedicated to the current kernel, instead of being shared across kernels. + + Usage: %%matlab {CMD_NEW_SESSION} or %%matlab {CMD_INFO} + + Note: To change from a shared MATLAB to a dedicated MATLAB after you have already run MATLAB code in a notebook, you must first restart the kernel. + """ + skip_matlab_execution = False + + def before_cell_execute(self): + """ + Processes the MATLAB magic command before cell execution. + + This method validates the parameters passed to the MATLAB magic command, + and yields appropriate callbacks based on the command type. + + Raises: + MagicError: If the number of parameters is not exactly one or if an unknown argument is provided. + + Yields: + dict: A dictionary containing callback information for the kernel to process. + The dictionary must contain a key called "type". Kernel injects itself into the callback function while + making the call. This ensures Kernel object is available to magic instance. + + Examples: To start a new matlab session or to display information about assigned MATLAB: + { + "type": "callback", + "callback_function": "function local to this module to be called from kernel", + } + """ + if len(self.parameters) != 1: + raise MagicError( + f"matlab magic expects only one argument. Received: {self.parameters}. Choose one of: {[arg for arg in self.get_supported_arguments()]}" + ) + + command = self.parameters[0] + # Handles "new_session" argument + if command == CMD_NEW_SESSION: + yield { + "type": "callback", + "callback_function": handle_new_matlab_session, + } + + # Handles "info" argument + elif command == CMD_INFO: + yield { + "type": "callback", + "callback_function": get_kernel_info, + } + + # Handles unknown arguments + else: + raise MagicError( + f"Unknown argument {command}. Choose one of: {[arg for arg in self.get_supported_arguments()]}" + ) + + def do_complete(self, parameters, parameter_pos, cursor_pos): + """ + Provides autocompletion for the matlab magic command. + + Args: + parameters (list): The parameters passed to the magic command + parameter_pos (int): The position of the parameter being completed + cursor_pos (int): The cursor position within the parameter + + Returns: + list: A list of possible completions + """ + matches = [] + if parameter_pos == 1: + # Show all the arguments under matlab magic + if cursor_pos == 0: + matches = self.get_supported_arguments() + # For partial input, match arguments that start with the current input + else: + matches = [ + s + for s in self.get_supported_arguments() + if s.startswith(parameters[0][:cursor_pos]) + ] + return matches + + def get_supported_arguments(self) -> list: + """ + Returns a list of supported arguments for the MATLAB magic command. + + Returns: + list: A list of supported arguments + """ + return [CMD_NEW_SESSION, CMD_INFO] diff --git a/src/jupyter_matlab_kernel/matlab/+jupyter/execute.m b/src/jupyter_matlab_kernel/matlab/+jupyter/execute.m index 31287aca1..0b0f00bd8 100644 --- a/src/jupyter_matlab_kernel/matlab/+jupyter/execute.m +++ b/src/jupyter_matlab_kernel/matlab/+jupyter/execute.m @@ -214,7 +214,12 @@ persistent idler; if isempty(webwindow) - url = 'toolbox/matlab/codetools/liveeditor/index.html'; + % Use liveeditor index.html in older MATLAB versions + if isMATLABReleaseOlderThan("R2026a") + url = 'toolbox/matlab/codetools/liveeditor/index.html'; + else + url = 'toolbox/matlab/editor/application/index.html'; + end % MATLAB versions R2020b and R2021a requires specifying the base url. % Not doing so results in the URL not being loaded with the error diff --git a/src/jupyter_matlab_kernel/mpm_kernel.py b/src/jupyter_matlab_kernel/mpm_kernel.py index 1e2a96047..e52140a81 100644 --- a/src/jupyter_matlab_kernel/mpm_kernel.py +++ b/src/jupyter_matlab_kernel/mpm_kernel.py @@ -18,9 +18,6 @@ class MATLABKernelUsingMPM(base.BaseMATLABKernel): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - # Used to detect if this Kernel has been assigned a MATLAB-proxy server or not - self.is_matlab_assigned = False - # Serves as the auth token to secure communication between Jupyter Server and MATLAB proxy manager self.mpm_auth_token = None @@ -35,41 +32,9 @@ def __init__(self, *args, **kwargs): # ipykernel Interface API # https://ipython.readthedocs.io/en/stable/development/wrapperkernels.html - async def do_execute( - self, - code, - silent, - store_history=True, - user_expressions=None, - allow_stdin=False, - *, - cell_id=None, - ): - """ - Used by ipykernel infrastructure for execution. For more info, look at - https://jupyter-client.readthedocs.io/en/stable/messaging.html#execute - """ - self.log.debug(f"Received execution request from Jupyter with code:\n{code}") - - # Starts the matlab proxy process if this kernel hasn't yet been assigned a - # matlab proxy and sets the attributes on kernel to talk to the correct backend. - if not self.is_matlab_assigned: - self.log.debug("Starting matlab-proxy") - await self._start_matlab_proxy_and_comm_helper() - self.is_matlab_assigned = True - - return await super().do_execute( - code=code, - silent=silent, - store_history=store_history, - user_expressions=user_expressions, - allow_stdin=allow_stdin, - cell_id=cell_id, - ) - async def do_shutdown(self, restart): self.log.debug("Received shutdown request from Jupyter") - if self.is_matlab_assigned: + if self.is_matlab_assigned and self.mwi_comm_helper: try: # Cleans up internal live editor state, client session await self.mwi_comm_helper.send_shutdown_request_to_matlab() @@ -80,25 +45,28 @@ async def do_shutdown(self, restart): f"Exception occurred while sending shutdown request to MATLAB:\n{e}" ) except Exception as e: - self.log.debug("Exception during shutdown", e) + self.log.debug("Exception during shutdown: %s", e) finally: - # Shuts down matlab assigned to this Kernel (based on satisfying certain criteria) - await mpm_lib.shutdown( - self.parent_pid, self.kernel_id, self.mpm_auth_token - ) - self.is_matlab_assigned = False + await self.cleanup_matlab_proxy() return super().do_shutdown(restart) + # Helper functions + + async def cleanup_matlab_proxy(self): + # Shuts down matlab-proxy and MATLAB assigned to this Kernel. + # matlab-proxy process is cleaned up when this Kernel process is the + # only reference to the assigned matlab-proxy instance + await mpm_lib.shutdown(self.parent_pid, self.kernel_id, self.mpm_auth_token) + self.is_matlab_assigned = False + async def perform_startup_checks(self): """Overriding base function to provide a different iframe source""" await super().perform_startup_checks( self.jupyter_base_url, f"{self.matlab_proxy_base_url}/" ) - # Helper functions - - async def _start_matlab_proxy_and_comm_helper(self) -> None: + async def start_matlab_proxy_and_comm_helper(self) -> None: """ Starts the MATLAB proxy using the proxy manager and fetches its status. """ @@ -137,7 +105,7 @@ async def _initialize_matlab_proxy_with_mpm(self, _logger: Logger): response = await mpm_lib.start_matlab_proxy_for_kernel( caller_id=self.kernel_id, parent_id=self.parent_pid, - is_shared_matlab=True, + is_shared_matlab=self.is_shared_matlab, base_url_prefix=self.jupyter_base_url, ) err = response.get("errors") diff --git a/src/jupyter_matlab_kernel/mwi_comm_helpers.py b/src/jupyter_matlab_kernel/mwi_comm_helpers.py index 8365a9b45..f3f542810 100644 --- a/src/jupyter_matlab_kernel/mwi_comm_helpers.py +++ b/src/jupyter_matlab_kernel/mwi_comm_helpers.py @@ -1,9 +1,11 @@ -# Copyright 2023-2024 The MathWorks, Inc. +# Copyright 2023-2025 The MathWorks, Inc. # Helper functions to communicate with matlab-proxy and MATLAB import http import json import pathlib +from dataclasses import dataclass +from typing import Optional import aiohttp from matlab_proxy.util.mwi.embedded_connector.helpers import ( @@ -27,6 +29,27 @@ def check_licensing_status(data): return licensing_status +@dataclass +class MATLABStatus: + """Represents the status of MATLAB and its licensing information. + + Attributes: + is_matlab_licensed (bool): Indicates whether MATLAB is properly licensed. + matlab_status (str): Current status of the MATLAB instance. + matlab_proxy_has_error (bool): Whether the MATLAB proxy has encountered an error. Defaults to False. + licensing_mode (str): The type of licensing being used. Defaults to an empty string. + matlab_version (str): Version of the MATLAB instance. Defaults to an empty string. + matlab_root_path (str): Root installation path of MATLAB. Defaults to an empty string. + """ + + is_matlab_licensed: bool + matlab_status: str + matlab_proxy_has_error: bool = False + licensing_mode: str = "" + matlab_version: str = "" + matlab_root_path: str = "" + + class MWICommHelper: def __init__( self, kernel_id, url, shell_loop, control_loop, headers=None, logger=_logger @@ -88,35 +111,84 @@ async def disconnect(self): if self._http_control_client: await self._http_control_client.close() - async def fetch_matlab_proxy_status(self): + async def fetch_matlab_root_path(self) -> Optional[str]: + """ + Fetches the MATLAB root path from the matlab-proxy server. + + Sends an HTTP GET request to the /get_env_config endpoint of matlab-proxy + to retrieve the filesystem path to the MATLAB installation. + + Returns: + Optional[str]: The filesystem path to the MATLAB installation root directory, + or None if the path could not be retrieved. + """ + self.logger.debug("Fetching MATLAB root path from matlab-proxy") + resp = await self._http_shell_client.get(self.url + "/get_env_config") + self.logger.debug( + f"Received status code for matlab-proxy get-env-config request: {resp.status}" + ) + + if resp.status == http.HTTPStatus.OK: + data = await resp.json() + self.logger.debug(f"get-env-config data:\n{data}") + matlab_data = data.get("matlab") or {} + return matlab_data.get("rootPath", None) + + self.logger.warning( + "Error occurred during retrieving environment config for matlab-proxy" + ) + return None + + async def fetch_matlab_proxy_status(self) -> Optional[MATLABStatus]: """ - Sends HTTP request to /get_status endpoint of matlab-proxy and returns - license and MATLAB status. + Fetches the current status of the MATLAB proxy server. + + Sends an HTTP GET request to the /get_status endpoint of matlab-proxy + to retrieve information about MATLAB licensing, runtime status, and any + errors that may have occurred. Returns: - Tuple (bool, string): - is_matlab_licensed (bool): True if matlab-proxy has license information, else False. - matlab_status (string): Status of MATLAB. Values could be "up", "down" and "starting" - matlab_proxy_has_error (bool): True if matlab-proxy faced any issues and unable to - start MATLAB + Optional[MATLABStatus]: A MATLABStatus object containing: + - is_matlab_licensed (bool): True if MATLAB has valid license + information, False otherwise. + - matlab_status (str): Current MATLAB state. Possible values: + "up" (running), "down" (stopped). + - matlab_proxy_has_error (bool): True if matlab-proxy encountered + errors preventing MATLAB startup, False otherwise. + - licensing_mode (str): The type of licensing being used + (e.g., "mhlm", "nlm", "existing_license"). + - matlab_version (str): The version string of the MATLAB installation + (e.g., "R2024a"). + Raises: - HTTPError: Occurs when connection to matlab-proxy cannot be established. + HTTPError: If the HTTP request fails or matlab-proxy returns a + non-200 status code, indicating connection issues or + server errors. + + Example: + >>> status = await comm_helper.fetch_matlab_proxy_status() + >>> if status and status.matlab_status == "up": + ... print(f"MATLAB {status.matlab_version} is running") """ self.logger.debug("Fetching matlab-proxy status") resp = await self._http_shell_client.get(self.url + "/get_status") self.logger.debug(f"Received status code: {resp.status}") if resp.status == http.HTTPStatus.OK: data = await resp.json() - self.logger.debug(f"Response:\n{data}") - is_matlab_licensed = check_licensing_status(data) - - matlab_status = data["matlab"]["status"] - matlab_proxy_has_error = data["error"] is not None - return is_matlab_licensed, matlab_status, matlab_proxy_has_error + self.logger.debug(f"matlab-proxy status:\n{data}") + matlab_data = data.get("matlab") or {} + return MATLABStatus( + is_matlab_licensed=check_licensing_status(data), + matlab_status=matlab_data.get("status", ""), + matlab_proxy_has_error=data.get("error") is not None, + licensing_mode=(data.get("licensing") or {}).get("type", ""), + matlab_version=matlab_data.get("version", ""), + ) else: self.logger.error("Error occurred during communication with matlab-proxy") resp.raise_for_status() + return None async def send_execution_request_to_matlab(self, code): """ @@ -179,6 +251,13 @@ async def send_shutdown_request_to_matlab(self): ) async def send_interrupt_request_to_matlab(self): + """Send an interrupt request to MATLAB to stop current execution. + + The interrupt request is sent through the control channel using a specific message format. + + Raises: + HTTPError: If the interrupt request fails or matlab-proxy communication errors occur + """ self.logger.debug("Sending interrupt request to MATLAB") req_body = { "messages": { @@ -201,6 +280,24 @@ async def send_interrupt_request_to_matlab(self): resp.raise_for_status() async def _send_feval_request_to_matlab(self, http_client, fname, nargout, *args): + """Execute a MATLAB function call (feval) through the matlab-proxy. + + Sends a function evaluation request to MATLAB, handling path setup and synchronous execution. + + Args: + http_client (aiohttp.ClientSession): HTTP client for sending the request + fname (str): Name of the MATLAB function to call + nargout (int): Number of output arguments expected + *args: Variable arguments to pass to the MATLAB function + + Returns: + list: Results from the MATLAB function execution if successful + Empty list if no outputs or nargout=0 + + Raises: + MATLABConnectionError: If MATLAB connection is lost or response is invalid + Exception: If function execution fails or is interrupted by user + """ self.logger.debug("Sending FEval request to MATLAB") # Add the MATLAB code shipped with kernel to the Path path = [str(pathlib.Path(__file__).parent / "matlab")] @@ -256,7 +353,7 @@ async def _send_feval_request_to_matlab(self, http_client, fname, nargout, *args ) else: self.logger.error( - f'Error during execution of FEval request in MATLAB:\n{feval_response["messageFaults"][0]["message"]}' + f"Error during execution of FEval request in MATLAB:\n{feval_response['messageFaults'][0]['message']}" ) error_message = "Failed to execute. Please try again." raise Exception(error_message) @@ -264,7 +361,36 @@ async def _send_feval_request_to_matlab(self, http_client, fname, nargout, *args self.logger.error("Error occurred during communication with matlab-proxy") raise resp.raise_for_status() + async def send_eval_request_to_matlab(self, mcode): + """Send an evaluation request to MATLAB using the shell client. + + Args: + mcode (str): MATLAB code to be evaluated + + Returns: + dict: The evaluation response from MATLAB containing results or error information + + Raises: + MATLABConnectionError: If MATLAB connection is not available + HTTPError: If there is an error in communication with matlab-proxy + """ + return await self._send_eval_request_to_matlab(self._http_shell_client, mcode) + async def _send_eval_request_to_matlab(self, http_client, mcode): + """Internal method to send and process an evaluation request to MATLAB. + + Args: + http_client (aiohttp.ClientSession): HTTP client to use for the request + mcode (str): MATLAB code to be evaluated + + Returns: + dict: The evaluation response containing results or error information + from the MATLAB execution + + Raises: + MATLABConnectionError: If MATLAB connection is not available or response is invalid + HTTPError: If there is an error in communication with matlab-proxy + """ self.logger.debug("Sending Eval request to MATLAB") # Add the MATLAB code shipped with kernel to the Path path = str(pathlib.Path(__file__).parent / "matlab") @@ -286,6 +412,7 @@ async def _send_eval_request_to_matlab(self, http_client, mcode): self.logger.debug(f"Response:\n{response_data}") try: eval_response = response_data["messages"]["EvalResponse"][0] + except KeyError: # In certain cases when the HTTPResponse is received, it does not # contain the expected data. In these cases most likely MATLAB has @@ -296,54 +423,27 @@ async def _send_eval_request_to_matlab(self, http_client, mcode): ) raise MATLABConnectionError() - # If the eval request succeeded, return the json decoded result. - if not eval_response["isError"]: - result_filepath = eval_response["responseStr"].strip() - - # If the filepath in the response is not empty, read the result from - # file and delete the file. - if result_filepath != "": - self.logger.debug(f"Found file with results: {result_filepath}") - self.logger.debug("Reading contents of the file") - with open(result_filepath, "r") as f: - result = f.read().strip() - self.logger.debug("Reading completed") - try: - import os - - self.logger.debug(f"Deleting file: {result_filepath}") - os.remove(result_filepath) - except Exception: - self.logger.error("Deleting file failed") - else: - self.logger.debug("No result in EvalResponse") - result = "" - - # If result is empty, populate dummy json - if result == "": - result = "[]" - return json.loads(result) - - # Handle the error cases - if eval_response["messageFaults"]: - # This happens when "Interrupt Kernel" is issued from a different - # kernel. There may be other cases also. - self.logger.error( - f'Error during execution of Eval request in MATLAB:\n{eval_response["messageFaults"][0]["message"]}' - ) - error_message = ( - "Failed to execute. Operation may have been interrupted by user." - ) - else: - # This happens when "Interrupt Kernel" is issued from the same kernel. - # The responseStr contains the error message - error_message = eval_response["responseStr"].strip() - raise Exception(error_message) + return eval_response + else: self.logger.error("Error during communication with matlab-proxy") raise resp.raise_for_status() async def _send_jupyter_request_to_matlab(self, request_type, inputs, http_client): + """Process and send a Jupyter request to MATLAB using either feval or eval execution. + + Args: + request_type (str): Type of request (execute, complete, shutdown) + inputs (list): List of input arguments for the request + http_client (aiohttp.ClientSession): HTTP client to use for the request + + Returns: + dict: Response from MATLAB containing results of the request execution + + Raises: + MATLABConnectionError: If MATLAB connection is not available + Exception: If request execution fails or is interrupted + """ execution_request_type = "feval" inputs.insert(0, request_type) @@ -353,10 +453,14 @@ async def _send_jupyter_request_to_matlab(self, request_type, inputs, http_clien f"Using {execution_request_type} request type for communication with EC" ) + resp = None if execution_request_type == "feval": resp = await self._send_feval_request_to_matlab( http_client, "processJupyterKernelRequest", 1, *inputs ) + + # The 'else' condition is an artifact and is present here incase we ever want to test + # eval execution. else: user_mcode = inputs[2] # Construct a string which can be evaluated in MATLAB. For example @@ -376,6 +480,66 @@ async def _send_jupyter_request_to_matlab(self, request_type, inputs, http_clien args = args + "," + str(cursor_pos) eval_mcode = f"processJupyterKernelRequest({args})" - resp = await self._send_eval_request_to_matlab(http_client, eval_mcode) + eval_response = await self._send_eval_request_to_matlab( + http_client, eval_mcode + ) + resp = await self._read_eval_response_from_file(eval_response) return resp + + async def _read_eval_response_from_file(self, eval_response): + """Read and process MATLAB evaluation results from a response file. + + Args: + eval_response (dict): Response dictionary from MATLAB eval request containing + file path and error information + + Returns: + dict: JSON decoded results from the response file + + Raises: + Exception: If evaluation failed or was interrupted by user + """ + # If the eval request succeeded, return the json decoded result. + if not eval_response["isError"]: + result_filepath = eval_response["responseStr"].strip() + + # If the filepath in the response is not empty, read the result from + # file and delete the file. + if result_filepath != "": + self.logger.debug(f"Found file with results: {result_filepath}") + self.logger.debug("Reading contents of the file") + with open(result_filepath, "r") as f: + result = f.read().strip() + self.logger.debug("Reading completed") + try: + import os + + self.logger.debug(f"Deleting file: {result_filepath}") + os.remove(result_filepath) + except Exception: + self.logger.error("Deleting file failed") + else: + self.logger.debug("No result in EvalResponse") + result = "" + + # If result is empty, populate dummy json + if result == "": + result = "[]" + return json.loads(result) + + # Handle the error cases + if eval_response["messageFaults"]: + # This happens when "Interrupt Kernel" is issued from a different + # kernel. There may be other cases also. + self.logger.error( + f'Error during execution of Eval request in MATLAB:\n{eval_response["messageFaults"][0]["message"]}' + ) + error_message = ( + "Failed to execute. Operation may have been interrupted by user." + ) + else: + # This happens when "Interrupt Kernel" is issued from the same kernel. + # The responseStr contains the error message + error_message = eval_response["responseStr"].strip() + raise Exception(error_message) diff --git a/src/jupyter_matlab_kernel/mwi_exceptions.py b/src/jupyter_matlab_kernel/mwi_exceptions.py index 58671158a..da45bc463 100644 --- a/src/jupyter_matlab_kernel/mwi_exceptions.py +++ b/src/jupyter_matlab_kernel/mwi_exceptions.py @@ -1,4 +1,4 @@ -# Copyright 2024 The MathWorks, Inc. +# Copyright 2024-2025 The MathWorks, Inc. # Custom Exceptions used in MATLAB Kernel @@ -38,5 +38,5 @@ class MATLABConnectionError(Exception): def __init__(self, message=None): if message is None: - message = 'Error connecting to MATLAB. Check the status of MATLAB by clicking the "Open MATLAB" button. Retry after ensuring MATLAB is running successfully' + message = 'Error connecting to MATLAB. Check the status of MATLAB by clicking the "Open MATLAB" button. Retry after ensuring MATLAB is running successfully.' super().__init__(message) diff --git a/src/jupyter_matlab_labextension/.eslintrc.json b/src/jupyter_matlab_labextension/.eslintrc.json index b5c169fc9..983f3e9d5 100644 --- a/src/jupyter_matlab_labextension/.eslintrc.json +++ b/src/jupyter_matlab_labextension/.eslintrc.json @@ -1,10 +1,9 @@ - { "root": true, "parser": "@typescript-eslint/parser", "parserOptions": { - "ecmaVersion": 6, - "sourceType": "module" + "ecmaVersion": 6, + "sourceType": "module" }, "env": { "jest": true @@ -12,33 +11,32 @@ "plugins": ["@typescript-eslint"], "extends": ["standard"], "rules": { - "indent": ["error", 4, { "SwitchCase": 1 }], - "semi": ["error", "always"], - "no-extra-semi": "error", - "quote-props": "warn", - "dot-notation": "warn", - "object-curly-newline": "warn", - "multiline-ternary": "warn", - "prefer-const": "warn", - "no-prototype-builtins": "warn", - "array-callback-return": "warn", - "array-bracket-spacing": "warn", - "quotes": "warn", - "lines-between-class-members": "warn", - "no-empty": "warn", - "prefer-regex-literals": "warn", - "no-useless-catch": "warn", - "no-case-declarations": "warn", - "computed-property-spacing": "warn", - "no-async-promise-executor": "warn", - "no-unused-vars": "warn", - "no-unreachable-loop": "warn", - "no-void": "warn", - "import/no-webpack-loader-syntax": "warn", - "node/no-callback-literal": ["off", "warn"], - "node/handle-callback-err": ["off", "warn"], - "node/no-deprecated-api": ["off", "warn"] + "indent": ["error", 4, { "SwitchCase": 1 }], + "semi": ["error", "always"], + "no-extra-semi": "error", + "quote-props": "warn", + "dot-notation": "warn", + "object-curly-newline": "warn", + "multiline-ternary": "warn", + "prefer-const": "warn", + "no-prototype-builtins": "warn", + "array-callback-return": "warn", + "array-bracket-spacing": "warn", + "quotes": "warn", + "lines-between-class-members": "warn", + "no-empty": "warn", + "prefer-regex-literals": "warn", + "no-useless-catch": "warn", + "no-case-declarations": "warn", + "computed-property-spacing": "warn", + "no-async-promise-executor": "warn", + "no-unused-vars": "warn", + "no-unreachable-loop": "warn", + "no-void": "warn", + "import/no-webpack-loader-syntax": "warn", + "node/no-callback-literal": ["off", "warn"], + "node/handle-callback-err": ["off", "warn"], + "node/no-deprecated-api": ["off", "warn"], + "no-multiple-empty-lines": "warn" } } - - diff --git a/src/jupyter_matlab_labextension/.styleintrc.json b/src/jupyter_matlab_labextension/.styleintrc.json new file mode 100644 index 000000000..e69de29bb diff --git a/src/jupyter_matlab_labextension/jest.config.js b/src/jupyter_matlab_labextension/jest.config.js index ab6e1bba0..2f10abe16 100644 --- a/src/jupyter_matlab_labextension/jest.config.js +++ b/src/jupyter_matlab_labextension/jest.config.js @@ -1,7 +1,19 @@ // Copyright 2025 The MathWorks, Inc. module.exports = { - preset: 'ts-jest', - testEnvironment: 'node', - testMatch: ['**/__tests__/**/*.ts?(x)', '**/?(*.)+(spec|test).ts?(x)'], - moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'], + preset: "ts-jest", + testEnvironment: "node", + testMatch: ["src/tests/**/*.ts?(x)", "**/?(*.)+(spec|test).ts?(x)"], + testPathIgnorePatterns: ["/node_modules/", "/src/tests/jest-setup.ts"], + moduleFileExtensions: ["ts", "tsx", "js", "jsx", "json", "node"], + setupFilesAfterEnv: ["/src/tests/jest-setup.ts"], + transform: { + "^.+\\.(ts|tsx)$": "ts-jest", + }, + transformIgnorePatterns: [ + "/node_modules/(?!(@jupyterlab)/)", // Transform @jupyterlab packages + ], + moduleNameMapper: { + // Mock @jupyterlab/ui-components to avoid ES modules issues + "@jupyterlab/ui-components": "/src/tests/mocks/ui-components.js", + }, }; diff --git a/src/jupyter_matlab_labextension/package.json b/src/jupyter_matlab_labextension/package.json index 45f6200ef..3f10baca4 100644 --- a/src/jupyter_matlab_labextension/package.json +++ b/src/jupyter_matlab_labextension/package.json @@ -29,7 +29,7 @@ "build:labextension": "jupyter labextension build .", "build:labextension:dev": "jupyter labextension build --development True .", "build:lib": "jlpm build:lezer && tsc", - "build:lezer": "cd src/lezer-matlab && npm install", + "build:lezer": "cd src/lezer-matlab && npm install && npm run build", "clean": "jlpm clean:lib", "clean:lib": "rimraf lib tsconfig.tsbuildinfo", "clean:lintcache": "rimraf .eslintcache .stylelintcache", @@ -60,6 +60,7 @@ "@jupyterlab/coreutils": "^6.0.0", "@jupyterlab/docregistry": "^4.0.0", "@jupyterlab/launcher": "^4.0.0", + "@jupyterlab/mainmenu": "^4.3.4", "@jupyterlab/notebook": "^4.0.0", "@jupyterlab/ui-components": "^4.0.0", "@lumino/coreutils": "^2.0.0", @@ -68,6 +69,7 @@ "devDependencies": { "@jupyterlab/builder": ">=4.0.0", "@types/jest": "^29.5.14", + "@types/node": "^24.0.4", "@typescript-eslint/eslint-plugin": "^5.62.0", "@typescript-eslint/parser": "^5.62.0", "cross-spawn": "^6.0.6", diff --git a/src/jupyter_matlab_labextension/src/codemirror-lang-matlab/codemirror-lang-matlab.ts b/src/jupyter_matlab_labextension/src/codemirror-lang-matlab/codemirror-lang-matlab.ts index d1203fb9a..bd94c11b0 100644 --- a/src/jupyter_matlab_labextension/src/codemirror-lang-matlab/codemirror-lang-matlab.ts +++ b/src/jupyter_matlab_labextension/src/codemirror-lang-matlab/codemirror-lang-matlab.ts @@ -1,13 +1,21 @@ // Copyright 2024-2025 The MathWorks, Inc. import { parser } from '../lezer-matlab/dist/index'; -import { indentNodeProp, LanguageSupport, LRLanguage, TreeIndentContext } from '@codemirror/language'; +import { + indentNodeProp, + LanguageSupport, + LRLanguage, + TreeIndentContext +} from '@codemirror/language'; import { lineIndent, getDedentPattern } from './indent-matlab'; function determineLineIndent (context: TreeIndentContext) { - if (context.pos === 0) { return null; } + if (context.pos === 0) { + return null; + } const currentLine = context.lineAt(context.pos); - const previousLine = currentLine.text.length === 0 + const previousLine = + currentLine.text.length === 0 ? context.lineAt(context.pos, -1) // Look to the left of the simulated line break. : context.lineAt(context.pos - 1); // Not on a simulated line break, so step back to the previous line. if (previousLine === null || currentLine === null) { diff --git a/src/jupyter_matlab_labextension/src/codemirror-lang-matlab/indent-matlab.ts b/src/jupyter_matlab_labextension/src/codemirror-lang-matlab/indent-matlab.ts index d25be0b15..83dce4b0f 100644 --- a/src/jupyter_matlab_labextension/src/codemirror-lang-matlab/indent-matlab.ts +++ b/src/jupyter_matlab_labextension/src/codemirror-lang-matlab/indent-matlab.ts @@ -1,7 +1,8 @@ // Copyright 2025 The MathWorks, Inc. // Indent after these keywords unless the line ends with "end". -const indentPattern = /^(?:\s*)(arguments|case|catch|classdef|else|elseif|enumeration|for|function|if|methods|otherwise|parfor|properties|switch|try|while)\b(?!.*\bend;?$)/; +const indentPattern = + /^(?:\s*)(arguments|case|catch|classdef|else|elseif|enumeration|for|function|if|methods|otherwise|parfor|properties|switch|try|while)\b(?!.*\bend;?$)/; const dedentPattern = /^(?:\s*)(case|catch|else|end|otherwise)\b$/; const leadingWhitespacePattern = /^\s*/; @@ -9,16 +10,31 @@ export function getDedentPattern (): RegExp { return new RegExp(dedentPattern); } -export function lineIndent (indentUnit: number, currentLineText: string, previousLineText: string) { - const prevLeadingWhitespace = previousLineText.match(leadingWhitespacePattern); - const prevLineIndent = prevLeadingWhitespace ? prevLeadingWhitespace[0].length : 0; - if (currentLineText.match(/^(?:\s*)(case)\b$/) && previousLineText.match(/^(?:\s*)(switch)\b/)) { +export function lineIndent ( + indentUnit: number, + currentLineText: string, + previousLineText: string +) { + const prevLeadingWhitespace = previousLineText.match( + leadingWhitespacePattern + ); + const prevLineIndent = prevLeadingWhitespace + ? prevLeadingWhitespace[0].length + : 0; + if ( + currentLineText.match(/^(?:\s*)(case)\b$/) && + previousLineText.match(/^(?:\s*)(switch)\b/) + ) { // First case in a switch statement. return prevLineIndent + indentUnit; } else if (currentLineText.match(/^(?:\s*)(end)\b$/)) { // Treat "end" separately to avoid mistakenly correcting the end of a switch statement. - const currentLeadingWhitespace = currentLineText.match(leadingWhitespacePattern); - const currentLineIndent = currentLeadingWhitespace ? currentLeadingWhitespace[0].length : 0; + const currentLeadingWhitespace = currentLineText.match( + leadingWhitespacePattern + ); + const currentLineIndent = currentLeadingWhitespace + ? currentLeadingWhitespace[0].length + : 0; const indentMatch = previousLineText.match(indentPattern); if (indentMatch) { return Math.min(prevLineIndent, currentLineIndent); diff --git a/src/jupyter_matlab_labextension/src/index.ts b/src/jupyter_matlab_labextension/src/index.ts index bc2d8c760..946310e87 100644 --- a/src/jupyter_matlab_labextension/src/index.ts +++ b/src/jupyter_matlab_labextension/src/index.ts @@ -1,9 +1,15 @@ -// Copyright 2023-2024 The MathWorks, Inc. +// Copyright 2023-2025 The MathWorks, Inc. import { JupyterFrontEndPlugin } from '@jupyterlab/application'; -import { matlabToolbarButtonPlugin } from './matlab_browser_button'; -import { matlabMFilesPlugin } from './matlab_files'; -import { matlabCodeMirror6Plugin } from './matlab_cm6_mode'; +import { matlabToolbarButtonPlugin } from './plugins/matlabToolbarButton'; +import { matlabMFilesPlugin } from './plugins/matlabFiles'; +import { matlabCodeMirror6Plugin } from './plugins/matlabCM6Mode'; +import { matlabCommPlugin } from './plugins/matlabCommunication'; -const plugins: JupyterFrontEndPlugin[] = [matlabToolbarButtonPlugin, matlabMFilesPlugin, matlabCodeMirror6Plugin]; +const plugins: JupyterFrontEndPlugin[] = [ + matlabToolbarButtonPlugin, + matlabMFilesPlugin, + matlabCodeMirror6Plugin, + matlabCommPlugin +]; export default plugins; diff --git a/src/jupyter_matlab_labextension/src/lezer-matlab/package-lock.json b/src/jupyter_matlab_labextension/src/lezer-matlab/package-lock.json index f049cc669..e94080583 100644 --- a/src/jupyter_matlab_labextension/src/lezer-matlab/package-lock.json +++ b/src/jupyter_matlab_labextension/src/lezer-matlab/package-lock.json @@ -391,10 +391,11 @@ } }, "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==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" } @@ -838,10 +839,11 @@ } }, "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==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", "dev": true, + "license": "MIT", "dependencies": { "argparse": "^2.0.1" }, @@ -1060,6 +1062,7 @@ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.22.4.tgz", "integrity": "sha512-vD8HJ5raRcWOyymsR6Z3o6+RzfEPCnVLMFJ6vRslO1jt4LO6dUo5Qnpg7y4RkZFM2DMe3WUirkI5c16onjrc6A==", "dev": true, + "peer": true, "dependencies": { "@types/estree": "1.0.5" }, diff --git a/src/jupyter_matlab_labextension/src/lezer-matlab/rollup.config.js b/src/jupyter_matlab_labextension/src/lezer-matlab/rollup.config.js index b285d4511..8e3b4f682 100644 --- a/src/jupyter_matlab_labextension/src/lezer-matlab/rollup.config.js +++ b/src/jupyter_matlab_labextension/src/lezer-matlab/rollup.config.js @@ -1,26 +1,27 @@ -// Copyright 2024 The MathWorks, Inc. +// Copyright 2024-2025 The MathWorks, Inc. -import { nodeResolve } from '@rollup/plugin-node-resolve'; -import path from 'path'; +import { nodeResolve } from "@rollup/plugin-node-resolve"; +import path from "path"; -const entryModule = './src/parser.js'; +const entryModule = "./src/parser.js"; export default { input: entryModule, - output: [{ - format: "cjs", - file: "./dist/index.cjs" - }, { - format: "es", - file: "./dist/index.js" - }], + output: [ + { + format: "cjs", + file: "./dist/index.cjs", + }, + { + format: "es", + file: "./dist/index.js", + }, + ], external(id) { if (id === path.resolve(entryModule)) { return false; } return !/^[\.\/]/.test(id); }, - plugins: [ - nodeResolve() - ] -} + plugins: [nodeResolve()], +}; diff --git a/src/jupyter_matlab_labextension/src/lezer-matlab/src/highlight.js b/src/jupyter_matlab_labextension/src/lezer-matlab/src/highlight.js index d15b68191..4c289ce38 100644 --- a/src/jupyter_matlab_labextension/src/lezer-matlab/src/highlight.js +++ b/src/jupyter_matlab_labextension/src/lezer-matlab/src/highlight.js @@ -1,18 +1,18 @@ -// Copyright 2024 The MathWorks, Inc. +// Copyright 2024-2025 The MathWorks, Inc. -import { styleTags, tags as t } from '@lezer/highlight'; +import { styleTags, tags as t } from "@lezer/highlight"; // Associate nodes in the Lezer tree with styles. // https://lezer.codemirror.net/docs/ref/#highlight.styleTags export const matlabHighlighting = styleTags({ - Keyword: t.keyword, - Identifier: t.variableName, - LineComment: t.comment, - MultilineComment: t.comment, - SystemCommand: t.meta, - String: t.string, - Magic: t.monospace, - '( )': t.paren, - '[ ]': t.squareBracket, - '{ }': t.brace + Keyword: t.keyword, + Identifier: t.variableName, + LineComment: t.comment, + MultilineComment: t.comment, + SystemCommand: t.meta, + String: t.string, + Magic: t.monospace, + "( )": t.paren, + "[ ]": t.squareBracket, + "{ }": t.brace, }); diff --git a/src/jupyter_matlab_labextension/src/lezer-matlab/src/parse_comments.js b/src/jupyter_matlab_labextension/src/lezer-matlab/src/parse_comments.js index 29f4d91e0..ef8e65d9d 100644 --- a/src/jupyter_matlab_labextension/src/lezer-matlab/src/parse_comments.js +++ b/src/jupyter_matlab_labextension/src/lezer-matlab/src/parse_comments.js @@ -1,16 +1,16 @@ -// Copyright 2024 The MathWorks, Inc. +// Copyright 2024-2025 The MathWorks, Inc. -import { ExternalTokenizer } from '@lezer/lr'; +import { ExternalTokenizer } from "@lezer/lr"; // This file is created by lezer-generator during the build. -import { MultilineComment, LineComment, Magic } from './parser.terms.js'; +import { MultilineComment, LineComment, Magic } from "./parser.terms.js"; -const percent = '%'.charCodeAt(0); -const openBrace = '{'.charCodeAt(0); -const closeBrace = '}'.charCodeAt(0); +const percent = "%".charCodeAt(0); +const openBrace = "{".charCodeAt(0); +const closeBrace = "}".charCodeAt(0); const fileStart = -1; const fileEnd = -1; -const newline = '\n'.charCodeAt(0); -const carriageReturn = '\r'.charCodeAt(0); +const newline = "\n".charCodeAt(0); +const carriageReturn = "\r".charCodeAt(0); const isAlphabetical = (char) => /^[a-zA-Z]$/.test(String.fromCharCode(char)); @@ -19,142 +19,144 @@ const lineEndArray = [newline, carriageReturn, fileEnd, fileStart]; const isWhitespace = (char) => /\s/.test(String.fromCharCode(char)); const precededByWhitespaceOnly = (input) => { - // Scan from current position to start of line. - // Return False if non-whitespace found. - // Always return input back to where it started. - const startPos = input.pos; - let onlyWhitespace = true; - while (!lineEndArray.includes(input.peek(-1))) { - if (isWhitespace(input.peek(-1))) { - input.advance(-1); - } else { - onlyWhitespace = false; - break; - } + // Scan from current position to start of line. + // Return False if non-whitespace found. + // Always return input back to where it started. + const startPos = input.pos; + let onlyWhitespace = true; + while (!lineEndArray.includes(input.peek(-1))) { + if (isWhitespace(input.peek(-1))) { + input.advance(-1); + } else { + onlyWhitespace = false; + break; } - while (input.pos < startPos) { input.advance(1); } - return onlyWhitespace; + } + while (input.pos < startPos) { + input.advance(1); + } + return onlyWhitespace; }; const followedByWhitespaceOnly = (input) => { - // Scan from current position to end of line. - // Return False if non-whitespace found. - // Always return input back to where it started. - const startPos = input.pos; - let onlyWhitespace = true; - while (!lineEndArray.includes(input.peek(0))) { - if (isWhitespace(input.peek(0))) { - input.advance(1); - } else { - onlyWhitespace = false; - break; - } + // Scan from current position to end of line. + // Return False if non-whitespace found. + // Always return input back to where it started. + const startPos = input.pos; + let onlyWhitespace = true; + while (!lineEndArray.includes(input.peek(0))) { + if (isWhitespace(input.peek(0))) { + input.advance(1); + } else { + onlyWhitespace = false; + break; } - while (input.pos > startPos) { input.advance(-1); } - return onlyWhitespace; + } + while (input.pos > startPos) { + input.advance(-1); + } + return onlyWhitespace; }; const validMultiLineCommentStart = (input) => { - if (input.peek(0) !== percent || input.peek(1) !== openBrace) { - return false; - } - if (!precededByWhitespaceOnly(input)) { - return false; - } - // Consume the %{ - input.advance(2); - if (!followedByWhitespaceOnly(input)) { - return false; - } - input.advance(-2); - return true; + if (input.peek(0) !== percent || input.peek(1) !== openBrace) { + return false; + } + if (!precededByWhitespaceOnly(input)) { + return false; + } + // Consume the %{ + input.advance(2); + if (!followedByWhitespaceOnly(input)) { + return false; + } + input.advance(-2); + return true; }; const validMultiLineCommentEnd = (input) => { - if (input.peek(0) !== percent || input.peek(1) !== closeBrace) { - return false; - } - if (!precededByWhitespaceOnly(input)) { - return false; - } - // Consume the %} - input.advance(2); - if (!followedByWhitespaceOnly(input)) { - return false; - } - input.advance(-2); - return true; + if (input.peek(0) !== percent || input.peek(1) !== closeBrace) { + return false; + } + if (!precededByWhitespaceOnly(input)) { + return false; + } + // Consume the %} + input.advance(2); + if (!followedByWhitespaceOnly(input)) { + return false; + } + input.advance(-2); + return true; }; const validMagic = (input) => { - if (input.notMagic !== undefined) { - return false; - } - var isMagic = false; - if ( - input.peek(0) === percent && - input.peek(1) === percent && - isAlphabetical(input.peek(2)) - ) { - isMagic = true; - } - return isMagic; + if (input.notMagic !== undefined) { + return false; + } + var isMagic = false; + if ( + input.peek(0) === percent && + input.peek(1) === percent && + isAlphabetical(input.peek(2)) + ) { + isMagic = true; + } + return isMagic; }; export const parseComments = new ExternalTokenizer((input) => { - // Tokenize only if the line is a comment, multiline comment - // or a magic and starts with a percentage. - if (input.peek(0) !== percent) { - // If the line starts with anything other than a percentage then it is MATLAB Code. - // If the input.input.string exists then check it's length otherwise ignore the keys by returning true. - if ( - !lineEndArray.includes(input.peek(0)) && - ( - !('input' in input) || - !('string' in input.input) || - input.input.string.length !== 0 - ) - ) { - input.notMagic = true; - } - return; - } else if (validMagic(input)) { - while (!lineEndArray.includes(input.peek(0))) { - input.advance(1); - } - input.acceptToken(Magic); - return; - } else if (validMultiLineCommentStart(input)) { - // Consume the %{ + // Tokenize only if the line is a comment, multiline comment + // or a magic and starts with a percentage. + if (input.peek(0) !== percent) { + // If the line starts with anything other than a percentage then it is MATLAB Code. + // If the input.input.string exists then check it's length otherwise ignore the keys by returning true. + if ( + !lineEndArray.includes(input.peek(0)) && + (!("input" in input) || + !("string" in input.input) || + input.input.string.length !== 0) + ) { + input.notMagic = true; + } + return; + } else if (validMagic(input)) { + while (!lineEndArray.includes(input.peek(0))) { + input.advance(1); + } + input.acceptToken(Magic); + return; + } else if (validMultiLineCommentStart(input)) { + // Consume the %{ + input.advance(2); + // Multiline comments are treated as MATLAB Code. + input.notMagic = true; + // Now we know we've started a multiline comment, so + // continue until the end of the input or until the comment is closed. + // We need to keep track of the depth of nested multiline comments. + let depth = 1; + while (input.peek(0) !== fileEnd) { + if (validMultiLineCommentEnd(input)) { input.advance(2); - // Multiline comments are treated as MATLAB Code. - input.notMagic = true; - // Now we know we've started a multiline comment, so - // continue until the end of the input or until the comment is closed. - // We need to keep track of the depth of nested multiline comments. - let depth = 1; - while (input.peek(0) !== fileEnd) { - if (validMultiLineCommentEnd(input)) { - input.advance(2); - depth--; - if (depth === 0) { - break; - } - } else if (validMultiLineCommentStart(input)) { - depth++; - } - input.advance(1); + depth--; + if (depth === 0) { + break; } + } else if (validMultiLineCommentStart(input)) { + depth++; + } + input.advance(1); + } - // Emit the token for the entire multiline comment - input.acceptToken(MultilineComment); - } else { - // Comments are also treated as MATLAB Code. - input.notMagic = true; - while (!lineEndArray.includes(input.peek(0))) { - input.advance(1); - } - input.acceptToken(LineComment); - return; + // Emit the token for the entire multiline comment + input.acceptToken(MultilineComment); + } else { + // Comments are also treated as MATLAB Code. + input.notMagic = true; + while (!lineEndArray.includes(input.peek(0))) { + input.advance(1); } + input.acceptToken(LineComment); + return; + } }); diff --git a/src/jupyter_matlab_labextension/src/lezer-matlab/test/test-long-matlab-files.js b/src/jupyter_matlab_labextension/src/lezer-matlab/test/test-long-matlab-files.js index 173a4ae70..c5bbd785e 100644 --- a/src/jupyter_matlab_labextension/src/lezer-matlab/test/test-long-matlab-files.js +++ b/src/jupyter_matlab_labextension/src/lezer-matlab/test/test-long-matlab-files.js @@ -1,46 +1,58 @@ -// Copyright 2024 The MathWorks, Inc. +// Copyright 2024-2025 The MathWorks, Inc. -import {parser} from "../dist/index.js" -import {fileTests} from "@lezer/generator/dist/test" +import { parser } from "../dist/index.js"; +import { fileTests } from "@lezer/generator/dist/test"; -let N = 10000 +let N = 10000; -let long_file_spec = `Script(${"Keyword,Identifier,Symbol,Identifier,Symbol,Identifier,Identifier,Symbol,Identifier,Symbol,Symbol,Keyword,".repeat(N)}LineComment)` +let long_file_spec = `Script(${"Keyword,Identifier,Symbol,Identifier,Symbol,Identifier,Identifier,Symbol,Identifier,Symbol,Symbol,Keyword,".repeat( + N +)}LineComment)`; let long_file_input = ` ${"for c = 1:100\n\tdisp(c);\nend\n".repeat(N)} % Long file - ` + `; -let long_line_spec = `Script(${"Keyword,Identifier,Symbol,Identifier,Symbol,Identifier,Symbol,Identifier,Symbol,Identifier,Symbol,Symbol,Keyword,Symbol,".repeat(N)}LineComment)` +let long_line_spec = `Script(${"Keyword,Identifier,Symbol,Identifier,Symbol,Identifier,Symbol,Identifier,Symbol,Identifier,Symbol,Symbol,Keyword,Symbol,".repeat( + N +)}LineComment)`; let long_line_input = ` ${"for c = 1:100;\tdisp(c);end;".repeat(N)} % Long line - ` + `; describe("Long file", () => { it("parses long files correctly", () => { - let tree = parser.parse(long_file_input) - if (tree.toString() != long_file_spec) throw new Error("Parsed tree does not match long file spec") - }) -}) + let tree = parser.parse(long_file_input); + if (tree.toString() != long_file_spec) + throw new Error("Parsed tree does not match long file spec"); + }); +}); describe("Long line", () => { it("parses long lines correctly", () => { - let tree = parser.parse(long_line_input) - if (tree.toString() != long_line_spec) throw new Error("Parsed tree does not match long line spec") - }) -}) + let tree = parser.parse(long_line_input); + if (tree.toString() != long_line_spec) + throw new Error("Parsed tree does not match long line spec"); + }); +}); describe("Long file (multiline comment)", () => { it("parses long files correctly", () => { - let tree = parser.parse(`%{\n${long_file_input}\n%}`) - if (tree.toString() != "Script(MultilineComment)") throw new Error("Parsed tree does not match long file spec (multiline comment)") - }) -}) + let tree = parser.parse(`%{\n${long_file_input}\n%}`); + if (tree.toString() != "Script(MultilineComment)") + throw new Error( + "Parsed tree does not match long file spec (multiline comment)" + ); + }); +}); describe("Long line (multiline comment)", () => { it("parses long lines correctly", () => { - let tree = parser.parse(`%{\n${long_line_input}\n%}`) - if (tree.toString() != "Script(MultilineComment)") throw new Error("Parsed tree does not match long line spec (multiline comment)") - }) -}) + let tree = parser.parse(`%{\n${long_line_input}\n%}`); + if (tree.toString() != "Script(MultilineComment)") + throw new Error( + "Parsed tree does not match long line spec (multiline comment)" + ); + }); +}); diff --git a/src/jupyter_matlab_labextension/src/lezer-matlab/test/test-matlab.js b/src/jupyter_matlab_labextension/src/lezer-matlab/test/test-matlab.js index 2562f1800..a83590cca 100644 --- a/src/jupyter_matlab_labextension/src/lezer-matlab/test/test-matlab.js +++ b/src/jupyter_matlab_labextension/src/lezer-matlab/test/test-matlab.js @@ -1,24 +1,20 @@ -// Copyright 2024 The MathWorks, Inc. +// Copyright 2024-2025 The MathWorks, Inc. -import {parser} from "../dist/index.js" -import {fileTests} from "@lezer/generator/dist/test" +import { parser } from "../dist/index.js"; +import { fileTests } from "@lezer/generator/dist/test"; import { describe, it } from "mocha"; -import * as fs from "fs" -import * as path from "path" +import * as fs from "fs"; +import * as path from "path"; -const testFileNames = [ - "basic_terms", - "nested_terms", - "additional_cases" -]; +const testFileNames = ["basic_terms", "nested_terms", "additional_cases"]; for (const testFileName of testFileNames) { - describe(`MATLAB Lezer grammar ${testFileName}`, function() { + describe(`MATLAB Lezer grammar ${testFileName}`, function () { const testFileContent = fs.readFileSync(`test/${testFileName}.txt`, "utf8"); - - for (let {name, run} of fileTests(testFileContent, testFileName)) { - it(name, () => run(parser)) + + for (let { name, run } of fileTests(testFileContent, testFileName)) { + it(name, () => run(parser)); } - }) -}; + }); +} diff --git a/src/jupyter_matlab_labextension/src/matlab_browser_button.ts b/src/jupyter_matlab_labextension/src/matlab_browser_button.ts deleted file mode 100644 index 5f061755a..000000000 --- a/src/jupyter_matlab_labextension/src/matlab_browser_button.ts +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright 2023 The MathWorks, Inc. - -// Registers the button which allows access to MATLAB in a browser, which will -// appear in the notebook toolbar. - -import { - JupyterFrontEnd, - JupyterFrontEndPlugin -} from '@jupyterlab/application'; -import { ToolbarButton } from '@jupyterlab/apputils'; -import { PageConfig } from '@jupyterlab/coreutils'; -import { DocumentRegistry } from '@jupyterlab/docregistry'; -import { INotebookModel, NotebookPanel } from '@jupyterlab/notebook'; - -import { IDisposable } from '@lumino/disposable'; - -import { matlabIcon } from './icons'; - -/** Wait until the kernel has loaded, then check if it is a MATLAB kernel. */ -const insertButton = async (panel: NotebookPanel, matlabToolbarButton: ToolbarButton): Promise => { - await panel.sessionContext.ready; - if (panel.sessionContext.kernelDisplayName === 'MATLAB Kernel') { - panel.toolbar.insertItem(10, 'matlabToolbarButton', matlabToolbarButton); - } -}; - -class MatlabToolbarButtonExtension implements DocumentRegistry.IWidgetExtension { - createNew (panel: NotebookPanel, context: DocumentRegistry.IContext): IDisposable { - /** Create the toolbar button to open MATLAB in a browser. */ - const matlabToolbarButton = new ToolbarButton({ - className: 'openMATLABButton', - icon: matlabIcon, - label: 'Open MATLAB', - tooltip: 'Open MATLAB', - onClick: (): void => { - const baseUrl = PageConfig.getBaseUrl(); - // "_blank" is the option to open in a new browser tab - window.open(baseUrl + 'matlab', '_blank'); - } - }); - insertButton(panel, matlabToolbarButton); - return matlabToolbarButton; - } -} - -export const matlabToolbarButtonPlugin: JupyterFrontEndPlugin = { - id: '@mathworks/matlabToolbarButtonPlugin', - autoStart: true, - activate: ( - app: JupyterFrontEnd - ) => { - const matlabToolbarButton = new MatlabToolbarButtonExtension(); - app.docRegistry.addWidgetExtension('Notebook', matlabToolbarButton); - } -}; diff --git a/src/jupyter_matlab_labextension/src/matlab_cm6_mode.ts b/src/jupyter_matlab_labextension/src/plugins/matlabCM6Mode.ts similarity index 72% rename from src/jupyter_matlab_labextension/src/matlab_cm6_mode.ts rename to src/jupyter_matlab_labextension/src/plugins/matlabCM6Mode.ts index 0a39bfea3..30ea04982 100644 --- a/src/jupyter_matlab_labextension/src/matlab_cm6_mode.ts +++ b/src/jupyter_matlab_labextension/src/plugins/matlabCM6Mode.ts @@ -1,4 +1,4 @@ -// Copyright 2023-2024 The MathWorks, Inc. +// Copyright 2023-2025 The MathWorks, Inc. // Set up CodeMirror for the MATLAB language. @@ -7,9 +7,7 @@ import { JupyterFrontEndPlugin } from '@jupyterlab/application'; -import { - IEditorLanguageRegistry -} from '@jupyterlab/codemirror'; +import { IEditorLanguageRegistry } from '@jupyterlab/codemirror'; /** Register language with CodeMirror */ export function addMATLABCodeMirror (languageRegistry: IEditorLanguageRegistry) { @@ -20,7 +18,9 @@ export function addMATLABCodeMirror (languageRegistry: IEditorLanguageRegistry) extensions: ['m', 'mlx'], filename: /^[a-zA-Z][a-zA-Z0-9_]*\.m$/, async load () { - const m = await import('./codemirror-lang-matlab/codemirror-lang-matlab'); + const m = await import( + '../codemirror-lang-matlab/codemirror-lang-matlab' + ); return m.matlab(); } }); @@ -30,10 +30,7 @@ export const matlabCodeMirror6Plugin: JupyterFrontEndPlugin = { id: '@mathworks/matlabCodeMirror6Plugin', autoStart: true, requires: [IEditorLanguageRegistry], - activate: ( - app: JupyterFrontEnd, - codeMirror: IEditorLanguageRegistry - ) => { + activate: (app: JupyterFrontEnd, codeMirror: IEditorLanguageRegistry) => { addMATLABCodeMirror(codeMirror); } }; diff --git a/src/jupyter_matlab_labextension/src/plugins/matlabCommunication.ts b/src/jupyter_matlab_labextension/src/plugins/matlabCommunication.ts new file mode 100644 index 000000000..2968eb12f --- /dev/null +++ b/src/jupyter_matlab_labextension/src/plugins/matlabCommunication.ts @@ -0,0 +1,186 @@ +// Copyright 2025 The MathWorks, Inc. + +import { + JupyterFrontEnd, + JupyterFrontEndPlugin +} from '@jupyterlab/application'; + +import { DocumentRegistry } from '@jupyterlab/docregistry'; +import { + INotebookModel, + INotebookTracker, + NotebookPanel +} from '@jupyterlab/notebook'; +import { KernelMessage, Kernel } from '@jupyterlab/services'; +import { JSONObject, JSONValue, Token } from '@lumino/coreutils'; +import { DisposableDelegate } from '@lumino/disposable'; +import { NotebookInfo } from '../utils/notebook'; + +// Add more action types as needed +type CommunicationData = { + action: string; + data: JSONValue; +}; + +export interface ICommunicationChannel { + readonly commId: string; + readonly targetName: string; + readonly isDisposed: boolean; + onMsg: (msg: KernelMessage.ICommMsgMsg) => void | PromiseLike; + onClose: (msg: KernelMessage.ICommCloseMsg) => void | PromiseLike; + close: ( + data?: JSONValue, + metadata?: JSONObject, + buffers?: (ArrayBuffer | ArrayBufferView)[] + ) => void; + send: ( + data: CommunicationData, + metadata?: JSONObject, + buffers?: (ArrayBuffer | ArrayBufferView)[], + disposeOnDone?: boolean + ) => void; +} +export interface ICommunicationService { + getComm(notebookID: string): ICommunicationChannel; +} + +export class MatlabCommunicationExtension +implements + DocumentRegistry.IWidgetExtension, + ICommunicationService { + private _comms = new Map(); + + /* + * Attempts to open a comm channel with a retry mechanism. + * @param kernel The kernel for which a comm channel is being created. + + * @returns A promise that resolves when the comm is open. + */ + private async _createAndOpenCommWithRetry (kernel: Kernel.IKernelConnection, channelName: string): Promise { + let attempt = 1; + let delayInMS = 200; + const maxRetries = 5; + + while (attempt <= maxRetries) { + try { + // Creates comm object on the client side + const comm = kernel.createComm(channelName); + + // Attempts to open a channel with the kernel + await comm.open().done; + console.log('Communication channel opened successfully with ID:', comm.commId); + return comm; + } catch (error) { + console.error('Error opening communication channel', error); + console.error(`Attempt #${attempt} failed. Waiting ${delayInMS}ms before next attempt.`); + } + // Wait for the delay + await new Promise(resolve => setTimeout(resolve, delayInMS)); + + // Update + delayInMS *= 2; + attempt += 1; + } + + console.error(`Failed to create communication channel after ${attempt} attempts.`); + return null; + } + + createNew ( + panel: NotebookPanel, + context: DocumentRegistry.IContext + ): DisposableDelegate { + panel.sessionContext.ready + .then(async () => { + const kernel = panel.sessionContext.session?.kernel; + // If kernel is available, create channel and set up listeners. + if (!kernel) { + console.error("Kernel not ready! Can't create communication channel"); + return new DisposableDelegate(() => {}); + } + + const notebookInfo = new NotebookInfo(); + await notebookInfo.update(panel); + + if (!notebookInfo.isMatlabNotebook()) { + console.debug('Not a MATLAB notebook, skipping communication setup'); + return new DisposableDelegate(() => {}); + } + + console.log('MATLAB Communication plugin activated for ', panel.id); + + // Create a unique channel name for this notebook + const channelName = 'matlab_comm_' + panel.id; + console.log( + 'Attempting to establish communication with the kernel' + ); + + const comm = await this._createAndOpenCommWithRetry(kernel, channelName); + if (!comm) { + return new DisposableDelegate(() => {}); + } + + // Listen for messages from the kernel + comm.onMsg = (msg: KernelMessage.ICommMsgMsg) => { + const data = msg.content.data as CommunicationData; + console.debug('Recieved data from kernel: ', data); + }; + + // Handle comm close + comm.onClose = (msg) => { + console.debug(`Received data:${msg} for comm close event.`); + console.log(`Comm with ID:${comm.commId} closed.`); + }; + + this._comms.set(panel.id, comm); + }) + .catch((error) => { + console.error('Notebook panel was not ready', error); + }); + + return new DisposableDelegate(() => { + const comm = this._comms.get(panel.id); + if (comm && !comm.isDisposed) { + comm.close(); + this._comms.delete(panel.id); + } + }); + } + + getComm (notebookId: string): ICommunicationChannel { + const commChannel = this._comms.get(notebookId); + if (!commChannel) { + throw new Error( + `No communication channel found for notebook ID: ${notebookId}` + ); + } + return commChannel; + } + + deleteComms (): void { + this._comms.clear(); + } +} + +// A unique token for the comm service +export const IMatlabCommunication = new Token('@mathworks/matlab-comm:IMatlabCommunication'); + +export const matlabCommPlugin: JupyterFrontEndPlugin = + { + id: '@mathworks/matlabCommPlugin', + autoStart: true, + requires: [INotebookTracker], + provides: IMatlabCommunication, + activate: (app: JupyterFrontEnd): MatlabCommunicationExtension => { + const matlabCommExtension = new MatlabCommunicationExtension(); + app.docRegistry.addWidgetExtension('Notebook', matlabCommExtension); + + // Dispose resources created by this plugin when the page unloads. + // Need to handle this separately for the case when jupyterlab tab is closed directly + window.addEventListener('beforeunload', () => { + matlabCommExtension.deleteComms(); + }); + + return matlabCommExtension; + } + }; diff --git a/src/jupyter_matlab_labextension/src/matlab_files.ts b/src/jupyter_matlab_labextension/src/plugins/matlabFiles.ts similarity index 89% rename from src/jupyter_matlab_labextension/src/matlab_files.ts rename to src/jupyter_matlab_labextension/src/plugins/matlabFiles.ts index 17cf655df..47bdf0865 100644 --- a/src/jupyter_matlab_labextension/src/matlab_files.ts +++ b/src/jupyter_matlab_labextension/src/plugins/matlabFiles.ts @@ -1,4 +1,4 @@ -// Copyright 2023-2024 The MathWorks, Inc. +// Copyright 2023-2025 The MathWorks, Inc. // Create a command to open a new .m file. // Add this command to the Launcher (under "Other"), @@ -14,17 +14,21 @@ import { ILauncher } from '@jupyterlab/launcher'; import { ReadonlyPartialJSONObject } from '@lumino/coreutils'; -import { newMFileIcon, matlabIcon } from './icons'; +import { newMFileIcon, matlabIcon } from '../icons'; const FACTORY = 'Editor'; const PALETTE_CATEGORY = 'Other'; const command = 'matlab:new-matlab-file'; -function registerMFiles (app: JupyterFrontEnd, launcher: ILauncher | null, palette: ICommandPalette | null) { +function registerMFiles ( + app: JupyterFrontEnd, + launcher: ILauncher | null, + palette: ICommandPalette | null +) { const { commands } = app; const createNewMatlabFile = async (args: ReadonlyPartialJSONObject) => { /** Get the directory in which the MATLAB file must be created; - * otherwise take the current filebrowser directory. */ + * otherwise take the current filebrowser directory. */ const cwd = args.cwd; /** Create a new untitled MATLAB file. */ diff --git a/src/jupyter_matlab_labextension/src/plugins/matlabToolbarButton.ts b/src/jupyter_matlab_labextension/src/plugins/matlabToolbarButton.ts new file mode 100644 index 000000000..d0cc0dfdc --- /dev/null +++ b/src/jupyter_matlab_labextension/src/plugins/matlabToolbarButton.ts @@ -0,0 +1,111 @@ +// Copyright 2023-2025 The MathWorks, Inc. + +// Registers the button which allows access to MATLAB in a browser, which will +// appear in the notebook toolbar. + +import { + JupyterFrontEnd, + JupyterFrontEndPlugin +} from '@jupyterlab/application'; +import { ToolbarButton } from '@jupyterlab/apputils'; +import { PageConfig } from '@jupyterlab/coreutils'; +import { DocumentRegistry } from '@jupyterlab/docregistry'; +import { INotebookModel, NotebookPanel } from '@jupyterlab/notebook'; + +import { DisposableDelegate } from '@lumino/disposable'; + +import { matlabIcon } from '../icons'; + +function createMATLABToolbarButton (targetUrl: string): ToolbarButton { + return new ToolbarButton({ + className: 'openMATLABButton matlab-toolbar-button-spaced', + icon: matlabIcon, + label: 'Open MATLAB', + tooltip: 'Open MATLAB', + onClick: (): void => { + window.open(targetUrl, '_blank'); + } + }); +} + +/** Wait until the kernel has loaded, then check if it is a MATLAB kernel. */ +export const insertButton = async (panel: NotebookPanel): Promise => { + try { + await panel.sessionContext.ready; + let targetUrl = ''; + let matlabToolbarButton: ToolbarButton | null = null; + + // Function to update the target URL based on kernel ID + const updateTargetUrl = (): void => { + // Check if the kernel is a MATLAB Kernel + if (panel.sessionContext.kernelDisplayName === 'MATLAB Kernel') { + let kernelId = ''; + + // Check that session and kernel exist and then retrieve kernel ID + if (panel.sessionContext.session && panel.sessionContext.session.kernel) { + kernelId = panel.sessionContext.session.kernel.id; + } + + if (kernelId !== '') { + targetUrl = PageConfig.getBaseUrl() + 'matlab/' + kernelId + '/'; + + // Create the button if it doesn't exist yet + if (!matlabToolbarButton) { + matlabToolbarButton = createMATLABToolbarButton(targetUrl); + panel.toolbar.insertItem(10, 'matlabToolbarButton', matlabToolbarButton); + } else { + // Update the button's onClick handler + matlabToolbarButton.onClick = () => { + window.open(targetUrl, '_blank'); + }; + } + } + } + }; + + // Create Open MATLAB toolbar button + updateTargetUrl(); + + // Listen for kernel changes + panel.sessionContext.kernelChanged.connect(() => { + updateTargetUrl(); + }); + + // Create a disposable that will clean up the listener + return new DisposableDelegate(() => { + if (matlabToolbarButton) { + matlabToolbarButton.dispose(); + } + panel.sessionContext.kernelChanged.disconnect(() => { + updateTargetUrl(); + }); + }); + } catch (error) { + console.error('Failed to insert MATLAB toolbar button: ', error); + return new DisposableDelegate(() => {}); + } +}; + +export class MatlabToolbarButtonExtension +implements DocumentRegistry.IWidgetExtension { + createNew ( + panel: NotebookPanel, + context: DocumentRegistry.IContext + ): DisposableDelegate { + /** Create the toolbar button to open MATLAB in a browser. */ + insertButton(panel).catch(error => { + console.error('Error inserting MATLAB toolbar button:', error); + }); + // Return a dummy disposable immediately + return new DisposableDelegate(() => {}); + } +} + +export const matlabToolbarButtonPlugin: JupyterFrontEndPlugin = { + id: '@mathworks/matlabToolbarButtonPlugin', + autoStart: true, + activate: (app: JupyterFrontEnd) => { + const matlabToolbarButton = new MatlabToolbarButtonExtension(); + app.docRegistry.addWidgetExtension('Notebook', matlabToolbarButton); + } +}; diff --git a/src/jupyter_matlab_labextension/src/tests/jest-setup.ts b/src/jupyter_matlab_labextension/src/tests/jest-setup.ts new file mode 100644 index 000000000..c0b71c40c --- /dev/null +++ b/src/jupyter_matlab_labextension/src/tests/jest-setup.ts @@ -0,0 +1,17 @@ +// Copyright 2025 The MathWorks, Inc. + +// Mock global objects that might not be available in the Node.js environment + +// Mock window object if needed +// Tests run in a node environment, where 'window' is not defined. +// This mock ensures that 'window' is defined during tests. +if (typeof window === 'undefined') { + (global as any).window = { + open: jest.fn() + }; +} + +// Reset mocks before each test +beforeEach(() => { + jest.clearAllMocks(); +}); diff --git a/src/jupyter_matlab_labextension/src/tests/matlabCommunication.test.ts b/src/jupyter_matlab_labextension/src/tests/matlabCommunication.test.ts new file mode 100644 index 000000000..fa2b809ec --- /dev/null +++ b/src/jupyter_matlab_labextension/src/tests/matlabCommunication.test.ts @@ -0,0 +1,145 @@ +// Copyright 2025 The MathWorks, Inc. + +// Mock dependencies from JupyterLab and other modules +import { MatlabCommunicationExtension } from '../plugins/matlabCommunication'; +import { NotebookPanel } from '@jupyterlab/notebook'; +import { DocumentRegistry } from '@jupyterlab/docregistry'; + +jest.mock('@jupyterlab/services', () => ({ + KernelMessage: { + createMessage: jest.fn() + } +})); + +jest.mock('@jupyterlab/notebook', () => ({ + NotebookPanel: jest.fn() +})); + +jest.mock('../utils/notebook', () => ({ + NotebookInfo: jest.fn().mockImplementation(() => ({ + update: jest.fn(), + isMatlabNotebook: jest.fn(() => true) + })) +})); + +// Begin testing MatlabCommunicationExtension +describe('MatlabCommunicationExtension', () => { + let panel: NotebookPanel; + let context: DocumentRegistry.IContext; + let extension: MatlabCommunicationExtension; + + beforeEach(() => { + jest.clearAllMocks(); + + const notebookInfoMock = require('../utils/notebook').NotebookInfo; + notebookInfoMock.mockImplementation(() => ({ + update: jest.fn(), + isMatlabNotebook: jest.fn(() => true) // Reset to true for default behavior + })); + + // Mock NotebookPanel and context + panel = { + id: 'notebook-1', + sessionContext: { + ready: Promise.resolve(), + session: { + kernel: { + createComm: jest.fn(() => ({ + commId: 'test-comm-id', + targetName: 'matlab', + onMsg: jest.fn(), + onClose: jest.fn(), + open: () => ({ + done: { + then: jest.fn().mockImplementation((cb) => cb()), + catch: jest.fn().mockImplementation((cb) => cb()) + } + }), + close: jest.fn() + })) + } + } + }, + disposed: { + connect: jest.fn() + } + } as unknown as NotebookPanel; + + context = {} as DocumentRegistry.IContext; + + extension = new MatlabCommunicationExtension(); + }); + + it('should create a new communication channel for MATLAB notebooks', async () => { + const disposable = extension.createNew(panel, context); + + // Wait for async operations to complete + await panel.sessionContext.ready; + + const { kernel } = panel.sessionContext.session!; + + // Wait a bit more to ensure the async then block has executed + await new Promise((resolve) => setTimeout(resolve, 0)); + + expect(kernel?.createComm).toHaveBeenCalled(); + expect(disposable.dispose).toBeDefined(); + }); + + it('should not create a communication channel for non-MATLAB notebooks', async () => { + const notebookInfoMock = require('../utils/notebook').NotebookInfo; + notebookInfoMock.mockImplementation(() => ({ + update: jest.fn(), + isMatlabNotebook: jest.fn(() => false) + })); + + const disposable = extension.createNew(panel, context); + + await panel.sessionContext.ready; + + // Ensure no channel is created + expect( + panel.sessionContext.session!.kernel!.createComm + ).not.toHaveBeenCalled(); + expect(disposable.dispose).toBeDefined(); + }); + + it('should clean up communication channels when the panel is disposed', async () => { + const disposable = extension.createNew(panel, context); + + await panel.sessionContext.ready; + await new Promise((resolve) => setTimeout(resolve, 10)); + + // Verify a comm was created + const { kernel } = panel.sessionContext.session!; + expect(kernel?.createComm).toHaveBeenCalled(); + + // Comm should exist before disposal + expect(() => extension.getComm(panel.id)).not.toThrow(); + + // Simulate disposal via DisposableDelegate + disposable.dispose(); + + // Comm should now be cleaned up + expect(() => extension.getComm(panel.id)).toThrow(); + }); + + it('should throw an error if getComm is called with an invalid notebook ID', async () => { + // First, create a communication channel for a valid notebook + extension.createNew(panel, context); + await panel.sessionContext.ready; + await new Promise((resolve) => setTimeout(resolve, 0)); + + // Verify that the valid notebook ID works (doesn't throw) + expect(() => extension.getComm(panel.id)).not.toThrow(); + + // Now test that an invalid ID throws the expected error + expect(() => extension.getComm('invalid-id')).toThrowError( + 'No communication channel found for notebook ID: invalid-id' + ); + }); + + // it('should delete all communication channels during cleanup', () => { + // extension.deleteComms(); + // expect(() => extension.getComm(panel.id)).toThrowError(); + // }); +}); diff --git a/src/jupyter_matlab_labextension/src/tests/matlabToolbarButton.test.ts b/src/jupyter_matlab_labextension/src/tests/matlabToolbarButton.test.ts new file mode 100644 index 000000000..c2167ac33 --- /dev/null +++ b/src/jupyter_matlab_labextension/src/tests/matlabToolbarButton.test.ts @@ -0,0 +1,346 @@ +// Copyright 2025 The MathWorks, Inc. + +// Mock the icons module +import { + insertButton, + MatlabToolbarButtonExtension, + matlabToolbarButtonPlugin +} from '../plugins/matlabToolbarButton'; +import { NotebookPanel, INotebookModel } from '@jupyterlab/notebook'; +import { JupyterFrontEnd } from '@jupyterlab/application'; +import { DocumentRegistry } from '@jupyterlab/docregistry'; +import { Signal } from '@lumino/signaling'; + +jest.mock('../icons', () => ({ + matlabIcon: { + name: 'matlab-icon', + svgstr: '' + } +})); + +// Mock JupyterLab dependencies +jest.mock('@jupyterlab/apputils', () => ({ + ToolbarButton: jest.fn().mockImplementation((options: any) => ({ + ...options, + dispose: jest.fn() + })) +})); + +jest.mock('@jupyterlab/coreutils', () => ({ + PageConfig: { + getBaseUrl: jest.fn().mockReturnValue('http://localhost:8888/') + } +})); + +// Mock window.open +const originalWindowOpen = window.open; +window.open = jest.fn(); + +// Mock for NotebookPanel with kernel change signal +const createMockNotebookPanel = (kernelDisplayName = 'MATLAB Kernel', kernelId = '12345') => { + const kernelChangedSignal = new Signal({}); + + return { + sessionContext: { + ready: Promise.resolve(), + kernelDisplayName, + session: kernelId + ? { + kernel: { + id: kernelId + } + } + : null, + kernelChanged: kernelChangedSignal, + initialize: jest.fn(), + isReady: true, + isTerminating: false, + // Add other required methods as + dispose: jest.fn() + }, + toolbar: { + insertItem: jest.fn(), + names: [] + } + }; +}; + +// Mock for JupyterFrontEnd +const createMockJupyterFrontEnd = () => ({ + docRegistry: { + addWidgetExtension: jest.fn(), + // Add other required properties with mock implementations + changed: { connect: jest.fn() }, + isDisposed: false, + dispose: jest.fn(), + addWidgetFactory: jest.fn() + } +}); + +describe('matlab_browser_button', () => { + afterEach(() => { + jest.clearAllMocks(); + }); + + afterAll(() => { + window.open = originalWindowOpen; + }); + + describe('insertButton', () => { + test('should insert button when kernel is MATLAB Kernel with valid kernel ID', async () => { + // Arrange + const panel = createMockNotebookPanel('MATLAB Kernel', 'test-kernel-123'); + + // Act + await insertButton(panel as unknown as NotebookPanel); + + // Assert + expect(panel.toolbar!.insertItem).toHaveBeenCalledWith( + 10, + 'matlabToolbarButton', + expect.objectContaining({ + className: 'openMATLABButton matlab-toolbar-button-spaced', + label: 'Open MATLAB' + }) + ); + }); + + test('should not insert button when kernel is not MATLAB Kernel', async () => { + // Arrange + const panel = createMockNotebookPanel('Python 3'); + + // Act + await insertButton(panel as unknown as NotebookPanel); + + // Assert + expect(panel.toolbar!.insertItem).not.toHaveBeenCalled(); + }); + + test('should not insert button when kernel ID is empty', async () => { + const panel = createMockNotebookPanel('MATLAB Kernel', ''); + + await insertButton(panel as unknown as NotebookPanel); + + expect(panel.toolbar.insertItem).not.toHaveBeenCalled(); + }); + + test('should not insert button when session is null', async () => { + const panel = createMockNotebookPanel('MATLAB Kernel'); + panel.sessionContext.session = null; + + await insertButton(panel as unknown as NotebookPanel); + + expect(panel.toolbar.insertItem).not.toHaveBeenCalled(); + }); + + test('should not insert button when kernel is null', async () => { + const panel = createMockNotebookPanel('MATLAB Kernel'); + panel.sessionContext.session = { kernel: null as any }; + + await insertButton(panel as unknown as NotebookPanel); + + expect(panel.toolbar.insertItem).not.toHaveBeenCalled(); + }); + + test('should construct correct target URL with kernel ID', async () => { + const ToolbarButtonMock = jest.requireMock('@jupyterlab/apputils').ToolbarButton; + let capturedOnClick: () => void = () => {}; + + ToolbarButtonMock.mockImplementationOnce((options: any) => { + capturedOnClick = options.onClick; + return { + ...options, + dispose: jest.fn() + }; + }); + + const panel = createMockNotebookPanel('MATLAB Kernel', 'kernel-abc-123'); + await insertButton(panel as unknown as NotebookPanel); + + capturedOnClick(); + + expect(window.open).toHaveBeenCalledWith( + 'http://localhost:8888/matlab/kernel-abc-123/', + '_blank' + ); + }); + + test('should wait for session context to be ready before checking kernel', async () => { + // Arrange + const readyPromise = new Promise((resolve) => + setTimeout(resolve, 10) + ); + const panel = { + sessionContext: { + ready: readyPromise, + kernelDisplayName: 'MATLAB Kernel', + session: { + kernel: { + id: 'test-kernel' + } + }, + kernelChanged: new Signal({}) + }, + toolbar: { + insertItem: jest.fn() + } + }; + + // Act + const insertPromise = insertButton(panel as any); + + // Assert - insertItem should not be called before ready resolves + expect(panel.toolbar.insertItem).not.toHaveBeenCalled(); + + // Wait for ready promise to resolve + await insertPromise; + + // Now insertItem should have been called + expect(panel.toolbar.insertItem).toHaveBeenCalled(); + }); + + test('should update button onClick when kernel changes', async () => { + const ToolbarButtonMock = jest.requireMock('@jupyterlab/apputils').ToolbarButton; + const mockButton = { + onClick: jest.fn(), + dispose: jest.fn() + }; + + ToolbarButtonMock.mockReturnValue(mockButton); + + const panel = createMockNotebookPanel('MATLAB Kernel', 'kernel-1'); + await insertButton(panel as unknown as NotebookPanel); + + // Simulate kernel change + panel.sessionContext.session = { + kernel: { id: 'kernel-2' } + }; + panel.sessionContext.kernelChanged.emit({}); + + // Wait for async operations + await new Promise(resolve => setTimeout(resolve, 0)); + + expect(mockButton.onClick).toBeDefined(); + }); + + test('should handle errors gracefully', async () => { + const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(); + const panel = { + sessionContext: { + ready: Promise.reject(new Error('Session failed')) + } + }; + + const result = await insertButton(panel as any); + + expect(consoleErrorSpy).toHaveBeenCalledWith( + 'Failed to insert MATLAB toolbar button: ', + expect.any(Error) + ); + expect(result.dispose).toBeDefined(); + + consoleErrorSpy.mockRestore(); + }); + }); + + describe('MatlabToolbarButtonExtension', () => { + let extension: MatlabToolbarButtonExtension; + let panel: any; + let context: any; + + beforeEach(() => { + extension = new MatlabToolbarButtonExtension(); + panel = createMockNotebookPanel('MATLAB Kernel', 'test-kernel'); + context = {}; + }); + + test('should return a disposable object', () => { + // Act + const result = extension.createNew(panel, context); + + // Assert + expect(result.dispose).toBeDefined(); + expect(typeof result.dispose).toBe('function'); + }); + + test('should call insertButton when createNew is invoked', () => { + const matlabButtonModule = require('../plugins/matlabToolbarButton'); + const spy = jest + .spyOn(matlabButtonModule, 'insertButton') + .mockResolvedValue({ dispose: jest.fn() }); + + extension.createNew( + panel as unknown as NotebookPanel, + context as unknown as DocumentRegistry.IContext + ); + + expect(spy).toHaveBeenCalledWith(panel); + + spy.mockRestore(); + }); + + test('should call insertButton with panel and button', () => { + // Arrange + // Import the module using ES modules syntax for TypeScript compatibility + const matlabButtonModule = require('../plugins/matlabToolbarButton'); + const spy = jest + .spyOn(matlabButtonModule, 'insertButton') + .mockImplementation(() => Promise.resolve()); + + // Act + extension.createNew( + panel as unknown as NotebookPanel, + context as unknown as DocumentRegistry.IContext + ); + + // Assert + expect(spy).toHaveBeenCalledWith(panel); + + // Cleanup + spy.mockRestore(); + }); + }); + + describe('matlabToolbarButtonPlugin', () => { + test('should have correct id and autoStart properties', () => { + // Assert + expect(matlabToolbarButtonPlugin.id).toBe( + '@mathworks/matlabToolbarButtonPlugin' + ); + expect(matlabToolbarButtonPlugin.autoStart).toBe(true); + }); + + test('should register extension with docRegistry on activation', () => { + // Arrange + const app = createMockJupyterFrontEnd(); + + // Act + matlabToolbarButtonPlugin.activate(app as unknown as JupyterFrontEnd); + + // Assert + expect(app.docRegistry!.addWidgetExtension).toHaveBeenCalledWith( + 'Notebook', + expect.any(MatlabToolbarButtonExtension) + ); + }); + + test('should create a MatlabToolbarButtonExtension instance on activation', () => { + // Arrange + const app = createMockJupyterFrontEnd(); + + // Act + matlabToolbarButtonPlugin.activate(app as unknown as JupyterFrontEnd); + + // Assert - Check if addWidgetExtension was called with an instance of MatlabToolbarButtonExtension + expect(app.docRegistry!.addWidgetExtension).toHaveBeenCalledWith( + 'Notebook', + expect.any(MatlabToolbarButtonExtension) + ); + + // Additional check - verify the argument is an instance of MatlabToolbarButtonExtension + const extensionArg = (app.docRegistry!.addWidgetExtension as jest.Mock) + .mock.calls[0][1]; + expect(extensionArg).toBeInstanceOf(MatlabToolbarButtonExtension); + }); + }); +}); diff --git a/src/jupyter_matlab_labextension/src/tests/mocks/ui-components.js b/src/jupyter_matlab_labextension/src/tests/mocks/ui-components.js new file mode 100644 index 000000000..6ebde50c2 --- /dev/null +++ b/src/jupyter_matlab_labextension/src/tests/mocks/ui-components.js @@ -0,0 +1,15 @@ +// Copyright 2025 The MathWorks, Inc. + +// Mock for @jupyterlab/ui-components +module.exports = { + LabIcon: class LabIcon { + constructor(name, options) { + this.name = name; + this.svgstr = options?.svgstr || ""; + } + + static resolve(icon) { + return icon; + } + }, +}; diff --git a/src/jupyter_matlab_labextension/src/utils/notebook.ts b/src/jupyter_matlab_labextension/src/utils/notebook.ts new file mode 100644 index 000000000..1eecd78f1 --- /dev/null +++ b/src/jupyter_matlab_labextension/src/utils/notebook.ts @@ -0,0 +1,132 @@ +// Copyright 2025 The MathWorks, Inc. + +import path from 'path'; +import { NotebookPanel } from '@jupyterlab/notebook'; +import { PageConfig } from '@jupyterlab/coreutils'; + +/** + * Tracks metadata and kernel state for the currently active Jupyter notebook panel. + * Provides helpers to determine whether the notebook is a MATLAB notebook, whether + * its kernel is busy, resolve the notebook's file path, and control the kernel. + */ +export class NotebookInfo { + private _notebookName: string | undefined = undefined; + private _isMatlabNotebook: boolean = false; + private _isBusy: boolean = false; + private _panel: NotebookPanel | null = null; + + /* + * Whether the current notebook’s kernelspec indicates MATLAB. + * + * @returns True if the current notebook is a MATLAB notebook; otherwise false. + */ + isMatlabNotebook (): boolean { + return this._isMatlabNotebook; + } + + /* + * Whether the kernel is busy, but only for MATLAB notebooks. + * If the current notebook is not MATLAB, returns false. + * + * @returns True if the notebook is MATLAB and the kernel is busy; otherwise false. + */ + isBusy (): boolean { + return this._isMatlabNotebook ? this._isBusy : false; + } + + /* + * Absolute path to the current notebook on the filesystem. + * + * Combines the Jupyter server root with the notebook's relative path. + * + * @returns The absolute file path if available; otherwise undefined. + */ + getCurrentFilePath (): string | undefined { + if (this._notebookName) { + return path.join(PageConfig.getOption('serverRoot'), this._notebookName); + } else { + return undefined; + } + } + + /* + * Waits until the associated kernel reaches the 'idle' status. + * + * @throws Error if no notebook panel has been set via update(). + * @returns A promise that resolves when the kernel status becomes 'idle'. + */ + async waitForIdleStatus (): Promise { + if (!this._panel) { + throw Error('No notebook panel provided'); + } else { + return new Promise((resolve) => { + if (this._panel!.sessionContext.session?.kernel?.status === 'idle') { + resolve(); + } else { + const onStatusChanged = (connection: any, status: string) => { + if (status === 'idle') { + // Disconnect listener from statusChanged signal so that it doesn't get called again. + connection.statusChanged.disconnect(onStatusChanged); + resolve(); + } + }; + this._panel!.sessionContext.session?.kernel?.statusChanged.connect( + onStatusChanged + ); + } + }); + } + } + + /* + * Updates the tracked notebook panel and refreshes its derived state: + * - whether it is a MATLAB notebook (via kernelspec metadata), + * - whether the kernel is currently busy, + * - the notebook’s path. + * + * If panel is null, clears all tracked state. + * + * Note: Waits for the session context to be ready before reading kernel status. + * + * @param panel The active NotebookPanel to track, or null to reset. + * @returns A promise that resolves when the state has been updated. + */ + async update (panel: NotebookPanel | null): Promise { + if (panel) { + // Wait for session context to be ready + if (!panel.sessionContext.isReady) { + await panel.sessionContext.ready; + } + this._panel = panel; + this._isMatlabNotebook = panel.sessionContext.kernelDisplayName === 'MATLAB Kernel'; + const context = panel.context; + this._isBusy = panel.sessionContext.session?.kernel?.status === 'busy'; + this._notebookName = context.path; + } else { + this._notebookName = undefined; + this._isMatlabNotebook = false; + this._isBusy = false; + this._panel = null; + } + } + + /* + * Sends an interrupt to the associated kernel, if available. + * No-op if there is no tracked panel/session/kernel. + */ + interrupt (): void { + if (this._panel) { + this._panel.sessionContext.session?.kernel?.interrupt(); + console.log('Kernel interupted'); + } + } + + /* + * Returns the current notebook’s path relative to the server root. + * + * @returns The relative path (e.g., 'folder/notebook.ipynb') or undefined if none is set. + */ + getCurrentFilename (): string | undefined { + return this._notebookName; + } +} diff --git a/src/jupyter_matlab_labextension/style/base.css b/src/jupyter_matlab_labextension/style/base.css index e11f45777..de8fe6aaf 100644 --- a/src/jupyter_matlab_labextension/style/base.css +++ b/src/jupyter_matlab_labextension/style/base.css @@ -1,5 +1,11 @@ +/* # Copyright 2024-2025 The MathWorks, Inc. */ /* See the JupyterLab Developer Guide for useful CSS Patterns: https://jupyterlab.readthedocs.io/en/stable/developer/css.html */ + +/* Adds spacing between toolbar icon and label */ +.matlab-toolbar-button-spaced .jp-ToolbarButtonComponent-label { + margin-left: 4px; +} \ No newline at end of file diff --git a/src/jupyter_matlab_labextension/style/index.css b/src/jupyter_matlab_labextension/style/index.css index 8a7ea29e6..01d5beb98 100644 --- a/src/jupyter_matlab_labextension/style/index.css +++ b/src/jupyter_matlab_labextension/style/index.css @@ -1 +1,2 @@ -@import url('base.css'); +/* Copyright 2025 The MathWorks, Inc. */ +@import url("base.css"); diff --git a/src/jupyter_matlab_labextension/style/index.js b/src/jupyter_matlab_labextension/style/index.js index a028a7640..e216e5878 100644 --- a/src/jupyter_matlab_labextension/style/index.js +++ b/src/jupyter_matlab_labextension/style/index.js @@ -1 +1,2 @@ -import './base.css'; +// Copyright 2025 The MathWorks, Inc. +import "./base.css"; diff --git a/src/jupyter_matlab_labextension/tsconfig.json b/src/jupyter_matlab_labextension/tsconfig.json index fbfef6dfd..45d016956 100644 --- a/src/jupyter_matlab_labextension/tsconfig.json +++ b/src/jupyter_matlab_labextension/tsconfig.json @@ -19,7 +19,7 @@ "strict": true, "strictNullChecks": true, "target": "es2018", - "types": ["jest"], + "types": ["jest", "node"], "lib": ["DOM", "ES2018", "ES2020.Intl"] }, "include": ["src"] diff --git a/src/jupyter_matlab_labextension/yarn.lock b/src/jupyter_matlab_labextension/yarn.lock index 16a5f9e3e..9c8dd694e 100644 --- a/src/jupyter_matlab_labextension/yarn.lock +++ b/src/jupyter_matlab_labextension/yarn.lock @@ -1081,6 +1081,20 @@ __metadata: languageName: node linkType: hard +"@jupyter/ydoc@npm:^3.0.4": + version: 3.0.5 + resolution: "@jupyter/ydoc@npm:3.0.5" + dependencies: + "@jupyterlab/nbformat": ^3.0.0 || ^4.0.0-alpha.21 || ^4.0.0 + "@lumino/coreutils": ^1.11.0 || ^2.0.0 + "@lumino/disposable": ^1.10.0 || ^2.0.0 + "@lumino/signaling": ^1.10.0 || ^2.0.0 + y-protocols: ^1.0.5 + yjs: ^13.5.40 + checksum: a4f8074790e34b649e581e093806ec84ccfdcd676735d35efdba74e93114c5ff3d40e5909322ce7fc7acd0faf379ecfb8979ab88af1db9705d74b0eff4e1c75c + languageName: node + linkType: hard + "@jupyterlab/application@npm:^4.0.0": version: 4.3.4 resolution: "@jupyterlab/application@npm:4.3.4" @@ -1138,6 +1152,35 @@ __metadata: languageName: node linkType: hard +"@jupyterlab/apputils@npm:^4.5.3": + version: 4.5.3 + resolution: "@jupyterlab/apputils@npm:4.5.3" + dependencies: + "@jupyterlab/coreutils": ^6.4.3 + "@jupyterlab/observables": ^5.4.3 + "@jupyterlab/rendermime-interfaces": ^3.12.3 + "@jupyterlab/services": ^7.4.3 + "@jupyterlab/settingregistry": ^4.4.3 + "@jupyterlab/statedb": ^4.4.3 + "@jupyterlab/statusbar": ^4.4.3 + "@jupyterlab/translation": ^4.4.3 + "@jupyterlab/ui-components": ^4.4.3 + "@lumino/algorithm": ^2.0.3 + "@lumino/commands": ^2.3.2 + "@lumino/coreutils": ^2.2.1 + "@lumino/disposable": ^2.1.4 + "@lumino/domutils": ^2.0.3 + "@lumino/messaging": ^2.0.3 + "@lumino/signaling": ^2.1.4 + "@lumino/virtualdom": ^2.0.3 + "@lumino/widgets": ^2.7.1 + "@types/react": ^18.0.26 + react: ^18.2.0 + sanitize-html: ~2.12.1 + checksum: 02a642db18a7efb2cff01e52d5ca52fb36fcecbed678ba9379455ff00ac6e6437d03439991f9312e0d1cf89eec3018e72065eb615d747642820c7dfa5af6a33a + languageName: node + linkType: hard + "@jupyterlab/attachments@npm:^4.3.4": version: 4.3.4 resolution: "@jupyterlab/attachments@npm:4.3.4" @@ -1309,6 +1352,20 @@ __metadata: languageName: node linkType: hard +"@jupyterlab/coreutils@npm:^6.4.3": + version: 6.4.3 + resolution: "@jupyterlab/coreutils@npm:6.4.3" + dependencies: + "@lumino/coreutils": ^2.2.1 + "@lumino/disposable": ^2.1.4 + "@lumino/signaling": ^2.1.4 + minimist: ~1.2.0 + path-browserify: ^1.0.0 + url-parse: ~1.5.4 + checksum: f9ffd692d4e30f4813e4985972ff4323752dd30497877b58a8a791f71eda8655efe2297c38cb83b6b0ed057707c8bf50633d7bdd9c2d388a400d986458307cf4 + languageName: node + linkType: hard + "@jupyterlab/docmanager@npm:^4.3.4": version: 4.3.4 resolution: "@jupyterlab/docmanager@npm:4.3.4" @@ -1448,6 +1505,21 @@ __metadata: languageName: node linkType: hard +"@jupyterlab/mainmenu@npm:^4.3.4": + version: 4.4.3 + resolution: "@jupyterlab/mainmenu@npm:4.4.3" + dependencies: + "@jupyterlab/apputils": ^4.5.3 + "@jupyterlab/translation": ^4.4.3 + "@jupyterlab/ui-components": ^4.4.3 + "@lumino/algorithm": ^2.0.3 + "@lumino/commands": ^2.3.2 + "@lumino/coreutils": ^2.2.1 + "@lumino/widgets": ^2.7.1 + checksum: 99c3efe7556c4a704813ad4d5ebef61caada4e1384214730b45b3666e6cc6c21e894be485582a25b2d4c39ed35f07274b8b848616e1dd2bc2c2ae216a3895e44 + languageName: node + linkType: hard + "@jupyterlab/nbformat@npm:^3.0.0 || ^4.0.0-alpha.21 || ^4.0.0, @jupyterlab/nbformat@npm:^4.3.4": version: 4.3.4 resolution: "@jupyterlab/nbformat@npm:4.3.4" @@ -1457,6 +1529,15 @@ __metadata: languageName: node linkType: hard +"@jupyterlab/nbformat@npm:^4.4.3": + version: 4.4.3 + resolution: "@jupyterlab/nbformat@npm:4.4.3" + dependencies: + "@lumino/coreutils": ^2.2.1 + checksum: 2e743fcf41fa7e0bbbe06fb417467b32b3679544f5b6ebf33623ce92e04e0d545c879e5eead6b201a83e2d8aa503df9c95050be67244cc4a6c65355120f9b0fe + languageName: node + linkType: hard + "@jupyterlab/notebook@npm:^4.0.0": version: 4.3.4 resolution: "@jupyterlab/notebook@npm:4.3.4" @@ -1508,6 +1589,19 @@ __metadata: languageName: node linkType: hard +"@jupyterlab/observables@npm:^5.4.3": + version: 5.4.3 + resolution: "@jupyterlab/observables@npm:5.4.3" + dependencies: + "@lumino/algorithm": ^2.0.3 + "@lumino/coreutils": ^2.2.1 + "@lumino/disposable": ^2.1.4 + "@lumino/messaging": ^2.0.3 + "@lumino/signaling": ^2.1.4 + checksum: 97189f20cc95e3eaed94c4a2915e778be5282f7dbf04d99d1e0a348ee57601800b80244c5d7502f3c8d61ea496aeaa9d24c081deec8a6bd4c7f247d6a3c43fdc + languageName: node + linkType: hard + "@jupyterlab/outputarea@npm:^4.3.4": version: 4.3.4 resolution: "@jupyterlab/outputarea@npm:4.3.4" @@ -1540,6 +1634,16 @@ __metadata: languageName: node linkType: hard +"@jupyterlab/rendermime-interfaces@npm:^3.12.3": + version: 3.12.3 + resolution: "@jupyterlab/rendermime-interfaces@npm:3.12.3" + dependencies: + "@lumino/coreutils": ^1.11.0 || ^2.2.1 + "@lumino/widgets": ^1.37.2 || ^2.7.1 + checksum: 182560f1ba710a996d886a64c9b035a4a9da3bf65460ab451d0b4b0c1d46cba1339b682446d67f408bce32ae631a7090fba041777d4c93621249300522b9172d + languageName: node + linkType: hard + "@jupyterlab/rendermime@npm:^4.3.4": version: 4.3.4 resolution: "@jupyterlab/rendermime@npm:4.3.4" @@ -1579,6 +1683,25 @@ __metadata: languageName: node linkType: hard +"@jupyterlab/services@npm:^7.4.3": + version: 7.4.3 + resolution: "@jupyterlab/services@npm:7.4.3" + dependencies: + "@jupyter/ydoc": ^3.0.4 + "@jupyterlab/coreutils": ^6.4.3 + "@jupyterlab/nbformat": ^4.4.3 + "@jupyterlab/settingregistry": ^4.4.3 + "@jupyterlab/statedb": ^4.4.3 + "@lumino/coreutils": ^2.2.1 + "@lumino/disposable": ^2.1.4 + "@lumino/polling": ^2.1.4 + "@lumino/properties": ^2.0.3 + "@lumino/signaling": ^2.1.4 + ws: ^8.11.0 + checksum: 83c3e903c54e665e73d5beec7bf0de8785a124974eefce13b146fcd9afa5f353e5a379097d7913d87f92800097f95e104ce224061bd113776092d31bb07bd8b4 + languageName: node + linkType: hard + "@jupyterlab/settingregistry@npm:^4.3.4": version: 4.3.4 resolution: "@jupyterlab/settingregistry@npm:4.3.4" @@ -1598,6 +1721,25 @@ __metadata: languageName: node linkType: hard +"@jupyterlab/settingregistry@npm:^4.4.3": + version: 4.4.3 + resolution: "@jupyterlab/settingregistry@npm:4.4.3" + dependencies: + "@jupyterlab/nbformat": ^4.4.3 + "@jupyterlab/statedb": ^4.4.3 + "@lumino/commands": ^2.3.2 + "@lumino/coreutils": ^2.2.1 + "@lumino/disposable": ^2.1.4 + "@lumino/signaling": ^2.1.4 + "@rjsf/utils": ^5.13.4 + ajv: ^8.12.0 + json5: ^2.2.3 + peerDependencies: + react: ">=16" + checksum: d9a6a4d130d7e7190633d08bb9d28c8273aba967b350f6d55c6281f36cda8a3cd71942daee34b32f47b94b2a1cd215b3c434b9243af54c494be1b86c2eea39e9 + languageName: node + linkType: hard + "@jupyterlab/statedb@npm:^4.3.4": version: 4.3.4 resolution: "@jupyterlab/statedb@npm:4.3.4" @@ -1611,6 +1753,19 @@ __metadata: languageName: node linkType: hard +"@jupyterlab/statedb@npm:^4.4.3": + version: 4.4.3 + resolution: "@jupyterlab/statedb@npm:4.4.3" + dependencies: + "@lumino/commands": ^2.3.2 + "@lumino/coreutils": ^2.2.1 + "@lumino/disposable": ^2.1.4 + "@lumino/properties": ^2.0.3 + "@lumino/signaling": ^2.1.4 + checksum: ae50806cac848c752f4ecda6906d6566c626f28739a276438445a87a3f8a206ce920623486c499f433ae57d818a98e815b89bda34479c9d4bbfef86c784ad8b0 + languageName: node + linkType: hard + "@jupyterlab/statusbar@npm:^4.3.4": version: 4.3.4 resolution: "@jupyterlab/statusbar@npm:4.3.4" @@ -1627,6 +1782,22 @@ __metadata: languageName: node linkType: hard +"@jupyterlab/statusbar@npm:^4.4.3": + version: 4.4.3 + resolution: "@jupyterlab/statusbar@npm:4.4.3" + dependencies: + "@jupyterlab/ui-components": ^4.4.3 + "@lumino/algorithm": ^2.0.3 + "@lumino/coreutils": ^2.2.1 + "@lumino/disposable": ^2.1.4 + "@lumino/messaging": ^2.0.3 + "@lumino/signaling": ^2.1.4 + "@lumino/widgets": ^2.7.1 + react: ^18.2.0 + checksum: 9d9bf4de8222e93997a65969fe110ec7687b3948da01a788cc2685015d0fc56f7aaf8c5665e98c790426594db79a175f276984fa68ecfb0136e5fa65c9080bdd + languageName: node + linkType: hard + "@jupyterlab/toc@npm:^6.3.4": version: 6.3.4 resolution: "@jupyterlab/toc@npm:6.3.4" @@ -1663,6 +1834,19 @@ __metadata: languageName: node linkType: hard +"@jupyterlab/translation@npm:^4.4.3": + version: 4.4.3 + resolution: "@jupyterlab/translation@npm:4.4.3" + dependencies: + "@jupyterlab/coreutils": ^6.4.3 + "@jupyterlab/rendermime-interfaces": ^3.12.3 + "@jupyterlab/services": ^7.4.3 + "@jupyterlab/statedb": ^4.4.3 + "@lumino/coreutils": ^2.2.1 + checksum: 252eba345ca627c39e8253d4776677e1bb83e629cad09cd702b0cccd5c132f386c1bf68c9bfb976934960c355104c03f4ca94ad4f4b59c7123eb2566102e7c28 + languageName: node + linkType: hard + "@jupyterlab/ui-components@npm:^4.0.0, @jupyterlab/ui-components@npm:^4.3.4": version: 4.3.4 resolution: "@jupyterlab/ui-components@npm:4.3.4" @@ -1694,6 +1878,37 @@ __metadata: languageName: node linkType: hard +"@jupyterlab/ui-components@npm:^4.4.3": + version: 4.4.3 + resolution: "@jupyterlab/ui-components@npm:4.4.3" + dependencies: + "@jupyter/react-components": ^0.16.6 + "@jupyter/web-components": ^0.16.6 + "@jupyterlab/coreutils": ^6.4.3 + "@jupyterlab/observables": ^5.4.3 + "@jupyterlab/rendermime-interfaces": ^3.12.3 + "@jupyterlab/translation": ^4.4.3 + "@lumino/algorithm": ^2.0.3 + "@lumino/commands": ^2.3.2 + "@lumino/coreutils": ^2.2.1 + "@lumino/disposable": ^2.1.4 + "@lumino/messaging": ^2.0.3 + "@lumino/polling": ^2.1.4 + "@lumino/properties": ^2.0.3 + "@lumino/signaling": ^2.1.4 + "@lumino/virtualdom": ^2.0.3 + "@lumino/widgets": ^2.7.1 + "@rjsf/core": ^5.13.4 + "@rjsf/utils": ^5.13.4 + react: ^18.2.0 + react-dom: ^18.2.0 + typestyle: ^2.0.4 + peerDependencies: + react: ^18.2.0 + checksum: a7a1ef9e5a48754d4545fdbe1908c361604cabb5665541f3a1a42d7fcf0ec0c6bc78e9c3c83999fb356f3542a01666ba9e036dcef2819ae435f47e83628ae3ca + languageName: node + linkType: hard + "@lezer/common@npm:^1.0.0, @lezer/common@npm:^1.0.2, @lezer/common@npm:^1.1.0, @lezer/common@npm:^1.2.0, @lezer/common@npm:^1.2.1": version: 1.2.3 resolution: "@lezer/common@npm:1.2.3" @@ -1858,6 +2073,13 @@ __metadata: languageName: node linkType: hard +"@lumino/algorithm@npm:^2.0.3": + version: 2.0.3 + resolution: "@lumino/algorithm@npm:2.0.3" + checksum: 03932cdc39d612a00579ee40bafb0b1d8bf5f8a12449f777a1ae7201843ddefb557bc3f9260aa6b9441d87bfc43e53cced854e71c4737de59e32cd00d4ac1394 + languageName: node + linkType: hard + "@lumino/application@npm:^2.4.1": version: 2.4.1 resolution: "@lumino/application@npm:2.4.1" @@ -1878,6 +2100,15 @@ __metadata: languageName: node linkType: hard +"@lumino/collections@npm:^2.0.3": + version: 2.0.3 + resolution: "@lumino/collections@npm:2.0.3" + dependencies: + "@lumino/algorithm": ^2.0.3 + checksum: 1c7aca239731e6c7379ce593318fd3f646b38c1903e81e884e36ed01f61017498f6699ba58848c43191f4825a9968b7f9c94e9355f1614c9baee84ce9ea6221f + languageName: node + linkType: hard + "@lumino/commands@npm:^2.3.1": version: 2.3.1 resolution: "@lumino/commands@npm:2.3.1" @@ -1893,6 +2124,21 @@ __metadata: languageName: node linkType: hard +"@lumino/commands@npm:^2.3.2": + version: 2.3.2 + resolution: "@lumino/commands@npm:2.3.2" + dependencies: + "@lumino/algorithm": ^2.0.3 + "@lumino/coreutils": ^2.2.1 + "@lumino/disposable": ^2.1.4 + "@lumino/domutils": ^2.0.3 + "@lumino/keyboard": ^2.0.3 + "@lumino/signaling": ^2.1.4 + "@lumino/virtualdom": ^2.0.3 + checksum: 090454bcc07aeb71f0791d6ca86ca4857b16bb6286a47ab6e59c3046e7f99cd3ef27c36d2dd35de7cf2bdeeaf5fc00ae8f29246a39e276eac2d186ae3cd7023e + languageName: node + linkType: hard + "@lumino/coreutils@npm:^1.11.0 || ^2.0.0, @lumino/coreutils@npm:^1.11.0 || ^2.2.0, @lumino/coreutils@npm:^2.0.0, @lumino/coreutils@npm:^2.2.0": version: 2.2.0 resolution: "@lumino/coreutils@npm:2.2.0" @@ -1902,6 +2148,15 @@ __metadata: languageName: node linkType: hard +"@lumino/coreutils@npm:^1.11.0 || ^2.2.1, @lumino/coreutils@npm:^2.2.1": + version: 2.2.1 + resolution: "@lumino/coreutils@npm:2.2.1" + dependencies: + "@lumino/algorithm": ^2.0.3 + checksum: d08570d1ebcf6bca973ba3af0836fb19a5a7a5b24979e90aab0fb4acb245e9619a0db356a78d67f618ae565435bb2aaf7c158c5bc0ae1ef9e9f1638ebfa05484 + languageName: node + linkType: hard + "@lumino/disposable@npm:^1.10.0 || ^2.0.0, @lumino/disposable@npm:^2.0.0, @lumino/disposable@npm:^2.1.3": version: 2.1.3 resolution: "@lumino/disposable@npm:2.1.3" @@ -1911,6 +2166,15 @@ __metadata: languageName: node linkType: hard +"@lumino/disposable@npm:^2.1.4": + version: 2.1.4 + resolution: "@lumino/disposable@npm:2.1.4" + dependencies: + "@lumino/signaling": ^2.1.4 + checksum: 0274c1cd81683f0d37c79795ed683fe49929452e6f075b9027b62dee376b5c6aa5f27b279236c4e1621bcbdcb844d5be0bbde3a065ab39159deb995244d1d2a7 + languageName: node + linkType: hard + "@lumino/domutils@npm:^2.0.2": version: 2.0.2 resolution: "@lumino/domutils@npm:2.0.2" @@ -1918,6 +2182,13 @@ __metadata: languageName: node linkType: hard +"@lumino/domutils@npm:^2.0.3": + version: 2.0.3 + resolution: "@lumino/domutils@npm:2.0.3" + checksum: 46cbcbd38f6abb53eab1b6de0a2ea8a9fa5e28b0f5aa4b058c35f2380cb8ec881fe7616c7468ba200b785f95357ac8cbac6b64512f9945f5973d1d425864b163 + languageName: node + linkType: hard + "@lumino/dragdrop@npm:^2.1.5": version: 2.1.5 resolution: "@lumino/dragdrop@npm:2.1.5" @@ -1928,6 +2199,16 @@ __metadata: languageName: node linkType: hard +"@lumino/dragdrop@npm:^2.1.6": + version: 2.1.6 + resolution: "@lumino/dragdrop@npm:2.1.6" + dependencies: + "@lumino/coreutils": ^2.2.1 + "@lumino/disposable": ^2.1.4 + checksum: 5a746ee0644e2fa02cba47d6ef45f3fb09ebc3391ac0f478f6f3073864a9637e13fcee666038c751ab8f17bc69c55299c85a88f526ea645cc3240a367490c8ca + languageName: node + linkType: hard + "@lumino/keyboard@npm:^2.0.2": version: 2.0.2 resolution: "@lumino/keyboard@npm:2.0.2" @@ -1935,6 +2216,13 @@ __metadata: languageName: node linkType: hard +"@lumino/keyboard@npm:^2.0.3": + version: 2.0.3 + resolution: "@lumino/keyboard@npm:2.0.3" + checksum: ca648cf978ddcf15fe3af2b8c8beb8aff153dfe616099df5a8bc7f43124420f77c358dbd33a988911b82f68debe07268d630c1777618b182ef7b520962d653e7 + languageName: node + linkType: hard + "@lumino/messaging@npm:^2.0.2": version: 2.0.2 resolution: "@lumino/messaging@npm:2.0.2" @@ -1945,6 +2233,16 @@ __metadata: languageName: node linkType: hard +"@lumino/messaging@npm:^2.0.3": + version: 2.0.3 + resolution: "@lumino/messaging@npm:2.0.3" + dependencies: + "@lumino/algorithm": ^2.0.3 + "@lumino/collections": ^2.0.3 + checksum: 9c2bea2a31d3922a29276df751b651e6bd41d1ed3a5f61ba94d3e90d454c53f07fc4dac7d435867fb8480415222a3d45d74188dd73e9c89c43110ebbee0ff301 + languageName: node + linkType: hard + "@lumino/polling@npm:^2.1.3": version: 2.1.3 resolution: "@lumino/polling@npm:2.1.3" @@ -1956,6 +2254,17 @@ __metadata: languageName: node linkType: hard +"@lumino/polling@npm:^2.1.4": + version: 2.1.4 + resolution: "@lumino/polling@npm:2.1.4" + dependencies: + "@lumino/coreutils": ^2.2.1 + "@lumino/disposable": ^2.1.4 + "@lumino/signaling": ^2.1.4 + checksum: e08d07d11eb030fed83bea232dba91af4ea40ef8f6ec7b8fe61722ebbd29faba10c67d269596c19c515c920f607c73bb64cdc9319af9ecef4619cddfd92ea764 + languageName: node + linkType: hard + "@lumino/properties@npm:^2.0.2": version: 2.0.2 resolution: "@lumino/properties@npm:2.0.2" @@ -1963,6 +2272,13 @@ __metadata: languageName: node linkType: hard +"@lumino/properties@npm:^2.0.3": + version: 2.0.3 + resolution: "@lumino/properties@npm:2.0.3" + checksum: a575d821f994090907abb567d3af21a828f528ae5f329ada92719eba9818bbb2b0955e675b91bd392043a5d835c345d7b500994a77157c5ea317f36442ce570e + languageName: node + linkType: hard + "@lumino/signaling@npm:^1.10.0 || ^2.0.0, @lumino/signaling@npm:^2.1.3": version: 2.1.3 resolution: "@lumino/signaling@npm:2.1.3" @@ -1973,6 +2289,16 @@ __metadata: languageName: node linkType: hard +"@lumino/signaling@npm:^2.1.4": + version: 2.1.4 + resolution: "@lumino/signaling@npm:2.1.4" + dependencies: + "@lumino/algorithm": ^2.0.3 + "@lumino/coreutils": ^2.2.1 + checksum: 554a5135c8742ed3f61a4923b1f26cb29b55447ca5939df70033449cfb654a37048d7a3e2fd0932497099cd24501a3819b85cd1fdf4e76023ba0af747c171d53 + languageName: node + linkType: hard + "@lumino/virtualdom@npm:^2.0.2": version: 2.0.2 resolution: "@lumino/virtualdom@npm:2.0.2" @@ -1982,6 +2308,15 @@ __metadata: languageName: node linkType: hard +"@lumino/virtualdom@npm:^2.0.3": + version: 2.0.3 + resolution: "@lumino/virtualdom@npm:2.0.3" + dependencies: + "@lumino/algorithm": ^2.0.3 + checksum: 66c18494fdfc1b87e76286140cd256b3616aede262641912646a18395226e200048ddeaa6d1644dff3f597b1cde8e583968cb973d64a9e9d4f45e2b24c1e2c7c + languageName: node + linkType: hard + "@lumino/widgets@npm:^1.37.2 || ^2.5.0, @lumino/widgets@npm:^2.5.0": version: 2.5.0 resolution: "@lumino/widgets@npm:2.5.0" @@ -2001,6 +2336,25 @@ __metadata: languageName: node linkType: hard +"@lumino/widgets@npm:^1.37.2 || ^2.7.1, @lumino/widgets@npm:^2.7.1": + version: 2.7.1 + resolution: "@lumino/widgets@npm:2.7.1" + dependencies: + "@lumino/algorithm": ^2.0.3 + "@lumino/commands": ^2.3.2 + "@lumino/coreutils": ^2.2.1 + "@lumino/disposable": ^2.1.4 + "@lumino/domutils": ^2.0.3 + "@lumino/dragdrop": ^2.1.6 + "@lumino/keyboard": ^2.0.3 + "@lumino/messaging": ^2.0.3 + "@lumino/properties": ^2.0.3 + "@lumino/signaling": ^2.1.4 + "@lumino/virtualdom": ^2.0.3 + checksum: c57f7e6cfbaddbd830e14db55242dcbdf531524cdf8641214ce737f43a6684004219eb58a572838f99f78af433bb8f9f19fd2ac6f0ffab4a635bd20164b75cec + languageName: node + linkType: hard + "@marijn/find-cluster-break@npm:^1.0.0": version: 1.0.2 resolution: "@marijn/find-cluster-break@npm:1.0.2" @@ -2332,6 +2686,15 @@ __metadata: languageName: node linkType: hard +"@types/node@npm:^24.0.4": + version: 24.0.4 + resolution: "@types/node@npm:24.0.4" + dependencies: + undici-types: ~7.8.0 + checksum: db036e3f1fea5a92fd6cd76064cf07d517b245ace73d6f46bcd8e35f38ff856f86f3311aabc7c7406de2bdae9f8308070541dacfce20fec2510d42b3cd00bb9e + languageName: node + linkType: hard + "@types/prop-types@npm:*": version: 15.7.14 resolution: "@types/prop-types@npm:15.7.14" @@ -6155,11 +6518,13 @@ __metadata: "@jupyterlab/coreutils": ^6.0.0 "@jupyterlab/docregistry": ^4.0.0 "@jupyterlab/launcher": ^4.0.0 + "@jupyterlab/mainmenu": ^4.3.4 "@jupyterlab/notebook": ^4.0.0 "@jupyterlab/ui-components": ^4.0.0 "@lumino/coreutils": ^2.0.0 "@lumino/disposable": ^2.0.0 "@types/jest": ^29.5.14 + "@types/node": ^24.0.4 "@typescript-eslint/eslint-plugin": ^5.62.0 "@typescript-eslint/parser": ^5.62.0 cross-spawn: ^6.0.6 @@ -8496,6 +8861,13 @@ __metadata: languageName: node linkType: hard +"undici-types@npm:~7.8.0": + version: 7.8.0 + resolution: "undici-types@npm:7.8.0" + checksum: 59521a5b9b50e72cb838a29466b3557b4eacbc191a83f4df5a2f7b156bc8263072b145dc4bb8ec41da7d56a7e9b178892458da02af769243d57f801a50ac5751 + languageName: node + linkType: hard + "unique-filename@npm:^4.0.0": version: 4.0.0 resolution: "unique-filename@npm:4.0.0" diff --git a/src/jupyter_matlab_proxy/README.md b/src/jupyter_matlab_proxy/README.md index 069ea121f..9ce0ceeca 100644 --- a/src/jupyter_matlab_proxy/README.md +++ b/src/jupyter_matlab_proxy/README.md @@ -52,8 +52,10 @@ To report any issues or suggestions, see the [Feedback](https://github.com/mathw | Help | Open a help pop-up for a detailed description of the options.| ## Limitations -This package supports the same subset of MATLAB features and commands as MATLAB® Online, except there is no support for Simulink® Online. -[Click here for a full list of Specifications and Limitations for MATLAB Online](https://www.mathworks.com/products/matlab-online/limitations.html). +This package supports the same set of MATLAB features and commands as MATLAB® Online. For the full list, see +[Specifications and Limitations for MATLAB Online](https://www.mathworks.com/products/matlab-online/limitations.html). + +Simulink Online is supported exclusively on Linux platforms starting from MATLAB R2024b. If you need to use functionality that is not yet supported, or for versions of MATLAB earlier than R2020b, you can use the alternative [MATLAB Integration for Jupyter using VNC](https://github.com/mathworks/jupyter-matlab-vnc-proxy). diff --git a/tests/e2e/package-lock.json b/tests/e2e/package-lock.json index 885ce17a2..63bee2ca3 100644 --- a/tests/e2e/package-lock.json +++ b/tests/e2e/package-lock.json @@ -19,29 +19,125 @@ "typescript": "^5.0.0" } }, + "node_modules/@antfu/install-pkg": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@antfu/install-pkg/-/install-pkg-1.1.0.tgz", + "integrity": "sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "package-manager-detector": "^1.3.0", + "tinyexec": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@antfu/utils": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/@antfu/utils/-/utils-9.3.0.tgz", + "integrity": "sha512-9hFT4RauhcUzqOE4f1+frMKLZrgNog5b06I7VmZQV1BkvwvqrbC8EBZf3L1eEL2AKb6rNKjER0sEvJiSP1FXEA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@braintree/sanitize-url": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@braintree/sanitize-url/-/sanitize-url-7.1.1.tgz", + "integrity": "sha512-i1L7noDNxtFyL5DmZafWy1wRVhGehQmzZaz1HiN5e7iylJMSZR7ekOV7NsIqa5qBldlLrsKv4HbgFUVlQrz8Mw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@chevrotain/cst-dts-gen": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@chevrotain/cst-dts-gen/-/cst-dts-gen-11.0.3.tgz", + "integrity": "sha512-BvIKpRLeS/8UbfxXxgC33xOumsacaeCKAjAeLyOn7Pcp95HiRbrpl14S+9vaZLolnbssPIUuiUd8IvgkRyt6NQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@chevrotain/gast": "11.0.3", + "@chevrotain/types": "11.0.3", + "lodash-es": "4.17.21" + } + }, + "node_modules/@chevrotain/gast": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@chevrotain/gast/-/gast-11.0.3.tgz", + "integrity": "sha512-+qNfcoNk70PyS/uxmj3li5NiECO+2YKZZQMbmjTqRI3Qchu8Hig/Q9vgkHpI3alNjr7M+a2St5pw5w5F6NL5/Q==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@chevrotain/types": "11.0.3", + "lodash-es": "4.17.21" + } + }, + "node_modules/@chevrotain/regexp-to-ast": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@chevrotain/regexp-to-ast/-/regexp-to-ast-11.0.3.tgz", + "integrity": "sha512-1fMHaBZxLFvWI067AVbGJav1eRY7N8DDvYCTwGBiE/ytKBgP8azTdgyrKyWZ9Mfh09eHWb5PgTSO8wi7U824RA==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/@chevrotain/types": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@chevrotain/types/-/types-11.0.3.tgz", + "integrity": "sha512-gsiM3G8b58kZC2HaWR50gu6Y1440cHiJ+i3JUvcp/35JchYejb2+5MVeJK0iKThYpAa/P2PYFV4hoi44HD+aHQ==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/@chevrotain/utils": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/@chevrotain/utils/-/utils-11.0.3.tgz", + "integrity": "sha512-YslZMgtJUyuMbZ+aKvfF3x1f5liK4mWNxghFRv7jqRR9C3R3fAOGTTKvxXDa2Y1s9zSbcpuO0cAxDYsc9SrXoQ==", + "dev": true, + "license": "Apache-2.0" + }, "node_modules/@codemirror/autocomplete": { - "version": "6.18.3", - "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.18.3.tgz", - "integrity": "sha512-1dNIOmiM0z4BIBwxmxEfA1yoxh1MF/6KPBbh20a5vphGV0ictKlgQsbJs6D6SkR6iJpGbpwRsa6PFMNlg9T9pQ==", + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.20.0.tgz", + "integrity": "sha512-bOwvTOIJcG5FVo5gUUupiwYh8MioPLQ4UcqbcRf7UQ98X90tCa9E1kZ3Z7tqwpZxYyOvh1YTYbmZE9RTfTp5hg==", "dev": true, + "license": "MIT", "dependencies": { "@codemirror/language": "^6.0.0", "@codemirror/state": "^6.0.0", "@codemirror/view": "^6.17.0", "@lezer/common": "^1.0.0" - }, - "peerDependencies": { - "@codemirror/language": "^6.0.0", - "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.0.0", - "@lezer/common": "^1.0.0" } }, "node_modules/@codemirror/commands": { - "version": "6.7.1", - "resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.7.1.tgz", - "integrity": "sha512-llTrboQYw5H4THfhN4U3qCnSZ1SOJ60ohhz+SzU0ADGtwlc533DtklQP0vSFaQuCPDn3BPpOd1GbbnUtwNjsrw==", + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.10.0.tgz", + "integrity": "sha512-2xUIc5mHXQzT16JnyOFkh8PvfeXuIut3pslWGfsGOhxP/lpgRm9HOl/mpzLErgt5mXDovqA0d11P21gofRLb9w==", "dev": true, + "license": "MIT", "dependencies": { "@codemirror/language": "^6.0.0", "@codemirror/state": "^6.4.0", @@ -50,20 +146,22 @@ } }, "node_modules/@codemirror/lang-cpp": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@codemirror/lang-cpp/-/lang-cpp-6.0.2.tgz", - "integrity": "sha512-6oYEYUKHvrnacXxWxYa6t4puTlbN3dgV662BDfSH8+MfjQjVmP697/KYTDOqpxgerkvoNm7q5wlFMBeX8ZMocg==", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@codemirror/lang-cpp/-/lang-cpp-6.0.3.tgz", + "integrity": "sha512-URM26M3vunFFn9/sm6rzqrBzDgfWuDixp85uTY49wKudToc2jTHUrKIGGKs+QWND+YLofNNZpxcNGRynFJfvgA==", "dev": true, + "license": "MIT", "dependencies": { "@codemirror/language": "^6.0.0", "@lezer/cpp": "^1.0.0" } }, "node_modules/@codemirror/lang-css": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/@codemirror/lang-css/-/lang-css-6.3.0.tgz", - "integrity": "sha512-CyR4rUNG9OYcXDZwMPvJdtb6PHbBDKUc/6Na2BIwZ6dKab1JQqKa4di+RNRY9Myn7JB81vayKwJeQ7jEdmNVDA==", + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/@codemirror/lang-css/-/lang-css-6.3.1.tgz", + "integrity": "sha512-kr5fwBGiGtmz6l0LSJIbno9QrifNMUusivHbnA1H6Dmqy4HZFte3UAICix1VuKo0lMPKQr2rqB+0BkKi/S3Ejg==", "dev": true, + "license": "MIT", "dependencies": { "@codemirror/autocomplete": "^6.0.0", "@codemirror/language": "^6.0.0", @@ -73,10 +171,11 @@ } }, "node_modules/@codemirror/lang-html": { - "version": "6.4.9", - "resolved": "https://registry.npmjs.org/@codemirror/lang-html/-/lang-html-6.4.9.tgz", - "integrity": "sha512-aQv37pIMSlueybId/2PVSP6NPnmurFDVmZwzc7jszd2KAF8qd4VBbvNYPXWQq90WIARjsdVkPbw29pszmHws3Q==", + "version": "6.4.11", + "resolved": "https://registry.npmjs.org/@codemirror/lang-html/-/lang-html-6.4.11.tgz", + "integrity": "sha512-9NsXp7Nwp891pQchI7gPdTwBuSuT3K65NGTHWHNJ55HjYcHLllr0rbIZNdOzas9ztc1EUVBlHou85FFZS4BNnw==", "dev": true, + "license": "MIT", "dependencies": { "@codemirror/autocomplete": "^6.0.0", "@codemirror/lang-css": "^6.0.0", @@ -86,24 +185,26 @@ "@codemirror/view": "^6.17.0", "@lezer/common": "^1.0.0", "@lezer/css": "^1.1.0", - "@lezer/html": "^1.3.0" + "@lezer/html": "^1.3.12" } }, "node_modules/@codemirror/lang-java": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/@codemirror/lang-java/-/lang-java-6.0.1.tgz", - "integrity": "sha512-OOnmhH67h97jHzCuFaIEspbmsT98fNdhVhmA3zCxW0cn7l8rChDhZtwiwJ/JOKXgfm4J+ELxQihxaI7bj7mJRg==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@codemirror/lang-java/-/lang-java-6.0.2.tgz", + "integrity": "sha512-m5Nt1mQ/cznJY7tMfQTJchmrjdjQ71IDs+55d1GAa8DGaB8JXWsVCkVT284C3RTASaY43YknrK2X3hPO/J3MOQ==", "dev": true, + "license": "MIT", "dependencies": { "@codemirror/language": "^6.0.0", "@lezer/java": "^1.0.0" } }, "node_modules/@codemirror/lang-javascript": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/@codemirror/lang-javascript/-/lang-javascript-6.2.2.tgz", - "integrity": "sha512-VGQfY+FCc285AhWuwjYxQyUQcYurWlxdKYT4bqwr3Twnd5wP5WSeu52t4tvvuWmljT4EmgEgZCqSieokhtY8hg==", + "version": "6.2.4", + "resolved": "https://registry.npmjs.org/@codemirror/lang-javascript/-/lang-javascript-6.2.4.tgz", + "integrity": "sha512-0WVmhp1QOqZ4Rt6GlVGwKJN3KW7Xh4H2q8ZZNGZaP6lRdxXJzmjm4FqvmOojVj6khWJHIb9sp7U/72W7xQgqAA==", "dev": true, + "license": "MIT", "dependencies": { "@codemirror/autocomplete": "^6.0.0", "@codemirror/language": "^6.6.0", @@ -115,20 +216,22 @@ } }, "node_modules/@codemirror/lang-json": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/@codemirror/lang-json/-/lang-json-6.0.1.tgz", - "integrity": "sha512-+T1flHdgpqDDlJZ2Lkil/rLiRy684WMLc74xUnjJH48GQdfJo/pudlTRreZmKwzP8/tGdKf83wlbAdOCzlJOGQ==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@codemirror/lang-json/-/lang-json-6.0.2.tgz", + "integrity": "sha512-x2OtO+AvwEHrEwR0FyyPtfDUiloG3rnVTSZV1W8UteaLL8/MajQd8DpvUb2YVzC+/T18aSDv0H9mu+xw0EStoQ==", "dev": true, + "license": "MIT", "dependencies": { "@codemirror/language": "^6.0.0", "@lezer/json": "^1.0.0" } }, "node_modules/@codemirror/lang-markdown": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/@codemirror/lang-markdown/-/lang-markdown-6.3.1.tgz", - "integrity": "sha512-y3sSPuQjBKZQbQwe3ZJKrSW6Silyl9PnrU/Mf0m2OQgIlPoSYTtOvEL7xs94SVMkb8f4x+SQFnzXPdX4Wk2lsg==", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@codemirror/lang-markdown/-/lang-markdown-6.5.0.tgz", + "integrity": "sha512-0K40bZ35jpHya6FriukbgaleaqzBLZfOh7HuzqbMxBXkbYMJDxfF39c23xOgxFezR+3G+tR2/Mup+Xk865OMvw==", "dev": true, + "license": "MIT", "dependencies": { "@codemirror/autocomplete": "^6.7.1", "@codemirror/lang-html": "^6.0.0", @@ -140,10 +243,11 @@ } }, "node_modules/@codemirror/lang-php": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/@codemirror/lang-php/-/lang-php-6.0.1.tgz", - "integrity": "sha512-ublojMdw/PNWa7qdN5TMsjmqkNuTBD3k6ndZ4Z0S25SBAiweFGyY68AS3xNcIOlb6DDFDvKlinLQ40vSLqf8xA==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@codemirror/lang-php/-/lang-php-6.0.2.tgz", + "integrity": "sha512-ZKy2v1n8Fc8oEXj0Th0PUMXzQJ0AIR6TaZU+PbDHExFwdu+guzOA4jmCHS1Nz4vbFezwD7LyBdDnddSJeScMCA==", "dev": true, + "license": "MIT", "dependencies": { "@codemirror/lang-html": "^6.0.0", "@codemirror/language": "^6.0.0", @@ -153,10 +257,11 @@ } }, "node_modules/@codemirror/lang-python": { - "version": "6.1.6", - "resolved": "https://registry.npmjs.org/@codemirror/lang-python/-/lang-python-6.1.6.tgz", - "integrity": "sha512-ai+01WfZhWqM92UqjnvorkxosZ2aq2u28kHvr+N3gu012XqY2CThD67JPMHnGceRfXPDBmn1HnyqowdpF57bNg==", + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/@codemirror/lang-python/-/lang-python-6.2.1.tgz", + "integrity": "sha512-IRjC8RUBhn9mGR9ywecNhB51yePWCGgvHfY1lWN/Mrp3cKuHr0isDKia+9HnvhiWNnMpbGhWrkhuWOc09exRyw==", "dev": true, + "license": "MIT", "dependencies": { "@codemirror/autocomplete": "^6.3.2", "@codemirror/language": "^6.8.0", @@ -166,20 +271,22 @@ } }, "node_modules/@codemirror/lang-rust": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/@codemirror/lang-rust/-/lang-rust-6.0.1.tgz", - "integrity": "sha512-344EMWFBzWArHWdZn/NcgkwMvZIWUR1GEBdwG8FEp++6o6vT6KL9V7vGs2ONsKxxFUPXKI0SPcWhyYyl2zPYxQ==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@codemirror/lang-rust/-/lang-rust-6.0.2.tgz", + "integrity": "sha512-EZaGjCUegtiU7kSMvOfEZpaCReowEf3yNidYu7+vfuGTm9ow4mthAparY5hisJqOHmJowVH3Upu+eJlUji6qqA==", "dev": true, + "license": "MIT", "dependencies": { "@codemirror/language": "^6.0.0", "@lezer/rust": "^1.0.0" } }, "node_modules/@codemirror/lang-sql": { - "version": "6.8.0", - "resolved": "https://registry.npmjs.org/@codemirror/lang-sql/-/lang-sql-6.8.0.tgz", - "integrity": "sha512-aGLmY4OwGqN3TdSx3h6QeA1NrvaYtF7kkoWR/+W7/JzB0gQtJ+VJxewlnE3+VImhA4WVlhmkJr109PefOOhjLg==", + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/@codemirror/lang-sql/-/lang-sql-6.10.0.tgz", + "integrity": "sha512-6ayPkEd/yRw0XKBx5uAiToSgGECo/GY2NoJIHXIIQh1EVwLuKoU8BP/qK0qH5NLXAbtJRLuT73hx7P9X34iO4w==", "dev": true, + "license": "MIT", "dependencies": { "@codemirror/autocomplete": "^6.0.0", "@codemirror/language": "^6.0.0", @@ -194,6 +301,7 @@ "resolved": "https://registry.npmjs.org/@codemirror/lang-wast/-/lang-wast-6.0.2.tgz", "integrity": "sha512-Imi2KTpVGm7TKuUkqyJ5NRmeFWF7aMpNiwHnLQe0x9kmrxElndyH0K6H/gXtWwY6UshMRAhpENsgfpSwsgmC6Q==", "dev": true, + "license": "MIT", "dependencies": { "@codemirror/language": "^6.0.0", "@lezer/common": "^1.2.0", @@ -206,6 +314,7 @@ "resolved": "https://registry.npmjs.org/@codemirror/lang-xml/-/lang-xml-6.1.0.tgz", "integrity": "sha512-3z0blhicHLfwi2UgkZYRPioSgVTo9PV5GP5ducFH6FaHy0IAJRg+ixj5gTR1gnT/glAIC8xv4w2VL1LoZfs+Jg==", "dev": true, + "license": "MIT", "dependencies": { "@codemirror/autocomplete": "^6.0.0", "@codemirror/language": "^6.4.0", @@ -216,10 +325,11 @@ } }, "node_modules/@codemirror/language": { - "version": "6.10.3", - "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.10.3.tgz", - "integrity": "sha512-kDqEU5sCP55Oabl6E7m5N+vZRoc0iWqgDVhEKifcHzPzjqCegcO4amfrYVL9PmPZpl4G0yjkpTpUO/Ui8CzO8A==", + "version": "6.11.3", + "resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.11.3.tgz", + "integrity": "sha512-9HBM2XnwDj7fnu0551HkGdrUrrqmYq/WC5iv6nbY2WdicXdGbhR/gfbZOH73Aqj4351alY1+aoG9rCNfiwS1RA==", "dev": true, + "license": "MIT", "dependencies": { "@codemirror/state": "^6.0.0", "@codemirror/view": "^6.23.0", @@ -230,30 +340,33 @@ } }, "node_modules/@codemirror/legacy-modes": { - "version": "6.4.2", - "resolved": "https://registry.npmjs.org/@codemirror/legacy-modes/-/legacy-modes-6.4.2.tgz", - "integrity": "sha512-HsvWu08gOIIk303eZQCal4H4t65O/qp1V4ul4zVa3MHK5FJ0gz3qz3O55FIkm+aQUcshUOjBx38t2hPiJwW5/g==", + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/@codemirror/legacy-modes/-/legacy-modes-6.5.2.tgz", + "integrity": "sha512-/jJbwSTazlQEDOQw2FJ8LEEKVS72pU0lx6oM54kGpL8t/NJ2Jda3CZ4pcltiKTdqYSRk3ug1B3pil1gsjA6+8Q==", "dev": true, + "license": "MIT", "dependencies": { "@codemirror/language": "^6.0.0" } }, "node_modules/@codemirror/lint": { - "version": "6.8.2", - "resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.8.2.tgz", - "integrity": "sha512-PDFG5DjHxSEjOXk9TQYYVjZDqlZTFaDBfhQixHnQOEVDDNHUbEh/hstAjcQJaA6FQdZTD1hquXTK0rVBLADR1g==", + "version": "6.9.2", + "resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.9.2.tgz", + "integrity": "sha512-sv3DylBiIyi+xKwRCJAAsBZZZWo82shJ/RTMymLabAdtbkV5cSKwWDeCgtUq3v8flTaXS2y1kKkICuRYtUswyQ==", "dev": true, + "license": "MIT", "dependencies": { "@codemirror/state": "^6.0.0", - "@codemirror/view": "^6.0.0", + "@codemirror/view": "^6.35.0", "crelt": "^1.0.5" } }, "node_modules/@codemirror/search": { - "version": "6.5.7", - "resolved": "https://registry.npmjs.org/@codemirror/search/-/search-6.5.7.tgz", - "integrity": "sha512-6+iLsXvITWKHYlkgHPCs/qiX4dNzn8N78YfhOFvPtPYCkuXqZq10rAfsUMhOq7O/1VjJqdXRflyExlfVcu/9VQ==", + "version": "6.5.11", + "resolved": "https://registry.npmjs.org/@codemirror/search/-/search-6.5.11.tgz", + "integrity": "sha512-KmWepDE6jUdL6n8cAAqIpRmLPBZ5ZKnicE8oGU/s3QrAVID+0VhLFrzUucVKHG5035/BSykhExDL/Xm7dHthiA==", "dev": true, + "license": "MIT", "dependencies": { "@codemirror/state": "^6.0.0", "@codemirror/view": "^6.0.0", @@ -261,18 +374,24 @@ } }, "node_modules/@codemirror/state": { - "version": "6.4.1", - "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.4.1.tgz", - "integrity": "sha512-QkEyUiLhsJoZkbumGZlswmAhA7CBU02Wrz7zvH4SrcifbsqwlXShVXg65f3v/ts57W3dqyamEriMhij1Z3Zz4A==", - "dev": true + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.5.2.tgz", + "integrity": "sha512-FVqsPqtPWKVVL3dPSxy8wEF/ymIEuVzF1PK3VbUgrxXpJUSHQWWZz4JMToquRxnkw+36LTamCZG2iua2Ptq0fA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@marijn/find-cluster-break": "^1.0.0" + } }, "node_modules/@codemirror/view": { - "version": "6.34.3", - "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.34.3.tgz", - "integrity": "sha512-Ph5d+u8DxIeSgssXEakaakImkzBV4+slwIbcxl9oc9evexJhImeu/G8TK7+zp+IFK9KuJ0BdSn6kTBJeH2CHvA==", + "version": "6.38.8", + "resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.38.8.tgz", + "integrity": "sha512-XcE9fcnkHCbWkjeKyi0lllwXmBLtyYb5dt89dJyx23I9+LSh5vZDIuk7OLG4VM1lgrXZQcY6cxyZyk5WVPRv/A==", "dev": true, + "license": "MIT", "dependencies": { - "@codemirror/state": "^6.4.0", + "@codemirror/state": "^6.5.0", + "crelt": "^1.0.6", "style-mod": "^4.1.0", "w3c-keyname": "^2.2.4" } @@ -344,10 +463,11 @@ } }, "node_modules/@eslint/eslintrc/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==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -409,6 +529,7 @@ "integrity": "sha512-eYm8vijH/hpzr/6/1CJ/V/Eb1xQFW2nnUKArb3z+yUWv7HTwj6M7SP957oMjfZjAHU6qpoNc2wQvIxBLWYa/Jg==", "dev": true, "hasInstallScript": true, + "license": "(CC-BY-4.0 AND OFL-1.1 AND MIT)", "engines": { "node": ">=6" } @@ -429,10 +550,11 @@ } }, "node_modules/@humanwhocodes/config-array/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==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -493,11 +615,139 @@ "deprecated": "Use @eslint/object-schema instead", "dev": true }, + "node_modules/@iconify/types": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@iconify/types/-/types-2.0.0.tgz", + "integrity": "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@iconify/utils": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@iconify/utils/-/utils-3.0.2.tgz", + "integrity": "sha512-EfJS0rLfVuRuJRn4psJHtK2A9TqVnkxPpHY6lYHiB9+8eSuudsxbwMiavocG45ujOo6FJ+CIRlRnlOGinzkaGQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@antfu/install-pkg": "^1.1.0", + "@antfu/utils": "^9.2.0", + "@iconify/types": "^2.0.0", + "debug": "^4.4.1", + "globals": "^15.15.0", + "kolorist": "^1.8.0", + "local-pkg": "^1.1.1", + "mlly": "^1.7.4" + } + }, + "node_modules/@iconify/utils/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@iconify/utils/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/@iconify/utils/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/@jest/environment": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", + "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/fake-timers": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", + "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@sinonjs/fake-timers": "^10.0.2", + "@types/node": "*", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, "node_modules/@jupyter/react-components": { "version": "0.16.7", "resolved": "https://registry.npmjs.org/@jupyter/react-components/-/react-components-0.16.7.tgz", "integrity": "sha512-BKIPkJ9V011uhtdq1xBOu2M3up59CqsRbDS4aq8XhnHR4pwqfRV6k6irE5YBOETCoIwWZZ5RZO+cJcZ3DcsT5A==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "@jupyter/web-components": "^0.16.7", "react": ">=17.0.0 <19.0.0" @@ -508,6 +758,7 @@ "resolved": "https://registry.npmjs.org/@jupyter/web-components/-/web-components-0.16.7.tgz", "integrity": "sha512-1a8awgvvP9J9pCV5vBRuQxdBk29764qiMJsJYEndrWH3cB/FlaO+sZIBm4OTf56Eqdgl8R3/ZSLM1+3mgXOkPg==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "@microsoft/fast-colors": "^5.3.1", "@microsoft/fast-element": "^1.12.0", @@ -516,10 +767,11 @@ } }, "node_modules/@jupyter/ydoc": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@jupyter/ydoc/-/ydoc-3.0.1.tgz", - "integrity": "sha512-zO6PEe/kNpb9oRPhxytLdnL4zclVDJVCIEavprIlIs/qCxGbgC/BvIoK6N/Ny525Ljrev8Ku9B+y6P2qFn3zqg==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@jupyter/ydoc/-/ydoc-3.3.1.tgz", + "integrity": "sha512-q//mryEjBYgS83oxRoVkW9gRKp/34u0KtLguOjFvqwJDOn5Ksg2BtSwwMjcIru8i9MMtPqZywD7ZXSUAk3oJnw==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "@jupyterlab/nbformat": "^3.0.0 || ^4.0.0-alpha.21 || ^4.0.0", "@lumino/coreutils": "^1.11.0 || ^2.0.0", @@ -530,396 +782,415 @@ } }, "node_modules/@jupyterlab/application": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@jupyterlab/application/-/application-4.3.1.tgz", - "integrity": "sha512-+Nzs99Umz36lH2a1E5u7ie68e4L1GnZX0YJHvFoREVvTXxwvGWvwrrAmS360GrlLD1kszg5LjAKV4H1hfoXXJQ==", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@jupyterlab/application/-/application-4.5.0.tgz", + "integrity": "sha512-8Xqu5sfEWhJkXqv5XY5kKhhtuvvrlXiUU3jw20gHNWVgy7m/RqJODEq1FMkcFpij0hW4cl62dkmmEjlQU8TGmQ==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "@fortawesome/fontawesome-free": "^5.12.0", - "@jupyterlab/apputils": "^4.4.1", - "@jupyterlab/coreutils": "^6.3.1", - "@jupyterlab/docregistry": "^4.3.1", - "@jupyterlab/rendermime": "^4.3.1", - "@jupyterlab/rendermime-interfaces": "^3.11.1", - "@jupyterlab/services": "^7.3.1", - "@jupyterlab/statedb": "^4.3.1", - "@jupyterlab/translation": "^4.3.1", - "@jupyterlab/ui-components": "^4.3.1", - "@lumino/algorithm": "^2.0.2", - "@lumino/application": "^2.4.1", - "@lumino/commands": "^2.3.1", - "@lumino/coreutils": "^2.2.0", - "@lumino/disposable": "^2.1.3", - "@lumino/messaging": "^2.0.2", - "@lumino/polling": "^2.1.3", - "@lumino/properties": "^2.0.2", - "@lumino/signaling": "^2.1.3", - "@lumino/widgets": "^2.5.0" + "@jupyterlab/apputils": "^4.6.0", + "@jupyterlab/coreutils": "^6.5.0", + "@jupyterlab/docregistry": "^4.5.0", + "@jupyterlab/rendermime": "^4.5.0", + "@jupyterlab/rendermime-interfaces": "^3.13.0", + "@jupyterlab/services": "^7.5.0", + "@jupyterlab/statedb": "^4.5.0", + "@jupyterlab/translation": "^4.5.0", + "@jupyterlab/ui-components": "^4.5.0", + "@lumino/algorithm": "^2.0.4", + "@lumino/application": "^2.4.5", + "@lumino/commands": "^2.3.3", + "@lumino/coreutils": "^2.2.2", + "@lumino/disposable": "^2.1.5", + "@lumino/messaging": "^2.0.4", + "@lumino/polling": "^2.1.5", + "@lumino/properties": "^2.0.4", + "@lumino/signaling": "^2.1.5", + "@lumino/widgets": "^2.7.2" } }, "node_modules/@jupyterlab/apputils": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/@jupyterlab/apputils/-/apputils-4.4.1.tgz", - "integrity": "sha512-Yc66aJaTrktvJAuNP5/nQFeqZqiYRoMs7J1zbbPbPVn8+bRiuSF/0eCaCYY6X4U0cC0mZjCVmS6zrPHNthnktg==", - "dev": true, - "dependencies": { - "@jupyterlab/coreutils": "^6.3.1", - "@jupyterlab/observables": "^5.3.1", - "@jupyterlab/rendermime-interfaces": "^3.11.1", - "@jupyterlab/services": "^7.3.1", - "@jupyterlab/settingregistry": "^4.3.1", - "@jupyterlab/statedb": "^4.3.1", - "@jupyterlab/statusbar": "^4.3.1", - "@jupyterlab/translation": "^4.3.1", - "@jupyterlab/ui-components": "^4.3.1", - "@lumino/algorithm": "^2.0.2", - "@lumino/commands": "^2.3.1", - "@lumino/coreutils": "^2.2.0", - "@lumino/disposable": "^2.1.3", - "@lumino/domutils": "^2.0.2", - "@lumino/messaging": "^2.0.2", - "@lumino/signaling": "^2.1.3", - "@lumino/virtualdom": "^2.0.2", - "@lumino/widgets": "^2.5.0", + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@jupyterlab/apputils/-/apputils-4.6.0.tgz", + "integrity": "sha512-vdT7mdt+fQUE/iKFwx7UAmSIXtbBopzFastnA6xeJOrHVyJfrnTblJEVeNmGhFjh9zhAhzSfer9GZgZhjFoetw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@jupyterlab/coreutils": "^6.5.0", + "@jupyterlab/observables": "^5.5.0", + "@jupyterlab/rendermime-interfaces": "^3.13.0", + "@jupyterlab/services": "^7.5.0", + "@jupyterlab/settingregistry": "^4.5.0", + "@jupyterlab/statedb": "^4.5.0", + "@jupyterlab/statusbar": "^4.5.0", + "@jupyterlab/translation": "^4.5.0", + "@jupyterlab/ui-components": "^4.5.0", + "@lumino/algorithm": "^2.0.4", + "@lumino/commands": "^2.3.3", + "@lumino/coreutils": "^2.2.2", + "@lumino/disposable": "^2.1.5", + "@lumino/domutils": "^2.0.4", + "@lumino/messaging": "^2.0.4", + "@lumino/signaling": "^2.1.5", + "@lumino/virtualdom": "^2.0.4", + "@lumino/widgets": "^2.7.2", "@types/react": "^18.0.26", "react": "^18.2.0", "sanitize-html": "~2.12.1" } }, "node_modules/@jupyterlab/attachments": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@jupyterlab/attachments/-/attachments-4.3.1.tgz", - "integrity": "sha512-QLrL2o3NIlvPyIOZFzeCu8Pqis/rDP3V5AbQBbxLKBVQGjPHipRo6Xa9idXdgHdUdYbCi1h19MU3mYQajI0+/g==", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@jupyterlab/attachments/-/attachments-4.5.0.tgz", + "integrity": "sha512-MJSi5SWi7BCjASNPNiJNWTORk1hcmzyuR5oMJ0L1GHmJvykA8EjL/5n1MgksSLV7N2QpT9wDTv2Ycgh3VVYEhQ==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "@jupyterlab/nbformat": "^4.3.1", - "@jupyterlab/observables": "^5.3.1", - "@jupyterlab/rendermime": "^4.3.1", - "@jupyterlab/rendermime-interfaces": "^3.11.1", - "@lumino/disposable": "^2.1.3", - "@lumino/signaling": "^2.1.3" + "@jupyterlab/nbformat": "^4.5.0", + "@jupyterlab/observables": "^5.5.0", + "@jupyterlab/rendermime": "^4.5.0", + "@jupyterlab/rendermime-interfaces": "^3.13.0", + "@lumino/disposable": "^2.1.5", + "@lumino/signaling": "^2.1.5" } }, "node_modules/@jupyterlab/cells": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@jupyterlab/cells/-/cells-4.3.1.tgz", - "integrity": "sha512-/HUBYJSU/p20d4WeZCmr0Oj/ykwmpZwnESV2+D7V2EWGt1k7OiyEFQvJuLi1Nyt8znME7+p7ulUA118VXse6Bg==", - "dev": true, - "dependencies": { - "@codemirror/state": "^6.4.1", - "@codemirror/view": "^6.26.3", - "@jupyter/ydoc": "^3.0.0", - "@jupyterlab/apputils": "^4.4.1", - "@jupyterlab/attachments": "^4.3.1", - "@jupyterlab/codeeditor": "^4.3.1", - "@jupyterlab/codemirror": "^4.3.1", - "@jupyterlab/coreutils": "^6.3.1", - "@jupyterlab/documentsearch": "^4.3.1", - "@jupyterlab/filebrowser": "^4.3.1", - "@jupyterlab/nbformat": "^4.3.1", - "@jupyterlab/observables": "^5.3.1", - "@jupyterlab/outputarea": "^4.3.1", - "@jupyterlab/rendermime": "^4.3.1", - "@jupyterlab/services": "^7.3.1", - "@jupyterlab/toc": "^6.3.1", - "@jupyterlab/translation": "^4.3.1", - "@jupyterlab/ui-components": "^4.3.1", - "@lumino/algorithm": "^2.0.2", - "@lumino/coreutils": "^2.2.0", - "@lumino/domutils": "^2.0.2", - "@lumino/dragdrop": "^2.1.5", - "@lumino/messaging": "^2.0.2", - "@lumino/polling": "^2.1.3", - "@lumino/signaling": "^2.1.3", - "@lumino/virtualdom": "^2.0.2", - "@lumino/widgets": "^2.5.0", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@jupyterlab/cells/-/cells-4.5.0.tgz", + "integrity": "sha512-53Vhj4I6LFTCy9UklUxNQM308VFSLebVXuNsP0w6OYQHZ3F4IYmJvK8pt+HZDxQI7FszleDu/iEbVsKmV1DTSw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@codemirror/state": "^6.5.2", + "@codemirror/view": "^6.38.1", + "@jupyter/ydoc": "^3.1.0", + "@jupyterlab/apputils": "^4.6.0", + "@jupyterlab/attachments": "^4.5.0", + "@jupyterlab/codeeditor": "^4.5.0", + "@jupyterlab/codemirror": "^4.5.0", + "@jupyterlab/coreutils": "^6.5.0", + "@jupyterlab/documentsearch": "^4.5.0", + "@jupyterlab/filebrowser": "^4.5.0", + "@jupyterlab/nbformat": "^4.5.0", + "@jupyterlab/observables": "^5.5.0", + "@jupyterlab/outputarea": "^4.5.0", + "@jupyterlab/rendermime": "^4.5.0", + "@jupyterlab/services": "^7.5.0", + "@jupyterlab/toc": "^6.5.0", + "@jupyterlab/translation": "^4.5.0", + "@jupyterlab/ui-components": "^4.5.0", + "@lumino/algorithm": "^2.0.4", + "@lumino/coreutils": "^2.2.2", + "@lumino/domutils": "^2.0.4", + "@lumino/dragdrop": "^2.1.7", + "@lumino/messaging": "^2.0.4", + "@lumino/polling": "^2.1.5", + "@lumino/signaling": "^2.1.5", + "@lumino/virtualdom": "^2.0.4", + "@lumino/widgets": "^2.7.2", "react": "^18.2.0" } }, "node_modules/@jupyterlab/codeeditor": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@jupyterlab/codeeditor/-/codeeditor-4.3.1.tgz", - "integrity": "sha512-E7puhQOkxegQ4qDeA+xc7V+ien9bXndjflIw0tJCOPkN21XNcsPhm7ULuy/Kij/t+0w+a3NNoAbSROS4wnhEUw==", - "dev": true, - "dependencies": { - "@codemirror/state": "^6.4.1", - "@jupyter/ydoc": "^3.0.0", - "@jupyterlab/apputils": "^4.4.1", - "@jupyterlab/coreutils": "^6.3.1", - "@jupyterlab/nbformat": "^4.3.1", - "@jupyterlab/observables": "^5.3.1", - "@jupyterlab/statusbar": "^4.3.1", - "@jupyterlab/translation": "^4.3.1", - "@jupyterlab/ui-components": "^4.3.1", - "@lumino/coreutils": "^2.2.0", - "@lumino/disposable": "^2.1.3", - "@lumino/dragdrop": "^2.1.5", - "@lumino/messaging": "^2.0.2", - "@lumino/signaling": "^2.1.3", - "@lumino/widgets": "^2.5.0", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@jupyterlab/codeeditor/-/codeeditor-4.5.0.tgz", + "integrity": "sha512-CkETFC1IoaCk6sMyJfVFCYSN8Pw9/LxRzsZ1CDqWFCXHv9y8s9nuCj0cgYYXFJEdR9116C5EqZEkJrFl430bOQ==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@codemirror/state": "^6.5.2", + "@jupyter/ydoc": "^3.1.0", + "@jupyterlab/apputils": "^4.6.0", + "@jupyterlab/coreutils": "^6.5.0", + "@jupyterlab/nbformat": "^4.5.0", + "@jupyterlab/observables": "^5.5.0", + "@jupyterlab/statusbar": "^4.5.0", + "@jupyterlab/translation": "^4.5.0", + "@jupyterlab/ui-components": "^4.5.0", + "@lumino/coreutils": "^2.2.2", + "@lumino/disposable": "^2.1.5", + "@lumino/dragdrop": "^2.1.7", + "@lumino/messaging": "^2.0.4", + "@lumino/signaling": "^2.1.5", + "@lumino/widgets": "^2.7.2", "react": "^18.2.0" } }, "node_modules/@jupyterlab/codemirror": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@jupyterlab/codemirror/-/codemirror-4.3.1.tgz", - "integrity": "sha512-0k8qts5p3GK0I8h3l7gvOcb+5HB9ExBTzWiLE0YWMAS7C/WTBKVdKOs1HwZJqt18nPfF6T3Q4kK/ZDBBNCE1LA==", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@jupyterlab/codemirror/-/codemirror-4.5.0.tgz", + "integrity": "sha512-tV/F/W23QqrjiqWoZGOM571/hM0OqNxwR5WZ6yp8mJPPgsuXY0bzfexX/JDRcHOuB9oZIc47S7i8O0lbMZRCgg==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "@codemirror/autocomplete": "^6.16.0", - "@codemirror/commands": "^6.5.0", + "@codemirror/autocomplete": "^6.18.6", + "@codemirror/commands": "^6.8.1", "@codemirror/lang-cpp": "^6.0.2", - "@codemirror/lang-css": "^6.2.1", + "@codemirror/lang-css": "^6.3.1", "@codemirror/lang-html": "^6.4.9", "@codemirror/lang-java": "^6.0.1", - "@codemirror/lang-javascript": "^6.2.2", + "@codemirror/lang-javascript": "^6.2.3", "@codemirror/lang-json": "^6.0.1", - "@codemirror/lang-markdown": "^6.2.5", + "@codemirror/lang-markdown": "^6.3.2", "@codemirror/lang-php": "^6.0.1", - "@codemirror/lang-python": "^6.1.6", + "@codemirror/lang-python": "^6.2.0", "@codemirror/lang-rust": "^6.0.1", - "@codemirror/lang-sql": "^6.6.4", + "@codemirror/lang-sql": "^6.8.0", "@codemirror/lang-wast": "^6.0.2", "@codemirror/lang-xml": "^6.1.0", - "@codemirror/language": "^6.10.1", - "@codemirror/legacy-modes": "^6.4.0", - "@codemirror/search": "^6.5.6", - "@codemirror/state": "^6.4.1", - "@codemirror/view": "^6.26.3", - "@jupyter/ydoc": "^3.0.0", - "@jupyterlab/codeeditor": "^4.3.1", - "@jupyterlab/coreutils": "^6.3.1", - "@jupyterlab/documentsearch": "^4.3.1", - "@jupyterlab/nbformat": "^4.3.1", - "@jupyterlab/translation": "^4.3.1", + "@codemirror/language": "^6.11.0", + "@codemirror/legacy-modes": "^6.5.1", + "@codemirror/search": "^6.5.10", + "@codemirror/state": "^6.5.2", + "@codemirror/view": "^6.38.1", + "@jupyter/ydoc": "^3.1.0", + "@jupyterlab/codeeditor": "^4.5.0", + "@jupyterlab/coreutils": "^6.5.0", + "@jupyterlab/documentsearch": "^4.5.0", + "@jupyterlab/nbformat": "^4.5.0", + "@jupyterlab/translation": "^4.5.0", "@lezer/common": "^1.2.1", "@lezer/generator": "^1.7.0", "@lezer/highlight": "^1.2.0", "@lezer/markdown": "^1.3.0", - "@lumino/coreutils": "^2.2.0", - "@lumino/disposable": "^2.1.3", - "@lumino/signaling": "^2.1.3", + "@lumino/coreutils": "^2.2.2", + "@lumino/disposable": "^2.1.5", + "@lumino/signaling": "^2.1.5", "yjs": "^13.5.40" } }, "node_modules/@jupyterlab/console": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@jupyterlab/console/-/console-4.3.1.tgz", - "integrity": "sha512-rl5jXBgW0US8zucBQmABrLsUulWdW5Wssv/E1ZPNYyppaqd9rbiW8LsSSYxyi2pBFvnDbAZVf+MVh06HBivH6g==", - "dev": true, - "dependencies": { - "@jupyter/ydoc": "^3.0.0", - "@jupyterlab/apputils": "^4.4.1", - "@jupyterlab/cells": "^4.3.1", - "@jupyterlab/codeeditor": "^4.3.1", - "@jupyterlab/coreutils": "^6.3.1", - "@jupyterlab/nbformat": "^4.3.1", - "@jupyterlab/observables": "^5.3.1", - "@jupyterlab/rendermime": "^4.3.1", - "@jupyterlab/services": "^7.3.1", - "@jupyterlab/translation": "^4.3.1", - "@jupyterlab/ui-components": "^4.3.1", - "@lumino/coreutils": "^2.2.0", - "@lumino/disposable": "^2.1.3", - "@lumino/dragdrop": "^2.1.5", - "@lumino/messaging": "^2.0.2", - "@lumino/signaling": "^2.1.3", - "@lumino/widgets": "^2.5.0" + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@jupyterlab/console/-/console-4.5.0.tgz", + "integrity": "sha512-qqz+Jhf4uuoAlOzhTumLK4LigDNB4dWZ8t+xnKAWCuZczgzlos7mvQNdw0Q12AZEPQmvAklsmY/C9Z3L7HaFVw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@jupyter/ydoc": "^3.1.0", + "@jupyterlab/apputils": "^4.6.0", + "@jupyterlab/cells": "^4.5.0", + "@jupyterlab/codeeditor": "^4.5.0", + "@jupyterlab/coreutils": "^6.5.0", + "@jupyterlab/nbformat": "^4.5.0", + "@jupyterlab/observables": "^5.5.0", + "@jupyterlab/rendermime": "^4.5.0", + "@jupyterlab/services": "^7.5.0", + "@jupyterlab/translation": "^4.5.0", + "@jupyterlab/ui-components": "^4.5.0", + "@lumino/coreutils": "^2.2.2", + "@lumino/disposable": "^2.1.5", + "@lumino/dragdrop": "^2.1.7", + "@lumino/messaging": "^2.0.4", + "@lumino/signaling": "^2.1.5", + "@lumino/widgets": "^2.7.2" } }, "node_modules/@jupyterlab/coreutils": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/@jupyterlab/coreutils/-/coreutils-6.3.1.tgz", - "integrity": "sha512-72Lyg2XA0Fsyn+62FpVIf67TGrxQZ4CNvldDvmlkixyY/n63bB38AcC0zvvCFDRv7QwXQzp7DaA0xfvAPoQ3bw==", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@jupyterlab/coreutils/-/coreutils-6.5.0.tgz", + "integrity": "sha512-lowpjjZMRupNNvBxG3IwQ4pSKWGtNaDPA//cL5PxiUd5Y1xkUcUy+DPq8HjcSWvDe3viCwpi7rCII8v4btPtuw==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "@lumino/coreutils": "^2.2.0", - "@lumino/disposable": "^2.1.3", - "@lumino/signaling": "^2.1.3", + "@lumino/coreutils": "^2.2.2", + "@lumino/disposable": "^2.1.5", + "@lumino/signaling": "^2.1.5", "minimist": "~1.2.0", "path-browserify": "^1.0.0", "url-parse": "~1.5.4" } }, "node_modules/@jupyterlab/debugger": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@jupyterlab/debugger/-/debugger-4.3.1.tgz", - "integrity": "sha512-ao1zZ8/A1c+fbJQH7xshlEemB77W7tYdJUg3Jek9udeC84fBiGQ99iq9KXje2tg5DrMS3gSoS1RVLYrF4JFtLQ==", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@jupyterlab/debugger/-/debugger-4.5.0.tgz", + "integrity": "sha512-0jpf+OLM6VEAKWYypGHFqrsKBLvyafi8bW843K04v13U6jKHXCql8GkuIvqN/1mnxnu6DTvL8TXYncQXEvvmqw==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "@codemirror/state": "^6.4.1", - "@codemirror/view": "^6.26.3", + "@codemirror/state": "^6.5.2", + "@codemirror/view": "^6.38.1", "@jupyter/react-components": "^0.16.6", - "@jupyter/ydoc": "^3.0.0", - "@jupyterlab/application": "^4.3.1", - "@jupyterlab/apputils": "^4.4.1", - "@jupyterlab/cells": "^4.3.1", - "@jupyterlab/codeeditor": "^4.3.1", - "@jupyterlab/codemirror": "^4.3.1", - "@jupyterlab/console": "^4.3.1", - "@jupyterlab/coreutils": "^6.3.1", - "@jupyterlab/docregistry": "^4.3.1", - "@jupyterlab/fileeditor": "^4.3.1", - "@jupyterlab/notebook": "^4.3.1", - "@jupyterlab/observables": "^5.3.1", - "@jupyterlab/rendermime": "^4.3.1", - "@jupyterlab/services": "^7.3.1", - "@jupyterlab/translation": "^4.3.1", - "@jupyterlab/ui-components": "^4.3.1", - "@lumino/algorithm": "^2.0.2", - "@lumino/commands": "^2.3.1", - "@lumino/coreutils": "^2.2.0", - "@lumino/datagrid": "^2.4.1", - "@lumino/disposable": "^2.1.3", - "@lumino/messaging": "^2.0.2", - "@lumino/polling": "^2.1.3", - "@lumino/signaling": "^2.1.3", - "@lumino/widgets": "^2.5.0", + "@jupyter/ydoc": "^3.1.0", + "@jupyterlab/application": "^4.5.0", + "@jupyterlab/apputils": "^4.6.0", + "@jupyterlab/cells": "^4.5.0", + "@jupyterlab/codeeditor": "^4.5.0", + "@jupyterlab/codemirror": "^4.5.0", + "@jupyterlab/console": "^4.5.0", + "@jupyterlab/coreutils": "^6.5.0", + "@jupyterlab/docregistry": "^4.5.0", + "@jupyterlab/fileeditor": "^4.5.0", + "@jupyterlab/notebook": "^4.5.0", + "@jupyterlab/observables": "^5.5.0", + "@jupyterlab/rendermime": "^4.5.0", + "@jupyterlab/services": "^7.5.0", + "@jupyterlab/settingregistry": "^4.5.0", + "@jupyterlab/translation": "^4.5.0", + "@jupyterlab/ui-components": "^4.5.0", + "@lumino/algorithm": "^2.0.4", + "@lumino/commands": "^2.3.3", + "@lumino/coreutils": "^2.2.2", + "@lumino/datagrid": "^2.5.3", + "@lumino/disposable": "^2.1.5", + "@lumino/messaging": "^2.0.4", + "@lumino/polling": "^2.1.5", + "@lumino/signaling": "^2.1.5", + "@lumino/widgets": "^2.7.2", "@vscode/debugprotocol": "^1.51.0", "react": "^18.2.0" } }, "node_modules/@jupyterlab/docmanager": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@jupyterlab/docmanager/-/docmanager-4.3.1.tgz", - "integrity": "sha512-k2KzicklzXYQcgJGdrlyJcbFTAwwKN2Eo3KSRT+Zh6hJgqWlIaMP0D9M5rUsgd1ml3b41yK2UZGeULjpoZa6GA==", - "dev": true, - "dependencies": { - "@jupyterlab/apputils": "^4.4.1", - "@jupyterlab/coreutils": "^6.3.1", - "@jupyterlab/docregistry": "^4.3.1", - "@jupyterlab/services": "^7.3.1", - "@jupyterlab/statedb": "^4.3.1", - "@jupyterlab/statusbar": "^4.3.1", - "@jupyterlab/translation": "^4.3.1", - "@jupyterlab/ui-components": "^4.3.1", - "@lumino/algorithm": "^2.0.2", - "@lumino/coreutils": "^2.2.0", - "@lumino/disposable": "^2.1.3", - "@lumino/messaging": "^2.0.2", - "@lumino/polling": "^2.1.3", - "@lumino/properties": "^2.0.2", - "@lumino/signaling": "^2.1.3", - "@lumino/widgets": "^2.5.0", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@jupyterlab/docmanager/-/docmanager-4.5.0.tgz", + "integrity": "sha512-gQnXVPV1tAaMD77PVe67rjQYcnhM3Dsst6ffuE9Yf5hw1OjnU9ZLSHwqXUBWIRnD5J0vUrEtJLVOsfjHtq2mPg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@jupyterlab/apputils": "^4.6.0", + "@jupyterlab/coreutils": "^6.5.0", + "@jupyterlab/docregistry": "^4.5.0", + "@jupyterlab/rendermime": "^4.5.0", + "@jupyterlab/services": "^7.5.0", + "@jupyterlab/statedb": "^4.5.0", + "@jupyterlab/statusbar": "^4.5.0", + "@jupyterlab/translation": "^4.5.0", + "@jupyterlab/ui-components": "^4.5.0", + "@lumino/algorithm": "^2.0.4", + "@lumino/coreutils": "^2.2.2", + "@lumino/disposable": "^2.1.5", + "@lumino/messaging": "^2.0.4", + "@lumino/polling": "^2.1.5", + "@lumino/properties": "^2.0.4", + "@lumino/signaling": "^2.1.5", + "@lumino/widgets": "^2.7.2", "react": "^18.2.0" } }, "node_modules/@jupyterlab/docregistry": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@jupyterlab/docregistry/-/docregistry-4.3.1.tgz", - "integrity": "sha512-aPNYbs/gnvs3BChGvVRkPV8cAUpP3Xg4wS91beVIEzr84/YdZ+ekqQvL5wnIOjyKPoi6KmGmgQC2Tlz37W2ZdA==", - "dev": true, - "dependencies": { - "@jupyter/ydoc": "^3.0.0", - "@jupyterlab/apputils": "^4.4.1", - "@jupyterlab/codeeditor": "^4.3.1", - "@jupyterlab/coreutils": "^6.3.1", - "@jupyterlab/observables": "^5.3.1", - "@jupyterlab/rendermime": "^4.3.1", - "@jupyterlab/rendermime-interfaces": "^3.11.1", - "@jupyterlab/services": "^7.3.1", - "@jupyterlab/translation": "^4.3.1", - "@jupyterlab/ui-components": "^4.3.1", - "@lumino/algorithm": "^2.0.2", - "@lumino/coreutils": "^2.2.0", - "@lumino/disposable": "^2.1.3", - "@lumino/messaging": "^2.0.2", - "@lumino/properties": "^2.0.2", - "@lumino/signaling": "^2.1.3", - "@lumino/widgets": "^2.5.0", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@jupyterlab/docregistry/-/docregistry-4.5.0.tgz", + "integrity": "sha512-0K4lxmRrVp/dlR39Z5VQXlTIvXNo1ziIGrkggkSx8E6HRNeoURhUj37/CJ6cPzxY5H+fUAiTPVYlicq/T5N7QA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@jupyter/ydoc": "^3.1.0", + "@jupyterlab/apputils": "^4.6.0", + "@jupyterlab/codeeditor": "^4.5.0", + "@jupyterlab/coreutils": "^6.5.0", + "@jupyterlab/observables": "^5.5.0", + "@jupyterlab/rendermime": "^4.5.0", + "@jupyterlab/rendermime-interfaces": "^3.13.0", + "@jupyterlab/services": "^7.5.0", + "@jupyterlab/translation": "^4.5.0", + "@jupyterlab/ui-components": "^4.5.0", + "@lumino/algorithm": "^2.0.4", + "@lumino/coreutils": "^2.2.2", + "@lumino/disposable": "^2.1.5", + "@lumino/messaging": "^2.0.4", + "@lumino/properties": "^2.0.4", + "@lumino/signaling": "^2.1.5", + "@lumino/widgets": "^2.7.2", "react": "^18.2.0" } }, "node_modules/@jupyterlab/documentsearch": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@jupyterlab/documentsearch/-/documentsearch-4.3.1.tgz", - "integrity": "sha512-oeEdx3CLOzIweRqgOBb5VUfOSkBs34lxC6Ih5fKgg4Ql+wuWAu61Bj0baykXA/q2637kPeDrELjOK9zB6bkd2g==", - "dev": true, - "dependencies": { - "@jupyterlab/apputils": "^4.4.1", - "@jupyterlab/translation": "^4.3.1", - "@jupyterlab/ui-components": "^4.3.1", - "@lumino/commands": "^2.3.1", - "@lumino/coreutils": "^2.2.0", - "@lumino/disposable": "^2.1.3", - "@lumino/messaging": "^2.0.2", - "@lumino/polling": "^2.1.3", - "@lumino/signaling": "^2.1.3", - "@lumino/widgets": "^2.5.0", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@jupyterlab/documentsearch/-/documentsearch-4.5.0.tgz", + "integrity": "sha512-Fdh18NLFyEXHQPPwnPgpKoFxTKNz5n1GiU3e2AgkE4Yzt2BVdM3xC0LoDd6uiWyg+FRtWVuAP+9qZHniOJCnHw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@jupyterlab/apputils": "^4.6.0", + "@jupyterlab/translation": "^4.5.0", + "@jupyterlab/ui-components": "^4.5.0", + "@lumino/commands": "^2.3.3", + "@lumino/coreutils": "^2.2.2", + "@lumino/disposable": "^2.1.5", + "@lumino/messaging": "^2.0.4", + "@lumino/polling": "^2.1.5", + "@lumino/signaling": "^2.1.5", + "@lumino/widgets": "^2.7.2", "react": "^18.2.0" } }, "node_modules/@jupyterlab/filebrowser": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@jupyterlab/filebrowser/-/filebrowser-4.3.1.tgz", - "integrity": "sha512-LNxXBdQBLn3ttuZ1PThMK1+saazSDZFrwUkGyQBU+NxY4nN1tPuwTrJaOCr2+4CzI6VY3asOEJ7grDILXqY0jg==", - "dev": true, - "dependencies": { - "@jupyterlab/apputils": "^4.4.1", - "@jupyterlab/coreutils": "^6.3.1", - "@jupyterlab/docmanager": "^4.3.1", - "@jupyterlab/docregistry": "^4.3.1", - "@jupyterlab/services": "^7.3.1", - "@jupyterlab/statedb": "^4.3.1", - "@jupyterlab/statusbar": "^4.3.1", - "@jupyterlab/translation": "^4.3.1", - "@jupyterlab/ui-components": "^4.3.1", - "@lumino/algorithm": "^2.0.2", - "@lumino/coreutils": "^2.2.0", - "@lumino/disposable": "^2.1.3", - "@lumino/domutils": "^2.0.2", - "@lumino/dragdrop": "^2.1.5", - "@lumino/messaging": "^2.0.2", - "@lumino/polling": "^2.1.3", - "@lumino/signaling": "^2.1.3", - "@lumino/virtualdom": "^2.0.2", - "@lumino/widgets": "^2.5.0", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@jupyterlab/filebrowser/-/filebrowser-4.5.0.tgz", + "integrity": "sha512-KWKyThGUBGahUzAefw0tjjP+lbzLcx7k870FTBpSf92neRSCsoQ3Qkso6zPFExM4T0vTo0gv6vq7cLE8tzWnmw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@jupyterlab/apputils": "^4.6.0", + "@jupyterlab/coreutils": "^6.5.0", + "@jupyterlab/docmanager": "^4.5.0", + "@jupyterlab/docregistry": "^4.5.0", + "@jupyterlab/services": "^7.5.0", + "@jupyterlab/statedb": "^4.5.0", + "@jupyterlab/statusbar": "^4.5.0", + "@jupyterlab/translation": "^4.5.0", + "@jupyterlab/ui-components": "^4.5.0", + "@lumino/algorithm": "^2.0.4", + "@lumino/coreutils": "^2.2.2", + "@lumino/disposable": "^2.1.5", + "@lumino/domutils": "^2.0.4", + "@lumino/dragdrop": "^2.1.7", + "@lumino/messaging": "^2.0.4", + "@lumino/polling": "^2.1.5", + "@lumino/signaling": "^2.1.5", + "@lumino/virtualdom": "^2.0.4", + "@lumino/widgets": "^2.7.2", + "jest-environment-jsdom": "^29.3.0", "react": "^18.2.0" } }, "node_modules/@jupyterlab/fileeditor": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@jupyterlab/fileeditor/-/fileeditor-4.3.1.tgz", - "integrity": "sha512-gMGZZ4cvpJyIGsapfTequzWqMs5pWCuU8Z2kn+INKq0U+xpimZoz/0mzND6veQHzXQkZwST2CTJOllnMLEVz4Q==", - "dev": true, - "dependencies": { - "@jupyter/ydoc": "^3.0.0", - "@jupyterlab/apputils": "^4.4.1", - "@jupyterlab/codeeditor": "^4.3.1", - "@jupyterlab/codemirror": "^4.3.1", - "@jupyterlab/coreutils": "^6.3.1", - "@jupyterlab/docregistry": "^4.3.1", - "@jupyterlab/documentsearch": "^4.3.1", - "@jupyterlab/lsp": "^4.3.1", - "@jupyterlab/statusbar": "^4.3.1", - "@jupyterlab/toc": "^6.3.1", - "@jupyterlab/translation": "^4.3.1", - "@jupyterlab/ui-components": "^4.3.1", - "@lumino/commands": "^2.3.1", - "@lumino/coreutils": "^2.2.0", - "@lumino/messaging": "^2.0.2", - "@lumino/widgets": "^2.5.0", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@jupyterlab/fileeditor/-/fileeditor-4.5.0.tgz", + "integrity": "sha512-IqfeiBetowWuQHshRiDAeQeB1hkLeRNfWp8COkceh8ghHYjDIfu7e2kJzQ6bkAg1lhqvtw7JmxySqub/1JEIhw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@jupyter/ydoc": "^3.1.0", + "@jupyterlab/apputils": "^4.6.0", + "@jupyterlab/codeeditor": "^4.5.0", + "@jupyterlab/codemirror": "^4.5.0", + "@jupyterlab/coreutils": "^6.5.0", + "@jupyterlab/docregistry": "^4.5.0", + "@jupyterlab/documentsearch": "^4.5.0", + "@jupyterlab/lsp": "^4.5.0", + "@jupyterlab/rendermime": "^4.5.0", + "@jupyterlab/statusbar": "^4.5.0", + "@jupyterlab/toc": "^6.5.0", + "@jupyterlab/translation": "^4.5.0", + "@jupyterlab/ui-components": "^4.5.0", + "@lumino/commands": "^2.3.3", + "@lumino/coreutils": "^2.2.2", + "@lumino/messaging": "^2.0.4", + "@lumino/widgets": "^2.7.2", "react": "^18.2.0", "regexp-match-indices": "^1.0.2" } }, "node_modules/@jupyterlab/galata": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/@jupyterlab/galata/-/galata-5.3.1.tgz", - "integrity": "sha512-6k9A8wlADtHlkKwXRsf+3bx0cusbIx7U38c5MARpkaUS5rvimtAWPpJXjMbHGMFUbtsCLczv7zJdrwl/mNEy0Q==", - "dev": true, - "dependencies": { - "@jupyterlab/application": "^4.3.1", - "@jupyterlab/apputils": "^4.4.1", - "@jupyterlab/coreutils": "^6.3.1", - "@jupyterlab/debugger": "^4.3.1", - "@jupyterlab/docmanager": "^4.3.1", - "@jupyterlab/nbformat": "^4.3.1", - "@jupyterlab/notebook": "^4.3.1", - "@jupyterlab/services": "^7.3.1", - "@jupyterlab/settingregistry": "^4.3.1", - "@lumino/coreutils": "^2.2.0", - "@playwright/test": "^1.48.0", + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@jupyterlab/galata/-/galata-5.5.0.tgz", + "integrity": "sha512-1BvcmBUvpMYxdsZ5tAUfojCLoZAbiJn1RrYVpPFlJqkdF00H/ikf8fGrgxiTgrfUU5qizDVLqspFVk+hRb4uAA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@jupyterlab/application": "^4.5.0", + "@jupyterlab/apputils": "^4.6.0", + "@jupyterlab/coreutils": "^6.5.0", + "@jupyterlab/debugger": "^4.5.0", + "@jupyterlab/docmanager": "^4.5.0", + "@jupyterlab/nbformat": "^4.5.0", + "@jupyterlab/notebook": "^4.5.0", + "@jupyterlab/services": "^7.5.0", + "@jupyterlab/settingregistry": "^4.5.0", + "@lumino/coreutils": "^2.2.2", + "@playwright/test": "^1.56.0", "@stdlib/stats": "~0.0.13", "fs-extra": "^10.1.0", "json5": "^2.2.3", @@ -931,171 +1202,215 @@ } }, "node_modules/@jupyterlab/lsp": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@jupyterlab/lsp/-/lsp-4.3.1.tgz", - "integrity": "sha512-XpMBRq2kSnlZKPCo46uVonc7d7YXJQhyu5uqxoikbbp95oLE+LNcF90iJ1pFqXYGV4JpxZhvH9FO5+YP2uTiQQ==", - "dev": true, - "dependencies": { - "@jupyterlab/apputils": "^4.4.1", - "@jupyterlab/codeeditor": "^4.3.1", - "@jupyterlab/codemirror": "^4.3.1", - "@jupyterlab/coreutils": "^6.3.1", - "@jupyterlab/docregistry": "^4.3.1", - "@jupyterlab/services": "^7.3.1", - "@jupyterlab/translation": "^4.3.1", - "@lumino/coreutils": "^2.2.0", - "@lumino/disposable": "^2.1.3", - "@lumino/signaling": "^2.1.3", - "@lumino/widgets": "^2.5.0", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@jupyterlab/lsp/-/lsp-4.5.0.tgz", + "integrity": "sha512-Hz3mm4eJejKmZi0aLMHfW2FsiOynvetfMoXfQ8spcgPQ1yu76YjW6e8Djs417sukZ8XEOXhd/WoLL+69Q+8k6A==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@jupyterlab/apputils": "^4.6.0", + "@jupyterlab/codeeditor": "^4.5.0", + "@jupyterlab/codemirror": "^4.5.0", + "@jupyterlab/coreutils": "^6.5.0", + "@jupyterlab/docregistry": "^4.5.0", + "@jupyterlab/services": "^7.5.0", + "@jupyterlab/translation": "^4.5.0", + "@lumino/coreutils": "^2.2.2", + "@lumino/disposable": "^2.1.5", + "@lumino/signaling": "^2.1.5", + "@lumino/widgets": "^2.7.2", "lodash.mergewith": "^4.6.1", "vscode-jsonrpc": "^6.0.0", "vscode-languageserver-protocol": "^3.17.0", "vscode-ws-jsonrpc": "~1.0.2" } }, + "node_modules/@jupyterlab/markedparser-extension": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@jupyterlab/markedparser-extension/-/markedparser-extension-4.5.0.tgz", + "integrity": "sha512-F/DjHsXNmzmbv+V5yHhGFyLbnNRZM5fYCmC6Gx0FScN0tLtkVZlFpIiw6HZCm/dRPFH9zKyCqgsdP2Al8IQ1Qw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@jupyterlab/application": "^4.5.0", + "@jupyterlab/codemirror": "^4.5.0", + "@jupyterlab/coreutils": "^6.5.0", + "@jupyterlab/mermaid": "^4.5.0", + "@jupyterlab/rendermime": "^4.5.0", + "@lumino/coreutils": "^2.2.2", + "marked": "^16.2.1", + "marked-gfm-heading-id": "^4.1.2", + "marked-mangle": "^1.1.11" + } + }, + "node_modules/@jupyterlab/mermaid": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@jupyterlab/mermaid/-/mermaid-4.5.0.tgz", + "integrity": "sha512-R2jin3NI8sFMqL5Bx//Q/+3lyLPBbfaVaERprCSgGUzizK6q6GXoeQuJh4Hl5NafqPgKyUtiwBwmQZZJsbCEwQ==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@jupyterlab/apputils": "^4.6.0", + "@jupyterlab/coreutils": "^6.5.0", + "@jupyterlab/rendermime-interfaces": "^3.13.0", + "@lumino/coreutils": "^2.2.2", + "@lumino/widgets": "^2.7.2", + "@mermaid-js/layout-elk": "^0.2.0", + "mermaid": "^11.12.1" + } + }, "node_modules/@jupyterlab/nbformat": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@jupyterlab/nbformat/-/nbformat-4.3.1.tgz", - "integrity": "sha512-9dIcagn5xQMKijPiu08MGAnfWQzk5mkqHRltHUfYbydV33lWgTjFBEpugHncEZGIxIJz+iqw2H9KJ8HSb7aJqw==", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@jupyterlab/nbformat/-/nbformat-4.5.0.tgz", + "integrity": "sha512-yNG4EGawtyM398VEyfXlDvJP5mRJMUs06oVBhigUDsKdyhc7xO++z2zMSj9sJOUfV27qh6B/4RppFzIl8vvWBA==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "@lumino/coreutils": "^2.2.0" + "@lumino/coreutils": "^2.2.2" } }, "node_modules/@jupyterlab/notebook": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@jupyterlab/notebook/-/notebook-4.3.1.tgz", - "integrity": "sha512-NjtAsZczoVPp+mdJFFykqeCCGEXqzUKgab27dbJRqG4aWpHVQjrLxLGOv1mde70je836lEyMKZet9fxnlDOgKg==", - "dev": true, - "dependencies": { - "@jupyter/ydoc": "^3.0.0", - "@jupyterlab/apputils": "^4.4.1", - "@jupyterlab/cells": "^4.3.1", - "@jupyterlab/codeeditor": "^4.3.1", - "@jupyterlab/codemirror": "^4.3.1", - "@jupyterlab/coreutils": "^6.3.1", - "@jupyterlab/docregistry": "^4.3.1", - "@jupyterlab/documentsearch": "^4.3.1", - "@jupyterlab/lsp": "^4.3.1", - "@jupyterlab/nbformat": "^4.3.1", - "@jupyterlab/observables": "^5.3.1", - "@jupyterlab/rendermime": "^4.3.1", - "@jupyterlab/services": "^7.3.1", - "@jupyterlab/settingregistry": "^4.3.1", - "@jupyterlab/statusbar": "^4.3.1", - "@jupyterlab/toc": "^6.3.1", - "@jupyterlab/translation": "^4.3.1", - "@jupyterlab/ui-components": "^4.3.1", - "@lumino/algorithm": "^2.0.2", - "@lumino/coreutils": "^2.2.0", - "@lumino/disposable": "^2.1.3", - "@lumino/domutils": "^2.0.2", - "@lumino/dragdrop": "^2.1.5", - "@lumino/messaging": "^2.0.2", - "@lumino/polling": "^2.1.3", - "@lumino/properties": "^2.0.2", - "@lumino/signaling": "^2.1.3", - "@lumino/virtualdom": "^2.0.2", - "@lumino/widgets": "^2.5.0", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@jupyterlab/notebook/-/notebook-4.5.0.tgz", + "integrity": "sha512-I5FY13yru5PytY5wDoUoe+Df1U0wMI6EUykPOCQCSqQfyQ/rlzAbjwdba1hG8CZoN0c4l10i1SCNffcjuNeqOw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@jupyter/ydoc": "^3.1.0", + "@jupyterlab/apputils": "^4.6.0", + "@jupyterlab/cells": "^4.5.0", + "@jupyterlab/codeeditor": "^4.5.0", + "@jupyterlab/codemirror": "^4.5.0", + "@jupyterlab/coreutils": "^6.5.0", + "@jupyterlab/docregistry": "^4.5.0", + "@jupyterlab/documentsearch": "^4.5.0", + "@jupyterlab/lsp": "^4.5.0", + "@jupyterlab/markedparser-extension": "^4.5.0", + "@jupyterlab/nbformat": "^4.5.0", + "@jupyterlab/observables": "^5.5.0", + "@jupyterlab/rendermime": "^4.5.0", + "@jupyterlab/services": "^7.5.0", + "@jupyterlab/settingregistry": "^4.5.0", + "@jupyterlab/statusbar": "^4.5.0", + "@jupyterlab/toc": "^6.5.0", + "@jupyterlab/translation": "^4.5.0", + "@jupyterlab/ui-components": "^4.5.0", + "@lumino/algorithm": "^2.0.4", + "@lumino/coreutils": "^2.2.2", + "@lumino/disposable": "^2.1.5", + "@lumino/domutils": "^2.0.4", + "@lumino/dragdrop": "^2.1.7", + "@lumino/messaging": "^2.0.4", + "@lumino/polling": "^2.1.5", + "@lumino/properties": "^2.0.4", + "@lumino/signaling": "^2.1.5", + "@lumino/virtualdom": "^2.0.4", + "@lumino/widgets": "^2.7.2", "react": "^18.2.0" } }, "node_modules/@jupyterlab/observables": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/@jupyterlab/observables/-/observables-5.3.1.tgz", - "integrity": "sha512-iS67+D/aMz9elySBic+453RUOYbIM9/zCZboF4OoBJ9f8ClY5SUIwvAlkkcMK8rn8tphSbh3Ik7U7Ea/HSdzjw==", + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@jupyterlab/observables/-/observables-5.5.0.tgz", + "integrity": "sha512-DWNtwNRqVhrX+ebKhebAZYDsUiuDBDo7SZcsAkx3lXEhmbDkSwlwiZrmdZ/9C3fpyAPAjdbEDpGDBMBwt+6spQ==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "@lumino/algorithm": "^2.0.2", - "@lumino/coreutils": "^2.2.0", - "@lumino/disposable": "^2.1.3", - "@lumino/messaging": "^2.0.2", - "@lumino/signaling": "^2.1.3" + "@lumino/algorithm": "^2.0.4", + "@lumino/coreutils": "^2.2.2", + "@lumino/disposable": "^2.1.5", + "@lumino/messaging": "^2.0.4", + "@lumino/signaling": "^2.1.5" } }, "node_modules/@jupyterlab/outputarea": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@jupyterlab/outputarea/-/outputarea-4.3.1.tgz", - "integrity": "sha512-zGeIDe1xpoXve9VAj8qHW6sKSOZo9sW5gJbDfATguYHS44cX1XGNkLHAfW2GozP4y+M5De0v5cKf2EVnTfL6OA==", - "dev": true, - "dependencies": { - "@jupyterlab/apputils": "^4.4.1", - "@jupyterlab/nbformat": "^4.3.1", - "@jupyterlab/observables": "^5.3.1", - "@jupyterlab/rendermime": "^4.3.1", - "@jupyterlab/rendermime-interfaces": "^3.11.1", - "@jupyterlab/services": "^7.3.1", - "@jupyterlab/translation": "^4.3.1", - "@lumino/algorithm": "^2.0.2", - "@lumino/coreutils": "^2.2.0", - "@lumino/disposable": "^2.1.3", - "@lumino/messaging": "^2.0.2", - "@lumino/properties": "^2.0.2", - "@lumino/signaling": "^2.1.3", - "@lumino/widgets": "^2.5.0" + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@jupyterlab/outputarea/-/outputarea-4.5.0.tgz", + "integrity": "sha512-xt/tq+3sTWzbuRjifmA+kSaa9SHHX3Da/E96OIQhC9eckdfIaKnUJ8PCTrnHxor3OKLxpc9NE04nJMonXBkDPA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@jupyterlab/apputils": "^4.6.0", + "@jupyterlab/nbformat": "^4.5.0", + "@jupyterlab/observables": "^5.5.0", + "@jupyterlab/rendermime": "^4.5.0", + "@jupyterlab/rendermime-interfaces": "^3.13.0", + "@jupyterlab/services": "^7.5.0", + "@jupyterlab/translation": "^4.5.0", + "@lumino/algorithm": "^2.0.4", + "@lumino/coreutils": "^2.2.2", + "@lumino/disposable": "^2.1.5", + "@lumino/messaging": "^2.0.4", + "@lumino/properties": "^2.0.4", + "@lumino/signaling": "^2.1.5", + "@lumino/widgets": "^2.7.2" } }, "node_modules/@jupyterlab/rendermime": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@jupyterlab/rendermime/-/rendermime-4.3.1.tgz", - "integrity": "sha512-b44RZKrdYkM/CqUWNT36Tl6Q/sYeyb1oCECH1Jb4wRNZC88SG57373rENGj28BqWdhk+0aYAvxbB9r2gTy8csA==", - "dev": true, - "dependencies": { - "@jupyterlab/apputils": "^4.4.1", - "@jupyterlab/coreutils": "^6.3.1", - "@jupyterlab/nbformat": "^4.3.1", - "@jupyterlab/observables": "^5.3.1", - "@jupyterlab/rendermime-interfaces": "^3.11.1", - "@jupyterlab/services": "^7.3.1", - "@jupyterlab/translation": "^4.3.1", - "@lumino/coreutils": "^2.2.0", - "@lumino/messaging": "^2.0.2", - "@lumino/signaling": "^2.1.3", - "@lumino/widgets": "^2.5.0", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@jupyterlab/rendermime/-/rendermime-4.5.0.tgz", + "integrity": "sha512-3+q+OtSKaX27BapoyJJco17jnBU8gCW7/tfQTms+zH0cVhtSTvK+VGTERlwp/229UlZvnYmC6iROIirZ9jVwig==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@jupyterlab/apputils": "^4.6.0", + "@jupyterlab/coreutils": "^6.5.0", + "@jupyterlab/nbformat": "^4.5.0", + "@jupyterlab/observables": "^5.5.0", + "@jupyterlab/rendermime-interfaces": "^3.13.0", + "@jupyterlab/services": "^7.5.0", + "@jupyterlab/translation": "^4.5.0", + "@lumino/coreutils": "^2.2.2", + "@lumino/messaging": "^2.0.4", + "@lumino/signaling": "^2.1.5", + "@lumino/widgets": "^2.7.2", "lodash.escape": "^4.0.1" } }, "node_modules/@jupyterlab/rendermime-interfaces": { - "version": "3.11.1", - "resolved": "https://registry.npmjs.org/@jupyterlab/rendermime-interfaces/-/rendermime-interfaces-3.11.1.tgz", - "integrity": "sha512-T2eThoZ34qqEOiVdjqGbWyWoF4tTQUpdOFuqFEQMTgXWuagyMGr91aLapB0ABMuUH8R0TBfS6HjWCIQ+wD5AOw==", + "version": "3.13.0", + "resolved": "https://registry.npmjs.org/@jupyterlab/rendermime-interfaces/-/rendermime-interfaces-3.13.0.tgz", + "integrity": "sha512-FfGpT7qjBHIgdMJO/wJgQws1X7DxpyKEUvnzqzXp5dKOwUuE4SSINAa4vKR9cZ/jkKNKOEyeOTvF58OQxF0dYQ==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "@lumino/coreutils": "^1.11.0 || ^2.2.0", - "@lumino/widgets": "^1.37.2 || ^2.5.0" + "@lumino/coreutils": "^1.11.0 || ^2.2.2", + "@lumino/widgets": "^1.37.2 || ^2.7.2" } }, "node_modules/@jupyterlab/services": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/@jupyterlab/services/-/services-7.3.1.tgz", - "integrity": "sha512-ZXRW2wDV7kIoFRj8M3nkbaSxC6pYn4QlRtwJJEz6Ewges9x+EJEKoN7V0AVQ2zDG1XSmMvRLAvv1YvLgEk4Bug==", - "dev": true, - "dependencies": { - "@jupyter/ydoc": "^3.0.0", - "@jupyterlab/coreutils": "^6.3.1", - "@jupyterlab/nbformat": "^4.3.1", - "@jupyterlab/settingregistry": "^4.3.1", - "@jupyterlab/statedb": "^4.3.1", - "@lumino/coreutils": "^2.2.0", - "@lumino/disposable": "^2.1.3", - "@lumino/polling": "^2.1.3", - "@lumino/properties": "^2.0.2", - "@lumino/signaling": "^2.1.3", + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@jupyterlab/services/-/services-7.5.0.tgz", + "integrity": "sha512-PzOAFp8BxFHyc7ZHrQSWJ5S/9wFNUqdMyPVb+cM5B3Hveh9pBpRd7A41ua5mV8xt56J6lPUgo5+FIVUGV7vHbA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@jupyter/ydoc": "^3.1.0", + "@jupyterlab/coreutils": "^6.5.0", + "@jupyterlab/nbformat": "^4.5.0", + "@jupyterlab/settingregistry": "^4.5.0", + "@jupyterlab/statedb": "^4.5.0", + "@lumino/coreutils": "^2.2.2", + "@lumino/disposable": "^2.1.5", + "@lumino/polling": "^2.1.5", + "@lumino/properties": "^2.0.4", + "@lumino/signaling": "^2.1.5", "ws": "^8.11.0" } }, "node_modules/@jupyterlab/settingregistry": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@jupyterlab/settingregistry/-/settingregistry-4.3.1.tgz", - "integrity": "sha512-tlRYh+CouyyDY3nGsCcD9hQKftdYKMwkTSV4X09vHlIEZoMNrbXtkyADgErgQGaIMwx7gtaLU4qCWK2LLMGyDw==", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@jupyterlab/settingregistry/-/settingregistry-4.5.0.tgz", + "integrity": "sha512-hnuAODpFVQEHrkaVBvJLjOm2aurIglv2HUS+GIKN2VcHehRVgEi30XUgCEuf7YVH+KkRLXDIZlCGDBfItw3SBw==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "@jupyterlab/nbformat": "^4.3.1", - "@jupyterlab/statedb": "^4.3.1", - "@lumino/commands": "^2.3.1", - "@lumino/coreutils": "^2.2.0", - "@lumino/disposable": "^2.1.3", - "@lumino/signaling": "^2.1.3", + "@jupyterlab/nbformat": "^4.5.0", + "@jupyterlab/statedb": "^4.5.0", + "@lumino/commands": "^2.3.3", + "@lumino/coreutils": "^2.2.2", + "@lumino/disposable": "^2.1.5", + "@lumino/signaling": "^2.1.5", "@rjsf/utils": "^5.13.4", "ajv": "^8.12.0", "json5": "^2.2.3" @@ -1105,92 +1420,97 @@ } }, "node_modules/@jupyterlab/statedb": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@jupyterlab/statedb/-/statedb-4.3.1.tgz", - "integrity": "sha512-Uph0kEPgCZvI6XnRsf7Md4WWA+4WnJMzwubc7cxk7B2KjdRYOqJHVtQbdJk4ZUA2ARhyS8tBtit/BZb9e3wLZw==", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@jupyterlab/statedb/-/statedb-4.5.0.tgz", + "integrity": "sha512-OERjbE0LV1wYhEH71eF71hpdCPbM/Y+LkGK+pvKJzYy9NqaSbUDTT9GMurT+43tfdUYeUE/nls5Ibr8ZkTakDA==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "@lumino/commands": "^2.3.1", - "@lumino/coreutils": "^2.2.0", - "@lumino/disposable": "^2.1.3", - "@lumino/properties": "^2.0.2", - "@lumino/signaling": "^2.1.3" + "@lumino/commands": "^2.3.3", + "@lumino/coreutils": "^2.2.2", + "@lumino/disposable": "^2.1.5", + "@lumino/properties": "^2.0.4", + "@lumino/signaling": "^2.1.5" } }, "node_modules/@jupyterlab/statusbar": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@jupyterlab/statusbar/-/statusbar-4.3.1.tgz", - "integrity": "sha512-5XZJsIvdWENto4of4p1c2hrd4pJuQW5FYevCETMRhIeVnI6vM+JB67LCe84+nKjEt4wiYJhF7I8Bf9No6fUFTw==", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@jupyterlab/statusbar/-/statusbar-4.5.0.tgz", + "integrity": "sha512-vT0rSVJDA3gdxioNIYQ6upiPYa3BjDztgKFIbCYEdOJmp7d64rOyPpDPaonbFewu+6L+V2s1oJEky/6jSw+CgQ==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "@jupyterlab/ui-components": "^4.3.1", - "@lumino/algorithm": "^2.0.2", - "@lumino/coreutils": "^2.2.0", - "@lumino/disposable": "^2.1.3", - "@lumino/messaging": "^2.0.2", - "@lumino/signaling": "^2.1.3", - "@lumino/widgets": "^2.5.0", + "@jupyterlab/ui-components": "^4.5.0", + "@lumino/algorithm": "^2.0.4", + "@lumino/coreutils": "^2.2.2", + "@lumino/disposable": "^2.1.5", + "@lumino/messaging": "^2.0.4", + "@lumino/signaling": "^2.1.5", + "@lumino/widgets": "^2.7.2", "react": "^18.2.0" } }, "node_modules/@jupyterlab/toc": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/@jupyterlab/toc/-/toc-6.3.1.tgz", - "integrity": "sha512-+QOChkEJ8xCI407MSUWMOAu/rb0SigGmeKPvxycaWZZY2RQnTQvavEUYBimgg0gh80XWqDfpR1SB56dtZp8ifw==", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@jupyterlab/toc/-/toc-6.5.0.tgz", + "integrity": "sha512-1UDT1PRWFKgsJhLRyyBXVA1zKUgJRFBTU8X4uglhshNEC2XtlNWQ2CvW/nlH3glW5p6lyVKIfqKIRCfF7U522A==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "@jupyter/react-components": "^0.16.6", - "@jupyterlab/apputils": "^4.4.1", - "@jupyterlab/coreutils": "^6.3.1", - "@jupyterlab/docregistry": "^4.3.1", - "@jupyterlab/observables": "^5.3.1", - "@jupyterlab/rendermime": "^4.3.1", - "@jupyterlab/rendermime-interfaces": "^3.11.1", - "@jupyterlab/translation": "^4.3.1", - "@jupyterlab/ui-components": "^4.3.1", - "@lumino/coreutils": "^2.2.0", - "@lumino/disposable": "^2.1.3", - "@lumino/messaging": "^2.0.2", - "@lumino/signaling": "^2.1.3", - "@lumino/widgets": "^2.5.0", + "@jupyterlab/apputils": "^4.6.0", + "@jupyterlab/coreutils": "^6.5.0", + "@jupyterlab/docregistry": "^4.5.0", + "@jupyterlab/observables": "^5.5.0", + "@jupyterlab/rendermime": "^4.5.0", + "@jupyterlab/rendermime-interfaces": "^3.13.0", + "@jupyterlab/translation": "^4.5.0", + "@jupyterlab/ui-components": "^4.5.0", + "@lumino/coreutils": "^2.2.2", + "@lumino/disposable": "^2.1.5", + "@lumino/messaging": "^2.0.4", + "@lumino/signaling": "^2.1.5", + "@lumino/widgets": "^2.7.2", "react": "^18.2.0" } }, "node_modules/@jupyterlab/translation": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@jupyterlab/translation/-/translation-4.3.1.tgz", - "integrity": "sha512-hFlzzVAliO+0vrGGCD9us23GZ1WwF7vWDHBAnMJdIelpydOvzIBASoZJrGMIohq+7cO/eOlHqQguhUaBbBzh/g==", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@jupyterlab/translation/-/translation-4.5.0.tgz", + "integrity": "sha512-hCtLfkJdxseTbuhFZSYLCLz7lQl/uRbVzhmEAN642J5BSAuHE6DJDtV0beIDnkk1OgmGiy5qsSGtW+Sa98D2qw==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "@jupyterlab/coreutils": "^6.3.1", - "@jupyterlab/rendermime-interfaces": "^3.11.1", - "@jupyterlab/services": "^7.3.1", - "@jupyterlab/statedb": "^4.3.1", - "@lumino/coreutils": "^2.2.0" + "@jupyterlab/coreutils": "^6.5.0", + "@jupyterlab/rendermime-interfaces": "^3.13.0", + "@jupyterlab/services": "^7.5.0", + "@jupyterlab/statedb": "^4.5.0", + "@lumino/coreutils": "^2.2.2" } }, "node_modules/@jupyterlab/ui-components": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/@jupyterlab/ui-components/-/ui-components-4.3.1.tgz", - "integrity": "sha512-csWs6iG6Qg2NDAJ1uFzqGBInE6lDmqEAONqP7uFvCK247xhj5Ps4tUeOWOfKs9sH5Pv4wvCVmB+eNcxoZy3QJA==", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/@jupyterlab/ui-components/-/ui-components-4.5.0.tgz", + "integrity": "sha512-Vh1STa9CoaK6jdCj5W3hx3gC+U/6DQT1pTp3ApSLp+ZzT6QWbIp+Gf5+gEYrCdnZK8MS68sS9Imp6r1gmvYLlA==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "@jupyter/react-components": "^0.16.6", "@jupyter/web-components": "^0.16.6", - "@jupyterlab/coreutils": "^6.3.1", - "@jupyterlab/observables": "^5.3.1", - "@jupyterlab/rendermime-interfaces": "^3.11.1", - "@jupyterlab/translation": "^4.3.1", - "@lumino/algorithm": "^2.0.2", - "@lumino/commands": "^2.3.1", - "@lumino/coreutils": "^2.2.0", - "@lumino/disposable": "^2.1.3", - "@lumino/messaging": "^2.0.2", - "@lumino/polling": "^2.1.3", - "@lumino/properties": "^2.0.2", - "@lumino/signaling": "^2.1.3", - "@lumino/virtualdom": "^2.0.2", - "@lumino/widgets": "^2.5.0", + "@jupyterlab/coreutils": "^6.5.0", + "@jupyterlab/observables": "^5.5.0", + "@jupyterlab/rendermime-interfaces": "^3.13.0", + "@jupyterlab/translation": "^4.5.0", + "@lumino/algorithm": "^2.0.4", + "@lumino/commands": "^2.3.3", + "@lumino/coreutils": "^2.2.2", + "@lumino/disposable": "^2.1.5", + "@lumino/messaging": "^2.0.4", + "@lumino/polling": "^2.1.5", + "@lumino/properties": "^2.0.4", + "@lumino/signaling": "^2.1.5", + "@lumino/virtualdom": "^2.0.4", + "@lumino/widgets": "^2.7.2", "@rjsf/core": "^5.13.4", "@rjsf/utils": "^5.13.4", "react": "^18.2.0", @@ -1202,16 +1522,18 @@ } }, "node_modules/@lezer/common": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.2.3.tgz", - "integrity": "sha512-w7ojc8ejBqr2REPsWxJjrMFsA/ysDCFICn8zEOR9mrqzOu2amhITYuLD8ag6XZf0CFXDrhKqw7+tW8cX66NaDA==", - "dev": true + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.3.0.tgz", + "integrity": "sha512-L9X8uHCYU310o99L3/MpJKYxPzXPOS7S0NmBaM7UO/x2Kb2WbmMLSkfvdr1KxRIFYOpbY0Jhn7CfLSUDzL8arQ==", + "dev": true, + "license": "MIT" }, "node_modules/@lezer/cpp": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@lezer/cpp/-/cpp-1.1.2.tgz", - "integrity": "sha512-macwKtyeUO0EW86r3xWQCzOV9/CF8imJLpJlPv3sDY57cPGeUZ8gXWOWNlJr52TVByMV3PayFQCA5SHEERDmVQ==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@lezer/cpp/-/cpp-1.1.3.tgz", + "integrity": "sha512-ykYvuFQKGsRi6IcE+/hCSGUhb/I4WPjd3ELhEblm2wS2cOznDFzO+ubK2c+ioysOnlZ3EduV+MVQFCPzAIoY3w==", "dev": true, + "license": "MIT", "dependencies": { "@lezer/common": "^1.2.0", "@lezer/highlight": "^1.0.0", @@ -1219,21 +1541,23 @@ } }, "node_modules/@lezer/css": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/@lezer/css/-/css-1.1.9.tgz", - "integrity": "sha512-TYwgljcDv+YrV0MZFFvYFQHCfGgbPMR6nuqLabBdmZoFH3EP1gvw8t0vae326Ne3PszQkbXfVBjCnf3ZVCr0bA==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@lezer/css/-/css-1.3.0.tgz", + "integrity": "sha512-pBL7hup88KbI7hXnZV3PQsn43DHy6TWyzuyk2AO9UyoXcDltvIdqWKE1dLL/45JVZ+YZkHe1WVHqO6wugZZWcw==", "dev": true, + "license": "MIT", "dependencies": { "@lezer/common": "^1.2.0", "@lezer/highlight": "^1.0.0", - "@lezer/lr": "^1.0.0" + "@lezer/lr": "^1.3.0" } }, "node_modules/@lezer/generator": { - "version": "1.7.1", - "resolved": "https://registry.npmjs.org/@lezer/generator/-/generator-1.7.1.tgz", - "integrity": "sha512-MgPJN9Si+ccxzXl3OAmCeZuUKw4XiPl4y664FX/hnnyG9CTqUPq65N3/VGPA2jD23D7QgMTtNqflta+cPN+5mQ==", + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@lezer/generator/-/generator-1.8.0.tgz", + "integrity": "sha512-/SF4EDWowPqV1jOgoGSGTIFsE7Ezdr7ZYxyihl5eMKVO5tlnpIhFcDavgm1hHY5GEonoOAEnJ0CU0x+tvuAuUg==", "dev": true, + "license": "MIT", "dependencies": { "@lezer/common": "^1.1.0", "@lezer/lr": "^1.3.0" @@ -1243,19 +1567,21 @@ } }, "node_modules/@lezer/highlight": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.2.1.tgz", - "integrity": "sha512-Z5duk4RN/3zuVO7Jq0pGLJ3qynpxUVsh7IbUbGj88+uV2ApSAn6kWg2au3iJb+0Zi7kKtqffIESgNcRXWZWmSA==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.2.3.tgz", + "integrity": "sha512-qXdH7UqTvGfdVBINrgKhDsVTJTxactNNxLk7+UMwZhU13lMHaOBlJe9Vqp907ya56Y3+ed2tlqzys7jDkTmW0g==", "dev": true, + "license": "MIT", "dependencies": { - "@lezer/common": "^1.0.0" + "@lezer/common": "^1.3.0" } }, "node_modules/@lezer/html": { - "version": "1.3.10", - "resolved": "https://registry.npmjs.org/@lezer/html/-/html-1.3.10.tgz", - "integrity": "sha512-dqpT8nISx/p9Do3AchvYGV3qYc4/rKr3IBZxlHmpIKam56P47RSHkSF5f13Vu9hebS1jM0HmtJIwLbWz1VIY6w==", + "version": "1.3.12", + "resolved": "https://registry.npmjs.org/@lezer/html/-/html-1.3.12.tgz", + "integrity": "sha512-RJ7eRWdaJe3bsiiLLHjCFT1JMk8m1YP9kaUbvu2rMLEoOnke9mcTVDyfOslsln0LtujdWespjJ39w6zo+RsQYw==", "dev": true, + "license": "MIT", "dependencies": { "@lezer/common": "^1.2.0", "@lezer/highlight": "^1.0.0", @@ -1267,6 +1593,7 @@ "resolved": "https://registry.npmjs.org/@lezer/java/-/java-1.1.3.tgz", "integrity": "sha512-yHquUfujwg6Yu4Fd1GNHCvidIvJwi/1Xu2DaKl/pfWIA2c1oXkVvawH3NyXhCaFx4OdlYBVX5wvz2f7Aoa/4Xw==", "dev": true, + "license": "MIT", "dependencies": { "@lezer/common": "^1.2.0", "@lezer/highlight": "^1.0.0", @@ -1274,10 +1601,11 @@ } }, "node_modules/@lezer/javascript": { - "version": "1.4.19", - "resolved": "https://registry.npmjs.org/@lezer/javascript/-/javascript-1.4.19.tgz", - "integrity": "sha512-j44kbR1QL26l6dMunZ1uhKBFteVGLVCBGNUD2sUaMnic+rbTviVuoK0CD1l9FTW31EueWvFFswCKMH7Z+M3JRA==", + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@lezer/javascript/-/javascript-1.5.4.tgz", + "integrity": "sha512-vvYx3MhWqeZtGPwDStM2dwgljd5smolYD2lR2UyFcHfxbBQebqx8yjmFmxtJ/E6nN6u1D9srOiVWm3Rb4tmcUA==", "dev": true, + "license": "MIT", "dependencies": { "@lezer/common": "^1.2.0", "@lezer/highlight": "^1.1.3", @@ -1285,10 +1613,11 @@ } }, "node_modules/@lezer/json": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@lezer/json/-/json-1.0.2.tgz", - "integrity": "sha512-xHT2P4S5eeCYECyKNPhr4cbEL9tc8w83SPwRC373o9uEdrvGKTZoJVAGxpOsZckMlEh9W23Pc72ew918RWQOBQ==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@lezer/json/-/json-1.0.3.tgz", + "integrity": "sha512-BP9KzdF9Y35PDpv04r0VeSTKDeox5vVr3efE7eBbx3r4s3oNLfunchejZhjArmeieBH+nVOpgIiBJpEAv8ilqQ==", "dev": true, + "license": "MIT", "dependencies": { "@lezer/common": "^1.2.0", "@lezer/highlight": "^1.0.0", @@ -1296,29 +1625,32 @@ } }, "node_modules/@lezer/lr": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.4.2.tgz", - "integrity": "sha512-pu0K1jCIdnQ12aWNaAVU5bzi7Bd1w54J3ECgANPmYLtQKP0HBj2cE/5coBD66MT10xbtIuUr7tg0Shbsvk0mDA==", + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/@lezer/lr/-/lr-1.4.3.tgz", + "integrity": "sha512-yenN5SqAxAPv/qMnpWW0AT7l+SxVrgG+u0tNsRQWqbrz66HIl8DnEbBObvy21J5K7+I1v7gsAnlE2VQ5yYVSeA==", "dev": true, + "license": "MIT", "dependencies": { "@lezer/common": "^1.0.0" } }, "node_modules/@lezer/markdown": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@lezer/markdown/-/markdown-1.3.2.tgz", - "integrity": "sha512-Wu7B6VnrKTbBEohqa63h5vxXjiC4pO5ZQJ/TDbhJxPQaaIoRD/6UVDhSDtVsCwVZV12vvN9KxuLL3ATMnlG0oQ==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@lezer/markdown/-/markdown-1.6.0.tgz", + "integrity": "sha512-AXb98u3M6BEzTnreBnGtQaF7xFTiMA92Dsy5tqEjpacbjRxDSFdN4bKJo9uvU4cEEOS7D2B9MT7kvDgOEIzJSw==", "dev": true, + "license": "MIT", "dependencies": { "@lezer/common": "^1.0.0", "@lezer/highlight": "^1.0.0" } }, "node_modules/@lezer/php": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@lezer/php/-/php-1.0.2.tgz", - "integrity": "sha512-GN7BnqtGRpFyeoKSEqxvGvhJQiI4zkgmYnDk/JIyc7H7Ifc1tkPnUn/R2R8meH3h/aBf5rzjvU8ZQoyiNDtDrA==", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@lezer/php/-/php-1.0.5.tgz", + "integrity": "sha512-W7asp9DhM6q0W6DYNwIkLSKOvxlXRrif+UXBMxzsJUuqmhE7oVU+gS3THO4S/Puh7Xzgm858UNaFi6dxTP8dJA==", "dev": true, + "license": "MIT", "dependencies": { "@lezer/common": "^1.2.0", "@lezer/highlight": "^1.0.0", @@ -1326,10 +1658,11 @@ } }, "node_modules/@lezer/python": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/@lezer/python/-/python-1.1.14.tgz", - "integrity": "sha512-ykDOb2Ti24n76PJsSa4ZoDF0zH12BSw1LGfQXCYJhJyOGiFTfGaX0Du66Ze72R+u/P35U+O6I9m8TFXov1JzsA==", + "version": "1.1.18", + "resolved": "https://registry.npmjs.org/@lezer/python/-/python-1.1.18.tgz", + "integrity": "sha512-31FiUrU7z9+d/ElGQLJFXl+dKOdx0jALlP3KEOsGTex8mvj+SoE1FgItcHWK/axkxCHGUSpqIHt6JAWfWu9Rhg==", "dev": true, + "license": "MIT", "dependencies": { "@lezer/common": "^1.2.0", "@lezer/highlight": "^1.0.0", @@ -1341,6 +1674,7 @@ "resolved": "https://registry.npmjs.org/@lezer/rust/-/rust-1.0.2.tgz", "integrity": "sha512-Lz5sIPBdF2FUXcWeCu1//ojFAZqzTQNRga0aYv6dYXqJqPfMdCAI0NzajWUd4Xijj1IKJLtjoXRPMvTKWBcqKg==", "dev": true, + "license": "MIT", "dependencies": { "@lezer/common": "^1.2.0", "@lezer/highlight": "^1.0.0", @@ -1348,10 +1682,11 @@ } }, "node_modules/@lezer/xml": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@lezer/xml/-/xml-1.0.5.tgz", - "integrity": "sha512-VFouqOzmUWfIg+tfmpcdV33ewtK+NSwd4ngSe1aG7HFb4BN0ExyY1b8msp+ndFrnlG4V4iC8yXacjFtrwERnaw==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@lezer/xml/-/xml-1.0.6.tgz", + "integrity": "sha512-CdDwirL0OEaStFue/66ZmFSeppuL6Dwjlk8qk153mSQwiSH/Dlri4GNymrNWnUmPl2Um7QfV1FO9KFUyX3Twww==", "dev": true, + "license": "MIT", "dependencies": { "@lezer/common": "^1.2.0", "@lezer/highlight": "^1.0.0", @@ -1359,185 +1694,235 @@ } }, "node_modules/@lumino/algorithm": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@lumino/algorithm/-/algorithm-2.0.2.tgz", - "integrity": "sha512-cI8yJ2+QK1yM5ZRU3Kuaw9fJ/64JEDZEwWWp7+U0cd/mvcZ44BGdJJ29w+tIet1QXxPAvnsUleWyQ5qm4qUouA==", - "dev": true + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@lumino/algorithm/-/algorithm-2.0.4.tgz", + "integrity": "sha512-gddBhESPqu25KWLeAK9Kz8tS9Ph7P45i0CNG7Ia4XMhK9PHLtTsBdJTC9jP+MqhbzC8zDT/4ekvYRV9ojRPj7Q==", + "dev": true, + "license": "BSD-3-Clause" }, "node_modules/@lumino/application": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@lumino/application/-/application-2.4.1.tgz", - "integrity": "sha512-XdCAlNajcsGdK6ep+s6QC70EY+uBnP3kDiWthFLl3EMkvkYwmjOPzIPGlwLEd9Hu0XCO+1Vd2PlpeTnxw5D3/g==", + "version": "2.4.5", + "resolved": "https://registry.npmjs.org/@lumino/application/-/application-2.4.5.tgz", + "integrity": "sha512-b6JLzjQ0qAV7wJWS0v6z+9FeY8HTuteKjdXaHZQToLutGb67leOtp+/JoKI37HFn1FQdeMgoMHIBXxrT58xXGA==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "@lumino/commands": "^2.3.1", - "@lumino/coreutils": "^2.2.0", - "@lumino/widgets": "^2.5.0" + "@lumino/commands": "^2.3.3", + "@lumino/coreutils": "^2.2.2", + "@lumino/widgets": "^2.7.2" } }, "node_modules/@lumino/collections": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@lumino/collections/-/collections-2.0.2.tgz", - "integrity": "sha512-o0QmfV1D3WhAeA8GI1/YmEPaK89JtHVa764rQ5T0LdbDEwUtUDbjavHs1E/+y66tNTXz9RUJ4D2rcSb9tysYsg==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@lumino/collections/-/collections-2.0.4.tgz", + "integrity": "sha512-D/Py9L5HET6+XUYGxFqDEEth4B65X2c7B/GQVRR8q5Fl7EArVL6e98ZXw8BMkuPcTNa0zlENpCKXzlcoJZxXgQ==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "@lumino/algorithm": "^2.0.2" + "@lumino/algorithm": "^2.0.4" } }, "node_modules/@lumino/commands": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/@lumino/commands/-/commands-2.3.1.tgz", - "integrity": "sha512-DpX1kkE4PhILpvK1T4ZnaFb6UP4+YTkdZifvN3nbiomD64O2CTd+wcWIBpZMgy6MMgbVgrE8dzHxHk1EsKxNxw==", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/@lumino/commands/-/commands-2.3.3.tgz", + "integrity": "sha512-7Ci0QdFzt4NKFMhULr19sJPpOLHJw/oYlq6Pb0/Kq1s05+cIoLimr5wiyjkbAlNoGO/8A8SEBGHy3uctZz6G3A==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "@lumino/algorithm": "^2.0.2", - "@lumino/coreutils": "^2.2.0", - "@lumino/disposable": "^2.1.3", - "@lumino/domutils": "^2.0.2", - "@lumino/keyboard": "^2.0.2", - "@lumino/signaling": "^2.1.3", - "@lumino/virtualdom": "^2.0.2" + "@lumino/algorithm": "^2.0.4", + "@lumino/coreutils": "^2.2.2", + "@lumino/disposable": "^2.1.5", + "@lumino/domutils": "^2.0.4", + "@lumino/keyboard": "^2.0.4", + "@lumino/signaling": "^2.1.5", + "@lumino/virtualdom": "^2.0.4" } }, "node_modules/@lumino/coreutils": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@lumino/coreutils/-/coreutils-2.2.0.tgz", - "integrity": "sha512-x5wnQ/GjWBayJ6vXVaUi6+Q6ETDdcUiH9eSfpRZFbgMQyyM6pi6baKqJBK2CHkCc/YbAEl6ipApTgm3KOJ/I3g==", + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@lumino/coreutils/-/coreutils-2.2.2.tgz", + "integrity": "sha512-zaKJaK7rawPATn2BGHkbMrR6oK3s9PxNe9KreLwWF2dB4ZBHDiEmNLRyHRorfJ7XqVOEXAsAAj0jFn+qJPC/4Q==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "@lumino/algorithm": "^2.0.2" + "@lumino/algorithm": "^2.0.4" } }, "node_modules/@lumino/datagrid": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/@lumino/datagrid/-/datagrid-2.4.1.tgz", - "integrity": "sha512-9sJg8UU/hqcKDqO5Rd0Blm2JYKT9nyAK/kuPYeaQc4ZLvtgn4SoOenNaShLDr3Wp54quBM8npAlk1mWG+yYC2g==", + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/@lumino/datagrid/-/datagrid-2.5.3.tgz", + "integrity": "sha512-iSwZWYV8qVZVSfOUUNMOEc0EbTwOUPmUKju2xTZ3vN39P80gPEaEhmwZFolIe8rJgpsYu+kacYU0HAb3dYVpKw==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "@lumino/algorithm": "^2.0.2", - "@lumino/coreutils": "^2.2.0", - "@lumino/disposable": "^2.1.3", - "@lumino/domutils": "^2.0.2", - "@lumino/dragdrop": "^2.1.5", - "@lumino/keyboard": "^2.0.2", - "@lumino/messaging": "^2.0.2", - "@lumino/signaling": "^2.1.3", - "@lumino/widgets": "^2.5.0" + "@lumino/algorithm": "^2.0.4", + "@lumino/coreutils": "^2.2.2", + "@lumino/disposable": "^2.1.5", + "@lumino/domutils": "^2.0.4", + "@lumino/dragdrop": "^2.1.7", + "@lumino/keyboard": "^2.0.4", + "@lumino/messaging": "^2.0.4", + "@lumino/signaling": "^2.1.5", + "@lumino/widgets": "^2.7.2" } }, "node_modules/@lumino/disposable": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/@lumino/disposable/-/disposable-2.1.3.tgz", - "integrity": "sha512-k5KXy/+T3UItiWHY4WwQawnsJnGo3aNtP5CTRKqo4+tbTNuhc3rTSvygJlNKIbEfIZXW2EWYnwfFDozkYx95eA==", + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@lumino/disposable/-/disposable-2.1.5.tgz", + "integrity": "sha512-hO9AkJK0oEGzxopuxI8LaZqwzSNwXJTGCdr5K4gh6al+zxpN7rOCh6Aq3zDxkIHJU4zybxv8r02ardx9XJsG3A==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "@lumino/signaling": "^2.1.3" + "@lumino/signaling": "^2.1.5" } }, "node_modules/@lumino/domutils": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@lumino/domutils/-/domutils-2.0.2.tgz", - "integrity": "sha512-2Kp6YHaMNI1rKB0PrALvOsZBHPy2EvVVAvJLWjlCm8MpWOVETjFp0MA9QpMubT9I76aKbaI5s1o1NJyZ8Y99pQ==", - "dev": true + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@lumino/domutils/-/domutils-2.0.4.tgz", + "integrity": "sha512-naYGUQn3e0CLtz/tjKOZP8SOBg0SW7EguhkxLpNUXlVUvx7rVsfr0VI22FVL+jgI0FbxXpEkxpSMxtK73jxJAg==", + "dev": true, + "license": "BSD-3-Clause" }, "node_modules/@lumino/dragdrop": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@lumino/dragdrop/-/dragdrop-2.1.5.tgz", - "integrity": "sha512-zqwR4GakrQBKZOW6S5pj2nfrQDurOErAoe9x3HS3BKLa1AzWA+t9PD5NESOKd81NqXFHjiMirSyFkTUs6pw+uA==", + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@lumino/dragdrop/-/dragdrop-2.1.7.tgz", + "integrity": "sha512-oa5EtBC37EiiJsuIFVcK1DywpEb4dnQCMzWnymvjlBXG3/fAIC+65Q/iLoNUWkMz1pc3ET2SZWDporlznxlEbw==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "@lumino/coreutils": "^2.2.0", - "@lumino/disposable": "^2.1.3" + "@lumino/coreutils": "^2.2.2", + "@lumino/disposable": "^2.1.5" } }, "node_modules/@lumino/keyboard": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@lumino/keyboard/-/keyboard-2.0.2.tgz", - "integrity": "sha512-icRUpvswDaFjqmAJNbQRb/aTu6Iugo6Y2oC08TiIwhQtLS9W+Ee9VofdqvbPSvCm6DkyP+DCWMuA3KXZ4V4g4g==", - "dev": true + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@lumino/keyboard/-/keyboard-2.0.4.tgz", + "integrity": "sha512-kIVkdSz8F5wtZr8hZp0CMX+E0eMCOnFH6XCT7j2UBQ80ERJHFy0eX+IbNo3dtRQ7+CcDhBV4hQquFNFa+/04QQ==", + "dev": true, + "license": "BSD-3-Clause" }, "node_modules/@lumino/messaging": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@lumino/messaging/-/messaging-2.0.2.tgz", - "integrity": "sha512-2sUF07cYA0f3mDil41Eh5sfBk0aGAH/mOh1I4+vyRUsKyBqp4WTUtpJFd8xVJGAntygxwnebIygkIaXXTIQvxA==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@lumino/messaging/-/messaging-2.0.4.tgz", + "integrity": "sha512-NbZnchAPOciSe9Qn/g6EzG0LRaw7bygFIXbCD440ZhzvugdBeAerwYhrA795jkXPNrrl3olp5AlO0cBB/XZNtg==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "@lumino/algorithm": "^2.0.2", - "@lumino/collections": "^2.0.2" + "@lumino/algorithm": "^2.0.4", + "@lumino/collections": "^2.0.4" } }, "node_modules/@lumino/polling": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/@lumino/polling/-/polling-2.1.3.tgz", - "integrity": "sha512-WEZk96ddK6eHEhdDkFUAAA40EOLit86QVbqQqnbPmhdGwFogek26Kq9b1U273LJeirv95zXCATOJAkjRyb7D+w==", + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@lumino/polling/-/polling-2.1.5.tgz", + "integrity": "sha512-YhQRWTNRVSi5R5uatwh1jkxASY5JKyAGWmtnfQOZWLDUFmsIjOTsS8NaYg1BgneZjWM3fbA18dCDDT7PPs5X1g==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "@lumino/coreutils": "^2.2.0", - "@lumino/disposable": "^2.1.3", - "@lumino/signaling": "^2.1.3" + "@lumino/coreutils": "^2.2.2", + "@lumino/disposable": "^2.1.5", + "@lumino/signaling": "^2.1.5" } }, "node_modules/@lumino/properties": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@lumino/properties/-/properties-2.0.2.tgz", - "integrity": "sha512-b312oA3Bh97WFK8efXejYmC3DVJmvzJk72LQB7H3fXhfqS5jUWvL7MSnNmgcQvGzl9fIhDWDWjhtSTi0KGYYBg==", - "dev": true + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@lumino/properties/-/properties-2.0.4.tgz", + "integrity": "sha512-XsL2qLZk+1FbfuTrkyjciI8PMDw3YcaBkqVQ+iv7OOJf9bUlrmTpCMY0Hu5d3hV2W3TWlRsdbvRRLEBJSKv0iA==", + "dev": true, + "license": "BSD-3-Clause" }, "node_modules/@lumino/signaling": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/@lumino/signaling/-/signaling-2.1.3.tgz", - "integrity": "sha512-9Wd4iMk8F1i6pYjy65bqKuPlzQMicyL9xy1/ccS20kovPcfD074waneL/7BVe+3M8i+fGa3x2qjbWrBzOdTdNw==", + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@lumino/signaling/-/signaling-2.1.5.tgz", + "integrity": "sha512-Wkx6WR45ynmKBlW0GBEoh4xk9+QluKr1JHuMftqcStBHSQBCnN54UKRRDbySXHGRhhx6p4neu7sGomgQSlQK8w==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "@lumino/algorithm": "^2.0.2", - "@lumino/coreutils": "^2.2.0" + "@lumino/algorithm": "^2.0.4", + "@lumino/coreutils": "^2.2.2" } }, "node_modules/@lumino/virtualdom": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@lumino/virtualdom/-/virtualdom-2.0.2.tgz", - "integrity": "sha512-HYZThOtZSoknjdXA102xpy5CiXtTFCVz45EXdWeYLx3NhuEwuAIX93QBBIhupalmtFlRg1yhdDNV40HxJ4kcXg==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@lumino/virtualdom/-/virtualdom-2.0.4.tgz", + "integrity": "sha512-7MFthA9KUsqZTGm/D98FZt1QupjIGyd3XyB4SIugn6DQAqhjBiyykCZydnRq3qmuMHybQel33dNIbHpzyNyQwA==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "@lumino/algorithm": "^2.0.2" + "@lumino/algorithm": "^2.0.4" } }, "node_modules/@lumino/widgets": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@lumino/widgets/-/widgets-2.5.0.tgz", - "integrity": "sha512-RSRpc6aIEiuw79jqWUHYWXLJ2GBy7vhwuqgo94UVzg6oeh3XBECX0OvXGjK2k7N2BhmRrIs9bXky7Dm861S6mQ==", + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/@lumino/widgets/-/widgets-2.7.2.tgz", + "integrity": "sha512-svp4Si10PcTr6Hfd1hZgkue1rKPYpnuCheGWkou/RlMZih94mVbQoDO1xppo/haYQg4GX2IgCFpUKsNb19oBAg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@lumino/algorithm": "^2.0.4", + "@lumino/commands": "^2.3.3", + "@lumino/coreutils": "^2.2.2", + "@lumino/disposable": "^2.1.5", + "@lumino/domutils": "^2.0.4", + "@lumino/dragdrop": "^2.1.7", + "@lumino/keyboard": "^2.0.4", + "@lumino/messaging": "^2.0.4", + "@lumino/properties": "^2.0.4", + "@lumino/signaling": "^2.1.5", + "@lumino/virtualdom": "^2.0.4" + } + }, + "node_modules/@marijn/find-cluster-break": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@marijn/find-cluster-break/-/find-cluster-break-1.0.2.tgz", + "integrity": "sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==", + "dev": true, + "license": "MIT" + }, + "node_modules/@mermaid-js/layout-elk": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@mermaid-js/layout-elk/-/layout-elk-0.2.0.tgz", + "integrity": "sha512-vjjYGnCCjYlIA/rR7M//eFi0rHM6dsMyN1JQKfckpt30DTC/esrw36hcrvA2FNPHaqh3Q/SyBWzddyaky8EtUQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "d3": "^7.9.0", + "elkjs": "^0.9.3" + }, + "peerDependencies": { + "mermaid": "^11.0.2" + } + }, + "node_modules/@mermaid-js/parser": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/@mermaid-js/parser/-/parser-0.6.3.tgz", + "integrity": "sha512-lnjOhe7zyHjc+If7yT4zoedx2vo4sHaTmtkl1+or8BRTnCtDmcTpAjpzDSfCZrshM5bCoz0GyidzadJAH1xobA==", "dev": true, + "license": "MIT", "dependencies": { - "@lumino/algorithm": "^2.0.2", - "@lumino/commands": "^2.3.1", - "@lumino/coreutils": "^2.2.0", - "@lumino/disposable": "^2.1.3", - "@lumino/domutils": "^2.0.2", - "@lumino/dragdrop": "^2.1.5", - "@lumino/keyboard": "^2.0.2", - "@lumino/messaging": "^2.0.2", - "@lumino/properties": "^2.0.2", - "@lumino/signaling": "^2.1.3", - "@lumino/virtualdom": "^2.0.2" + "langium": "3.3.1" } }, "node_modules/@microsoft/fast-colors": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/@microsoft/fast-colors/-/fast-colors-5.3.1.tgz", "integrity": "sha512-72RZXVfCbwQzvo5sXXkuLXLT7rMeYaSf5r/6ewQiv/trBtqpWRm4DEH2EilHw/iWTBKOXs1qZNQndgUMa5n4LA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@microsoft/fast-element": { "version": "1.14.0", "resolved": "https://registry.npmjs.org/@microsoft/fast-element/-/fast-element-1.14.0.tgz", "integrity": "sha512-zXvuSOzvsu8zDTy9eby8ix8VqLop2rwKRgp++ZN2kTCsoB3+QJVoaGD2T/Cyso2ViZQFXNpiNCVKfnmxBvmWkQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@microsoft/fast-foundation": { "version": "2.50.0", "resolved": "https://registry.npmjs.org/@microsoft/fast-foundation/-/fast-foundation-2.50.0.tgz", "integrity": "sha512-8mFYG88Xea1jZf2TI9Lm/jzZ6RWR8x29r24mGuLojNYqIR2Bl8+hnswoV6laApKdCbGMPKnsAL/O68Q0sRxeVg==", "dev": true, + "license": "MIT", "dependencies": { "@microsoft/fast-element": "^1.14.0", "@microsoft/fast-web-utilities": "^5.4.1", @@ -1550,6 +1935,7 @@ "resolved": "https://registry.npmjs.org/@microsoft/fast-web-utilities/-/fast-web-utilities-5.4.1.tgz", "integrity": "sha512-ReWYncndjV3c8D8iq9tp7NcFNc1vbVHvcBFPME2nNFKNbS1XCesYZGlIlf3ot5EmuOXPlrzUHOWzQ2vFpIkqDg==", "dev": true, + "license": "MIT", "dependencies": { "exenv-es6": "^1.1.1" } @@ -1590,12 +1976,13 @@ } }, "node_modules/@playwright/test": { - "version": "1.49.0", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.49.0.tgz", - "integrity": "sha512-DMulbwQURa8rNIQrf94+jPJQ4FmOVdpE5ZppRNvWVjvhC+6sOeo28r8MgIpQRYouXRtt/FCCXU7zn20jnHR4Qw==", + "version": "1.57.0", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.57.0.tgz", + "integrity": "sha512-6TyEnHgd6SArQO8UO2OMTxshln3QMWBtPGrOCgs3wVEmQmwyuNtB10IZMfmYDE0riwNR1cu4q+pPcxMVtaG3TA==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "playwright": "1.49.0" + "playwright": "1.57.0" }, "bin": { "playwright": "cli.js" @@ -1605,30 +1992,32 @@ } }, "node_modules/@rjsf/core": { - "version": "5.22.4", - "resolved": "https://registry.npmjs.org/@rjsf/core/-/core-5.22.4.tgz", - "integrity": "sha512-0QjAVPXDi/19jR/E44ULDzOkvC4Px5zcZhpGtBFNWNWWmb9UgyjPuvJYga2obzHU46P+5maLvUQEZVAeFwDuqQ==", + "version": "5.24.13", + "resolved": "https://registry.npmjs.org/@rjsf/core/-/core-5.24.13.tgz", + "integrity": "sha512-ONTr14s7LFIjx2VRFLuOpagL76sM/HPy6/OhdBfq6UukINmTIs6+aFN0GgcR0aXQHFDXQ7f/fel0o/SO05Htdg==", "dev": true, + "license": "Apache-2.0", "dependencies": { "lodash": "^4.17.21", "lodash-es": "^4.17.21", "markdown-to-jsx": "^7.4.1", - "nanoid": "^3.3.7", "prop-types": "^15.8.1" }, "engines": { "node": ">=14" }, "peerDependencies": { - "@rjsf/utils": "^5.22.x", + "@rjsf/utils": "^5.24.x", "react": "^16.14.0 || >=17" } }, "node_modules/@rjsf/utils": { - "version": "5.22.4", - "resolved": "https://registry.npmjs.org/@rjsf/utils/-/utils-5.22.4.tgz", - "integrity": "sha512-yQTdz5ryiYy258xCVthVPQ3DeaMzrRNrFcO8xvGHorp0/bLUxdTZ0iidXop49m3y8SaxxTZd398ZKWg2cqxiIA==", + "version": "5.24.13", + "resolved": "https://registry.npmjs.org/@rjsf/utils/-/utils-5.24.13.tgz", + "integrity": "sha512-rNF8tDxIwTtXzz5O/U23QU73nlhgQNYJ+Sv5BAwQOIyhIE2Z3S5tUiSVMwZHt0julkv/Ryfwi+qsD4FiE5rOuw==", "dev": true, + "license": "Apache-2.0", + "peer": true, "dependencies": { "json-schema-merge-allof": "^0.8.1", "jsonpointer": "^5.0.1", @@ -1649,6 +2038,33 @@ "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", "dev": true }, + "node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", + "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.0" + } + }, "node_modules/@stdlib/array": { "version": "0.0.12", "resolved": "https://registry.npmjs.org/@stdlib/array/-/array-0.0.12.tgz", @@ -2502,116 +2918,490 @@ "url": "https://www.patreon.com/athan" } }, - "node_modules/@types/estree": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", - "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", - "dev": true + "node_modules/@tootallnate/once": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", + "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10" + } }, - "node_modules/@types/geojson": { - "version": "7946.0.4", - "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.4.tgz", - "integrity": "sha512-MHmwBtCb7OCv1DSivz2UNJXPGU/1btAWRKlqJ2saEhVJkpkvqHMMaOpKg0v4sAbDWSQekHGvPVMM8nQ+Jen03Q==", + "node_modules/@types/d3": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@types/d3/-/d3-7.4.3.tgz", + "integrity": "sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/d3-array": "*", + "@types/d3-axis": "*", + "@types/d3-brush": "*", + "@types/d3-chord": "*", + "@types/d3-color": "*", + "@types/d3-contour": "*", + "@types/d3-delaunay": "*", + "@types/d3-dispatch": "*", + "@types/d3-drag": "*", + "@types/d3-dsv": "*", + "@types/d3-ease": "*", + "@types/d3-fetch": "*", + "@types/d3-force": "*", + "@types/d3-format": "*", + "@types/d3-geo": "*", + "@types/d3-hierarchy": "*", + "@types/d3-interpolate": "*", + "@types/d3-path": "*", + "@types/d3-polygon": "*", + "@types/d3-quadtree": "*", + "@types/d3-random": "*", + "@types/d3-scale": "*", + "@types/d3-scale-chromatic": "*", + "@types/d3-selection": "*", + "@types/d3-shape": "*", + "@types/d3-time": "*", + "@types/d3-time-format": "*", + "@types/d3-timer": "*", + "@types/d3-transition": "*", + "@types/d3-zoom": "*" + } + }, + "node_modules/@types/d3-array": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.2.tgz", + "integrity": "sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==", "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 - }, - "node_modules/@types/json5": { - "version": "0.0.29", - "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", - "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", - "dev": true - }, - "node_modules/@types/node": { - "version": "20.17.6", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.6.tgz", - "integrity": "sha512-VEI7OdvK2wP7XHnsuXbAJnEpEkF6NjSN45QJlL4VGqZSXsnicpesdTWsg9RISeSdYd3yeRj/y3k5KGjUXYnFwQ==", + "node_modules/@types/d3-axis": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-axis/-/d3-axis-3.0.6.tgz", + "integrity": "sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw==", "dev": true, + "license": "MIT", "dependencies": { - "undici-types": "~6.19.2" + "@types/d3-selection": "*" } }, - "node_modules/@types/prop-types": { - "version": "15.7.13", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.13.tgz", - "integrity": "sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA==", - "dev": true - }, - "node_modules/@types/react": { - "version": "18.3.12", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.12.tgz", - "integrity": "sha512-D2wOSq/d6Agt28q7rSI3jhU7G6aiuzljDGZ2hTZHIkrTLUI+AF3WMeKkEZ9nN2fkBAlcktT6vcZjDFiIhMYEQw==", + "node_modules/@types/d3-brush": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-brush/-/d3-brush-3.0.6.tgz", + "integrity": "sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A==", "dev": true, + "license": "MIT", "dependencies": { - "@types/prop-types": "*", - "csstype": "^3.0.2" + "@types/d3-selection": "*" } }, - "node_modules/@types/semver": { - "version": "7.5.8", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", - "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", - "dev": true + "node_modules/@types/d3-chord": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-chord/-/d3-chord-3.0.6.tgz", + "integrity": "sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg==", + "dev": true, + "license": "MIT" }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz", - "integrity": "sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA==", + "node_modules/@types/d3-color": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz", + "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/d3-contour": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-contour/-/d3-contour-3.0.6.tgz", + "integrity": "sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg==", "dev": true, + "license": "MIT", "dependencies": { - "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "6.21.0", - "@typescript-eslint/type-utils": "6.21.0", - "@typescript-eslint/utils": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0", - "debug": "^4.3.4", - "graphemer": "^1.4.0", - "ignore": "^5.2.4", - "natural-compare": "^1.4.0", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", - "eslint": "^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "@types/d3-array": "*", + "@types/geojson": "*" } }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "node_modules/@types/d3-delaunay": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-delaunay/-/d3-delaunay-6.0.4.tgz", + "integrity": "sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/d3-dispatch": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-dispatch/-/d3-dispatch-3.0.7.tgz", + "integrity": "sha512-5o9OIAdKkhN1QItV2oqaE5KMIiXAvDWBDPrD85e58Qlz1c1kI/J0NcqbEG88CoTwJrYe7ntUCVfeUl2UJKbWgA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/d3-drag": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-drag/-/d3-drag-3.0.7.tgz", + "integrity": "sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==", "dev": true, + "license": "MIT", "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "@types/d3-selection": "*" } }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/ms": { - "version": "2.1.3", + "node_modules/@types/d3-dsv": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-dsv/-/d3-dsv-3.0.7.tgz", + "integrity": "sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/d3-ease": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz", + "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/d3-fetch": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@types/d3-fetch/-/d3-fetch-3.0.7.tgz", + "integrity": "sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/d3-dsv": "*" + } + }, + "node_modules/@types/d3-force": { + "version": "3.0.10", + "resolved": "https://registry.npmjs.org/@types/d3-force/-/d3-force-3.0.10.tgz", + "integrity": "sha512-ZYeSaCF3p73RdOKcjj+swRlZfnYpK1EbaDiYICEEp5Q6sUiqFaFQ9qgoshp5CzIyyb/yD09kD9o2zEltCexlgw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/d3-format": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-format/-/d3-format-3.0.4.tgz", + "integrity": "sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/d3-geo": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-geo/-/d3-geo-3.1.0.tgz", + "integrity": "sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/geojson": "*" + } + }, + "node_modules/@types/d3-hierarchy": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@types/d3-hierarchy/-/d3-hierarchy-3.1.7.tgz", + "integrity": "sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/d3-interpolate": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", + "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/d3-color": "*" + } + }, + "node_modules/@types/d3-path": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz", + "integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/d3-polygon": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-polygon/-/d3-polygon-3.0.2.tgz", + "integrity": "sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/d3-quadtree": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/d3-quadtree/-/d3-quadtree-3.0.6.tgz", + "integrity": "sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/d3-random": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-random/-/d3-random-3.0.3.tgz", + "integrity": "sha512-Imagg1vJ3y76Y2ea0871wpabqp613+8/r0mCLEBfdtqC7xMSfj9idOnmBYyMoULfHePJyxMAw3nWhJxzc+LFwQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/d3-scale": { + "version": "4.0.9", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz", + "integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/d3-time": "*" + } + }, + "node_modules/@types/d3-scale-chromatic": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.1.0.tgz", + "integrity": "sha512-iWMJgwkK7yTRmWqRB5plb1kadXyQ5Sj8V/zYlFGMUBbIPKQScw+Dku9cAAMgJG+z5GYDoMjWGLVOvjghDEFnKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/d3-selection": { + "version": "3.0.11", + "resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-3.0.11.tgz", + "integrity": "sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/d3-shape": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.7.tgz", + "integrity": "sha512-VLvUQ33C+3J+8p+Daf+nYSOsjB4GXp19/S/aGo60m9h1v6XaxjiT82lKVWJCfzhtuZ3yD7i/TPeC/fuKLLOSmg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/d3-path": "*" + } + }, + "node_modules/@types/d3-time": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz", + "integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/d3-time-format": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-4.0.3.tgz", + "integrity": "sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/d3-timer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz", + "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/d3-transition": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-3.0.9.tgz", + "integrity": "sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-zoom": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/@types/d3-zoom/-/d3-zoom-3.0.8.tgz", + "integrity": "sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/d3-interpolate": "*", + "@types/d3-selection": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "dev": true + }, + "node_modules/@types/geojson": { + "version": "7946.0.4", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.4.tgz", + "integrity": "sha512-MHmwBtCb7OCv1DSivz2UNJXPGU/1btAWRKlqJ2saEhVJkpkvqHMMaOpKg0v4sAbDWSQekHGvPVMM8nQ+Jen03Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/jsdom": { + "version": "20.0.1", + "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-20.0.1.tgz", + "integrity": "sha512-d0r18sZPmMQr1eG35u12FZfhIXNrnsPU/g5wvRKCUf/tOGilKKwYMYGqh33BNR6ba+2gkHw1EUiHoN3mn7E5IQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/tough-cookie": "*", + "parse5": "^7.0.0" + } + }, + "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 + }, + "node_modules/@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "dev": true + }, + "node_modules/@types/node": { + "version": "20.17.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.6.tgz", + "integrity": "sha512-VEI7OdvK2wP7XHnsuXbAJnEpEkF6NjSN45QJlL4VGqZSXsnicpesdTWsg9RISeSdYd3yeRj/y3k5KGjUXYnFwQ==", + "dev": true, + "dependencies": { + "undici-types": "~6.19.2" + } + }, + "node_modules/@types/prop-types": { + "version": "15.7.15", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz", + "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/react": { + "version": "18.3.27", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.27.tgz", + "integrity": "sha512-cisd7gxkzjBKU2GgdYrTdtQx1SORymWyaAFhaxQPK9bYO9ot3Y5OikQRvY0VYQtvwjeQnizCINJAenh/V7MK2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/prop-types": "*", + "csstype": "^3.2.2" + } + }, + "node_modules/@types/semver": { + "version": "7.5.8", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", + "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", + "dev": true + }, + "node_modules/@types/stack-utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/tough-cookie": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", + "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/@types/yargs": { + "version": "17.0.35", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.35.tgz", + "integrity": "sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz", + "integrity": "sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA==", + "dev": true, + "dependencies": { + "@eslint-community/regexpp": "^4.5.1", + "@typescript-eslint/scope-manager": "6.21.0", + "@typescript-eslint/type-utils": "6.21.0", + "@typescript-eslint/utils": "6.21.0", + "@typescript-eslint/visitor-keys": "6.21.0", + "debug": "^4.3.4", + "graphemer": "^1.4.0", + "ignore": "^5.2.4", + "natural-compare": "^1.4.0", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/eslint-plugin/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, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/eslint-plugin/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 @@ -2621,6 +3411,7 @@ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.21.0.tgz", "integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==", "dev": true, + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "6.21.0", "@typescript-eslint/types": "6.21.0", @@ -2850,13 +3641,24 @@ "version": "1.68.0", "resolved": "https://registry.npmjs.org/@vscode/debugprotocol/-/debugprotocol-1.68.0.tgz", "integrity": "sha512-2J27dysaXmvnfuhFGhfeuxfHRXunqNPxtBoR3koiTOA9rdxWNDTa1zIFLCFMSHJ9MPTPKFcBeblsyaCJCIlQxg==", - "dev": true + "dev": true, + "license": "MIT" + }, + "node_modules/abab": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", + "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", + "deprecated": "Use your platform's native atob() and btoa() methods instead", + "dev": true, + "license": "BSD-3-Clause" }, "node_modules/acorn": { - "version": "8.14.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", - "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, + "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -2864,6 +3666,17 @@ "node": ">=0.4.0" } }, + "node_modules/acorn-globals": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-7.0.1.tgz", + "integrity": "sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.1.0", + "acorn-walk": "^8.0.2" + } + }, "node_modules/acorn-jsx": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", @@ -2873,11 +3686,63 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, + "node_modules/acorn-walk": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/agent-base/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/agent-base/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/ajv": { "version": "8.17.1", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "dev": true, + "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -3078,6 +3943,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "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/available-typed-arrays": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", @@ -3100,10 +3972,11 @@ "dev": true }, "node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" } @@ -3148,6 +4021,20 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -3173,6 +4060,51 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/chevrotain": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/chevrotain/-/chevrotain-11.0.3.tgz", + "integrity": "sha512-ci2iJH6LeIkvP9eJW6gpueU8cnZhv85ELY8w8WiFtNjMHA5ad6pQLaJo9mEly/9qUyCpvqX8/POVUTf18/HFdw==", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@chevrotain/cst-dts-gen": "11.0.3", + "@chevrotain/gast": "11.0.3", + "@chevrotain/regexp-to-ast": "11.0.3", + "@chevrotain/types": "11.0.3", + "@chevrotain/utils": "11.0.3", + "lodash-es": "4.17.21" + } + }, + "node_modules/chevrotain-allstar": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/chevrotain-allstar/-/chevrotain-allstar-0.3.1.tgz", + "integrity": "sha512-b7g+y9A0v4mxCW1qUhf3BSVPg+/NvGErk/dOkrDaHA0nQIQGAtrOjlX//9OQtRlSCy+x9rfB5N8yC71lH1nvMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "lodash-es": "^4.17.21" + }, + "peerDependencies": { + "chevrotain": "^11.0.0" + } + }, + "node_modules/ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/cliui": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", @@ -3205,6 +4137,19 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, + "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": "7.2.0", "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", @@ -3244,17 +4189,35 @@ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, + "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-util-is": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", "dev": true }, + "node_modules/cose-base": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/cose-base/-/cose-base-1.0.3.tgz", + "integrity": "sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg==", + "dev": true, + "license": "MIT", + "dependencies": { + "layout-base": "^1.0.0" + } + }, "node_modules/crelt": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz", "integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/cross-spawn": { "version": "7.0.6", @@ -3270,11 +4233,135 @@ "node": ">= 8" } }, + "node_modules/cssom": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz", + "integrity": "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==", + "dev": true, + "license": "MIT" + }, + "node_modules/cssstyle": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", + "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssom": "~0.3.6" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cssstyle/node_modules/cssom": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", + "dev": true, + "license": "MIT" + }, "node_modules/csstype": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", - "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "dev": true + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/cytoscape": { + "version": "3.33.1", + "resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.33.1.tgz", + "integrity": "sha512-iJc4TwyANnOGR1OmWhsS9ayRS3s+XQ185FmuHObThD+5AeJCakAAbWv8KimMTt08xCCLNgneQwFp+JRJOr9qGQ==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/cytoscape-cose-bilkent": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cytoscape-cose-bilkent/-/cytoscape-cose-bilkent-4.1.0.tgz", + "integrity": "sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "cose-base": "^1.0.0" + }, + "peerDependencies": { + "cytoscape": "^3.2.0" + } + }, + "node_modules/cytoscape-fcose": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cytoscape-fcose/-/cytoscape-fcose-2.2.0.tgz", + "integrity": "sha512-ki1/VuRIHFCzxWNrsshHYPs6L7TvLu3DL+TyIGEsRcvVERmxokbf5Gdk7mFxZnTdiGtnA4cfSmjZJMviqSuZrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "cose-base": "^2.2.0" + }, + "peerDependencies": { + "cytoscape": "^3.2.0" + } + }, + "node_modules/cytoscape-fcose/node_modules/cose-base": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cose-base/-/cose-base-2.2.0.tgz", + "integrity": "sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g==", + "dev": true, + "license": "MIT", + "dependencies": { + "layout-base": "^2.0.0" + } + }, + "node_modules/cytoscape-fcose/node_modules/layout-base": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/layout-base/-/layout-base-2.0.1.tgz", + "integrity": "sha512-dp3s92+uNI1hWIpPGH3jK2kxE2lMjdXdr+DH8ynZHpd6PUlH6x6cbuXnoMmiNumznqaNO31xu9e79F0uuZ0JFg==", + "dev": true, + "license": "MIT" + }, + "node_modules/d3": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/d3/-/d3-7.9.0.tgz", + "integrity": "sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==", + "dev": true, + "license": "ISC", + "dependencies": { + "d3-array": "3", + "d3-axis": "3", + "d3-brush": "3", + "d3-chord": "3", + "d3-color": "3", + "d3-contour": "4", + "d3-delaunay": "6", + "d3-dispatch": "3", + "d3-drag": "3", + "d3-dsv": "3", + "d3-ease": "3", + "d3-fetch": "3", + "d3-force": "3", + "d3-format": "3", + "d3-geo": "3", + "d3-hierarchy": "3", + "d3-interpolate": "3", + "d3-path": "3", + "d3-polygon": "3", + "d3-quadtree": "3", + "d3-random": "3", + "d3-scale": "4", + "d3-scale-chromatic": "3", + "d3-selection": "3", + "d3-shape": "3", + "d3-time": "3", + "d3-time-format": "4", + "d3-timer": "3", + "d3-transition": "3", + "d3-zoom": "3" + }, + "engines": { + "node": ">=12" + } }, "node_modules/d3-array": { "version": "3.2.4", @@ -3288,6 +4375,46 @@ "node": ">=12" } }, + "node_modules/d3-axis": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz", + "integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-brush": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz", + "integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "3", + "d3-transition": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-chord": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-3.0.1.tgz", + "integrity": "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==", + "dev": true, + "license": "ISC", + "dependencies": { + "d3-path": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/d3-color": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", @@ -3298,6 +4425,19 @@ "node": ">=12" } }, + "node_modules/d3-contour": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.2.tgz", + "integrity": "sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==", + "dev": true, + "license": "ISC", + "dependencies": { + "d3-array": "^3.2.0" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/d3-delaunay": { "version": "6.0.4", "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.4.tgz", @@ -3321,6 +4461,20 @@ "node": ">=12" } }, + "node_modules/d3-drag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz", + "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==", + "dev": true, + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-selection": "3" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/d3-dsv": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz", @@ -3347,6 +4501,29 @@ "node": ">=12" } }, + "node_modules/d3-ease": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-fetch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-3.0.1.tgz", + "integrity": "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==", + "dev": true, + "license": "ISC", + "dependencies": { + "d3-dsv": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/d3-force": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz", @@ -3440,6 +4617,16 @@ "node": ">=12" } }, + "node_modules/d3-polygon": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-3.0.1.tgz", + "integrity": "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, "node_modules/d3-quadtree": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz", @@ -3450,6 +4637,61 @@ "node": ">=12" } }, + "node_modules/d3-random": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz", + "integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-sankey": { + "version": "0.12.3", + "resolved": "https://registry.npmjs.org/d3-sankey/-/d3-sankey-0.12.3.tgz", + "integrity": "sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "d3-array": "1 - 2", + "d3-shape": "^1.2.0" + } + }, + "node_modules/d3-sankey/node_modules/d3-array": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-2.12.1.tgz", + "integrity": "sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "internmap": "^1.0.0" + } + }, + "node_modules/d3-sankey/node_modules/d3-path": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.9.tgz", + "integrity": "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/d3-sankey/node_modules/d3-shape": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.7.tgz", + "integrity": "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "d3-path": "1" + } + }, + "node_modules/d3-sankey/node_modules/internmap": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-1.0.1.tgz", + "integrity": "sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==", + "dev": true, + "license": "ISC" + }, "node_modules/d3-scale": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", @@ -3481,6 +4723,17 @@ "node": ">=12" } }, + "node_modules/d3-selection": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", + "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", + "dev": true, + "license": "ISC", + "peer": true, + "engines": { + "node": ">=12" + } + }, "node_modules/d3-shape": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", @@ -3530,6 +4783,106 @@ "node": ">=12" } }, + "node_modules/d3-transition": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz", + "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3", + "d3-dispatch": "1 - 3", + "d3-ease": "1 - 3", + "d3-interpolate": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "d3-selection": "2 - 3" + } + }, + "node_modules/d3-zoom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz", + "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==", + "dev": true, + "license": "ISC", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "2 - 3", + "d3-transition": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/dagre-d3-es": { + "version": "7.0.13", + "resolved": "https://registry.npmjs.org/dagre-d3-es/-/dagre-d3-es-7.0.13.tgz", + "integrity": "sha512-efEhnxpSuwpYOKRm/L5KbqoZmNNukHa/Flty4Wp62JRvgH2ojwVgPgdYyr4twpieZnyRDdIH7PY2mopX26+j2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "d3": "^7.9.0", + "lodash-es": "^4.17.21" + } + }, + "node_modules/data-urls": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-3.0.2.tgz", + "integrity": "sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "abab": "^2.0.6", + "whatwg-mimetype": "^3.0.0", + "whatwg-url": "^11.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/data-urls/node_modules/tr46": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", + "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/data-urls/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/data-urls/node_modules/whatwg-url": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz", + "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tr46": "^3.0.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/data-view-buffer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.1.tgz", @@ -3581,6 +4934,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/dayjs": { + "version": "1.11.19", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.19.tgz", + "integrity": "sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw==", + "dev": true, + "license": "MIT" + }, "node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -3590,6 +4950,13 @@ "ms": "2.0.0" } }, + "node_modules/decimal.js": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz", + "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", + "dev": true, + "license": "MIT" + }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -3601,6 +4968,7 @@ "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -3644,9 +5012,19 @@ "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.1.tgz", "integrity": "sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw==", "dev": true, - "license": "ISC", - "dependencies": { - "robust-predicates": "^3.0.2" + "license": "ISC", + "dependencies": { + "robust-predicates": "^3.0.2" + } + }, + "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/dir-glob": { @@ -3678,6 +5056,7 @@ "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", "dev": true, + "license": "MIT", "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.2", @@ -3687,6 +5066,19 @@ "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" } }, + "node_modules/dom-serializer/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/domelementtype": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", @@ -3697,13 +5089,39 @@ "type": "github", "url": "https://github.com/sponsors/fb55" } - ] + ], + "license": "BSD-2-Clause" + }, + "node_modules/domexception": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz", + "integrity": "sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==", + "deprecated": "Use your platform's native DOMException instead", + "dev": true, + "license": "MIT", + "dependencies": { + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/domexception/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/domhandler": { "version": "5.0.3", "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "domelementtype": "^2.3.0" }, @@ -3714,11 +5132,22 @@ "url": "https://github.com/fb55/domhandler?sponsor=1" } }, + "node_modules/dompurify": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.3.0.tgz", + "integrity": "sha512-r+f6MYR1gGN1eJv0TVQbhA7if/U7P87cdPl3HN5rikqaBSBxLiCb/b9O+2eG0cxz0ghyU+mU1QkbsOwERMYlWQ==", + "dev": true, + "license": "(MPL-2.0 OR Apache-2.0)", + "optionalDependencies": { + "@types/trusted-types": "^2.0.7" + } + }, "node_modules/domutils": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz", - "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", + "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "dom-serializer": "^2.0.0", "domelementtype": "^2.3.0", @@ -3740,6 +5169,28 @@ "url": "https://dotenvx.com" } }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/elkjs": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/elkjs/-/elkjs-0.9.3.tgz", + "integrity": "sha512-f/ZeWvW/BCXbhGEf1Ujp29EASo/lk1FDnETgNKwJrsVvGZhUWCZyg3xLJjAsxfOmt8KjswHmI5EwCQcPMpOYhQ==", + "dev": true, + "license": "EPL-2.0" + }, "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -3747,10 +5198,11 @@ "dev": true }, "node_modules/entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", "dev": true, + "license": "BSD-2-Clause", "engines": { "node": ">=0.12" }, @@ -3828,13 +5280,11 @@ } }, "node_modules/es-define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", - "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", "dev": true, - "dependencies": { - "get-intrinsic": "^1.2.4" - }, + "license": "MIT", "engines": { "node": ">= 0.4" } @@ -3875,10 +5325,11 @@ } }, "node_modules/es-object-atoms": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.0.0.tgz", - "integrity": "sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", "dev": true, + "license": "MIT", "dependencies": { "es-errors": "^1.3.0" }, @@ -3887,14 +5338,16 @@ } }, "node_modules/es-set-tostringtag": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz", - "integrity": "sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", "dev": true, + "license": "MIT", "dependencies": { - "get-intrinsic": "^1.2.4", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", - "hasown": "^2.0.1" + "hasown": "^2.0.2" }, "engines": { "node": ">= 0.4" @@ -3947,12 +5400,35 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/escodegen": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", + "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=6.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, "node_modules/eslint": { "version": "8.57.1", "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "dev": true, + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -4162,6 +5638,7 @@ "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.31.0.tgz", "integrity": "sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A==", "dev": true, + "peer": true, "dependencies": { "@rtsao/scc": "^1.1.0", "array-includes": "^3.1.8", @@ -4191,10 +5668,11 @@ } }, "node_modules/eslint-plugin-import/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==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -4253,6 +5731,7 @@ "resolved": "https://registry.npmjs.org/eslint-plugin-n/-/eslint-plugin-n-15.7.0.tgz", "integrity": "sha512-jDex9s7D/Qial8AGVIHq4W7NswpUD5DPDL2RH8Lzd9EloWUuvUkHfv4FRLMipH5q2UtyurorBkPeNi1wVWNh3Q==", "dev": true, + "peer": true, "dependencies": { "builtins": "^5.0.1", "eslint-plugin-es": "^4.1.0", @@ -4274,10 +5753,11 @@ } }, "node_modules/eslint-plugin-n/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==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -4300,6 +5780,7 @@ "resolved": "https://registry.npmjs.org/eslint-plugin-promise/-/eslint-plugin-promise-6.6.0.tgz", "integrity": "sha512-57Zzfw8G6+Gq7axm2Pdo3gW/Rx3h9Yywgn61uE/3elTCOePEHVrn2i5CdfBwA1BLK0Q0WqctICIUSqXZW/VprQ==", "dev": true, + "peer": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, @@ -4315,6 +5796,7 @@ "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.2.tgz", "integrity": "sha512-EsTAnj9fLVr/GZleBLFbj/sSuXeWmp1eXIN60ceYnZveqEaUCyW4X+Vh4WTdUhCkW4xutXYqTXCUSyqD4rB75w==", "dev": true, + "peer": true, "dependencies": { "array-includes": "^3.1.8", "array.prototype.findlast": "^1.2.5", @@ -4343,10 +5825,11 @@ } }, "node_modules/eslint-plugin-react/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==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -4474,10 +5957,11 @@ } }, "node_modules/eslint/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==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -4541,6 +6025,20 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/esquery": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", @@ -4587,7 +6085,15 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/exenv-es6/-/exenv-es6-1.1.1.tgz", "integrity": "sha512-vlVu3N8d6yEMpMsEm+7sUBAI81aqYYuEvfK0jNqmdb/OPXzzH7QWDDnVjMvDSY47JdHEqx/dfC/q8WkfoTmpGQ==", - "dev": true + "dev": true, + "license": "MIT" + }, + "node_modules/exsolve": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.8.tgz", + "integrity": "sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA==", + "dev": true, + "license": "MIT" }, "node_modules/fast-deep-equal": { "version": "3.1.3", @@ -4636,10 +6142,21 @@ "dev": true }, "node_modules/fast-uri": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.3.tgz", - "integrity": "sha512-aLrHthzCjH5He4Z2H9YZ+v6Ujb9ocRuW6ZzkJQOrTxleEijANq4v1TsaPaVG1PZcuurEzrLcWRyYBYXD5cEiaw==", - "dev": true + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" }, "node_modules/fastq": { "version": "1.17.1", @@ -4719,11 +6236,29 @@ "is-callable": "^1.1.3" } }, + "node_modules/form-data": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "dev": true, + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/free-style": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/free-style/-/free-style-3.1.0.tgz", "integrity": "sha512-vJujYSIyT30iDoaoeigNAxX4yB1RUrh+N2ZMhIElMr3BvCuGXOw7XNJMEEJkDUeamK2Rnb/IKFGKRKlTWIGRWA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/fs-extra": { "version": "10.1.0", @@ -4751,6 +6286,7 @@ "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", "dev": true, "hasInstallScript": true, + "license": "MIT", "optional": true, "os": [ "darwin" @@ -4805,16 +6341,22 @@ } }, "node_modules/get-intrinsic": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", - "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", "dev": true, + "license": "MIT", "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -4823,6 +6365,20 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/get-stdin": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-8.0.0.tgz", @@ -4852,6 +6408,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/github-slugger": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-2.0.0.tgz", + "integrity": "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==", + "dev": true, + "license": "ISC" + }, "node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", @@ -4886,10 +6449,11 @@ } }, "node_modules/glob/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==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -4959,12 +6523,13 @@ } }, "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", "dev": true, - "dependencies": { - "get-intrinsic": "^1.1.3" + "license": "MIT", + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -4982,6 +6547,13 @@ "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", "dev": true }, + "node_modules/hachure-fill": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/hachure-fill/-/hachure-fill-0.5.2.tgz", + "integrity": "sha512-3GKBOn+m2LX9iq+JC1064cSFprJY4jL1jCXTcpnfER5HYE2l/4EfWSGzkPa/ZDBmYI0ZOEj5VHV/eKnPGkHuOg==", + "dev": true, + "license": "MIT" + }, "node_modules/has-bigints": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", @@ -5025,10 +6597,11 @@ } }, "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -5063,24 +6636,130 @@ "node": ">= 0.4" } }, - "node_modules/htmlparser2": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", - "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", + "node_modules/html-encoding-sniffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", + "integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-encoding": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/htmlparser2": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", + "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", + "dev": true, + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1", + "entities": "^4.4.0" + } + }, + "node_modules/htmlparser2/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/http-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/http-proxy-agent/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/http-proxy-agent/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/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/https-proxy-agent/node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/https-proxy-agent/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, - "funding": [ - "https://github.com/fb55/htmlparser2?sponsor=1", - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], - "dependencies": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3", - "domutils": "^3.0.1", - "entities": "^4.4.0" - } + "license": "MIT" }, "node_modules/iconv-lite": { "version": "0.6.3", @@ -5410,10 +7089,18 @@ "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.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-regex": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", @@ -5559,6 +7246,7 @@ "resolved": "https://registry.npmjs.org/isomorphic.js/-/isomorphic.js-0.2.5.tgz", "integrity": "sha512-PIeMbHqMt4DnUP3MA/Flc0HElYjMXArsw1qwJZcm9sqR8mq3l8NYizFMty0pWwE/tzIGH3EKK5+jes5mAr85yw==", "dev": true, + "license": "MIT", "funding": { "type": "GitHub Sponsors ❤", "url": "https://github.com/sponsors/dmonad" @@ -5580,6 +7268,88 @@ "node": ">= 0.4" } }, + "node_modules/jest-environment-jsdom": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-29.7.0.tgz", + "integrity": "sha512-k9iQbsf9OyOfdzWH8HDmrRT0gSIcX+FLNW7IQq94tFX0gynPwqDTW0Ho6iMVNjGz/nb+l/vW3dWM2bbLLpkbXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/jsdom": "^20.0.0", + "@types/node": "*", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0", + "jsdom": "^20.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "canvas": "^2.5.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/jest-message-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", + "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.6.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-mock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", + "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -5587,10 +7357,11 @@ "dev": true }, "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==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", "dev": true, + "license": "MIT", "dependencies": { "argparse": "^2.0.1" }, @@ -5598,6 +7369,89 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/jsdom": { + "version": "20.0.3", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-20.0.3.tgz", + "integrity": "sha512-SYhBvTh89tTfCD/CRdSOm13mOBa42iTaTyfyEWBdKcGdPxPtLFBXuHR8XHb33YNYaP+lLbmSvBTsnoesCNJEsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "abab": "^2.0.6", + "acorn": "^8.8.1", + "acorn-globals": "^7.0.0", + "cssom": "^0.5.0", + "cssstyle": "^2.3.0", + "data-urls": "^3.0.2", + "decimal.js": "^10.4.2", + "domexception": "^4.0.0", + "escodegen": "^2.0.0", + "form-data": "^4.0.0", + "html-encoding-sniffer": "^3.0.0", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.1", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.2", + "parse5": "^7.1.1", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^4.1.2", + "w3c-xmlserializer": "^4.0.0", + "webidl-conversions": "^7.0.0", + "whatwg-encoding": "^2.0.0", + "whatwg-mimetype": "^3.0.0", + "whatwg-url": "^11.0.0", + "ws": "^8.11.0", + "xml-name-validator": "^4.0.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "canvas": "^2.5.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/jsdom/node_modules/tr46": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", + "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/jsdom/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/jsdom/node_modules/whatwg-url": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz", + "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tr46": "^3.0.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/json-buffer": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", @@ -5615,6 +7469,7 @@ "resolved": "https://registry.npmjs.org/json-schema-compare/-/json-schema-compare-0.2.2.tgz", "integrity": "sha512-c4WYmDKyJXhs7WWvAWm3uIYnfyWFoIp+JEoX34rctVvEkMYCPGhXtvmFFXiffBbxfZsvQ0RNnV5H7GvDF5HCqQ==", "dev": true, + "license": "MIT", "dependencies": { "lodash": "^4.17.4" } @@ -5624,6 +7479,7 @@ "resolved": "https://registry.npmjs.org/json-schema-merge-allof/-/json-schema-merge-allof-0.8.1.tgz", "integrity": "sha512-CTUKmIlPJbsWfzRRnOXz+0MjIqvnleIXwFTzz+t9T86HnYX/Rozria6ZVGLktAU9e+NygNljveP+yxqtQp/Q4w==", "dev": true, + "license": "MIT", "dependencies": { "compute-lcm": "^1.1.2", "json-schema-compare": "^0.2.2", @@ -5637,7 +7493,8 @@ "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 + "dev": true, + "license": "MIT" }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", @@ -5646,16 +7503,18 @@ "dev": true }, "node_modules/json-stringify-pretty-compact": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/json-stringify-pretty-compact/-/json-stringify-pretty-compact-3.0.0.tgz", - "integrity": "sha512-Rc2suX5meI0S3bfdZuA7JMFBGkJ875ApfVyq2WHELjBiiG22My/l7/8zPpH/CfFVQHuVLd8NLR0nv6vi0BYYKA==", - "dev": true + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/json-stringify-pretty-compact/-/json-stringify-pretty-compact-4.0.0.tgz", + "integrity": "sha512-3CNZ2DnrpByG9Nqj6Xo8vqbjT4F6N+tb4Gb28ESAZjYZ5yqvmc56J+/kuIwkaAMOyblTQhUW7PxMkUb8Q36N3Q==", + "dev": true, + "license": "MIT" }, "node_modules/json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true, + "license": "MIT", "bin": { "json5": "lib/cli.js" }, @@ -5680,6 +7539,7 @@ "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-5.0.1.tgz", "integrity": "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -5699,6 +7559,33 @@ "node": ">=4.0" } }, + "node_modules/katex": { + "version": "0.16.25", + "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.25.tgz", + "integrity": "sha512-woHRUZ/iF23GBP1dkDQMh1QBad9dmr8/PAwNA54VrSOVYgI12MAcE14TqnDdQOdzyEonGzMepYnqBMYdsoAr8Q==", + "dev": true, + "funding": [ + "https://opencollective.com/katex", + "https://github.com/sponsors/katex" + ], + "license": "MIT", + "dependencies": { + "commander": "^8.3.0" + }, + "bin": { + "katex": "cli.js" + } + }, + "node_modules/katex/node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", @@ -5708,6 +7595,43 @@ "json-buffer": "3.0.1" } }, + "node_modules/khroma": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/khroma/-/khroma-2.1.0.tgz", + "integrity": "sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw==", + "dev": true + }, + "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/langium": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/langium/-/langium-3.3.1.tgz", + "integrity": "sha512-QJv/h939gDpvT+9SiLVlY7tZC3xB2qK57v0J04Sh9wpMb6MP1q8gB21L3WIo8T5P1MSMg3Ep14L7KkDCFG3y4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "chevrotain": "~11.0.3", + "chevrotain-allstar": "~0.3.0", + "vscode-languageserver": "~9.0.1", + "vscode-languageserver-textdocument": "~1.0.11", + "vscode-uri": "~3.0.8" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/layout-base": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/layout-base/-/layout-base-1.0.2.tgz", + "integrity": "sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg==", + "dev": true, + "license": "MIT" + }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -5722,10 +7646,11 @@ } }, "node_modules/lib0": { - "version": "0.2.98", - "resolved": "https://registry.npmjs.org/lib0/-/lib0-0.2.98.tgz", - "integrity": "sha512-XteTiNO0qEXqqweWx+b21p/fBnNHUA1NwAtJNJek1oPrewEZs2uiT4gWivHKr9GqCjDPAhchz0UQO8NwU3bBNA==", + "version": "0.2.114", + "resolved": "https://registry.npmjs.org/lib0/-/lib0-0.2.114.tgz", + "integrity": "sha512-gcxmNFzA4hv8UYi8j43uPlQ7CGcyMJ2KQb5kZASw6SnAKAf10hK12i2fjrS3Cl/ugZa5Ui6WwIu1/6MIXiHttQ==", "dev": true, + "license": "MIT", "dependencies": { "isomorphic.js": "^0.2.4" }, @@ -5767,6 +7692,24 @@ "node": ">=6" } }, + "node_modules/local-pkg": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-1.1.2.tgz", + "integrity": "sha512-arhlxbFRmoQHl33a0Zkle/YWlmNwoyt6QNZEIJcqNbdrsix5Lvc4HyyI3EnwxTYlZYc32EbYrQ8SzEZ7dqgg9A==", + "dev": true, + "license": "MIT", + "dependencies": { + "mlly": "^1.7.4", + "pkg-types": "^2.3.0", + "quansync": "^0.2.11" + }, + "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", @@ -5786,19 +7729,22 @@ "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/lodash-es": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/lodash.escape": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/lodash.escape/-/lodash.escape-4.0.1.tgz", "integrity": "sha512-nXEOnb/jK9g0DYMr1/Xvq6l5xMD7GDG55+GSYIYmS0G4tBk/hURD4JR9WCavs04t33WmJx9kCyp9vJ+mr4BOUw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/lodash.merge": { "version": "4.6.2", @@ -5810,7 +7756,8 @@ "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz", "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/loose-envify": { "version": "1.4.0", @@ -5825,15 +7772,68 @@ } }, "node_modules/markdown-to-jsx": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/markdown-to-jsx/-/markdown-to-jsx-7.7.0.tgz", - "integrity": "sha512-130nIMbJY+woOQJ11xTqEtYko60t6EpNkZuqjKMferL3udtob3nRfzXOdsiA26NPemiR7w/hR8M3/B9yiYPGZg==", + "version": "7.7.17", + "resolved": "https://registry.npmjs.org/markdown-to-jsx/-/markdown-to-jsx-7.7.17.tgz", + "integrity": "sha512-7mG/1feQ0TX5I7YyMZVDgCC/y2I3CiEhIRQIhyov9nGBP5eoVrOXXHuL5ZP8GRfxVZKRiXWJgwXkb9It+nQZfQ==", "dev": true, + "license": "MIT", "engines": { "node": ">= 10" }, "peerDependencies": { "react": ">= 0.14.0" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + } + } + }, + "node_modules/marked": { + "version": "16.4.2", + "resolved": "https://registry.npmjs.org/marked/-/marked-16.4.2.tgz", + "integrity": "sha512-TI3V8YYWvkVf3KJe1dRkpnjs68JUPyEa5vjKrp1XEEJUAOaQc+Qj+L1qWbPd0SJuAdQkFU0h73sXXqwDYxsiDA==", + "dev": true, + "license": "MIT", + "peer": true, + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 20" + } + }, + "node_modules/marked-gfm-heading-id": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/marked-gfm-heading-id/-/marked-gfm-heading-id-4.1.3.tgz", + "integrity": "sha512-aR0i63LmFbuxU/gAgrgz1Ir+8HK6zAIFXMlckeKHpV+qKbYaOP95L4Ux5Gi+sKmCZU5qnN2rdKpvpb7PnUBIWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "github-slugger": "^2.0.0" + }, + "peerDependencies": { + "marked": ">=13 <18" + } + }, + "node_modules/marked-mangle": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/marked-mangle/-/marked-mangle-1.1.12.tgz", + "integrity": "sha512-bRrqNcfU9v3iRECb7YPvA+/xKZMjHojd9R92YwHbFjdPQ+Wc7vozkbGKAv4U8AUl798mNUuY3DTBQkedsV3TeQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "marked": ">=4 <18" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" } }, "node_modules/merge2": { @@ -5845,6 +7845,36 @@ "node": ">= 8" } }, + "node_modules/mermaid": { + "version": "11.12.1", + "resolved": "https://registry.npmjs.org/mermaid/-/mermaid-11.12.1.tgz", + "integrity": "sha512-UlIZrRariB11TY1RtTgUWp65tphtBv4CSq7vyS2ZZ2TgoMjs2nloq+wFqxiwcxlhHUvs7DPGgMjs2aeQxz5h9g==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@braintree/sanitize-url": "^7.1.1", + "@iconify/utils": "^3.0.1", + "@mermaid-js/parser": "^0.6.3", + "@types/d3": "^7.4.3", + "cytoscape": "^3.29.3", + "cytoscape-cose-bilkent": "^4.1.0", + "cytoscape-fcose": "^2.2.0", + "d3": "^7.9.0", + "d3-sankey": "^0.12.3", + "dagre-d3-es": "7.0.13", + "dayjs": "^1.11.18", + "dompurify": "^3.2.5", + "katex": "^0.16.22", + "khroma": "^2.1.0", + "lodash-es": "^4.17.21", + "marked": "^16.2.1", + "roughjs": "^4.6.6", + "stylis": "^4.3.6", + "ts-dedent": "^2.2.0", + "uuid": "^11.1.0" + } + }, "node_modules/micromatch": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", @@ -5858,6 +7888,29 @@ "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/minimatch": { "version": "9.0.3", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", @@ -5882,6 +7935,38 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/mlly": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.8.0.tgz", + "integrity": "sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.15.0", + "pathe": "^2.0.3", + "pkg-types": "^1.3.1", + "ufo": "^1.6.1" + } + }, + "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.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", @@ -5889,9 +7974,9 @@ "dev": true }, "node_modules/nanoid": { - "version": "3.3.8", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", - "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", "dev": true, "funding": [ { @@ -5934,6 +8019,13 @@ } } }, + "node_modules/nwsapi": { + "version": "2.2.22", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.22.tgz", + "integrity": "sha512-ujSMe1OWVn55euT1ihwCI1ZcAaAU3nxUiDwfDQldc51ZXaB9m2AyOn6/jh1BLe2t/G8xd6uKG1UBF2aZJeg2SQ==", + "dev": true, + "license": "MIT" + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -6110,6 +8202,13 @@ "node": ">=6" } }, + "node_modules/package-manager-detector": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/package-manager-detector/-/package-manager-detector-1.5.0.tgz", + "integrity": "sha512-uBj69dVlYe/+wxj8JOpr97XfsxH/eumMt6HqjNTmJDf/6NO9s+0uxeOneIz3AsPt2m6y9PqzDzd3ATcU17MNfw==", + "dev": true, + "license": "MIT" + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -6139,7 +8238,21 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/parse-srcset/-/parse-srcset-1.0.2.tgz", "integrity": "sha512-/2qh0lav6CmI15FzA3i/2Bzk2zCgQhGMkvhOhKNcBVQ1ldgpbfiNTVslmooUmWJcADi1f1kIeynbDRVzNlfR6Q==", - "dev": true + "dev": true, + "license": "MIT" + }, + "node_modules/parse5": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", + "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } }, "node_modules/path": { "version": "0.12.7", @@ -6155,7 +8268,15 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", "integrity": "sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==", - "dev": true + "dev": true, + "license": "MIT" + }, + "node_modules/path-data-parser": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/path-data-parser/-/path-data-parser-0.1.0.tgz", + "integrity": "sha512-NOnmBpt5Y2RWbuv0LMzsayp3lVylAHLPUTut412ZA3l+C4uw4ZVkQbjShYCQ8TCpUMdPapr4YjUqLYD6v68j+w==", + "dev": true, + "license": "MIT" }, "node_modules/path-exists": { "version": "4.0.0", @@ -6199,11 +8320,19 @@ "node": ">=8" } }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/picomatch": { "version": "2.3.1", @@ -6300,13 +8429,26 @@ "node": ">=4" } }, + "node_modules/pkg-types": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.3.0.tgz", + "integrity": "sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==", + "dev": true, + "license": "MIT", + "dependencies": { + "confbox": "^0.2.2", + "exsolve": "^1.0.7", + "pathe": "^2.0.3" + } + }, "node_modules/playwright": { - "version": "1.49.0", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.49.0.tgz", - "integrity": "sha512-eKpmys0UFDnfNb3vfsf8Vx2LEOtflgRebl0Im2eQQnYMA4Aqd+Zw8bEOB+7ZKvN76901mRnqdsiOGKxzVTbi7A==", + "version": "1.57.0", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.57.0.tgz", + "integrity": "sha512-ilYQj1s8sr2ppEJ2YVadYBN0Mb3mdo9J0wQ+UuDhzYqURwSoW4n1Xs5vs7ORwgDGmyEh33tRMeS8KhdkMoLXQw==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.49.0" + "playwright-core": "1.57.0" }, "bin": { "playwright": "cli.js" @@ -6319,10 +8461,11 @@ } }, "node_modules/playwright-core": { - "version": "1.49.0", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.49.0.tgz", - "integrity": "sha512-R+3KKTQF3npy5GTiKH/T+kdhoJfJojjHESR1YEWhYuEKRVfVaxH3+4+GvXE5xyCngCxhxnykk0Vlah9v8fs3jA==", + "version": "1.57.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.57.0.tgz", + "integrity": "sha512-agTcKlMw/mjBWOnD6kFZttAAGHgi/Nw0CZ2o6JqWSbMlI219lAFLZZCyqByTsvVAJq5XA5H8cA6PrvBRpBWEuQ==", "dev": true, + "license": "Apache-2.0", "bin": { "playwright-core": "cli.js" }, @@ -6330,6 +8473,24 @@ "node": ">=18" } }, + "node_modules/points-on-curve": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/points-on-curve/-/points-on-curve-0.2.0.tgz", + "integrity": "sha512-0mYKnYYe9ZcqMCWhUjItv/oHjvgEsfKvnUTg8sAtnHr3GVy7rGkXCb6d5cSyqrWqL4k81b9CPg3urd+T7aop3A==", + "dev": true, + "license": "MIT" + }, + "node_modules/points-on-path": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/points-on-path/-/points-on-path-0.2.1.tgz", + "integrity": "sha512-25ClnWWuw7JbWZcgqY/gJ4FQWadKxGWk+3kR/7kD0tCaDtPPMj7oHu2ToLaVhfpnHrZzYby2w6tUA0eOIuUg8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-data-parser": "0.1.0", + "points-on-curve": "0.2.0" + } + }, "node_modules/possible-typed-array-names": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz", @@ -6340,9 +8501,9 @@ } }, "node_modules/postcss": { - "version": "8.4.49", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz", - "integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==", + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", "dev": true, "funding": [ { @@ -6358,8 +8519,9 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { - "nanoid": "^3.3.7", + "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, @@ -6376,6 +8538,34 @@ "node": ">= 0.8.0" } }, + "node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "node_modules/process": { "version": "0.11.10", "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", @@ -6408,6 +8598,19 @@ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", "dev": true }, + "node_modules/psl": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz", + "integrity": "sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "funding": { + "url": "https://github.com/sponsors/lupomontero" + } + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -6417,11 +8620,29 @@ "node": ">=6" } }, + "node_modules/quansync": { + "version": "0.2.11", + "resolved": "https://registry.npmjs.org/quansync/-/quansync-0.2.11.tgz", + "integrity": "sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/antfu" + }, + { + "type": "individual", + "url": "https://github.com/sponsors/sxzz" + } + ], + "license": "MIT" + }, "node_modules/querystringify": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/queue-microtask": { "version": "1.2.3", @@ -6448,6 +8669,8 @@ "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", "dev": true, + "license": "MIT", + "peer": true, "dependencies": { "loose-envify": "^1.1.0" }, @@ -6460,6 +8683,7 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", "dev": true, + "license": "MIT", "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.2" @@ -6472,7 +8696,8 @@ "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/readable-stream": { "version": "2.3.8", @@ -6515,6 +8740,7 @@ "resolved": "https://registry.npmjs.org/regexp-match-indices/-/regexp-match-indices-1.0.2.tgz", "integrity": "sha512-DwZuAkt8NF5mKwGGER1EGh2PRqyvhRhhLviH+R8y8dIuaQROlUfXjt4s9ZTXstIsSkptf06BSvwcEmmfheJJWQ==", "dev": true, + "license": "Apache-2.0", "dependencies": { "regexp-tree": "^0.1.11" } @@ -6524,6 +8750,7 @@ "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" } @@ -6572,6 +8799,7 @@ "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" } @@ -6580,7 +8808,8 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/resolve": { "version": "1.22.8", @@ -6641,6 +8870,19 @@ "dev": true, "license": "Unlicense" }, + "node_modules/roughjs": { + "version": "4.6.6", + "resolved": "https://registry.npmjs.org/roughjs/-/roughjs-4.6.6.tgz", + "integrity": "sha512-ZUz/69+SYpFN/g/lUlo2FXcIjRkSu3nDarreVdGGndHEBJ6cXPdKguS8JGxwj5HA5xIbVKSmLgr5b3AWxtRfvQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "hachure-fill": "^0.5.2", + "path-data-parser": "^0.1.0", + "points-on-curve": "^0.2.0", + "points-on-path": "^0.2.1" + } + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -6730,6 +8972,7 @@ "resolved": "https://registry.npmjs.org/sanitize-html/-/sanitize-html-2.12.1.tgz", "integrity": "sha512-Plh+JAn0UVDpBRP/xEjsk+xDCoOvMBwQUf/K+/cBAVuTbtX8bj2VB7S1sL1dssVpykqp0/KPSesHrqXtokVBpA==", "dev": true, + "license": "MIT", "dependencies": { "deepmerge": "^4.2.2", "escape-string-regexp": "^4.0.0", @@ -6739,11 +8982,25 @@ "postcss": "^8.3.11" } }, + "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/scheduler": { "version": "0.23.2", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", "dev": true, + "license": "MIT", "dependencies": { "loose-envify": "^1.1.0" } @@ -6840,15 +9097,50 @@ "node": ">=8" } }, + "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", + "optional": true, + "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/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/stack-utils/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/standard": { "version": "17.1.2", "resolved": "https://registry.npmjs.org/standard/-/standard-17.1.2.tgz", @@ -7057,10 +9349,18 @@ } }, "node_modules/style-mod": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.2.tgz", - "integrity": "sha512-wnD1HyVqpJUI2+eKZ+eo1UwghftP6yuFheBqqe+bWCotBjC2K1YnteJILRMs3SM4V/0dLEW1SC27MWP5y+mwmw==", - "dev": true + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.1.3.tgz", + "integrity": "sha512-i/n8VsZydrugj3Iuzll8+x/00GH2vnYsk1eomD8QiRrSAeW6ItbCQDtfXCeJHd0iwiNagqjQkvpvREEPtW3IoQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/stylis": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.6.tgz", + "integrity": "sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ==", + "dev": true, + "license": "MIT" }, "node_modules/supports-color": { "version": "7.2.0", @@ -7086,6 +9386,13 @@ "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/systeminformation": { "version": "5.23.25", "resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-5.23.25.tgz", @@ -7117,7 +9424,8 @@ "version": "5.3.3", "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-5.3.3.tgz", "integrity": "sha512-QD9qKY3StfbZqWOPLp0++pOrAVb/HbUi5xCc8cUo4XjP19808oaMiDzn0leBY5mCespIBM0CIZePzZjgzR83kA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/text-table": { "version": "0.2.0", @@ -7125,6 +9433,16 @@ "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true }, + "node_modules/tinyexec": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.2.tgz", + "integrity": "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -7159,6 +9477,32 @@ "dev": true, "license": "MIT" }, + "node_modules/tough-cookie": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz", + "integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.2.0", + "url-parse": "^1.5.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tough-cookie/node_modules/universalify": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", + "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, "node_modules/tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", @@ -7178,6 +9522,16 @@ "typescript": ">=4.2.0" } }, + "node_modules/ts-dedent": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/ts-dedent/-/ts-dedent-2.2.0.tgz", + "integrity": "sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.10" + } + }, "node_modules/tsconfig-paths": { "version": "3.15.0", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", @@ -7206,7 +9560,8 @@ "version": "1.14.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true + "dev": true, + "license": "0BSD" }, "node_modules/type-check": { "version": "0.4.0", @@ -7220,6 +9575,16 @@ "node": ">= 0.8.0" } }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/type-fest": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", @@ -7310,6 +9675,7 @@ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", "dev": true, + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -7323,6 +9689,7 @@ "resolved": "https://registry.npmjs.org/typestyle/-/typestyle-2.4.0.tgz", "integrity": "sha512-/d1BL6Qi+YlMLEydnUEB8KL/CAjAN8cyt3/UyGnOyBrWf7bLGcR/6yhmsaUstO2IcYwZfagjE7AIzuI2vUW9mg==", "dev": true, + "license": "MIT", "dependencies": { "csstype": "3.0.10", "free-style": "3.1.0" @@ -7332,7 +9699,15 @@ "version": "3.0.10", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.10.tgz", "integrity": "sha512-2u44ZG2OcNUO9HDp/Jl8C07x6pU/eTR3ncV91SiK3dhG9TWvRVsCoJw14Ckx5DgWkzGA3waZWO3d7pgqpUI/XA==", - "dev": true + "dev": true, + "license": "MIT" + }, + "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/unbox-primitive": { "version": "1.0.2", @@ -7378,6 +9753,7 @@ "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", "dev": true, + "license": "MIT", "dependencies": { "querystringify": "^2.1.1", "requires-port": "^1.0.0" @@ -7404,11 +9780,26 @@ "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", "dev": true }, + "node_modules/uuid": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz", + "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==", + "dev": true, + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/esm/bin/uuid" + } + }, "node_modules/validate.io-array": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/validate.io-array/-/validate.io-array-1.0.6.tgz", "integrity": "sha512-DeOy7CnPEziggrOO5CZhVKJw6S3Yi7e9e65R1Nl/RTN1vTQKnzjfvks0/8kQ40FP/dsjRAOd4hxmJ7uLa6vxkg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/validate.io-function": { "version": "1.0.2", @@ -7529,13 +9920,14 @@ "dev": true }, "node_modules/vega-expression": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/vega-expression/-/vega-expression-5.1.1.tgz", - "integrity": "sha512-zv9L1Hm0KHE9M7mldHyz8sXbGu3KmC0Cdk7qfHkcTNS75Jpsem6jkbu6ZAwx5cNUeW91AxUQOu77r4mygq2wUQ==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/vega-expression/-/vega-expression-5.1.2.tgz", + "integrity": "sha512-fFeDTh4UtOxlZWL54jf1ZqJHinyerWq/ROiqrQxqLkNJRJ86RmxYTgXwt65UoZ/l4VUv9eAd2qoJeDEf610Umw==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "@types/estree": "^1.0.0", - "vega-util": "^1.17.2" + "vega-util": "^1.17.3" } }, "node_modules/vega-force": { @@ -7585,14 +9977,14 @@ } }, "node_modules/vega-functions/node_modules/vega-expression": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/vega-expression/-/vega-expression-5.2.0.tgz", - "integrity": "sha512-WRMa4ny3iZIVAzDlBh3ipY2QUuLk2hnJJbfbncPgvTF7BUgbIbKq947z+JicWksYbokl8n1JHXJoqi3XvpG0Zw==", + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/vega-expression/-/vega-expression-5.2.1.tgz", + "integrity": "sha512-9KKbI2q9qTI55NSjD/dVWg3aeCtw+gwyWCiLMM47ha6iXrAN9pQ+EKRJfxOHuoDfCTlJJTaUfnnXgbqm0HEszg==", "dev": true, "license": "BSD-3-Clause", "dependencies": { "@types/estree": "^1.0.0", - "vega-util": "^1.17.3" + "vega-util": "^1.17.4" } }, "node_modules/vega-geo": { @@ -7638,13 +10030,14 @@ } }, "node_modules/vega-lite": { - "version": "5.21.0", - "resolved": "https://registry.npmjs.org/vega-lite/-/vega-lite-5.21.0.tgz", - "integrity": "sha512-hNxM9nuMqpI1vkUOhEx6ewEf23WWLmJxSFJ4TA86AW43ixJyqcLV+iSCO0NipuVTE0rlDcc2e8joSewWyOlEwA==", + "version": "5.23.0", + "resolved": "https://registry.npmjs.org/vega-lite/-/vega-lite-5.23.0.tgz", + "integrity": "sha512-l4J6+AWE3DIjvovEoHl2LdtCUkfm4zs8Xxx7INwZEAv+XVb6kR6vIN1gt3t2gN2gs/y4DYTs/RPoTeYAuEg6mA==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "json-stringify-pretty-compact": "~3.0.0", - "tslib": "~2.6.3", + "json-stringify-pretty-compact": "~4.0.0", + "tslib": "~2.8.1", "vega-event-selector": "~3.0.1", "vega-expression": "~5.1.1", "vega-util": "~1.17.2", @@ -7664,10 +10057,11 @@ } }, "node_modules/vega-lite/node_modules/tslib": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", - "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==", - "dev": true + "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/vega-loader": { "version": "4.5.3", @@ -7776,14 +10170,14 @@ } }, "node_modules/vega-selections/node_modules/vega-expression": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/vega-expression/-/vega-expression-5.2.0.tgz", - "integrity": "sha512-WRMa4ny3iZIVAzDlBh3ipY2QUuLk2hnJJbfbncPgvTF7BUgbIbKq947z+JicWksYbokl8n1JHXJoqi3XvpG0Zw==", + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/vega-expression/-/vega-expression-5.2.1.tgz", + "integrity": "sha512-9KKbI2q9qTI55NSjD/dVWg3aeCtw+gwyWCiLMM47ha6iXrAN9pQ+EKRJfxOHuoDfCTlJJTaUfnnXgbqm0HEszg==", "dev": true, "license": "BSD-3-Clause", "dependencies": { "@types/estree": "^1.0.0", - "vega-util": "^1.17.3" + "vega-util": "^1.17.4" } }, "node_modules/vega-statistics": { @@ -7835,20 +10229,20 @@ } }, "node_modules/vega-typings/node_modules/vega-expression": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/vega-expression/-/vega-expression-5.2.0.tgz", - "integrity": "sha512-WRMa4ny3iZIVAzDlBh3ipY2QUuLk2hnJJbfbncPgvTF7BUgbIbKq947z+JicWksYbokl8n1JHXJoqi3XvpG0Zw==", + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/vega-expression/-/vega-expression-5.2.1.tgz", + "integrity": "sha512-9KKbI2q9qTI55NSjD/dVWg3aeCtw+gwyWCiLMM47ha6iXrAN9pQ+EKRJfxOHuoDfCTlJJTaUfnnXgbqm0HEszg==", "dev": true, "license": "BSD-3-Clause", "dependencies": { "@types/estree": "^1.0.0", - "vega-util": "^1.17.3" + "vega-util": "^1.17.4" } }, "node_modules/vega-util": { - "version": "1.17.3", - "resolved": "https://registry.npmjs.org/vega-util/-/vega-util-1.17.3.tgz", - "integrity": "sha512-nSNpZLUrRvFo46M5OK4O6x6f08WD1yOcEzHNlqivF+sDLSsVpstaF6fdJYwrbf/debFi2L9Tkp4gZQtssup9iQ==", + "version": "1.17.4", + "resolved": "https://registry.npmjs.org/vega-util/-/vega-util-1.17.4.tgz", + "integrity": "sha512-+y3ZW7dEqM8Ck+KRsd+jkMfxfE7MrQxUyIpNjkfhIpGEreym+aTn7XUw1DKXqclr8mqTQvbilPo16B3lnBr0wA==", "dev": true, "license": "BSD-3-Clause" }, @@ -7908,14 +10302,14 @@ } }, "node_modules/vega/node_modules/vega-expression": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/vega-expression/-/vega-expression-5.2.0.tgz", - "integrity": "sha512-WRMa4ny3iZIVAzDlBh3ipY2QUuLk2hnJJbfbncPgvTF7BUgbIbKq947z+JicWksYbokl8n1JHXJoqi3XvpG0Zw==", + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/vega-expression/-/vega-expression-5.2.1.tgz", + "integrity": "sha512-9KKbI2q9qTI55NSjD/dVWg3aeCtw+gwyWCiLMM47ha6iXrAN9pQ+EKRJfxOHuoDfCTlJJTaUfnnXgbqm0HEszg==", "dev": true, "license": "BSD-3-Clause", "dependencies": { "@types/estree": "^1.0.0", - "vega-util": "^1.17.3" + "vega-util": "^1.17.4" } }, "node_modules/version-guard": { @@ -7932,15 +10326,30 @@ "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-6.0.0.tgz", "integrity": "sha512-wnJA4BnEjOSyFMvjZdpiOwhSq9uDoK8e/kpRJDTaMYzwlkrhG1fwDIZI94CLsLzlCK5cIbMMtFlJlfR57Lavmg==", "dev": true, + "license": "MIT", "engines": { "node": ">=8.0.0 || >=10.0.0" } }, + "node_modules/vscode-languageserver": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-9.0.1.tgz", + "integrity": "sha512-woByF3PDpkHFUreUa7Hos7+pUWdeWMXRd26+ZX2A8cFx6v/JPTtd4/uN0/jB6XQHYaOlHbio03NTHCqrgG5n7g==", + "dev": true, + "license": "MIT", + "dependencies": { + "vscode-languageserver-protocol": "3.17.5" + }, + "bin": { + "installServerIntoExtension": "bin/installServerIntoExtension" + } + }, "node_modules/vscode-languageserver-protocol": { "version": "3.17.5", "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.5.tgz", "integrity": "sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==", "dev": true, + "license": "MIT", "dependencies": { "vscode-jsonrpc": "8.2.0", "vscode-languageserver-types": "3.17.5" @@ -7951,21 +10360,38 @@ "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz", "integrity": "sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==", "dev": true, + "license": "MIT", "engines": { "node": ">=14.0.0" } }, + "node_modules/vscode-languageserver-textdocument": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.12.tgz", + "integrity": "sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA==", + "dev": true, + "license": "MIT" + }, "node_modules/vscode-languageserver-types": { "version": "3.17.5", "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz", "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==", - "dev": true + "dev": true, + "license": "MIT" + }, + "node_modules/vscode-uri": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.8.tgz", + "integrity": "sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==", + "dev": true, + "license": "MIT" }, "node_modules/vscode-ws-jsonrpc": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/vscode-ws-jsonrpc/-/vscode-ws-jsonrpc-1.0.2.tgz", "integrity": "sha512-09OpRC0RcqZs4DleJRgs+R+7gQkwb4tgvsL43lzVZwW4N5NO3H/9sLNeKPBt83k7WyA8qBZjrzM6X7tKFpFrjQ==", "dev": true, + "license": "MIT", "dependencies": { "vscode-jsonrpc": "^8.0.2" } @@ -7975,6 +10401,7 @@ "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.1.tgz", "integrity": "sha512-kdjOSJ2lLIn7r1rtrMbbNCHjyMPfRnowdKjBQ+mGq6NAW5QY2bEZC/khaC5OR8svbbjvLEaIXkOq45e2X9BIbQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=14.0.0" } @@ -7983,7 +10410,21 @@ "version": "2.2.8", "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz", "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==", - "dev": true + "dev": true, + "license": "MIT" + }, + "node_modules/w3c-xmlserializer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz", + "integrity": "sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw==", + "dev": true, + "license": "MIT", + "dependencies": { + "xml-name-validator": "^4.0.0" + }, + "engines": { + "node": ">=14" + } }, "node_modules/webidl-conversions": { "version": "3.0.1", @@ -7992,6 +10433,29 @@ "dev": true, "license": "BSD-2-Clause" }, + "node_modules/whatwg-encoding": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", + "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-mimetype": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz", + "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, "node_modules/whatwg-url": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", @@ -8136,10 +10600,11 @@ "dev": true }, "node_modules/ws": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", - "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", + "version": "8.18.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", + "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", "dev": true, + "license": "MIT", "engines": { "node": ">=10.0.0" }, @@ -8165,11 +10630,29 @@ "node": ">=8" } }, + "node_modules/xml-name-validator": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz", + "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12" + } + }, + "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/y-protocols": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/y-protocols/-/y-protocols-1.0.6.tgz", "integrity": "sha512-vHRF2L6iT3rwj1jub/K5tYcTT/mEYDUppgNPXwp8fmLpui9f7Yeq3OEtTLVF012j39QnV+KEQpNqoN7CWU7Y9Q==", "dev": true, + "license": "MIT", "dependencies": { "lib0": "^0.2.85" }, @@ -8222,12 +10705,14 @@ } }, "node_modules/yjs": { - "version": "13.6.20", - "resolved": "https://registry.npmjs.org/yjs/-/yjs-13.6.20.tgz", - "integrity": "sha512-Z2YZI+SYqK7XdWlloI3lhMiKnCdFCVC4PchpdO+mCYwtiTwncjUbnRK9R1JmkNfdmHyDXuWN3ibJAt0wsqTbLQ==", + "version": "13.6.27", + "resolved": "https://registry.npmjs.org/yjs/-/yjs-13.6.27.tgz", + "integrity": "sha512-OIDwaflOaq4wC6YlPBy2L6ceKeKuF7DeTxx+jPzv1FHn9tCZ0ZwSRnUBxD05E3yed46fv/FWJbvR+Ud7x0L7zw==", "dev": true, + "license": "MIT", + "peer": true, "dependencies": { - "lib0": "^0.2.98" + "lib0": "^0.2.99" }, "engines": { "node": ">=16.0.0", diff --git a/tests/e2e/tests/ui_tests.test.ts b/tests/e2e/tests/ui_tests.test.ts index aa1c417bd..f2d391bd8 100644 --- a/tests/e2e/tests/ui_tests.test.ts +++ b/tests/e2e/tests/ui_tests.test.ts @@ -1,25 +1,20 @@ -// Copyright 2023-2024 The MathWorks, Inc. +// Copyright 2023-2025 The MathWorks, Inc. import { expect, test } from '@jupyterlab/galata'; -import { Locator } from '@playwright/test'; test.describe('MATLAB File button', () => { - let MATLABFileButton: Locator; - test.beforeEach(async ({ page }) => { - await page.launcher.waitFor({ state: 'visible' }); - MATLABFileButton = page.getByText('MATLAB File', { exact: true }); - }); - test('is visible on Launcher', async ({ page }) => { + const MATLABFileButton = page.getByRole('button', { name: 'MATLAB File' }); await expect(MATLABFileButton).toBeVisible(); }); test('takes you to .m file', async ({ page }) => { + const MATLABFileButton = page.getByRole('button', { name: 'MATLAB File' }); await MATLABFileButton.click(); // Expect a new untitled file ending in .m to be made, with optional // digits at the end of untitled, i.e. untitled1.m untitled1232.m) - await expect(page).toHaveURL(/.*untitled(\d+)?.m/); + await page.waitForURL(/.*untitled(\d+)?.m/); // MATLAB language mode selected: await expect(page.locator('#jp-main-statusbar') diff --git a/tests/integration/utils/integration_test_utils.py b/tests/integration/utils/integration_test_utils.py index 40a1be08b..9f37daf2b 100644 --- a/tests/integration/utils/integration_test_utils.py +++ b/tests/integration/utils/integration_test_utils.py @@ -1,4 +1,4 @@ -# Copyright 2023-2024 The MathWorks, Inc. +# Copyright 2023-2025 The MathWorks, Inc. # Utility functions for integration testing of jupyter-matlab-proxy import asyncio @@ -24,7 +24,7 @@ def perform_basic_checks(): # Check if MATLAB is in the system path assert matlab_path is not None, "MATLAB is not in system path" - # Check if MATLAB verison is >= R2020b + # Check if MATLAB version is >= R2020b assert ( matlab_proxy.settings.get_matlab_version(matlab_path) >= "R2020b" ), "MATLAB version should be R2020b or later" @@ -83,35 +83,33 @@ async def wait_matlab_proxy_ready(matlab_proxy_url): from jupyter_matlab_kernel.mwi_comm_helpers import MWICommHelper - is_matlab_licensed = False - matlab_status = "down" start_time = time.time() loop = asyncio.get_event_loop() - matlab_proxy = MWICommHelper("", matlab_proxy_url, loop, loop, {}) - await matlab_proxy.connect() + comm_helper = MWICommHelper("", matlab_proxy_url, loop, loop, {}) + await comm_helper.connect() + matlab_proxy_status = await comm_helper.fetch_matlab_proxy_status() # Poll for matlab-proxy to be up - while matlab_status in ["down", "starting"] and ( - time.time() - start_time < MATLAB_STARTUP_TIMEOUT + while ( + matlab_proxy_status + and matlab_proxy_status.matlab_status in ["down", "starting"] + and (time.time() - start_time < MATLAB_STARTUP_TIMEOUT) + and not matlab_proxy_status.matlab_proxy_has_error ): time.sleep(1) try: - ( - is_matlab_licensed, - matlab_status, - _, - ) = await matlab_proxy.fetch_matlab_proxy_status() + matlab_proxy_status = await comm_helper.fetch_matlab_proxy_status() except Exception: # The network connection can be flaky while the # matlab-proxy server is booting. There can also be some # intermediate connection errors pass - assert is_matlab_licensed is True, "MATLAB is not licensed" + assert matlab_proxy_status.is_matlab_licensed is True, "MATLAB is not licensed" assert ( - matlab_status == "up" - ), f"matlab-proxy process did not start successfully\nMATLAB Status is '{matlab_status}'" - await matlab_proxy.disconnect() + matlab_proxy_status.matlab_status == "up" + ), f"matlab-proxy process did not start successfully\nMATLAB Status is '{matlab_proxy_status.matlab_status}'" + await comm_helper.disconnect() def get_random_free_port() -> str: @@ -184,13 +182,14 @@ def license_matlab_proxy(matlab_proxy_url): status_info, "Verify if Licensing is successful. This might fail if incorrect credentials are provided", ).to_be_visible(timeout=60000) - except: + except Exception as e: # Grab screenshots log_dir = "./" file_name = "licensing-screenshot-failed.png" file_path = os.path.join(log_dir, file_name) os.makedirs(log_dir, exist_ok=True) page.screenshot(path=file_path) + print("Exception: %s", str(e)) finally: browser.close() diff --git a/tests/unit/jupyter_matlab_kernel/comms/test_labextension.py b/tests/unit/jupyter_matlab_kernel/comms/test_labextension.py new file mode 100644 index 000000000..45493cfa9 --- /dev/null +++ b/tests/unit/jupyter_matlab_kernel/comms/test_labextension.py @@ -0,0 +1,208 @@ +# Copyright 2025 The MathWorks, Inc. + +import pytest +from jupyter_matlab_kernel.comms.labextension import ( + LabExtensionCommunication, +) + +from unittest.mock import call + + +@pytest.fixture +def mock_kernel(mocker): + """Create a mock kernel for testing.""" + kernel = mocker.MagicMock() + return kernel + + +@pytest.fixture +def mock_stream(mocker): + """Create a mock stream for testing.""" + stream = mocker.MagicMock() + return stream + + +@pytest.fixture +def mock_ident(mocker): + """Create a mock ident for testing.""" + ident = mocker.MagicMock() + return ident + + +@pytest.fixture +def mock_comm(mocker): + """Create a mock comm object for testing.""" + comm = mocker.MagicMock() + return comm + + +@pytest.fixture +def labext_comm(mock_kernel): + """Create a LabExtensionCommunication instance for testing.""" + return LabExtensionCommunication(mock_kernel) + + +def test_init(mock_kernel): + """Test that LabExtensionCommunication initializes correctly.""" + labext_comm = LabExtensionCommunication(mock_kernel) + + assert labext_comm.comms == {} + assert labext_comm.kernel is mock_kernel + assert labext_comm.log is mock_kernel.log + + +def test_comm_open_creates_comm( + labext_comm, mocker, mock_stream, mock_ident, mock_comm +): + """Test that comm_open creates a communication channel.""" + # Arrange + # Mock the Comm class + mock_comm_class = mocker.patch( + "jupyter_matlab_kernel.comms.labextension.labextension.Comm", + return_value=mock_comm, + ) + + test_comm_id = "test-comm-id" + test_target_name = "test-target" + msg = {"content": {"comm_id": test_comm_id, "target_name": test_target_name}} + + # Act + labext_comm.comm_open(mock_stream, mock_ident, msg) + + # Assert + mock_comm_class.assert_called_once_with( + comm_id=test_comm_id, primary=False, target_name=test_target_name + ) + + # Verify comm is set + assert labext_comm.comms[test_comm_id] is mock_comm + # Verify debug is called twice and with the right messages + assert labext_comm.log.debug.call_count == 2 + expected_calls = [ + call( + f"Received comm_open message with id: {test_comm_id} and target_name: {test_target_name}" + ), + call( + f"Successfully created communication channel with labextension on: {test_comm_id}" + ), + ] + labext_comm.log.debug.assert_has_calls(expected_calls, any_order=True) + + +@pytest.mark.asyncio +async def test_comm_msg_with_valid_comm( + labext_comm, mock_stream, mock_ident, mock_comm +): + """Test that comm_msg processes messages when comm is available.""" + # Arrange + comm_id = "test-comm-id" + labext_comm.comms[comm_id] = mock_comm + + test_action = "test-action" + test_data = {"key": "value"} + msg = { + "content": { + "comm_id": comm_id, + "data": {"action": test_action, "data": test_data}, + } + } + + # Act + await labext_comm.comm_msg(mock_stream, mock_ident, msg) + + # Assert + labext_comm.log.debug.assert_called_once_with( + f"Received action_type:{test_action} with data:{test_data} from the lab extension" + ) + + +def test_comm_close_with_valid_comm_id(labext_comm, mock_stream, mock_ident, mock_comm): + """Test that comm_close closes the correct communication channel.""" + # Arrange + # Set up a mock comm with matching ID + comm_id = "test-comm-id" + mock_comm.comm_id = comm_id + labext_comm.comms = {comm_id: mock_comm} + + msg = {"content": {"comm_id": comm_id}} + + # Act + labext_comm.comm_close(mock_stream, mock_ident, msg) + + # Assert + # Verify comm is set to None + assert labext_comm.comms == {} + + # Verify logging + labext_comm.log.info.assert_called_once_with(f"Comm closed with id: {comm_id}") + + +def test_comm_close_with_non_matching_comm_id( + labext_comm, mock_stream, mock_ident, mock_comm +): + """Test that comm_close warns when trying to close unknown comm.""" + # Arrange + # Set up a mock comm with different ID + mock_comm.comm_id = "different-comm-id" + comm_id = "test-comm-id" + different_comm_id = "different-comm-id" + labext_comm.comms = {different_comm_id: mock_comm} + + msg = {"content": {"comm_id": comm_id}} + + # Act + labext_comm.comm_close(mock_stream, mock_ident, msg) + + # Assert + # Verify comm is not changed + assert labext_comm.comms[different_comm_id] is mock_comm + + # Verify warning logging + labext_comm.log.debug.assert_called_once_with( + f"Attempted to close unknown comm_id: {comm_id}" + ) + + +def test_comm_close_with_no_comm(labext_comm, mock_stream, mock_ident): + """Test that comm_close warns when no comm exists.""" + # Arrange + # Ensure comms is empty + labext_comm.comms = {} + test_comm_id = "test-comm-id" + msg = {"content": {"comm_id": test_comm_id}} + + # Act + labext_comm.comm_close(mock_stream, mock_ident, msg) + + # Assert + # Verify comm remains None + assert labext_comm.comms == {} + + # Verify warning logging + labext_comm.log.debug.assert_called_once_with( + f"Attempted to close unknown comm_id: {test_comm_id}" + ) + + +@pytest.mark.asyncio +async def test_comm_msg_extracts_data_correctly( + labext_comm, mock_stream, mock_ident, mock_comm +): + """Test that comm_msg correctly extracts action and data from message.""" + # Arrange + comm_id = "test-comm-id" + labext_comm.comms = {comm_id: mock_comm} + action_type = "execute_code" + data = {"code": "x = 1 + 1", "cell_id": "abc123"} + + msg = { + "content": {"comm_id": comm_id, "data": {"action": action_type, "data": data}} + } + + # Call the method + await labext_comm.comm_msg(mock_stream, mock_ident, msg) + + # Verify logging with correct extracted data + labext_comm.log.debug.assert_called_once_with( + f"Received action_type:{action_type} with data:{data} from the lab extension" + ) diff --git a/tests/unit/jupyter_matlab_kernel/magics/test_matlab.py b/tests/unit/jupyter_matlab_kernel/magics/test_matlab.py new file mode 100644 index 000000000..e40efa1ce --- /dev/null +++ b/tests/unit/jupyter_matlab_kernel/magics/test_matlab.py @@ -0,0 +1,202 @@ +# Copyright 2025 The MathWorks, Inc. + +import pytest + +from jupyter_matlab_kernel.magics.help import help +from jupyter_matlab_kernel.magics.matlab import ( + CMD_INFO, + CMD_NEW_SESSION, + DEDICATED_SESSION_CONFIRMATION_MSG, + EXISTING_NEW_SESSION_ERROR, + get_kernel_info, + handle_new_matlab_session, + matlab, +) +from jupyter_matlab_kernel.mwi_exceptions import MagicError + + +def test_help_magic(): + magic_object = help([matlab.__name__]) + before_cell_executor = magic_object.before_cell_execute() + output = next(before_cell_executor) + expected_output = matlab.info_about_magic + assert expected_output in output["value"][0] + + +@pytest.mark.parametrize( + "parameters", + [ + pytest.param([], id="atleast one argument is required"), + pytest.param( + ["invalid"], + id="Invalid argument should throw exception", + ), + pytest.param( + [CMD_INFO, CMD_NEW_SESSION], + id="more than one parameter should throw exception", + ), + ], +) +def test_matlab_magic_exceptions(parameters): + magic_object = matlab(parameters) + before_cell_executor = magic_object.before_cell_execute() + with pytest.raises(MagicError): + next(before_cell_executor) + + +@pytest.mark.parametrize( + "parameters, parameter_pos, cursor_pos, expected_output", + [ + pytest.param( + ["n"], + 1, + 1, + {"new_session"}, + id="n as parameter with parameter and cursor position as 1", + ), + pytest.param( + [""], + 1, + 1, + {"new_session", "info"}, + id="no parameter with parameter and cursor position as 1", + ), + pytest.param( + ["in"], + 1, + 2, + {"info"}, + id="i as parameter with parameter position as 1 and cursor position as 2", + ), + pytest.param( + ["i"], + 2, + 1, + set([]), + id="t as parameter with parameter position as 2 and cursor position as 1", + ), + pytest.param( + ["magic"], + 1, + 4, + set([]), + id="magic as parameter with parameter position as 1 and cursor position as 4", + ), + ], +) +def test_do_complete_in_matlab_magic( + parameters, parameter_pos, cursor_pos, expected_output +): + magic_object = matlab() + output = magic_object.do_complete(parameters, parameter_pos, cursor_pos) + assert expected_output.issubset(set(output)) + + +async def test_new_session_in_matlab_magic_while_already_in_new_session(mocker): + """ + Test that an appropriate message is displayed when trying to create a new session while already in a new session. + + This test verifies that when a %%matlab magic command with new_session option is executed + while MATLAB is already assigned to the kernel in a new session, an appropriate error message + is displayed indicating that the notebook is already linked to a new MATLAB session. + """ + mock_kernel = mocker.MagicMock() + mock_kernel.is_matlab_assigned = True + mock_kernel.is_shared_matlab = False + output = [] + async for result in handle_new_matlab_session(mock_kernel): + output.append(result) + + assert output is not None + assert EXISTING_NEW_SESSION_ERROR in output[0]["value"][0] + + +async def test_new_session_in_matlab_magic_while_already_in_shared_session(mocker): + """ + Test that an exception is raised when trying to switch from shared session to new session. + + This test verifies that when a %%matlab magic command with new_session option is executed + while MATLAB is already assigned to the kernel, an appropriate exception is raised + with a message indicating that the notebook is already linked to a MATLAB session. + """ + mock_kernel = mocker.MagicMock() + mock_kernel.is_matlab_assigned = True + mock_kernel.is_shared_matlab = True + with pytest.raises(Exception) as excinfo: + async for _ in handle_new_matlab_session(mock_kernel): + pass + + assert "linked to Default MATLAB session" in str(excinfo.value) + + +async def test_handle_new_matlab_session_success(mocker): + """ + Test that MATLAB proxy is started correctly when using MATLAB magic command. + + This test verifies that when a %%matlab magic command with new_session option is executed, + the kernel properly starts the MATLAB proxy, assigns MATLAB to the kernel + (is_matlab_assigned=True), and sets the shared MATLAB flag to False. + """ + mock_kernel = mocker.AsyncMock() + mock_kernel.is_matlab_assigned = False + output = [] + async for result in handle_new_matlab_session(mock_kernel): + output.append(result) + + assert output is not None + mock_kernel.start_matlab_proxy_and_comm_helper.assert_called_once() + mock_kernel.perform_startup_checks.assert_called_once() + mock_kernel.cleanup_matlab_proxy.assert_not_called() + assert mock_kernel.is_matlab_assigned is True + assert mock_kernel.is_shared_matlab is False + assert DEDICATED_SESSION_CONFIRMATION_MSG in output[0]["value"][0] + + +async def test_handle_new_matlab_session_raises_exception(mocker): + """ + Test that exceptions during MATLAB magic command execution are handled properly. + + This test verifies that when an exception occurs during the startup of the MATLAB proxy + (triggered by a %%matlab magic command), the kernel properly handles the error and + maintains the expected state (is_matlab_assigned=False, is_shared_matlab=True). + """ + mock_kernel = mocker.AsyncMock() + mock_kernel.is_matlab_assigned = False + output = [] + with pytest.raises(Exception): + async for result in handle_new_matlab_session(mock_kernel): + output.append(result) + + assert output is not None + mock_kernel.start_matlab_proxy_and_comm_helper.assert_called_once() + mock_kernel.perform_startup_checks.side_effect = Exception( + "MATLAB Connection Error" + ) + mock_kernel.cleanup_matlab_proxy.assert_called_once() + assert mock_kernel.is_matlab_assigned is False + assert mock_kernel.is_shared_matlab is True + + +@pytest.mark.parametrize( + "shared_matlab_status, expected_output", + [ + (True, "MATLAB Shared With Other Notebooks: True"), + (False, "MATLAB Shared With Other Notebooks: False"), + ], + ids=["Shared MATLAB", "Non-shared MATLAB"], +) +async def test_get_kernel_info_in_matlab_magic( + shared_matlab_status, expected_output, mocker +): + mock_kernel = mocker.MagicMock() + mock_kernel._get_kernel_info.return_value = { + "is_shared_matlab": shared_matlab_status, + "matlab_version": "R2025b", + "matlab_root_path": "/path/to/matlab", + "licensing_mode": "existing_license", + } + output = [] + async for result in get_kernel_info(mock_kernel): + output.append(result) + assert output is not None + assert expected_output in output[0]["value"][0] diff --git a/tests/unit/jupyter_matlab_kernel/mocks/mock_http_responses.py b/tests/unit/jupyter_matlab_kernel/mocks/mock_http_responses.py index 77284f221..6414c8ef5 100644 --- a/tests/unit/jupyter_matlab_kernel/mocks/mock_http_responses.py +++ b/tests/unit/jupyter_matlab_kernel/mocks/mock_http_responses.py @@ -1,4 +1,4 @@ -# Copyright 2023-2024 The MathWorks, Inc. +# Copyright 2023-2025 The MathWorks, Inc. """Mock matlab-proxy HTTP Responses.""" import http @@ -136,3 +136,50 @@ class MockError(Exception): """ pass + + +class MockEvalResponse: + """A mock of a successful eval response from matlab-proxy.""" + + def __init__(self, is_error=False, response_str="", message_faults=None): + """Construct a mock eval response. + + Args: + is_error (bool): indicates if the eval had an error. + response_str (str): the response string or file path. + message_faults (list): list of message faults if any. + """ + self.is_error = is_error + self.response_str = response_str + self.message_faults = message_faults or [] + + status = http.HTTPStatus.OK + + async def json(self): + """Return a matlab-proxy eval JSON response.""" + return { + "messages": { + "EvalResponse": [ + { + "isError": self.is_error, + "responseStr": self.response_str, + "messageFaults": self.message_faults, + } + ] + } + } + + +class MockEvalResponseMissingData: + """A mock of an eval response missing EvalResponse data.""" + + status = http.HTTPStatus.OK + + def raise_for_status(self): + """Raise a HTTPError for missing data.""" + raise aiohttp.client_exceptions.ClientError("Mock exception") + + @staticmethod + async def json(): + """Return a matlab-proxy response missing EvalResponse.""" + return {"messages": {}} diff --git a/tests/unit/jupyter_matlab_kernel/test_kernel.py b/tests/unit/jupyter_matlab_kernel/test_kernel.py index 9c833f711..d0591ad6b 100644 --- a/tests/unit/jupyter_matlab_kernel/test_kernel.py +++ b/tests/unit/jupyter_matlab_kernel/test_kernel.py @@ -1,4 +1,4 @@ -# Copyright 2023-2024 The MathWorks, Inc. +# Copyright 2023-2025 The MathWorks, Inc. # This file contains tests for jupyter_matlab_kernel.kernel import mocks.mock_jupyter_server as MockJupyterServer @@ -6,7 +6,8 @@ from jupyter_server import serverapp from mocks.mock_jupyter_server import MockJupyterServerFixture -from jupyter_matlab_kernel.jsp_kernel import MATLABKernelUsingJSP, start_matlab_proxy +from jupyter_matlab_kernel.jsp_kernel import start_matlab_proxy +from jupyter_matlab_kernel.mpm_kernel import MATLABKernelUsingMPM from jupyter_matlab_kernel.mwi_exceptions import MATLABConnectionError @@ -95,19 +96,24 @@ async def test_matlab_not_licensed_non_jupyter(mocker): exception (MATLABConnectionError) is raised when performing startup checks. """ # Mock the kernel's jupyter_base_url attribute to simulate a non-Jupyter environment - kernel = mocker.MagicMock(spec=MATLABKernelUsingJSP) + kernel = mocker.MagicMock(spec=MATLABKernelUsingMPM) kernel.jupyter_base_url = None kernel.startup_error = None + kernel.matlab_proxy_base_url = "/test" + + matlab_status_mock = mocker.Mock() + matlab_status_mock.is_matlab_licensed = False + matlab_status_mock.matlab_status = "down" + matlab_status_mock.matlab_proxy_has_error = False + kernel.mwi_comm_helper = mocker.Mock() kernel.mwi_comm_helper.fetch_matlab_proxy_status = mocker.AsyncMock( - return_value=(False, "down", False) + return_value=matlab_status_mock ) # Mock the perform_startup_checks method to actually call the implementation - async def mock_perform_startup_checks(*args, **kwargs): - return await MATLABKernelUsingJSP.perform_startup_checks( - kernel, *args, **kwargs - ) + async def mock_perform_startup_checks(): + return await MATLABKernelUsingMPM.perform_startup_checks(kernel) kernel.perform_startup_checks.side_effect = mock_perform_startup_checks diff --git a/tests/unit/jupyter_matlab_kernel/test_kernelspec.py b/tests/unit/jupyter_matlab_kernel/test_kernelspec.py index f338119f9..5246731ac 100644 --- a/tests/unit/jupyter_matlab_kernel/test_kernelspec.py +++ b/tests/unit/jupyter_matlab_kernel/test_kernelspec.py @@ -28,7 +28,7 @@ def test_get_kernel_spec_custom_executable(): """ Test that get_kernel_spec() uses a custom executable when provided. """ - custom_executable = "/usr/bin/python3.9" + custom_executable = "/usr/bin/python3.10" kernelspec = get_kernel_spec(executable=custom_executable) assert kernelspec["argv"][0] == custom_executable diff --git a/tests/unit/jupyter_matlab_kernel/test_mpm_kernel.py b/tests/unit/jupyter_matlab_kernel/test_mpm_kernel.py index 495eca8f5..f797d3760 100644 --- a/tests/unit/jupyter_matlab_kernel/test_mpm_kernel.py +++ b/tests/unit/jupyter_matlab_kernel/test_mpm_kernel.py @@ -19,13 +19,13 @@ def mpm_kernel_instance(mocker) -> MATLABKernelUsingMPM: return_value=uuid.uuid4().hex, ) mocker.patch( - "jupyter_matlab_kernel.base_kernel.BaseMATLABKernel.log", new=mock_logger + "jupyter_matlab_kernel.base_kernel.BaseMATLABKernel.log", + new=mock_logger, ) return MATLABKernelUsingMPM() -@pytest.mark.asyncio async def test_initialize_matlab_proxy_with_mpm_success(mocker, mpm_kernel_instance): mpm_lib_start_matlab_proxy_response = { "absolute_url": "dummyURL", @@ -165,3 +165,27 @@ async def test_do_shutdown_exception(mocker, mpm_kernel_instance): mpm_kernel_instance.mpm_auth_token, ) assert not mpm_kernel_instance.is_matlab_assigned + + +async def test_matlab_proxy_assignment_on_executing_matlab_command( + mocker, mpm_kernel_instance +): + """ + Test that MATLAB proxy is assigned when executing a MATLAB command. + + This test verifies that when a regular MATLAB command is executed while MATLAB + is not yet assigned, the kernel properly starts the MATLAB proxy and assigns + MATLAB to the kernel (is_matlab_assigned=True). + """ + code = "why" + mpm_kernel_instance.is_matlab_assigned = False + # Patch kernel instance to mock start_matlab_proxy_and_comm_helper method + mock_start_matlab_proxy = mocker.patch.object( + mpm_kernel_instance, + "start_matlab_proxy_and_comm_helper", + autospec=True, + ) + + await mpm_kernel_instance.do_execute(code, silent=True) + mock_start_matlab_proxy.assert_called_once() + assert mpm_kernel_instance.is_matlab_assigned is True diff --git a/tests/unit/jupyter_matlab_kernel/test_mwi_comm_helpers.py b/tests/unit/jupyter_matlab_kernel/test_mwi_comm_helpers.py index 4646b8de2..786dbd475 100644 --- a/tests/unit/jupyter_matlab_kernel/test_mwi_comm_helpers.py +++ b/tests/unit/jupyter_matlab_kernel/test_mwi_comm_helpers.py @@ -1,8 +1,11 @@ -# Copyright 2023-2024 The MathWorks, Inc. +# Copyright 2023-2025 The MathWorks, Inc. # This file contains tests for jupyter_matlab_kernel.mwi_comm_helpers import asyncio import http +import json +import tempfile +import os import aiohttp import aiohttp.client_exceptions @@ -11,6 +14,8 @@ MockMatlabProxyStatusResponse, MockSimpleBadResponse, MockUnauthorisedRequestResponse, + MockEvalResponse, + MockEvalResponseMissingData, ) from jupyter_matlab_kernel.mwi_comm_helpers import MWICommHelper @@ -18,7 +23,7 @@ @pytest.fixture -async def matlab_proxy_fixture(): +async def comm_helper_fixture(): url = "http://localhost" headers = {} kernel_id = "" @@ -31,7 +36,7 @@ async def matlab_proxy_fixture(): # Testing fetch_matlab_proxy_status async def test_fetch_matlab_proxy_status_unauth_request( - monkeypatch, matlab_proxy_fixture + monkeypatch, comm_helper_fixture ): """ This test checks that fetch_matlab_proxy_status throws an exception @@ -43,7 +48,7 @@ async def mock_get(*args, **kwargs): monkeypatch.setattr(aiohttp.ClientSession, "get", mock_get) with pytest.raises(aiohttp.client_exceptions.ClientError) as exceptionInfo: - await matlab_proxy_fixture.fetch_matlab_proxy_status() + await comm_helper_fixture.fetch_matlab_proxy_status() assert MockUnauthorisedRequestResponse().exception_msg in str(exceptionInfo.value) @@ -58,7 +63,7 @@ async def mock_get(*args, **kwargs): ], ) async def test_fetch_matlab_proxy_status( - input_lic_type, expected_license_status, monkeypatch, matlab_proxy_fixture + input_lic_type, expected_license_status, monkeypatch, comm_helper_fixture ): """ This test checks that fetch_matlab_proxy_status returns the correct @@ -72,17 +77,35 @@ async def mock_get(*args, **kwargs): monkeypatch.setattr(aiohttp.ClientSession, "get", mock_get) - ( - is_matlab_licensed, - matlab_status, - matlab_proxy_has_error, - ) = await matlab_proxy_fixture.fetch_matlab_proxy_status() - assert is_matlab_licensed == expected_license_status - assert matlab_status == "up" - assert matlab_proxy_has_error is False + matlab_proxy_status = await comm_helper_fixture.fetch_matlab_proxy_status() + assert matlab_proxy_status.is_matlab_licensed == expected_license_status + assert matlab_proxy_status.matlab_status == "up" + assert matlab_proxy_status.matlab_proxy_has_error is False -async def test_interrupt_request_bad_request(monkeypatch, matlab_proxy_fixture): +async def test_fetch_matlab_root_path(mocker, comm_helper_fixture): + """ + This test checks that fetch_matlab_root_path returns the correct matlab root path. + """ + mock_response = mocker.AsyncMock() + mock_response.status = http.HTTPStatus.OK + mock_response.json = mocker.AsyncMock( + return_value={ + "matlab": { + "rootPath": "test_root_path", + "version": "test_version", + } + }, + ) + mocker.patch( + "aiohttp.ClientSession.get", new=mocker.AsyncMock(return_value=mock_response) + ) + + matlab_root_path = await comm_helper_fixture.fetch_matlab_root_path() + assert matlab_root_path == "test_root_path" + + +async def test_interrupt_request_bad_request(monkeypatch, comm_helper_fixture): """ This test checks that send_interrupt_request_to_matlab raises an exception if the response to the HTTP post is not valid. @@ -96,12 +119,12 @@ async def mock_post(*args, **kwargs): monkeypatch.setattr(aiohttp.ClientSession, "post", mock_post) with pytest.raises(aiohttp.client_exceptions.ClientError) as exceptionInfo: - await matlab_proxy_fixture.send_interrupt_request_to_matlab() + await comm_helper_fixture.send_interrupt_request_to_matlab() assert mock_exception_message in str(exceptionInfo.value) # Testing send_execution_request_to_matlab -async def test_execution_request_bad_request(monkeypatch, matlab_proxy_fixture): +async def test_execution_request_bad_request(monkeypatch, comm_helper_fixture): """ This test checks that send_execution_request_to_matlab throws an exception if the response to the HTTP request is invalid. @@ -115,12 +138,12 @@ async def mock_post(*args, **kwargs): code = "placeholder for code" with pytest.raises(aiohttp.client_exceptions.ClientError) as exceptionInfo: - await matlab_proxy_fixture.send_execution_request_to_matlab(code) + await comm_helper_fixture.send_execution_request_to_matlab(code) assert mock_exception_message in str(exceptionInfo.value) async def test_execution_request_invalid_feval_response( - monkeypatch, matlab_proxy_fixture + monkeypatch, comm_helper_fixture ): """ This test checks that send_execution_request_to_matlab raises an exception @@ -145,11 +168,11 @@ async def mock_post(*args, **kwargs): code = "placeholder for code" with pytest.raises(MATLABConnectionError) as exceptionInfo: - await matlab_proxy_fixture.send_execution_request_to_matlab(code) + await comm_helper_fixture.send_execution_request_to_matlab(code) assert str(exceptionInfo.value) == str(MATLABConnectionError()) -async def test_execution_interrupt(monkeypatch, matlab_proxy_fixture): +async def test_execution_interrupt(monkeypatch, comm_helper_fixture): """ This test checks that send_execution_request_to_matlab raises an exception if the matlab command appears to have been interupted. @@ -185,11 +208,11 @@ async def mock_post(*args, **kwargs): code = "placeholder for code" with pytest.raises(Exception) as exceptionInfo: - await matlab_proxy_fixture.send_execution_request_to_matlab(code) + await comm_helper_fixture.send_execution_request_to_matlab(code) assert "Operation may have interrupted by user" in str(exceptionInfo.value) -async def test_execution_success(monkeypatch, matlab_proxy_fixture): +async def test_execution_success(monkeypatch, comm_helper_fixture): """ This test checks that send_execution_request_to_matlab returns the correct information from a valid response from MATLAB. @@ -220,8 +243,285 @@ async def mock_post(*args, **kwargs): code = "placeholder for code" try: - outputs = await matlab_proxy_fixture.send_execution_request_to_matlab(code) + outputs = await comm_helper_fixture.send_execution_request_to_matlab(code) except Exception: pytest.fail("Unexpected failured in execution request") assert "Mock results from feval" in outputs + + +# Testing send_eval_request_to_matlab +async def test_send_eval_request_to_matlab_success(monkeypatch, comm_helper_fixture): + """Test that send_eval_request_to_matlab returns eval response correctly.""" + + # Arrange + async def mock_post(*args, **kwargs): + return MockEvalResponse(is_error=False, response_str="", message_faults=[]) + + monkeypatch.setattr(aiohttp.ClientSession, "post", mock_post) + + mcode = "x = 1 + 1" + + # Act + result = await comm_helper_fixture.send_eval_request_to_matlab(mcode) + + # Assert + # Verify the eval response is returned as-is + expected_response = {"isError": False, "responseStr": "", "messageFaults": []} + assert result == expected_response + + +async def test_send_eval_request_to_matlab_with_error(monkeypatch, comm_helper_fixture): + """Test that send_eval_request_to_matlab returns error response correctly.""" + + # Arrange + async def mock_post(*args, **kwargs): + return MockEvalResponse( + is_error=True, + response_str="Error occurred", + message_faults=[{"message": "Syntax error"}], + ) + + monkeypatch.setattr(aiohttp.ClientSession, "post", mock_post) + + mcode = "invalid_syntax" + + # Act + result = await comm_helper_fixture.send_eval_request_to_matlab(mcode) + + # Assert + # Verify the error response is returned as-is + expected_response = { + "isError": True, + "responseStr": "Error occurred", + "messageFaults": [{"message": "Syntax error"}], + } + assert result == expected_response + + +async def test_send_eval_request_to_matlab_bad_request( + monkeypatch, comm_helper_fixture +): + """Test that send_eval_request_to_matlab raises exception for bad HTTP request.""" + # Arrange + mock_exception_message = "Mock exception thrown due to bad request status." + + async def mock_post(*args, **kwargs): + return MockSimpleBadResponse(mock_exception_message) + + monkeypatch.setattr(aiohttp.ClientSession, "post", mock_post) + + mcode = "x = 1 + 1" + + # Act + with pytest.raises(aiohttp.client_exceptions.ClientError) as exceptionInfo: + await comm_helper_fixture.send_eval_request_to_matlab(mcode) + + # Assert + assert mock_exception_message in str(exceptionInfo.value) + + +async def test_send_eval_request_to_matlab_missing_eval_response( + monkeypatch, comm_helper_fixture +): + """Test that send_eval_request_to_matlab raises MATLABConnectionError for missing EvalResponse.""" + + # Arrange + async def mock_post(*args, **kwargs): + return MockEvalResponseMissingData() + + monkeypatch.setattr(aiohttp.ClientSession, "post", mock_post) + + mcode = "x = 1 + 1" + with pytest.raises(MATLABConnectionError): + await comm_helper_fixture.send_eval_request_to_matlab(mcode) + + +# Testing _read_eval_response_from_file +async def test_read_eval_response_from_file_success_with_file(comm_helper_fixture): + """Test _read_eval_response_from_file with successful response and file.""" + # Arrange + # Create a temporary file with test data + test_data = [ + {"type": "stream", "content": {"name": "stdout", "text": "Hello World"}} + ] + with tempfile.NamedTemporaryFile(mode="w", delete=False, suffix=".json") as f: + json.dump(test_data, f) + temp_file_path = f.name + + try: + eval_response = { + "isError": False, + "responseStr": temp_file_path, + "messageFaults": [], + } + + # Act + result = await comm_helper_fixture._read_eval_response_from_file(eval_response) + + # Assert + # Verify the result + assert result == test_data + + # Verify the file was deleted + assert not os.path.exists(temp_file_path) + + finally: + # Clean up in case the test failed + if os.path.exists(temp_file_path): + os.remove(temp_file_path) + + +async def test_read_eval_response_from_file_success_without_file(comm_helper_fixture): + """Test _read_eval_response_from_file with successful response but no file.""" + # Arrange + eval_response = { + "isError": False, + "responseStr": "", # Empty file path + "messageFaults": [], + } + + # Act + result = await comm_helper_fixture._read_eval_response_from_file(eval_response) + + # Assert + # Verify empty result returns empty list + assert result == [] + + +async def test_read_eval_response_from_file_error_with_message_faults( + comm_helper_fixture, +): + """Test _read_eval_response_from_file with error response containing message faults.""" + # Arrange + eval_response = { + "isError": True, + "responseStr": "Error occurred", + "messageFaults": [{"message": "Syntax error in code"}], + } + + # Act + with pytest.raises( + Exception, + match="Failed to execute. Operation may have been interrupted by user.", + ): + await comm_helper_fixture._read_eval_response_from_file(eval_response) + + +async def test_read_eval_response_from_file_error_without_message_faults( + comm_helper_fixture, +): + """Test _read_eval_response_from_file with error response without message faults.""" + + eval_response = { + "isError": True, + "responseStr": "Custom error message", + "messageFaults": [], + } + + with pytest.raises(Exception, match="Custom error message"): + await comm_helper_fixture._read_eval_response_from_file(eval_response) + + +async def test_read_eval_response_from_file_handles_file_deletion_error( + comm_helper_fixture, monkeypatch +): + """Test _read_eval_response_from_file handles file deletion errors gracefully.""" + + # Create a temporary file with test data + test_data = [ + {"type": "stream", "content": {"name": "stdout", "text": "Hello World"}} + ] + with tempfile.NamedTemporaryFile(mode="w", delete=False, suffix=".json") as f: + json.dump(test_data, f) + temp_file_path = f.name + + # Mock os.remove to raise an exception + original_remove = os.remove + + def mock_remove(path): + if path == temp_file_path: + raise OSError("Permission denied") + return original_remove(path) + + monkeypatch.setattr(os, "remove", mock_remove) + + try: + eval_response = { + "isError": False, + "responseStr": temp_file_path, + "messageFaults": [], + } + + # Should not raise exception even if file deletion fails + result = await comm_helper_fixture._read_eval_response_from_file(eval_response) + + # Verify the result is still correct + assert result == test_data + + finally: + # Clean up manually since mocked remove failed + if os.path.exists(temp_file_path): + original_remove(temp_file_path) + + +async def test_read_eval_response_from_file_with_empty_file_content( + comm_helper_fixture, +): + """Test _read_eval_response_from_file with empty file content.""" + + # Create a temporary file with empty content + with tempfile.NamedTemporaryFile(mode="w", delete=False, suffix=".json") as f: + f.write("") # Empty content + temp_file_path = f.name + + try: + eval_response = { + "isError": False, + "responseStr": temp_file_path, + "messageFaults": [], + } + + result = await comm_helper_fixture._read_eval_response_from_file(eval_response) + + # Verify empty content returns empty list + assert result == [] + + # Verify the file was deleted + assert not os.path.exists(temp_file_path) + + finally: + # Clean up in case the test failed + if os.path.exists(temp_file_path): + os.remove(temp_file_path) + + +async def test_read_eval_response_from_file_with_whitespace_only_content( + comm_helper_fixture, +): + """Test _read_eval_response_from_file with whitespace-only file content.""" + + # Create a temporary file with whitespace content + with tempfile.NamedTemporaryFile(mode="w", delete=False, suffix=".json") as f: + f.write(" \n\t ") # Only whitespace + temp_file_path = f.name + + try: + eval_response = { + "isError": False, + "responseStr": temp_file_path, + "messageFaults": [], + } + + result = await comm_helper_fixture._read_eval_response_from_file(eval_response) + + # Verify whitespace-only content returns empty list + assert result == [] + + # Verify the file was deleted + assert not os.path.exists(temp_file_path) + + finally: + # Clean up in case the test failed + if os.path.exists(temp_file_path): + os.remove(temp_file_path)