diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index af8f94e52cd..2bcf19162f9 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -275,13 +275,14 @@ jobs: # When installing pre-release versions, we're only focused on jupyter & related packages. # Not pre-release versions of pandas, numpy or other such packages that are not core to Jupyter. packageVersion: [''] - tags: - [ + tags: [ '^[^@]+$|@mandatory|@kernelCore|@python|@jupyter', '@widgets', '@iw', '@webview|@export|@lsp|@variableViewer', - '@debugger' + '@debugger', + # '@notebookPerformance', # Disabled for now, separate PR this will be enabled via cron (want to make PR smaller) + '@executionPerformance' ] # We're not running CI on macOS for now because it's one less matrix entry to lower the number of runners used, # macOS runners are expensive, and we assume that Ubuntu is enough to cover the UNIX case. @@ -506,7 +507,7 @@ jobs: # debugpy is not shipped, only installed for local tests. # In production, we get debugpy from python extension. - name: Install Python Libs - if: matrix.python != 'conda' && matrix.python != 'noPython' && matrix.ipywidgetsVersion != '8' + if: matrix.python != 'conda' && matrix.python != 'noPython' && matrix.ipywidgetsVersion != '8' && matrix.tags != '@notebookPerformance' run: | python --version python -c "import sys;print(sys.executable)" @@ -521,7 +522,7 @@ jobs: python -m pip install jupyterlab notebook - name: Install Python Libs (IPyWidget 8) - if: matrix.python != 'conda' && matrix.python != 'noPython' && matrix.ipywidgetsVersion == '8' + if: matrix.python != 'conda' && matrix.python != 'noPython' && matrix.ipywidgetsVersion == '8' && matrix.tags != '@notebookPerformance' run: | python --version python -c "import sys;print(sys.executable)" @@ -542,7 +543,7 @@ jobs: - name: Install matplotlib widgets into user and system paths if: - matrix.os == 'ubuntu-latest' && matrix.python != 'conda' && matrix.python != 'noPython' && matrix.packageVersion != 'prerelease' && matrix.tags != '^[^@]+$|@mandatory' + matrix.os == 'ubuntu-latest' && matrix.python != 'conda' && matrix.python != 'noPython' && matrix.packageVersion != 'prerelease' && matrix.tags != '^[^@]+$|@mandatory' && matrix.tags != '@notebookPerformance' # This test will ensure widgets work when installed in 3 places # 1. In python environments site-packages folder (we have other 3rd party widgets in the python env) # 2. In user's home folder (ipympl will be installed in there) @@ -585,23 +586,30 @@ jobs: VSC_JUPYTER_CI_SKIP_WEB_BUNDLE: 1 VSC_JUPYTER_CI_FAST_COMPILATION: 1 + - name: Compile Performance Test Extension + if: matrix.tags == '@notebookPerformance' + run: npm run compile + working-directory: src/test/vscode-notebook-perf + # Used by tests for non-python kernels. # Test are enabled via env variable `VSC_JUPYTER_CI_RUN_NON_PYTHON_NB_TEST` - name: Install Deno uses: denoland/setup-deno@v1 + if: matrix.tags != '@notebookPerformance' with: deno-version: ${{ env.DENO_VERSION}} - name: Install Deno Kernel + if: matrix.tags != '@notebookPerformance' run: npx tsx ./build/installDenoKernel.ts - name: Create Virtual Env for Tests uses: ./.github/actions/create-venv-for-tests - if: matrix.python != 'conda' && matrix.python != 'noPython' && matrix.os != 'windows-latest' && matrix.jupyterConnection != 'remote' && matrix.tags != '^[^@]+$|@mandatory' && matrix.ipywidgetsVersion != '8' && matrix.jupyterConnection != 'web' && matrix.jupyterConnection != 'remote' && matrix.tags != '@debugger' && matrix.tags != '@webview|@export|@lsp|@variableViewer' + if: matrix.python != 'conda' && matrix.python != 'noPython' && matrix.os != 'windows-latest' && matrix.jupyterConnection != 'remote' && matrix.tags != '^[^@]+$|@mandatory' && matrix.ipywidgetsVersion != '8' && matrix.jupyterConnection != 'web' && matrix.jupyterConnection != 'remote' && matrix.tags != '@debugger' && matrix.tags != '@webview|@export|@lsp|@variableViewer' && matrix.tags != '@notebookPerformance' - name: Create Virtual Env for Tests (ipywidgets 8) uses: ./.github/actions/create-venv-for-tests - if: matrix.python != 'conda' && matrix.python != 'noPython' && matrix.os != 'windows-latest' && matrix.jupyterConnection != 'remote' && matrix.tags != '^[^@]+$|@mandatory' && matrix.ipywidgetsVersion == '8' && matrix.jupyterConnection != 'web' && matrix.jupyterConnection != 'remote' && matrix.tags != '@debugger' && matrix.tags != '@webview|@export|@lsp|@variableViewer' + if: matrix.python != 'conda' && matrix.python != 'noPython' && matrix.os != 'windows-latest' && matrix.jupyterConnection != 'remote' && matrix.tags != '^[^@]+$|@mandatory' && matrix.ipywidgetsVersion == '8' && matrix.jupyterConnection != 'web' && matrix.jupyterConnection != 'remote' && matrix.tags != '@debugger' && matrix.tags != '@webview|@export|@lsp|@variableViewer' && matrix.tags != '@notebookPerformance' with: IPyWidgetVersion: '8' @@ -646,7 +654,45 @@ jobs: VSC_JUPYTER_CI_TEST_VSC_CHANNEL: 'insiders' VSC_JUPYTER_CI_TEST_GREP: ${{ matrix.tags }} id: test_notebook_vscode_ubuntu - if: matrix.python != 'noPython' && matrix.os == 'ubuntu-latest' && matrix.jupyterConnection != 'web' + if: matrix.python != 'noPython' && matrix.os == 'ubuntu-latest' && matrix.jupyterConnection != 'web' && matrix.tags != '@notebookPerformance' + + - name: Run Notebook Perf Test Without Jupyter + uses: GabrielBB/xvfb-action@v1.4 + with: + run: ${{ env.xvfbCommand }} npm run testPerfInVSCode + env: + VSC_JUPYTER_FORCE_LOGGING: 1 + VSC_PYTHON_FORCE_LOGGING: 1 + VSC_JUPYTER_CI_RUN_NON_PYTHON_NB_TEST: 1 + VSC_JUPYTER_PERF_TEST: ${{ matrix.matrix == '@notebookPerformance' }} + VSC_JUPYTER_NOTEBOOK_PERF_TEST: ${{ matrix.matrix == '@notebookPerformance' }} + VSC_JUPYTER_CI_TEST_DO_NOT_INSTALL_PYTHON_EXT: ${{ matrix.matrix == '@notebookPerformance' }} + VSC_JUPYTER_REMOTE_NATIVE_TEST: ${{ matrix.jupyterConnection == 'remote' }} + VSC_JUPYTER_NON_RAW_NATIVE_TEST: ${{ matrix.jupyterConnection == 'local' }} + VSC_JUPYTER_CI_RUN_JAVA_NB_TEST: ${{ matrix.python == 'conda' }} + VSC_JUPYTER_CI_IS_CONDA: ${{ matrix.python == 'conda' }} + VSC_JUPYTER_CI_TEST_VSC_CHANNEL: 'insiders' + VSC_JUPYTER_CI_TEST_GREP: ${{ matrix.tags }} + id: test_notebook_perf_vscode_ubuntu + if: matrix.tags == '@notebookPerformance' + + - name: Run Execution Perf Test With Jupyter + uses: GabrielBB/xvfb-action@v1.4 + with: + run: ${{ env.xvfbCommand }} npm run testExecPerfInVSCode + env: + VSC_JUPYTER_FORCE_LOGGING: 1 + VSC_PYTHON_FORCE_LOGGING: 1 + VSC_JUPYTER_CI_RUN_NON_PYTHON_NB_TEST: 1 + VSC_JUPYTER_PERF_TEST: ${{ matrix.matrix == '@executionPerformance' }} + VSC_JUPYTER_REMOTE_NATIVE_TEST: ${{ matrix.jupyterConnection == 'remote' }} + VSC_JUPYTER_NON_RAW_NATIVE_TEST: ${{ matrix.jupyterConnection == 'local' }} + VSC_JUPYTER_CI_RUN_JAVA_NB_TEST: ${{ matrix.python == 'conda' }} + VSC_JUPYTER_CI_IS_CONDA: ${{ matrix.python == 'conda' }} + VSC_JUPYTER_CI_TEST_VSC_CHANNEL: 'insiders' + VSC_JUPYTER_CI_TEST_GREP: ${{ matrix.tags }} + id: test_exec_perf_vscode_ubuntu + if: matrix.tags == '@executionPerformance' - name: Build web bundle for testing run: npm run compile-web-test diff --git a/.vscode/launch.json b/.vscode/launch.json index 96f99139f2d..d35ef629090 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -302,6 +302,63 @@ "XVSC_JUPYTER_INSTRUMENT_CODE_FOR_COVERAGE_HTML": "1" //Enable to get full coverage repor (in coverage folder). } }, + { + "name": "Tests Notebook Performance without Jupyter & Python", + "type": "extensionHost", + "request": "launch", + "runtimeExecutable": "${execPath}", + "args": [ + "${workspaceFolder}/src/test/datascience", + "--enable-proposed-api", + "--disable-extensions", + "--extensionDevelopmentPath=${workspaceFolder}/src/test/vscode-notebook-perf", + "--extensionTestsPath=${workspaceFolder}/out/test/index.node.js" + ], + "env": { + "VSC_JUPYTER_PERF_TEST": "1", + "VSC_JUPYTER_CI_TEST_GREP": "@notebookPerformance", + "VSC_JUPYTER_CI_TEST_VSC_CHANNEL": "insiders", + "TEST_FILES_SUFFIX": "*.vscode.common.test", + "VSC_JUPYTER_CI_TEST_DO_NOT_INSTALL_PYTHON_EXT": "true" + }, + "sourceMaps": true, + "outFiles": [ + "${workspaceFolder}/out/**/*.js", + "${workspaceFolder}/src/test/vscode-notebook-perf/out/**/*.js", + "!${workspaceFolder}/**/node_modules**/*" + ], + "skipFiles": ["/**"], + "presentation": { + "group": "2_tests", + "order": 6 + } + }, + { + "name": "Tests Execution Performance with Jupyter & Python", + "type": "extensionHost", + "request": "launch", + "runtimeExecutable": "${execPath}", + "args": [ + "${workspaceFolder}/src/test/datascience", + "--enable-proposed-api", + "--extensionDevelopmentPath=${workspaceFolder}", + "--extensionTestsPath=${workspaceFolder}/out/test/index.node.js" + ], + "env": { + "VSC_JUPYTER_PERF_TEST": "1", + "VSC_JUPYTER_CI_TEST_GREP": "@executionPerformance", + "VSC_JUPYTER_CI_TEST_VSC_CHANNEL": "insiders", + "CI_PYTHON_PATH": "/Users/donjayamanne/Development/vsc/vscode-jupyter/.venv/bin/python", + "TEST_FILES_SUFFIX": "*.vscode.test" + }, + "sourceMaps": true, + "outFiles": ["${workspaceFolder}/out/**/*.js", "!${workspaceFolder}/**/node_modules**/*"], + "skipFiles": ["/**"], + "presentation": { + "group": "2_tests", + "order": 6 + } + }, { "type": "node", "request": "launch", diff --git a/package.json b/package.json index 81edbc85ea9..d163e45fd49 100644 --- a/package.json +++ b/package.json @@ -2108,6 +2108,10 @@ "testJediLSP": "node ./out/test/languageServers/jedi/lspSetup.js && cross-env CODE_TESTS_WORKSPACE=src/test VSC_JUPYTER_CI_TEST_GREP='Language Server:' node ./out/test/testBootstrap.node.js ./out/test/standardTest.node.js && node ./out/test/languageServers/jedi/lspTeardown.node.js", "pretestNativeNotebooksInVSCode": "cross-env VSC_JUPYTER_CI_TEST_VSC_CHANNEL=insiders node ./out/test/datascience/dsTestSetup.js", "testNativeNotebooksInVSCode": "cross-env CODE_TESTS_WORKSPACE=src/test/datascience VSC_JUPYTER_CI_TEST_VSC_CHANNEL=insiders TEST_FILES_SUFFIX=*.vscode.test,*.vscode.common.test VSC_JUPYTER_FORCE_LOGGING=1 node ./out/test/testBootstrap.node.js ./out/test/standardTest.node.js", + "pretestPerfInVSCode": "cross-env VSC_JUPYTER_CI_TEST_GREP=@notebookPerformance VSC_JUPYTER_CI_TEST_DO_NOT_INSTALL_PYTHON_EXT=true VSC_JUPYTER_PERF_TEST=1 VSC_JUPYTER_CI_TEST_VSC_CHANNEL=insiders node ./out/test/datascience/dsTestSetup.js", + "testPerfInVSCode": "cross-env VSC_JUPYTER_CI_TEST_GREP=@notebookPerformance VSC_JUPYTER_CI_TEST_DO_NOT_INSTALL_PYTHON_EXT=true VSC_JUPYTER_NOTEBOOK_PERF_TEST=true VSC_JUPYTER_PERF_TEST=1 CODE_TESTS_WORKSPACE=src/test/datascience VSC_JUPYTER_CI_TEST_VSC_CHANNEL=insiders TEST_FILES_SUFFIX=*.vscode.test,*.vscode.common.test VSC_JUPYTER_FORCE_LOGGING=1 node ./out/test/testBootstrap.node.js ./out/test/standardTest.node.js", + "pretestExecPerfInVSCode": "cross-env VSC_JUPYTER_CI_TEST_GREP=@executionPerformance VSC_JUPYTER_PERF_TEST=1 VSC_JUPYTER_CI_TEST_VSC_CHANNEL=insiders node ./out/test/datascience/dsTestSetup.js", + "testExecPerfInVSCode": "cross-env VSC_JUPYTER_CI_TEST_GREP=@executionPerformance VSC_JUPYTER_PERF_TEST=1 CODE_TESTS_WORKSPACE=src/test/datascience VSC_JUPYTER_CI_TEST_VSC_CHANNEL=insiders TEST_FILES_SUFFIX=*.vscode.test,*.vscode.common.test VSC_JUPYTER_FORCE_LOGGING= node ./out/test/testBootstrap.node.js ./out/test/standardTest.node.js", "testNativeNotebooksInVSCodeWithoutTestSuffix": "cross-env CODE_TESTS_WORKSPACE=src/test/datascience VSC_JUPYTER_CI_TEST_VSC_CHANNEL=insiders VSC_JUPYTER_FORCE_LOGGING=1 node ./out/test/testBootstrap.node.js ./out/test/standardTest.node.js", "pretestNativeNotebooksWithoutPythonInVSCode": "cross-env VSC_JUPYTER_CI_TEST_VSC_CHANNEL=insiders node ./out/test/datascience/dsTestSetup.js", "testNativeNotebooksWithoutPythonInVSCode": "cross-env CODE_TESTS_WORKSPACE=src/test/datascience VSC_JUPYTER_CI_TEST_VSC_CHANNEL=insiders TEST_FILES_SUFFIX=*.vscode.test,*.vscode.common.test VSC_JUPYTER_FORCE_LOGGING=1 VSC_JUPYTER_CI_TEST_GREP=@nonPython VSC_JUPYTER_CI_TEST_DO_NOT_INSTALL_PYTHON_EXT=true node ./out/test/testBootstrap.node.js ./out/test/standardTest.node.js", diff --git a/pvsc.code-workspace b/pvsc.code-workspace index 1adcd31cf65..e1e8b728dc9 100644 --- a/pvsc.code-workspace +++ b/pvsc.code-workspace @@ -21,6 +21,9 @@ { "name": "vscode-jupyter-powertoys", "path": "../vscode-jupyter-powertoys" + }, + { + "path": "src/test/vscode-notebook-perf" } ], "settings": { diff --git a/src/api.unstable.d.ts b/src/api.unstable.d.ts index ced9dc07212..3db8d9b79fc 100644 --- a/src/api.unstable.d.ts +++ b/src/api.unstable.d.ts @@ -3,6 +3,18 @@ import type { CancellationToken, Disposable, Event, NotebookDocument, Uri } from 'vscode'; import type { Session } from '@jupyterlab/services'; +type EnvironmentPath = { + /** + * The ID of the environment. + */ + readonly id: string; + /** + * Path to environment folder or path to python executable that uniquely identifies an environment. Environments + * lacking a python executable are identified by environment folder paths, whereas other envs can be identified + * using python executable path. + */ + readonly path: string; +}; declare module './api' { export interface Jupyter { @@ -32,10 +44,10 @@ declare module './api' { /** * Opens a notebook with a specific Python Environment as the active kernel. * @param {Uri} uri Uri of the notebook to open. - * @param {String} kernelId Id of the Python Environment + * @param {EnvironmentPath} pythonEnvironment Python Environment * @returns {Promise} Promise that resolves to the notebook document. */ - openNotebook(uri: Uri, pythonEnvironmentId: string): Promise; + openNotebook(uri: Uri, pythonEnvironment: EnvironmentPath): Promise; /** * Opens a notebook with a specific kernel as the active kernel. * @param {Uri} uri Uri of the notebook to open. diff --git a/src/gdpr.ts b/src/gdpr.ts index 066ccde89cc..d8a66ecb710 100644 --- a/src/gdpr.ts +++ b/src/gdpr.ts @@ -447,7 +447,20 @@ ] } */ -// (37). Telemetry.JupyterKernelApiAccess (DATASCIENCE.JUPYTER_KERNEL_API_ACCESS) +// (37). Telemetry.JupyterNotebookExecutionPerformance (DATASCIENCE.JUPYTER_JUPYTER_NOTEBOOK_EXEC_PERFORMANCE) +// Telemetry sent during test on CI to measure performance of execution of large notebooks. +/* __GDPR__ + "DATASCIENCE.JUPYTER_JUPYTER_NOTEBOOK_EXEC_PERFORMANCE" : { + "outputType": {"classification":"PublicNonPersonalData","purpose":"FeatureInsight","comment":"The kind of outputs generated in the notebook, text, html or images.","owner":"donjayamanne"}, + "codeCellCount": {"classification":"SystemMetaData","purpose":"PerformanceAndHealth","comment":"Total number of code cells. Code cell count","owner":"donjayamanne","isMeasurement":true}, + "markdownCellCount": {"classification":"SystemMetaData","purpose":"PerformanceAndHealth","comment":"Total number of markdown cells. Code cell count","owner":"donjayamanne","isMeasurement":true}, + "${include}": [ + "${F1}" + + ] + } + */ +// (38). Telemetry.JupyterKernelApiAccess (DATASCIENCE.JUPYTER_KERNEL_API_ACCESS) // Telemetry sent when an extension attempts to use our 3rd party API. /* __GDPR__ "DATASCIENCE.JUPYTER_KERNEL_API_ACCESS" : { @@ -459,7 +472,7 @@ ] } */ -// (38). Telemetry.JupyterKernelApiUsage (DATASCIENCE.JUPYTER_KERNEL_API_USAGE) +// (39). Telemetry.JupyterKernelApiUsage (DATASCIENCE.JUPYTER_KERNEL_API_USAGE) // Telemetry sent when an extension uses our 3rd party Kernel API. /* __GDPR__ "DATASCIENCE.JUPYTER_KERNEL_API_USAGE" : { @@ -471,7 +484,7 @@ ] } */ -// (39). Telemetry.KernelCodeCompletion (DATASCIENCE.JUPYTER_KERNEL_CODE_COMPLETION) +// (40). Telemetry.KernelCodeCompletion (DATASCIENCE.JUPYTER_KERNEL_CODE_COMPLETION) // Telemetry sent with the total time taken to provide completions from a kernel. /* __GDPR__ "DATASCIENCE.JUPYTER_KERNEL_CODE_COMPLETION" : { @@ -493,7 +506,7 @@ ] } */ -// (40). Telemetry.KernelCodeCompletionCannotResolve (DATASCIENCE.JUPYTER_KERNEL_CODE_COMPLETION_CANNOT_RESOLVE) +// (41). Telemetry.KernelCodeCompletionCannotResolve (DATASCIENCE.JUPYTER_KERNEL_CODE_COMPLETION_CANNOT_RESOLVE) // Telemetry sent when we the kernel does not reply back with a response for requestInspect message. // The requestInspect request is used to resolve completion items in auto complete lists. /* __GDPR__ @@ -507,7 +520,7 @@ ] } */ -// (41). Telemetry.JupyterKernelFilterUsed (DATASCIENCE.JUPYTER_KERNEL_FILTER_USED) +// (42). Telemetry.JupyterKernelFilterUsed (DATASCIENCE.JUPYTER_KERNEL_FILTER_USED) // Called when the user clicks accept on the kernel filter UI. /* __GDPR__ "DATASCIENCE.JUPYTER_KERNEL_FILTER_USED" : { @@ -517,7 +530,7 @@ ] } */ -// (42). Telemetry.JupyterKernelHiddenViaFilter (DATASCIENCE.JUPYTER_KERNEL_HIDDEN_VIA_FILTER) +// (43). Telemetry.JupyterKernelHiddenViaFilter (DATASCIENCE.JUPYTER_KERNEL_HIDDEN_VIA_FILTER) // Called when a controller that would have been shown is hidden by a filter. /* __GDPR__ "DATASCIENCE.JUPYTER_KERNEL_HIDDEN_VIA_FILTER" : { @@ -527,7 +540,7 @@ ] } */ -// (43). Telemetry.JupyterKernelSpecEnumeration (DATASCIENCE.JUPYTER_KERNEL_SPEC_FETCH_FAILURE) +// (44). Telemetry.JupyterKernelSpecEnumeration (DATASCIENCE.JUPYTER_KERNEL_SPEC_FETCH_FAILURE) // How often we wait to fetch remote kernel specs or how long it takes to fetch them. /* __GDPR__ "DATASCIENCE.JUPYTER_KERNEL_SPEC_FETCH_FAILURE" : { @@ -542,7 +555,7 @@ ] } */ -// (44). Telemetry.JupyterKernelStartupHook (DATASCIENCE.JUPYTER_KERNEL_STARTUP_HOOK) +// (45). Telemetry.JupyterKernelStartupHook (DATASCIENCE.JUPYTER_KERNEL_STARTUP_HOOK) // Telemetry sent when startup hooks for Jupyter Kernels are handled by 3rd party extensions. // Note: This only applies to kernels belonging to Jupyter Servers contributed by the same extension. /* __GDPR__ @@ -555,7 +568,7 @@ ] } */ -// (45). Telemetry.NewJupyterKernelApiExecution (DATASCIENCE.JUPYTER_NEW_KERNEL_API_EXEC) +// (46). Telemetry.NewJupyterKernelApiExecution (DATASCIENCE.JUPYTER_NEW_KERNEL_API_EXEC) // Telemetry sent when an extension uses our 3rd party Kernel Execution API. /* __GDPR__ "DATASCIENCE.JUPYTER_NEW_KERNEL_API_EXEC" : { @@ -577,7 +590,7 @@ ] } */ -// (46). Telemetry.NewJupyterKernelApiUsage (DATASCIENCE.JUPYTER_NEW_KERNEL_API_USAGE) +// (47). Telemetry.NewJupyterKernelApiUsage (DATASCIENCE.JUPYTER_NEW_KERNEL_API_USAGE) // Telemetry sent when an extension uses our 3rd party Kernel Execution API. /* __GDPR__ "DATASCIENCE.JUPYTER_NEW_KERNEL_API_USAGE" : { @@ -591,7 +604,7 @@ ] } */ -// (47). Telemetry.NewJupyterKernelsApiUsage (DATASCIENCE.JUPYTER_NEW_KERNELS_API_USAGE) +// (48). Telemetry.NewJupyterKernelsApiUsage (DATASCIENCE.JUPYTER_NEW_KERNELS_API_USAGE) // Telemetry sent when an extension uses our 3rd party Kernels API. /* __GDPR__ "DATASCIENCE.JUPYTER_NEW_KERNELS_API_USAGE" : { @@ -604,7 +617,35 @@ ] } */ -// (48). Telemetry.KernelCrash (DATASCIENCE.KERNEL_CRASH) +// (49). Telemetry.NativeNotebookEditPerformance (DATASCIENCE.JUPYTER_NOTEBOOK_EDIT_PERFORMANCE) +// Telemetry sent during test on CI to measure performance of execution of large notebooks. +/* __GDPR__ + "DATASCIENCE.JUPYTER_NOTEBOOK_EDIT_PERFORMANCE" : { + "codeCellCount": {"classification":"SystemMetaData","purpose":"PerformanceAndHealth","comment":"Total number of code cells. Code cell count","owner":"donjayamanne","isMeasurement":true}, + "markdownCellCount": {"classification":"SystemMetaData","purpose":"PerformanceAndHealth","comment":"Total number of markdown cells. Code cell count","owner":"donjayamanne","isMeasurement":true}, + "${include}": [ + "${F1}" + + ] + } + */ +// (50). Telemetry.NativeNotebookExecutionPerformance (DATASCIENCE.JUPYTER_NOTEBOOK_EXEC_PERFORMANCE) +// Telemetry sent during test on CI to measure performance of execution of large notebooks. +/* __GDPR__ + "DATASCIENCE.JUPYTER_NOTEBOOK_EXEC_PERFORMANCE" : { + "outputType": {"classification":"PublicNonPersonalData","purpose":"FeatureInsight","comment":"The kind of outputs generated in the notebook, text, html or images.","owner":"donjayamanne"}, + "codeCellCount": {"classification":"SystemMetaData","purpose":"PerformanceAndHealth","comment":"Total number of code cells. Code cell count","owner":"donjayamanne","isMeasurement":true}, + "markdownCellCount": {"classification":"SystemMetaData","purpose":"PerformanceAndHealth","comment":"Total number of markdown cells. Code cell count","owner":"donjayamanne","isMeasurement":true}, + "preExecuteDuration": {"classification":"SystemMetaData","purpose":"PerformanceAndHealth","comment":"Total time spent in VS Code before starting execution. Total time spent in VS Code before extension starts execution.","owner":"donjayamanne","isMeasurement":true}, + "executeDuration": {"classification":"SystemMetaData","purpose":"PerformanceAndHealth","comment":"Total time spent executing cells.","owner":"donjayamanne","isMeasurement":true}, + "postExecuteDuration": {"classification":"SystemMetaData","purpose":"PerformanceAndHealth","comment":"Total time spent in VS Code after executing cells.","owner":"donjayamanne","isMeasurement":true}, + "${include}": [ + "${F1}" + + ] + } + */ +// (51). Telemetry.KernelCrash (DATASCIENCE.KERNEL_CRASH) // Sent when Kernel crashes. /* __GDPR__ "DATASCIENCE.KERNEL_CRASH" : { @@ -631,7 +672,7 @@ ] } */ -// (49). Telemetry.KernelSpecLanguage (DATASCIENCE.KERNEL_SPEC_LANGUAGE) +// (52). Telemetry.KernelSpecLanguage (DATASCIENCE.KERNEL_SPEC_LANGUAGE) // Sent to detect the different languages of kernel specs used. /* __GDPR__ "DATASCIENCE.KERNEL_SPEC_LANGUAGE" : { @@ -644,7 +685,7 @@ ] } */ -// (50). Telemetry.KernelStartFailureDueToMissingEnv (DATASCIENCE.KERNEL_START_FAILURE_MISSING_ENV) +// (53). Telemetry.KernelStartFailureDueToMissingEnv (DATASCIENCE.KERNEL_START_FAILURE_MISSING_ENV) // Telemetry sent when user Kernel startup fails due to a missing python env. /* __GDPR__ "DATASCIENCE.KERNEL_START_FAILURE_MISSING_ENV" : { @@ -675,7 +716,7 @@ ] } */ -// (51). Telemetry.OpenNotebookAll (DATASCIENCE.NATIVE.OPEN_NOTEBOOK_ALL) +// (54). Telemetry.OpenNotebookAll (DATASCIENCE.NATIVE.OPEN_NOTEBOOK_ALL) // Sent when we have opened any Jupyter notebook in a VS Code session. // Not tagging as a user action as this could be something like auto opening a file // from a previous session and not a direct user action. @@ -689,7 +730,7 @@ ] } */ -// (52). Telemetry.NoActiveKernelSession (DATASCIENCE.NO_ACTIVE_KERNEL_SESSION) +// (55). Telemetry.NoActiveKernelSession (DATASCIENCE.NO_ACTIVE_KERNEL_SESSION) // Send when we want to install data viewer dependendies, but don't have an active kernel session. // Used by the dataViewerDependencyService. /* __GDPR__ @@ -700,7 +741,7 @@ ] } */ -// (53). Telemetry.NotebookFirstKernelAutoSelectionBreakDown (DATASCIENCE.NOTEBOOK_FIRST_KERNEL_AUTO_SELECTION_BREAKDOWN) +// (56). Telemetry.NotebookFirstKernelAutoSelectionBreakDown (DATASCIENCE.NOTEBOOK_FIRST_KERNEL_AUTO_SELECTION_BREAKDOWN) // This event is sent to measure the times involved in automatically selecting the first kernel of a notebook. /* __GDPR__ "DATASCIENCE.NOTEBOOK_FIRST_KERNEL_AUTO_SELECTION_BREAKDOWN" : { @@ -733,7 +774,7 @@ ] } */ -// (54). Telemetry.NotebookFirstStartBreakDown (DATASCIENCE.NOTEBOOK_FIRST_START_BREAKDOWN) +// (57). Telemetry.NotebookFirstStartBreakDown (DATASCIENCE.NOTEBOOK_FIRST_START_BREAKDOWN) // This event is sent to measure the times involved in various parts of extension when running a cell. // The reference time is `openedAfter` (time when the notebook was opened). // All other times are relative to this time, except in the case where a notebook was already opened before the extension was activated. @@ -792,7 +833,7 @@ ] } */ -// (55). Telemetry.NotebookInterrupt (DATASCIENCE.NOTEBOOK_INTERRUPT) +// (58). Telemetry.NotebookInterrupt (DATASCIENCE.NOTEBOOK_INTERRUPT) // Telemetry sent when user interrupts the kernel. // Check the `resourceType` to determine whether its a Jupyter Notebook or IW. /* __GDPR__ @@ -821,7 +862,7 @@ ] } */ -// (56). Telemetry.NotebookRestart (DATASCIENCE.NOTEBOOK_RESTART) +// (59). Telemetry.NotebookRestart (DATASCIENCE.NOTEBOOK_RESTART) // Telemetry sent when user Restarts the Kernel. // Check the `resourceType` to determine whether its a Jupyter Notebook or IW. /* __GDPR__ @@ -849,7 +890,7 @@ ] } */ -// (57). Telemetry.NotebookStart (DATASCIENCE.NOTEBOOK_START) +// (60). Telemetry.NotebookStart (DATASCIENCE.NOTEBOOK_START) // Send when a kernel starts. /* __GDPR__ "DATASCIENCE.NOTEBOOK_START" : { @@ -876,7 +917,7 @@ ] } */ -// (58). Telemetry.OpenPlotViewer (DATASCIENCE.OPEN_PLOT_VIEWER) +// (61). Telemetry.OpenPlotViewer (DATASCIENCE.OPEN_PLOT_VIEWER) // A new instance of the plot viewer was opened. /* __GDPR__ "DATASCIENCE.OPEN_PLOT_VIEWER" : { @@ -886,7 +927,7 @@ ] } */ -// (59). Telemetry.PythonVariableFetchingCodeFailure (DATASCIENCE.PYTHON_VARIABLE_FETCHING_CODE_FAILURE) +// (62). Telemetry.PythonVariableFetchingCodeFailure (DATASCIENCE.PYTHON_VARIABLE_FETCHING_CODE_FAILURE) // The Python code that we ran to fetch variables had a failure. /* __GDPR__ "DATASCIENCE.PYTHON_VARIABLE_FETCHING_CODE_FAILURE" : { @@ -896,7 +937,7 @@ ] } */ -// (60). Telemetry.RecommendExtension (DATASCIENCE.RECOMMENT_EXTENSION) +// (63). Telemetry.RecommendExtension (DATASCIENCE.RECOMMENT_EXTENSION) // Telemetry sent when we recommend installing an extension. /* __GDPR__ "DATASCIENCE.RECOMMENT_EXTENSION" : { @@ -908,7 +949,7 @@ ] } */ -// (61). Telemetry.RefreshDataViewer (DATASCIENCE.REFRESH_DATA_VIEWER) +// (64). Telemetry.RefreshDataViewer (DATASCIENCE.REFRESH_DATA_VIEWER) // Sent when the jupyter.refreshDataViewer command is invoked /* __GDPR__ "DATASCIENCE.REFRESH_DATA_VIEWER" : { @@ -918,7 +959,7 @@ ] } */ -// (62). Telemetry.ResumeCellExecution (DATASCIENCE.RESUME_EXECUTE_CELL) +// (65). Telemetry.ResumeCellExecution (DATASCIENCE.RESUME_EXECUTE_CELL) // Sent when we resume execution of a cell (e.g. when reloading VS Code while a cell was executing). /* __GDPR__ "DATASCIENCE.RESUME_EXECUTE_CELL" : { @@ -945,7 +986,7 @@ ] } */ -// (63). Telemetry.RunAllCells (DATASCIENCE.RUN_ALL_CELLS) +// (66). Telemetry.RunAllCells (DATASCIENCE.RUN_ALL_CELLS) // Command to Run all cells from the active python file in the Interactive Window /* __GDPR__ "DATASCIENCE.RUN_ALL_CELLS" : { @@ -955,7 +996,7 @@ ] } */ -// (64). Telemetry.RunAllCellsAbove (DATASCIENCE.RUN_ALL_CELLS_ABOVE) +// (67). Telemetry.RunAllCellsAbove (DATASCIENCE.RUN_ALL_CELLS_ABOVE) // Command to Run all the above cells in the Interactive Window /* __GDPR__ "DATASCIENCE.RUN_ALL_CELLS_ABOVE" : { @@ -965,7 +1006,7 @@ ] } */ -// (65). Telemetry.RunByLineVariableHover (DATASCIENCE.RUN_BY_LINE_VARIABLE_HOVER) +// (68). Telemetry.RunByLineVariableHover (DATASCIENCE.RUN_BY_LINE_VARIABLE_HOVER) // Fired when a user hovers a variable while debugging the IW. /* __GDPR__ "DATASCIENCE.RUN_BY_LINE_VARIABLE_HOVER" : { @@ -975,7 +1016,7 @@ ] } */ -// (66). Telemetry.RunCellAndAllBelow (DATASCIENCE.RUN_CELL_AND_ALL_BELOW) +// (69). Telemetry.RunCellAndAllBelow (DATASCIENCE.RUN_CELL_AND_ALL_BELOW) // Command to Run current cell and all below in the Interactive Window /* __GDPR__ "DATASCIENCE.RUN_CELL_AND_ALL_BELOW" : { @@ -985,7 +1026,7 @@ ] } */ -// (67). Telemetry.ChangeCellToCode (DATASCIENCE.RUN_CHANGE_CELL_TO_CODE) +// (70). Telemetry.ChangeCellToCode (DATASCIENCE.RUN_CHANGE_CELL_TO_CODE) // Cell Edit Command in Interactive Window /* __GDPR__ "DATASCIENCE.RUN_CHANGE_CELL_TO_CODE" : { @@ -995,7 +1036,7 @@ ] } */ -// (68). Telemetry.ChangeCellToMarkdown (DATASCIENCE.RUN_CHANGE_CELL_TO_MARKDOWN) +// (71). Telemetry.ChangeCellToMarkdown (DATASCIENCE.RUN_CHANGE_CELL_TO_MARKDOWN) // Cell Edit Command in Interactive Window /* __GDPR__ "DATASCIENCE.RUN_CHANGE_CELL_TO_MARKDOWN" : { @@ -1005,7 +1046,7 @@ ] } */ -// (69). Telemetry.RunCurrentCell (DATASCIENCE.RUN_CURRENT_CELL) +// (72). Telemetry.RunCurrentCell (DATASCIENCE.RUN_CURRENT_CELL) // Command to Run the current Cell in the Interactive Window /* __GDPR__ "DATASCIENCE.RUN_CURRENT_CELL" : { @@ -1015,7 +1056,7 @@ ] } */ -// (70). Telemetry.RunCurrentCellAndAddBelow (DATASCIENCE.RUN_CURRENT_CELL_AND_ADD_BELOW) +// (73). Telemetry.RunCurrentCellAndAddBelow (DATASCIENCE.RUN_CURRENT_CELL_AND_ADD_BELOW) // Run the cell and everything below it in the Interactive Window. /* __GDPR__ "DATASCIENCE.RUN_CURRENT_CELL_AND_ADD_BELOW" : { @@ -1025,7 +1066,7 @@ ] } */ -// (71). Telemetry.RunCurrentCellAndAdvance (DATASCIENCE.RUN_CURRENT_CELL_AND_ADVANCE) +// (74). Telemetry.RunCurrentCellAndAdvance (DATASCIENCE.RUN_CURRENT_CELL_AND_ADVANCE) // Command to Run current cell in the Interactive Window and advance cursor to the next cell /* __GDPR__ "DATASCIENCE.RUN_CURRENT_CELL_AND_ADVANCE" : { @@ -1035,7 +1076,7 @@ ] } */ -// (72). Telemetry.DeleteCells (DATASCIENCE.RUN_DELETE_CELLS) +// (75). Telemetry.DeleteCells (DATASCIENCE.RUN_DELETE_CELLS) // Cell Edit Command in Interactive Window /* __GDPR__ "DATASCIENCE.RUN_DELETE_CELLS" : { @@ -1045,7 +1086,7 @@ ] } */ -// (73). Telemetry.ExtendSelectionByCellAbove (DATASCIENCE.RUN_EXTEND_SELECTION_BY_CELL_ABOVE) +// (76). Telemetry.ExtendSelectionByCellAbove (DATASCIENCE.RUN_EXTEND_SELECTION_BY_CELL_ABOVE) // Cell Selection Command in Interactive Window /* __GDPR__ "DATASCIENCE.RUN_EXTEND_SELECTION_BY_CELL_ABOVE" : { @@ -1055,7 +1096,7 @@ ] } */ -// (74). Telemetry.ExtendSelectionByCellBelow (DATASCIENCE.RUN_EXTEND_SELECTION_BY_CELL_BELOW) +// (77). Telemetry.ExtendSelectionByCellBelow (DATASCIENCE.RUN_EXTEND_SELECTION_BY_CELL_BELOW) // Cell Selection Command in Interactive Window /* __GDPR__ "DATASCIENCE.RUN_EXTEND_SELECTION_BY_CELL_BELOW" : { @@ -1065,7 +1106,7 @@ ] } */ -// (75). Telemetry.RunFileInteractive (DATASCIENCE.RUN_FILE_INTERACTIVE) +// (78). Telemetry.RunFileInteractive (DATASCIENCE.RUN_FILE_INTERACTIVE) // Command to Run the active file in the Interactive Window /* __GDPR__ "DATASCIENCE.RUN_FILE_INTERACTIVE" : { @@ -1075,7 +1116,7 @@ ] } */ -// (76). Telemetry.RunFromLine (DATASCIENCE.RUN_FROM_LINE) +// (79). Telemetry.RunFromLine (DATASCIENCE.RUN_FROM_LINE) // Command to Run the active file contents from the cursor location in the Interactive Window /* __GDPR__ "DATASCIENCE.RUN_FROM_LINE" : { @@ -1085,7 +1126,7 @@ ] } */ -// (77). Telemetry.InsertCellAbove (DATASCIENCE.RUN_INSERT_CELL_ABOVE) +// (80). Telemetry.InsertCellAbove (DATASCIENCE.RUN_INSERT_CELL_ABOVE) // Cell Edit Command in Interactive Window /* __GDPR__ "DATASCIENCE.RUN_INSERT_CELL_ABOVE" : { @@ -1095,7 +1136,7 @@ ] } */ -// (78). Telemetry.InsertCellBelow (DATASCIENCE.RUN_INSERT_CELL_BELOW) +// (81). Telemetry.InsertCellBelow (DATASCIENCE.RUN_INSERT_CELL_BELOW) // Cell Edit Command in Interactive Window /* __GDPR__ "DATASCIENCE.RUN_INSERT_CELL_BELOW" : { @@ -1105,7 +1146,7 @@ ] } */ -// (79). Telemetry.InsertCellBelowPosition (DATASCIENCE.RUN_INSERT_CELL_BELOW_POSITION) +// (82). Telemetry.InsertCellBelowPosition (DATASCIENCE.RUN_INSERT_CELL_BELOW_POSITION) // Cell Edit Command in Interactive Window /* __GDPR__ "DATASCIENCE.RUN_INSERT_CELL_BELOW_POSITION" : { @@ -1115,7 +1156,7 @@ ] } */ -// (80). Telemetry.MoveCellsDown (DATASCIENCE.RUN_MOVE_CELLS_DOWN) +// (83). Telemetry.MoveCellsDown (DATASCIENCE.RUN_MOVE_CELLS_DOWN) // Cell Edit Command in Interactive Window /* __GDPR__ "DATASCIENCE.RUN_MOVE_CELLS_DOWN" : { @@ -1125,7 +1166,7 @@ ] } */ -// (81). Telemetry.MoveCellsUp (DATASCIENCE.RUN_MOVE_CELLS_UP) +// (84). Telemetry.MoveCellsUp (DATASCIENCE.RUN_MOVE_CELLS_UP) // Cell Edit Command in Interactive Window /* __GDPR__ "DATASCIENCE.RUN_MOVE_CELLS_UP" : { @@ -1135,7 +1176,7 @@ ] } */ -// (82). Telemetry.SelectCell (DATASCIENCE.RUN_SELECT_CELL) +// (85). Telemetry.SelectCell (DATASCIENCE.RUN_SELECT_CELL) // Cell Selection Command in Interactive Window /* __GDPR__ "DATASCIENCE.RUN_SELECT_CELL" : { @@ -1145,7 +1186,7 @@ ] } */ -// (83). Telemetry.SelectCellContents (DATASCIENCE.RUN_SELECT_CELL_CONTENTS) +// (86). Telemetry.SelectCellContents (DATASCIENCE.RUN_SELECT_CELL_CONTENTS) // Cell Selection Command in Interactive Window /* __GDPR__ "DATASCIENCE.RUN_SELECT_CELL_CONTENTS" : { @@ -1155,7 +1196,7 @@ ] } */ -// (84). Telemetry.RunSelectionOrLine (DATASCIENCE.RUN_SELECTION_OR_LINE) +// (87). Telemetry.RunSelectionOrLine (DATASCIENCE.RUN_SELECTION_OR_LINE) // Command to Run a Selection or Line in the Interactive Window /* __GDPR__ "DATASCIENCE.RUN_SELECTION_OR_LINE" : { @@ -1165,7 +1206,7 @@ ] } */ -// (85). Telemetry.RunToLine (DATASCIENCE.RUN_TO_LINE) +// (88). Telemetry.RunToLine (DATASCIENCE.RUN_TO_LINE) // Command to Run the active file contents up to the cursor location in the Interactive Window /* __GDPR__ "DATASCIENCE.RUN_TO_LINE" : { @@ -1175,7 +1216,7 @@ ] } */ -// (86). Telemetry.SelfCertsMessageClose (DATASCIENCE.SELFCERTSMESSAGECLOSE) +// (89). Telemetry.SelfCertsMessageClose (DATASCIENCE.SELFCERTSMESSAGECLOSE) // Sent when users chose not to allow connecting to Jupyter over HTTPS when certificate isn't trusted by a trusted CA. /* __GDPR__ "DATASCIENCE.SELFCERTSMESSAGECLOSE" : { @@ -1185,7 +1226,7 @@ ] } */ -// (87). Telemetry.SelfCertsMessageEnabled (DATASCIENCE.SELFCERTSMESSAGEENABLED) +// (90). Telemetry.SelfCertsMessageEnabled (DATASCIENCE.SELFCERTSMESSAGEENABLED) // Sent when users chose to use self-signed certificates when connecting to Jupyter over https. // Basically this means users has chosen to connect to Jupyter over HTTPS when certificate isn't trusted by a trusted CA. /* __GDPR__ @@ -1196,7 +1237,7 @@ ] } */ -// (88). Telemetry.ShowDataViewer (DATASCIENCE.SHOW_DATA_EXPLORER) +// (91). Telemetry.ShowDataViewer (DATASCIENCE.SHOW_DATA_EXPLORER) // Request was made to show the data viewer with specific data frame info. /* __GDPR__ "DATASCIENCE.SHOW_DATA_EXPLORER" : { @@ -1208,7 +1249,7 @@ ] } */ -// (89). Telemetry.ShowDataViewerRowsLoaded (DATASCIENCE.SHOW_DATA_EXPLORER_ROWS_LOADED) +// (92). Telemetry.ShowDataViewerRowsLoaded (DATASCIENCE.SHOW_DATA_EXPLORER_ROWS_LOADED) // Data viewer loads rows in chunks, this event is sent when the rows have all been loaded /* __GDPR__ "DATASCIENCE.SHOW_DATA_EXPLORER_ROWS_LOADED" : { @@ -1219,7 +1260,7 @@ ] } */ -// (90). Telemetry.StartShowDataViewer (DATASCIENCE.START_SHOW_DATA_EXPLORER) +// (93). Telemetry.StartShowDataViewer (DATASCIENCE.START_SHOW_DATA_EXPLORER) // User requested to open the data frame viewer. /* __GDPR__ "DATASCIENCE.START_SHOW_DATA_EXPLORER" : { @@ -1229,7 +1270,7 @@ ] } */ -// (91). Telemetry.UserDidNotInstallJupyter (DATASCIENCE.USER_DID_NOT_INSTALL_JUPYTER) +// (94). Telemetry.UserDidNotInstallJupyter (DATASCIENCE.USER_DID_NOT_INSTALL_JUPYTER) // Sent when user click `cancel` button when prompted to install Jupyter. /* __GDPR__ "DATASCIENCE.USER_DID_NOT_INSTALL_JUPYTER" : { @@ -1239,7 +1280,7 @@ ] } */ -// (92). Telemetry.UserDidNotInstallPandas (DATASCIENCE.USER_DID_NOT_INSTALL_PANDAS) +// (95). Telemetry.UserDidNotInstallPandas (DATASCIENCE.USER_DID_NOT_INSTALL_PANDAS) // Prompted to install Pandas and chose not to install // Note: This could be just ignoring the UI so not a user action. /* __GDPR__ @@ -1250,7 +1291,7 @@ ] } */ -// (93). Telemetry.UserInstalledJupyter (DATASCIENCE.USER_INSTALLED_JUPYTER) +// (96). Telemetry.UserInstalledJupyter (DATASCIENCE.USER_INSTALLED_JUPYTER) // Sent when user installs Jupyter. /* __GDPR__ "DATASCIENCE.USER_INSTALLED_JUPYTER" : { @@ -1260,7 +1301,7 @@ ] } */ -// (94). Telemetry.UserInstalledPandas (DATASCIENCE.USER_INSTALLED_PANDAS) +// (97). Telemetry.UserInstalledPandas (DATASCIENCE.USER_INSTALLED_PANDAS) // Installed the python Pandas package. /* __GDPR__ "DATASCIENCE.USER_INSTALLED_PANDAS" : { @@ -1270,7 +1311,7 @@ ] } */ -// (95). Telemetry.DataViewerUsingInterpreter (DATAVIEWER.USING_INTERPRETER) +// (98). Telemetry.DataViewerUsingInterpreter (DATAVIEWER.USING_INTERPRETER) // When the Data Viewer installer is using a Python interpreter to do the install. /* __GDPR__ "DATAVIEWER.USING_INTERPRETER" : { @@ -1280,7 +1321,7 @@ ] } */ -// (96). Telemetry.DataViewerUsingKernel (DATAVIEWER.USING_KERNEL) +// (99). Telemetry.DataViewerUsingKernel (DATAVIEWER.USING_KERNEL) // When the Data Viewer installer is using the Kernel to do the install. /* __GDPR__ "DATAVIEWER.USING_KERNEL" : { @@ -1290,7 +1331,7 @@ ] } */ -// (97). Telemetry.DataViewerWebviewLoaded (DATAVIEWER.WEBVIEW_LOADED) +// (100). Telemetry.DataViewerWebviewLoaded (DATAVIEWER.WEBVIEW_LOADED) // The Data Viewer webview was loaded. /* __GDPR__ "DATAVIEWER.WEBVIEW_LOADED" : { @@ -1300,7 +1341,7 @@ ] } */ -// (98). Telemetry.ActiveInterpreterListingPerf (DS_INTERNAL.ACTIVE_INTERPRETER_LISTING_PERF) +// (101). Telemetry.ActiveInterpreterListingPerf (DS_INTERNAL.ACTIVE_INTERPRETER_LISTING_PERF) // Total time taken by Python extension to return the active Python environment. /* __GDPR__ "DS_INTERNAL.ACTIVE_INTERPRETER_LISTING_PERF" : { @@ -1311,7 +1352,7 @@ ] } */ -// (99). Telemetry.CellOutputMimeType (DS_INTERNAL.CELL_OUTPUT_MIME_TYPE) +// (102). Telemetry.CellOutputMimeType (DS_INTERNAL.CELL_OUTPUT_MIME_TYPE) // Mime type of a cell output. // Used to detect the popularity of a mime type, that would help determine which mime types are most common. // E.g. if we see widget mimetype, then we know how many use ipywidgets and the like and helps us prioritize widget issues, @@ -1327,7 +1368,7 @@ ] } */ -// (100). Telemetry.CodeLensAverageAcquisitionTime (DS_INTERNAL.CODE_LENS_ACQ_TIME) +// (103). Telemetry.CodeLensAverageAcquisitionTime (DS_INTERNAL.CODE_LENS_ACQ_TIME) // How long on average we spent parsing code lens. Sent on shutdown. // We should be able to deprecate in favor of DocumentWithCodeCells, but we should compare the numbers first. /* __GDPR__ @@ -1338,7 +1379,7 @@ ] } */ -// (101). Telemetry.CommandExecuted (DS_INTERNAL.COMMAND_EXECUTED) +// (104). Telemetry.CommandExecuted (DS_INTERNAL.COMMAND_EXECUTED) // A command that the extension contributes is executed. /* __GDPR__ "DS_INTERNAL.COMMAND_EXECUTED" : { @@ -1349,7 +1390,7 @@ ] } */ -// (102). Telemetry.CreateInteractiveWindow (DS_INTERNAL.CREATED_INTERACTIVE_WINDOW) +// (105). Telemetry.CreateInteractiveWindow (DS_INTERNAL.CREATED_INTERACTIVE_WINDOW) /* __GDPR__ "DS_INTERNAL.CREATED_INTERACTIVE_WINDOW" : { "hasKernel": {"classification":"SystemMetaData","purpose":"FeatureInsight","comment":"If the kernel was known at the time of creation","owner":"amunger"}, @@ -1363,7 +1404,7 @@ ] } */ -// (103). Telemetry.DocumentWithCodeCells (DS_INTERNAL.DOCUMENT_WITH_CODE_CELLS) +// (106). Telemetry.DocumentWithCodeCells (DS_INTERNAL.DOCUMENT_WITH_CODE_CELLS) // Info about code lenses, count and average time to parse the document. /* __GDPR__ "DS_INTERNAL.DOCUMENT_WITH_CODE_CELLS" : { @@ -1375,7 +1416,7 @@ ] } */ -// (104). Telemetry.ExperimentLoad (DS_INTERNAL.EXPERIMENT_LOAD) +// (107). Telemetry.ExperimentLoad (DS_INTERNAL.EXPERIMENT_LOAD) // Telemetry event sent with perf measures related to loading experiments. /* __GDPR__ "DS_INTERNAL.EXPERIMENT_LOAD" : { @@ -1385,7 +1426,7 @@ ] } */ -// (105). Telemetry.GetActivatedEnvironmentVariables (DS_INTERNAL.GET_ACTIVATED_ENV_VARIABLES) +// (108). Telemetry.GetActivatedEnvironmentVariables (DS_INTERNAL.GET_ACTIVATED_ENV_VARIABLES) // Used to capture time taken to get environment variables for a python environment. // Also lets us know whether it worked or not. /* __GDPR__ @@ -1399,7 +1440,7 @@ ] } */ -// (106). Telemetry.GetPasswordFailure (DS_INTERNAL.GET_PASSWORD_FAILURE) +// (109). Telemetry.GetPasswordFailure (DS_INTERNAL.GET_PASSWORD_FAILURE) // Sent to indicate we've failed to connect to a Remote Jupyter Server successfully after requesting a password. /* __GDPR__ "DS_INTERNAL.GET_PASSWORD_FAILURE" : { @@ -1409,7 +1450,7 @@ ] } */ -// (107). Telemetry.GetPasswordSuccess (DS_INTERNAL.GET_PASSWORD_SUCCESS) +// (110). Telemetry.GetPasswordSuccess (DS_INTERNAL.GET_PASSWORD_SUCCESS) // Sent to indicate we've connected to a Remote Jupyter Server successfully after requesting a password. /* __GDPR__ "DS_INTERNAL.GET_PASSWORD_SUCCESS" : { @@ -1419,7 +1460,7 @@ ] } */ -// (108). Telemetry.InteractiveFileTooltipsPerf (DS_INTERNAL.INTERACTIVE_FILE_TOOLTIPS_PERF) +// (111). Telemetry.InteractiveFileTooltipsPerf (DS_INTERNAL.INTERACTIVE_FILE_TOOLTIPS_PERF) // How long it took to return our hover tooltips for a .py file. /* __GDPR__ "DS_INTERNAL.INTERACTIVE_FILE_TOOLTIPS_PERF" : { @@ -1430,7 +1471,7 @@ ] } */ -// (109). Telemetry.DiscoverIPyWidgetNamesPerf (DS_INTERNAL.IPYWIDGET_DISCOVER_WIDGETS_NB_EXTENSIONS) +// (112). Telemetry.DiscoverIPyWidgetNamesPerf (DS_INTERNAL.IPYWIDGET_DISCOVER_WIDGETS_NB_EXTENSIONS) // Total time taken to discover all IPyWidgets. // This is how long it takes to discover all widgets on disc (from python environment). /* __GDPR__ @@ -1442,7 +1483,7 @@ ] } */ -// (110). Telemetry.HashedIPyWidgetScriptDiscoveryError (DS_INTERNAL.IPYWIDGET_DISCOVERY_ERRORED) +// (113). Telemetry.HashedIPyWidgetScriptDiscoveryError (DS_INTERNAL.IPYWIDGET_DISCOVERY_ERRORED) // Something went wrong in looking for a widget. /* __GDPR__ "DS_INTERNAL.IPYWIDGET_DISCOVERY_ERRORED" : { @@ -1452,7 +1493,7 @@ ] } */ -// (111). Telemetry.IPyWidgetExtensionJsInfo (DS_INTERNAL.IPYWIDGET_EXTENSIONJS_INFO) +// (114). Telemetry.IPyWidgetExtensionJsInfo (DS_INTERNAL.IPYWIDGET_EXTENSIONJS_INFO) // Telemetry event sent once we've successfully or unsuccessfully parsed the extension.js file in the widget folder. // E.g. if we have a widget named ipyvolume, we attempt to parse the nbextensions/ipyvolume/extension.js file to get some info out of it. /* __GDPR__ @@ -1467,7 +1508,7 @@ ] } */ -// (112). Telemetry.IPyWidgetLoadFailure (DS_INTERNAL.IPYWIDGET_LOAD_FAILURE) +// (115). Telemetry.IPyWidgetLoadFailure (DS_INTERNAL.IPYWIDGET_LOAD_FAILURE) // Telemetry event sent when an ipywidget module fails to load. Module name is hashed. /* __GDPR__ "DS_INTERNAL.IPYWIDGET_LOAD_FAILURE" : { @@ -1481,7 +1522,7 @@ ] } */ -// (113). Telemetry.IPyWidgetLoadSuccess (DS_INTERNAL.IPYWIDGET_LOAD_SUCCESS) +// (116). Telemetry.IPyWidgetLoadSuccess (DS_INTERNAL.IPYWIDGET_LOAD_SUCCESS) // Telemetry event sent when an ipywidget module loads. Module name is hashed. /* __GDPR__ "DS_INTERNAL.IPYWIDGET_LOAD_SUCCESS" : { @@ -1493,7 +1534,7 @@ ] } */ -// (114). Telemetry.IPyWidgetOverhead (DS_INTERNAL.IPYWIDGET_OVERHEAD) +// (117). Telemetry.IPyWidgetOverhead (DS_INTERNAL.IPYWIDGET_OVERHEAD) // Telemetry event sent to indicate the overhead of syncing the kernel with the UI. /* __GDPR__ "DS_INTERNAL.IPYWIDGET_OVERHEAD" : { @@ -1507,7 +1548,7 @@ ] } */ -// (115). Telemetry.IPyWidgetPromptToUseCDN (DS_INTERNAL.IPYWIDGET_PROMPT_TO_USE_CDN) +// (118). Telemetry.IPyWidgetPromptToUseCDN (DS_INTERNAL.IPYWIDGET_PROMPT_TO_USE_CDN) // Telemetry sent when we prompt user to use a CDN for IPyWidget scripts. // This is always sent when we display a prompt. /* __GDPR__ @@ -1518,7 +1559,7 @@ ] } */ -// (116). Telemetry.IPyWidgetPromptToUseCDNSelection (DS_INTERNAL.IPYWIDGET_PROMPT_TO_USE_CDN_SELECTION) +// (119). Telemetry.IPyWidgetPromptToUseCDNSelection (DS_INTERNAL.IPYWIDGET_PROMPT_TO_USE_CDN_SELECTION) // Telemetry sent when user does something with the prompt displayed to user about using CDN for IPyWidget scripts. /* __GDPR__ "DS_INTERNAL.IPYWIDGET_PROMPT_TO_USE_CDN_SELECTION" : { @@ -1529,7 +1570,7 @@ ] } */ -// (117). Telemetry.IPyWidgetRenderFailure (DS_INTERNAL.IPYWIDGET_RENDER_FAILURE) +// (120). Telemetry.IPyWidgetRenderFailure (DS_INTERNAL.IPYWIDGET_RENDER_FAILURE) // Telemetry event sent when the widget render function fails (note, this may not be sufficient to capture all failures). /* __GDPR__ "DS_INTERNAL.IPYWIDGET_RENDER_FAILURE" : { @@ -1539,7 +1580,7 @@ ] } */ -// (118). Telemetry.IPyWidgetNbExtensionCopyTime (DS_INTERNAL.IPYWIDGET_TIME_TO_COPY_NBEXTENSIONS_DIR) +// (121). Telemetry.IPyWidgetNbExtensionCopyTime (DS_INTERNAL.IPYWIDGET_TIME_TO_COPY_NBEXTENSIONS_DIR) // Total time take to copy the nb extensions folder. /* __GDPR__ "DS_INTERNAL.IPYWIDGET_TIME_TO_COPY_NBEXTENSIONS_DIR" : { @@ -1549,7 +1590,7 @@ ] } */ -// (119). Telemetry.IPyWidgetUnhandledMessage (DS_INTERNAL.IPYWIDGET_UNHANDLED_MESSAGE) +// (122). Telemetry.IPyWidgetUnhandledMessage (DS_INTERNAL.IPYWIDGET_UNHANDLED_MESSAGE) // Telemetry event sent when the widget tries to send a kernel message but nothing was listening /* __GDPR__ "DS_INTERNAL.IPYWIDGET_UNHANDLED_MESSAGE" : { @@ -1560,7 +1601,7 @@ ] } */ -// (120). Telemetry.HashedIPyWidgetNameUsed (DS_INTERNAL.IPYWIDGET_USED_BY_USER) +// (123). Telemetry.HashedIPyWidgetNameUsed (DS_INTERNAL.IPYWIDGET_USED_BY_USER) // Telemetry event sent with name of a Widget that is used. // Helps determine which widgets are used the most, and which are not. // Useful in prioritizing which widgets to work on if things fail to work. @@ -1578,7 +1619,7 @@ ] } */ -// (121). Telemetry.IPyWidgetWidgetVersionNotSupportedLoadFailure (DS_INTERNAL.IPYWIDGET_WIDGET_VERSION_NOT_SUPPORTED_LOAD_FAILURE) +// (124). Telemetry.IPyWidgetWidgetVersionNotSupportedLoadFailure (DS_INTERNAL.IPYWIDGET_WIDGET_VERSION_NOT_SUPPORTED_LOAD_FAILURE) // Telemetry event sent when an ipywidget version that is not supported is used & we have trapped this and warned the user abou it. /* __GDPR__ "DS_INTERNAL.IPYWIDGET_WIDGET_VERSION_NOT_SUPPORTED_LOAD_FAILURE" : { @@ -1590,7 +1631,7 @@ ] } */ -// (122). Telemetry.CheckPasswordJupyterHub (DS_INTERNAL.JUPYTER_HUB_PASSWORD) +// (125). Telemetry.CheckPasswordJupyterHub (DS_INTERNAL.JUPYTER_HUB_PASSWORD) // Sent when checking for passwords for Jupyter Hub /* __GDPR__ "DS_INTERNAL.JUPYTER_HUB_PASSWORD" : { @@ -1601,7 +1642,7 @@ ] } */ -// (123). Telemetry.KernelSpec (DS_INTERNAL.JUPYTER_KERNEL_SPEC) +// (126). Telemetry.KernelSpec (DS_INTERNAL.JUPYTER_KERNEL_SPEC) // Information about KernelSpecs /* __GDPR__ "DS_INTERNAL.JUPYTER_KERNEL_SPEC" : { @@ -1622,7 +1663,7 @@ ] } */ -// (124). Telemetry.StartedRemoteJupyterSessionWithBackingFile (DS_INTERNAL.JUPYTER_STARTED_SESSION_WITH_BACKING_FILE) +// (127). Telemetry.StartedRemoteJupyterSessionWithBackingFile (DS_INTERNAL.JUPYTER_STARTED_SESSION_WITH_BACKING_FILE) // Whether we managed to start a remote kernel successfully without a backing file. /* __GDPR__ "DS_INTERNAL.JUPYTER_STARTED_SESSION_WITH_BACKING_FILE" : { @@ -1635,7 +1676,7 @@ ] } */ -// (125). Telemetry.ZMQSupport (DS_INTERNAL.JUPYTER_ZMQ_SUPPORT) +// (128). Telemetry.ZMQSupport (DS_INTERNAL.JUPYTER_ZMQ_SUPPORT) // Information used to determine the zmq binary support. // the alpine, libc, armv version is used by the node module /* __GDPR__ @@ -1653,7 +1694,7 @@ ] } */ -// (126). Telemetry.ZMQSupportFailure (DS_INTERNAL.JUPYTER_ZMQ_SUPPORT_FAILURE) +// (129). Telemetry.ZMQSupportFailure (DS_INTERNAL.JUPYTER_ZMQ_SUPPORT_FAILURE) // Information used to determine the zmq binary support. // the alpine, libc, armv version is used by the node module /* __GDPR__ @@ -1674,7 +1715,7 @@ ] } */ -// (127). Telemetry.KernelCount (DS_INTERNAL.KERNEL_COUNT) +// (130). Telemetry.KernelCount (DS_INTERNAL.KERNEL_COUNT) // Telemetry sent with the total number of different types of kernels in the kernel picker. /* __GDPR__ "DS_INTERNAL.KERNEL_COUNT" : { @@ -1689,7 +1730,7 @@ ] } */ -// (128). Telemetry.KernelLauncherPerf (DS_INTERNAL.KERNEL_LAUNCHER_PERF) +// (131). Telemetry.KernelLauncherPerf (DS_INTERNAL.KERNEL_LAUNCHER_PERF) // Total time taken to Launch a raw kernel. /* __GDPR__ "DS_INTERNAL.KERNEL_LAUNCHER_PERF" : { @@ -1700,7 +1741,7 @@ ] } */ -// (129). Telemetry.NativeVariableViewLoaded (DS_INTERNAL.NATIVE_VARIABLE_VIEW_LOADED) +// (132). Telemetry.NativeVariableViewLoaded (DS_INTERNAL.NATIVE_VARIABLE_VIEW_LOADED) // The Variable View webview was loaded. /* __GDPR__ "DS_INTERNAL.NATIVE_VARIABLE_VIEW_LOADED" : { @@ -1710,7 +1751,7 @@ ] } */ -// (130). Telemetry.NativeVariableViewMadeVisible (DS_INTERNAL.NATIVE_VARIABLE_VIEW_MADE_VISIBLE) +// (133). Telemetry.NativeVariableViewMadeVisible (DS_INTERNAL.NATIVE_VARIABLE_VIEW_MADE_VISIBLE) // The Variable View webview was made visible. /* __GDPR__ "DS_INTERNAL.NATIVE_VARIABLE_VIEW_MADE_VISIBLE" : { @@ -1720,7 +1761,7 @@ ] } */ -// (131). Telemetry.NewFileForInteractiveWindow (DS_INTERNAL.NEW_FILE_USED_IN_INTERACTIVE) +// (134). Telemetry.NewFileForInteractiveWindow (DS_INTERNAL.NEW_FILE_USED_IN_INTERACTIVE) // Telemetry event sent when a user runs the interactive window with a new file /* __GDPR__ "DS_INTERNAL.NEW_FILE_USED_IN_INTERACTIVE" : { @@ -1730,7 +1771,7 @@ ] } */ -// (132). Telemetry.PerceivedJupyterStartupNotebook (DS_INTERNAL.PERCEIVED_JUPYTER_STARTUP_NOTEBOOK) +// (135). Telemetry.PerceivedJupyterStartupNotebook (DS_INTERNAL.PERCEIVED_JUPYTER_STARTUP_NOTEBOOK) // Time take for jupyter server to start and be ready to run first user cell. // (Note: The property `notebook` only gets sent correctly in Jupyter version 2022.8.0 or later) /* __GDPR__ @@ -1758,7 +1799,7 @@ ] } */ -// (133). Telemetry.PreferredKernelExactMatch (DS_INTERNAL.PREFERRED_KERNEL_EXACT_MATCH) +// (136). Telemetry.PreferredKernelExactMatch (DS_INTERNAL.PREFERRED_KERNEL_EXACT_MATCH) // Send we we complete our preferred kernel match. Matched reason might be 'no match'. /* __GDPR__ "DS_INTERNAL.PREFERRED_KERNEL_EXACT_MATCH" : { @@ -1769,7 +1810,7 @@ ] } */ -// (134). Telemetry.PythonExtensionInstalledViaKernelPicker (DS_INTERNAL.PYTHON_EXTENSION_INSTALLED_VIA_KERNEL_PICKER) +// (137). Telemetry.PythonExtensionInstalledViaKernelPicker (DS_INTERNAL.PYTHON_EXTENSION_INSTALLED_VIA_KERNEL_PICKER) // Python extension was attempted to be installed via the kernel picker command. /* __GDPR__ "DS_INTERNAL.PYTHON_EXTENSION_INSTALLED_VIA_KERNEL_PICKER" : { @@ -1780,7 +1821,7 @@ ] } */ -// (135). Telemetry.PythonExtensionNotInstalled (DS_INTERNAL.PYTHON_EXTENSION_NOT_INSTALLED) +// (138). Telemetry.PythonExtensionNotInstalled (DS_INTERNAL.PYTHON_EXTENSION_NOT_INSTALLED) // The kernel picker command to install python extension was shown. /* __GDPR__ "DS_INTERNAL.PYTHON_EXTENSION_NOT_INSTALLED" : { @@ -1791,7 +1832,7 @@ ] } */ -// (136). Telemetry.PythonModuleInstall (DS_INTERNAL.PYTHON_MODULE_INSTALL) +// (139). Telemetry.PythonModuleInstall (DS_INTERNAL.PYTHON_MODULE_INSTALL) // Telemetry sent when user is presented with a dialog to install a python package. // Also sent with the user's response to the dialog. /* __GDPR__ @@ -1808,7 +1849,7 @@ ] } */ -// (137). Telemetry.PythonNotInstalled (DS_INTERNAL.PYTHON_NOT_INSTALLED) +// (140). Telemetry.PythonNotInstalled (DS_INTERNAL.PYTHON_NOT_INSTALLED) // The kernel picker command to install python was shown. /* __GDPR__ "DS_INTERNAL.PYTHON_NOT_INSTALLED" : { @@ -1819,7 +1860,7 @@ ] } */ -// (138). Telemetry.RawKernelInfoResponse (DS_INTERNAL.RAWKERNEL_INFO_RESPONSE) +// (141). Telemetry.RawKernelInfoResponse (DS_INTERNAL.RAWKERNEL_INFO_RESPONSE) // After starting a kernel we send a request to get the kernel info. // This tracks the total time taken to get the response back (or wether we timedout). // If we timeout and later we find successful comms for this session, then timeout is too low @@ -1851,7 +1892,7 @@ ] } */ -// (139). Telemetry.RawKernelProcessLaunch (DS_INTERNAL.RAWKERNEL_PROCESS_LAUNCH) +// (142). Telemetry.RawKernelProcessLaunch (DS_INTERNAL.RAWKERNEL_PROCESS_LAUNCH) // Sent to measure time taken to spawn the raw kernel process. /* __GDPR__ "DS_INTERNAL.RAWKERNEL_PROCESS_LAUNCH" : { @@ -1861,7 +1902,7 @@ ] } */ -// (140). Telemetry.RawKernelSessionDisposed (DS_INTERNAL.RAWKERNEL_SESSION_DISPOSED) +// (143). Telemetry.RawKernelSessionDisposed (DS_INTERNAL.RAWKERNEL_SESSION_DISPOSED) // This event is sent when a RawSession's `dispose` method is called. // Used to determine what part of the code that shut down the session, so as to determine when and how the kernel session crashed. /* __GDPR__ @@ -1890,7 +1931,7 @@ ] } */ -// (141). Telemetry.RawKernelSessionKernelProcessExited (DS_INTERNAL.RAWKERNEL_SESSION_KERNEL_PROCESS_EXITED) +// (144). Telemetry.RawKernelSessionKernelProcessExited (DS_INTERNAL.RAWKERNEL_SESSION_KERNEL_PROCESS_EXITED) // This event is sent when the underlying kernelProcess for a // RawJupyterSession exits. /* __GDPR__ @@ -1920,7 +1961,7 @@ ] } */ -// (142). Telemetry.RawKernelSessionStartNoIpykernel (DS_INTERNAL.RAWKERNEL_SESSION_NO_IPYKERNEL) +// (145). Telemetry.RawKernelSessionStartNoIpykernel (DS_INTERNAL.RAWKERNEL_SESSION_NO_IPYKERNEL) // Telemetry event sent when raw kernel startup fails due to missing ipykernel dependency. // This is useful to see what the user does with this error message. /* __GDPR__ @@ -1949,7 +1990,7 @@ ] } */ -// (143). Telemetry.RawKernelSessionShutdown (DS_INTERNAL.RAWKERNEL_SESSION_SHUTDOWN) +// (146). Telemetry.RawKernelSessionShutdown (DS_INTERNAL.RAWKERNEL_SESSION_SHUTDOWN) // This event is sent when a RawJupyterSession's `shutdownSession` method is called. // Used to determine what part of the code that shut down the session, so as to determine when and how the kernel session crashed. /* __GDPR__ @@ -1979,7 +2020,7 @@ ] } */ -// (144). Telemetry.RunTest (DS_INTERNAL.RUNTEST) +// (147). Telemetry.RunTest (DS_INTERNAL.RUNTEST) // A automated test has been run /* __GDPR__ "DS_INTERNAL.RUNTEST" : { @@ -1994,7 +2035,7 @@ ] } */ -// (145). Telemetry.ShiftEnterBannerShown (DS_INTERNAL.SHIFTENTER_BANNER_SHOWN) +// (148). Telemetry.ShiftEnterBannerShown (DS_INTERNAL.SHIFTENTER_BANNER_SHOWN) // Information banner displayed to give the user the option to configure shift+enter for the Interactive Window. /* __GDPR__ "DS_INTERNAL.SHIFTENTER_BANNER_SHOWN" : { @@ -2004,7 +2045,7 @@ ] } */ -// (146). Telemetry.PandasNotInstalled (DS_INTERNAL.SHOW_DATA_NO_PANDAS) +// (149). Telemetry.PandasNotInstalled (DS_INTERNAL.SHOW_DATA_NO_PANDAS) // User tried to open the data viewer and Pandas package was not installed. // Note: Not a failure state, as we prompt for install after this. /* __GDPR__ @@ -2015,7 +2056,7 @@ ] } */ -// (147). Telemetry.PandasInstallCanceled (DS_INTERNAL.SHOW_DATA_PANDAS_INSTALL_CANCELED) +// (150). Telemetry.PandasInstallCanceled (DS_INTERNAL.SHOW_DATA_PANDAS_INSTALL_CANCELED) // When opening the data viewer the user was prompted to install / upgrade // pandas and choose to cancel the operation. /* __GDPR__ @@ -2026,7 +2067,7 @@ ] } */ -// (148). Telemetry.PandasOK (DS_INTERNAL.SHOW_DATA_PANDAS_OK) +// (151). Telemetry.PandasOK (DS_INTERNAL.SHOW_DATA_PANDAS_OK) // When opening the data viewer the version of Pandas installed was ok. /* __GDPR__ "DS_INTERNAL.SHOW_DATA_PANDAS_OK" : { @@ -2036,7 +2077,7 @@ ] } */ -// (149). Telemetry.PandasTooOld (DS_INTERNAL.SHOW_DATA_PANDAS_TOO_OLD) +// (152). Telemetry.PandasTooOld (DS_INTERNAL.SHOW_DATA_PANDAS_TOO_OLD) // When opening the data viewer the version of Pandas installed was too old. /* __GDPR__ "DS_INTERNAL.SHOW_DATA_PANDAS_TOO_OLD" : { @@ -2046,7 +2087,7 @@ ] } */ -// (150). Telemetry.SwitchKernel (DS_INTERNAL.SWITCH_KERNEL) +// (153). Telemetry.SwitchKernel (DS_INTERNAL.SWITCH_KERNEL) // Triggered when the kernel selection changes (note: This can also happen automatically when a notebook is opened). // WARNING: Due to changes in VS Code, this isn't necessarily a user action, hence difficult to tell if the user changed it or it changed automatically. /* __GDPR__ @@ -2074,7 +2115,7 @@ ] } */ -// (151). Telemetry.VariableExplorerFetchTime (DS_INTERNAL.VARIABLE_EXPLORER_FETCH_TIME) +// (154). Telemetry.VariableExplorerFetchTime (DS_INTERNAL.VARIABLE_EXPLORER_FETCH_TIME) // How long did it take for a single variable request to be resolved. /* __GDPR__ "DS_INTERNAL.VARIABLE_EXPLORER_FETCH_TIME" : { @@ -2084,7 +2125,7 @@ ] } */ -// (152). Telemetry.VariableExplorerVariableCount (DS_INTERNAL.VARIABLE_EXPLORER_VARIABLE_COUNT) +// (155). Telemetry.VariableExplorerVariableCount (DS_INTERNAL.VARIABLE_EXPLORER_VARIABLE_COUNT) // Count how many variables were in a variable request. /* __GDPR__ "DS_INTERNAL.VARIABLE_EXPLORER_VARIABLE_COUNT" : { @@ -2095,7 +2136,7 @@ ] } */ -// (153). Telemetry.VSCNotebookCellTranslationFailed (DS_INTERNAL.VSCNOTEBOOK_CELL_TRANSLATION_FAILED) +// (156). Telemetry.VSCNotebookCellTranslationFailed (DS_INTERNAL.VSCNOTEBOOK_CELL_TRANSLATION_FAILED) // We've failed to translate a Jupyter cell output for serialization into a Notebook cell. /* __GDPR__ "DS_INTERNAL.VSCNOTEBOOK_CELL_TRANSLATION_FAILED" : { @@ -2106,7 +2147,7 @@ ] } */ -// (154). EventName.ENVFILE_VARIABLE_SUBSTITUTION (ENVFILE_VARIABLE_SUBSTITUTION) +// (157). EventName.ENVFILE_VARIABLE_SUBSTITUTION (ENVFILE_VARIABLE_SUBSTITUTION) // Telemetry event sent when substituting Environment variables to calculate value of variables. // E.g. user has a a .env file with tokens that need to be replaced with env variables. // such as an env file having the variable `${HOME}`. @@ -2119,7 +2160,7 @@ ] } */ -// (155). EventName.ENVFILE_WORKSPACE (ENVFILE_WORKSPACE) +// (158). EventName.ENVFILE_WORKSPACE (ENVFILE_WORKSPACE) // Telemetry event sent when an environment file is detected in the workspace. /* __GDPR__ "ENVFILE_WORKSPACE" : { @@ -2129,7 +2170,7 @@ ] } */ -// (156). EventName.EXTENSION_LOAD (EXTENSION.LOAD) +// (159). EventName.EXTENSION_LOAD (EXTENSION.LOAD) // Telemetry event sent with perf measures related to activation and loading of extension. /* __GDPR__ "EXTENSION.LOAD" : { @@ -2142,7 +2183,7 @@ ] } */ -// (157). Telemetry.AmbiguousGlobalKernelSpec (GLOBAL_PYTHON_KERNELSPEC) +// (160). Telemetry.AmbiguousGlobalKernelSpec (GLOBAL_PYTHON_KERNELSPEC) // We have a Python Kernel spec without fully qualified path of Python env. // We have no idea how to start these kernels if the user has more than one Python env. // @@ -2166,7 +2207,7 @@ ] } */ -// (158). EventName.HASHED_PACKAGE_NAME (HASHED_PACKAGE_NAME) +// (161). EventName.HASHED_PACKAGE_NAME (HASHED_PACKAGE_NAME) // Telemetry event sent with hash of an imported python package. // Used to detect the popularity of a package, that would help determine which packages // need to be prioritized when resolving issues with intellisense or supporting similar issues related to a (known) specific package. @@ -2181,7 +2222,7 @@ ] } */ -// (159). EventName.OPEN_DATAVIEWER_FROM_VARIABLE_WINDOW_ERROR (OPEN_DATAVIEWER_FROM_VARIABLE_WINDOW_ERROR_EX) +// (162). EventName.OPEN_DATAVIEWER_FROM_VARIABLE_WINDOW_ERROR (OPEN_DATAVIEWER_FROM_VARIABLE_WINDOW_ERROR_EX) // Telemetry event sent when user opens the data viewer via the variable view and there is an error in doing so. /* __GDPR__ "OPEN_DATAVIEWER_FROM_VARIABLE_WINDOW_ERROR_EX" : { @@ -2191,7 +2232,7 @@ ] } */ -// (160). EventName.OPEN_DATAVIEWER_FROM_VARIABLE_WINDOW_REQUEST (OPEN_DATAVIEWER_FROM_VARIABLE_WINDOW_REQUEST_EX) +// (163). EventName.OPEN_DATAVIEWER_FROM_VARIABLE_WINDOW_REQUEST (OPEN_DATAVIEWER_FROM_VARIABLE_WINDOW_REQUEST_EX) // Telemetry event sent when user opens the data viewer via the variable view. /* __GDPR__ "OPEN_DATAVIEWER_FROM_VARIABLE_WINDOW_REQUEST_EX" : { @@ -2201,7 +2242,7 @@ ] } */ -// (161). EventName.OPEN_DATAVIEWER_FROM_VARIABLE_WINDOW_SUCCESS (OPEN_DATAVIEWER_FROM_VARIABLE_WINDOW_SUCCESS_EX) +// (164). EventName.OPEN_DATAVIEWER_FROM_VARIABLE_WINDOW_SUCCESS (OPEN_DATAVIEWER_FROM_VARIABLE_WINDOW_SUCCESS_EX) // Telemetry event sent when user opens the data viewer via the variable view and we successfully open the view. /* __GDPR__ "OPEN_DATAVIEWER_FROM_VARIABLE_WINDOW_SUCCESS_EX" : { @@ -2211,7 +2252,7 @@ ] } */ -// (162). Telemetry.PlotViewerWebviewLoaded (PLOTVIEWER.WEBVIEW_LOADED) +// (165). Telemetry.PlotViewerWebviewLoaded (PLOTVIEWER.WEBVIEW_LOADED) // The Plot Viewer webview was loaded. /* __GDPR__ "PLOTVIEWER.WEBVIEW_LOADED" : { diff --git a/src/platform/common/constants.ts b/src/platform/common/constants.ts index 02984972254..d9d38353898 100644 --- a/src/platform/common/constants.ts +++ b/src/platform/common/constants.ts @@ -475,7 +475,10 @@ export enum Telemetry { DataViewerUsingKernel = 'DATAVIEWER.USING_KERNEL', DataViewerWebviewLoaded = 'DATAVIEWER.WEBVIEW_LOADED', PlotViewerWebviewLoaded = 'PLOTVIEWER.WEBVIEW_LOADED', - EnterRemoteJupyterUrl = 'DATASCIENCE.ENTER_REMOTE_JUPYTER_URL' + EnterRemoteJupyterUrl = 'DATASCIENCE.ENTER_REMOTE_JUPYTER_URL', + NativeNotebookExecutionPerformance = 'DATASCIENCE.JUPYTER_NOTEBOOK_EXEC_PERFORMANCE', + JupyterNotebookExecutionPerformance = 'DATASCIENCE.JUPYTER_JUPYTER_NOTEBOOK_EXEC_PERFORMANCE', + NativeNotebookEditPerformance = 'DATASCIENCE.JUPYTER_NOTEBOOK_EDIT_PERFORMANCE' } export enum JupyterCommands { diff --git a/src/platform/telemetry/index.ts b/src/platform/telemetry/index.ts index f4f53656bab..aea1861822a 100644 --- a/src/platform/telemetry/index.ts +++ b/src/platform/telemetry/index.ts @@ -2,7 +2,7 @@ // Licensed under the MIT License. import type TelemetryReporter from '@vscode/extension-telemetry'; -import { AppinsightsKey, isTestExecution, isUnitTestExecution } from '../common/constants'; +import { AppinsightsKey, Telemetry, isTestExecution, isUnitTestExecution } from '../common/constants'; import { traceError } from '../logging'; import { StopWatch } from '../common/utils/stopWatch'; import { ExcludeType, noop, PickType, UnionToIntersection } from '../common/utils/misc'; @@ -130,7 +130,7 @@ export function sendTelemetryEvent

= { + owner: 'donjayamanne', + feature: ['Notebook', 'Notebook'], + tags: ['Widgets'], + source: 'N/A', + measures: { + ...commonClassificationForDurationProperties(), + codeCellCount: { + classification: 'SystemMetaData', + purpose: 'PerformanceAndHealth', + comment: 'Total number of code cells.', + isMeasurement: true + }, + markdownCellCount: { + classification: 'SystemMetaData', + purpose: 'PerformanceAndHealth', + comment: 'Total number of markdown cells.', + isMeasurement: true + }, + preExecuteDuration: { + classification: 'SystemMetaData', + purpose: 'PerformanceAndHealth', + comment: 'Total time spent in VS Code before starting execution.', + isMeasurement: true + }, + executeDuration: { + classification: 'SystemMetaData', + purpose: 'PerformanceAndHealth', + comment: 'Total time spent executing cells.', + isMeasurement: true + }, + postExecuteDuration: { + classification: 'SystemMetaData', + purpose: 'PerformanceAndHealth', + comment: 'Total time spent in VS Code after executing cells.', + isMeasurement: true + } + }, + properties: { + outputType: { + classification: 'PublicNonPersonalData', + purpose: 'FeatureInsight', + comment: 'The kind of outputs generated in the notebook, text, html or images.' + } + } + }; + /** + * Telemetry sent during test on CI to measure performance of execution of large notebooks. + */ + [Telemetry.JupyterNotebookExecutionPerformance]: TelemetryEventInfo< + DurationMeasurement & { + /** + * The kind of outputs generated in the notebook + */ + outputType: 'text' | 'html' | 'image'; + /** + * Code cell count + */ + codeCellCount: number; + /** + * Code cell count + */ + markdownCellCount: number; + } + > = { + owner: 'donjayamanne', + feature: ['Notebook', 'Notebook'], + tags: ['Widgets'], + source: 'N/A', + measures: { + ...commonClassificationForDurationProperties(), + codeCellCount: { + classification: 'SystemMetaData', + purpose: 'PerformanceAndHealth', + comment: 'Total number of code cells.', + isMeasurement: true + }, + markdownCellCount: { + classification: 'SystemMetaData', + purpose: 'PerformanceAndHealth', + comment: 'Total number of markdown cells.', + isMeasurement: true + } + }, + properties: { + outputType: { + classification: 'PublicNonPersonalData', + purpose: 'FeatureInsight', + comment: 'The kind of outputs generated in the notebook, text, html or images.' + } + } + }; + /** + * Telemetry sent during test on CI to measure performance of execution of large notebooks. + */ + [Telemetry.NativeNotebookEditPerformance]: TelemetryEventInfo< + DurationMeasurement & { + /** + * Code cell count + */ + codeCellCount: number; + /** + * Code cell count + */ + markdownCellCount: number; + } + > = { + owner: 'donjayamanne', + feature: ['Notebook', 'Notebook'], + tags: ['Widgets'], + source: 'N/A', + measures: { + ...commonClassificationForDurationProperties(), + codeCellCount: { + classification: 'SystemMetaData', + purpose: 'PerformanceAndHealth', + comment: 'Total number of code cells.', + isMeasurement: true + }, + markdownCellCount: { + classification: 'SystemMetaData', + purpose: 'PerformanceAndHealth', + comment: 'Total number of markdown cells.', + isMeasurement: true + } + } + }; } diff --git a/src/test/common.node.ts b/src/test/common.node.ts index b86992b81b1..2f2fc392366 100644 --- a/src/test/common.node.ts +++ b/src/test/common.node.ts @@ -139,6 +139,17 @@ function getPythonPath(): string { if (process.env.CI_PYTHON_PATH && fs.existsSync(process.env.CI_PYTHON_PATH)) { return getNormalizedInterpreterPath(process.env.CI_PYTHON_PATH); } + if (os.platform() === 'win32') { + const venv = path.join(__dirname, '..', '..', '.venv', 'Scripts', 'python.exe'); + if (fs.existsSync(venv)) { + return venv; + } + } else { + const venv = path.join(__dirname, '..', '..', '.venv', 'bin', 'python'); + if (fs.existsSync(venv)) { + return venv; + } + } // eslint-disable-next-line // TODO: Change this to python3. // See https://github.com/microsoft/vscode-python/issues/10910. diff --git a/src/test/constants.node.ts b/src/test/constants.node.ts index 595698677b1..425c8fbf588 100644 --- a/src/test/constants.node.ts +++ b/src/test/constants.node.ts @@ -36,5 +36,5 @@ setTestSettings({ isCondaTest: (process.env.VSC_JUPYTER_CI_IS_CONDA || '').toLowerCase() === 'true', isNonRawNativeTest: (process.env.VSC_JUPYTER_NON_RAW_NATIVE_TEST || '').toLowerCase() === 'true', isRemoteNativeTest: (process.env.VSC_JUPYTER_REMOTE_NATIVE_TEST || '').toLowerCase() === 'true', - isPerfTest: process.env.VSC_JUPYTER_PERF_TEST === '1' + isPerfTest: process.env.VSC_JUPYTER_PERF_TEST === '1' || process.env.VSC_JUPYTER_PERF_TEST === 'true' }); diff --git a/src/test/constants.ts b/src/test/constants.ts index 1321bb90810..82c2a0b38d6 100644 --- a/src/test/constants.ts +++ b/src/test/constants.ts @@ -2,6 +2,7 @@ // Licensed under the MIT License. export const JVSC_EXTENSION_ID_FOR_TESTS = 'ms-toolsai.jupyter'; +export const PerformanceExtensionId = 'ms-toolsai.vscode-notebook-perf'; export type TestSettingsType = { isSmokeTest: boolean; diff --git a/src/test/initialize.node.ts b/src/test/initialize.node.ts index bf71f85b9f1..13877dac919 100644 --- a/src/test/initialize.node.ts +++ b/src/test/initialize.node.ts @@ -21,7 +21,10 @@ export async function initializePython() { initializeCommonNodeApi(); await setPythonPathInWorkspaceRoot(PYTHON_PATH); // Make sure the python extension can load if this test allows it - if (!process.env.VSC_JUPYTER_CI_TEST_DO_NOT_INSTALL_PYTHON_EXT) { + if ( + !process.env.VSC_JUPYTER_CI_TEST_DO_NOT_INSTALL_PYTHON_EXT && + process.env.VSC_JUPYTER_CI_TEST_DO_NOT_INSTALL_PYTHON_EXT !== 'false' + ) { const extension = vscode.extensions.getExtension(PythonExtension); if (!extension) { console.error('Python extension not found'); diff --git a/src/test/performance/executionPerf.vscode.test.ts b/src/test/performance/executionPerf.vscode.test.ts new file mode 100644 index 00000000000..2f867cab773 --- /dev/null +++ b/src/test/performance/executionPerf.vscode.test.ts @@ -0,0 +1,133 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import { + window, + extensions, + NotebookCellKind, + workspace, + NotebookCellData, + NotebookData, + commands, + type NotebookDocument +} from 'vscode'; +import { JupyterNotebookView, Telemetry } from '../../platform/common/constants'; +import { StopWatch } from '../../platform/common/utils/stopWatch'; +import { sendTelemetryEvent } from '../../telemetry'; +import { JVSC_EXTENSION_ID_FOR_TESTS } from '../constants.node'; +import { PVSC_EXTENSION_ID, PythonExtension, type Environment } from '@vscode/python-extension'; +import { PYTHON_PATH, sleep } from '../common.node'; +import type { IExtensionApi } from '../../standalone/api'; +import { DisposableStore } from '../../platform/common/utils/lifecycle'; +import { countCells, startKernelUsingApiByRunningFirstAvailableCodeCell } from '../utils/notebook'; + +suite('Extension Performance (@executionPerformance)', function () { + let notebook: NotebookDocument; + let activeEnv: Environment; + this.timeout(120_000); + const disposables = new DisposableStore(); + suiteSetup(async function () { + this.timeout(120_000); + const pythonExt = extensions.getExtension(PVSC_EXTENSION_ID)!; + await Promise.all([extensions.getExtension(JVSC_EXTENSION_ID_FOR_TESTS)!.activate(), pythonExt.activate()]); + + // start this. + const resolvedEnv = await pythonExt.exports.environments.resolveEnvironment(PYTHON_PATH); + if (!resolvedEnv) { + throw new Error(`Unable to resolve environment for ${PYTHON_PATH}`); + } + activeEnv = resolvedEnv; + }); + setup(async () => { + await commands.executeCommand('workbench.action.revertAndCloseActiveEditor'); + await commands.executeCommand('workbench.action.closeAllEditors'); + }); + teardown(async () => { + disposables.clear(); + await commands.executeCommand('workbench.action.revertAndCloseActiveEditor'); + await commands.executeCommand('workbench.action.closeAllEditors'); + }); + test('Text output', async () => runAndSendTelemetry('text')); + test('Html output', async () => runAndSendTelemetry('html')); + test('Image output', async () => runAndSendTelemetry('image')); + + async function createAndSetupNotebook(outputType: 'text' | 'html' | 'image') { + notebook = await workspace.openNotebookDocument(JupyterNotebookView, generateNotebookData(1_000, outputType)); + await window.showNotebookDocument(notebook); + await commands.executeCommand('outline.focus'); + const jupyterExt = extensions.getExtension(JVSC_EXTENSION_ID_FOR_TESTS)!.exports; + await jupyterExt.openNotebook(notebook.uri, activeEnv); + } + + async function runAndSendTelemetry(outputType: 'text' | 'html' | 'image') { + await createAndSetupNotebook(outputType); + const { duration } = await runAllCellsAndWaitForExecutionToComplete(); + const { codeCellCount, markdownCellCount } = countCells(notebook); + sendTelemetryEvent( + Telemetry.JupyterNotebookExecutionPerformance, + { duration, codeCellCount, markdownCellCount }, + { outputType } + ); + } + async function runAllCellsAndWaitForExecutionToComplete() { + await startKernelUsingApiByRunningFirstAvailableCodeCell(notebook); + const stopWatch = new StopWatch(); + let duration = 0; + void commands.executeCommand('notebook.cell.execute', { + ranges: [{ start: 0, end: notebook.cellCount }], + document: notebook.uri + }); + + let lastUpdateTime = Date.now(); + let startedUpdating = false; + const getTimeElapsedSinceLastUpdate = () => (startedUpdating ? Date.now() - lastUpdateTime : 0); + disposables.add( + workspace.onDidChangeNotebookDocument(() => { + lastUpdateTime = Date.now(); + startedUpdating = true; + duration = stopWatch.elapsedTime; + }) + ); + + while (getTimeElapsedSinceLastUpdate() < 10_000) { + await sleep(1000); + } + // Looks like no more updates are coming in. + return { duration }; + } + function generateNotebookData(cellCount: number, outputType: 'text' | 'html' | 'image') { + const markdownCellContents = ['# Header1', '## Header2', '### Header3', 'Hello World'].join('\n'); + let cellIndex = 0; + let codeCellNumber = 0; + const cells: NotebookCellData[] = []; + while (true) { + let code = ''; + const cellKind = cellIndex % 2 === 0 ? NotebookCellKind.Code : NotebookCellKind.Markup; + if (cellKind === NotebookCellKind.Code) { + codeCellNumber += 1; + code = `print(${codeCellNumber})`; + if (outputType === 'html') { + code = `%%html\n

Hello World
`; + } else if (outputType === 'image') { + code = [ + `from IPython import display`, + `from base64 import b64decode`, + ``, + `base64_data = "iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAApgAAAKYB3X3/OAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAANCSURBVEiJtZZPbBtFFMZ/M7ubXdtdb1xSFyeilBapySVU8h8OoFaooFSqiihIVIpQBKci6KEg9Q6H9kovIHoCIVQJJCKE1ENFjnAgcaSGC6rEnxBwA04Tx43t2FnvDAfjkNibxgHxnWb2e/u992bee7tCa00YFsffekFY+nUzFtjW0LrvjRXrCDIAaPLlW0nHL0SsZtVoaF98mLrx3pdhOqLtYPHChahZcYYO7KvPFxvRl5XPp1sN3adWiD1ZAqD6XYK1b/dvE5IWryTt2udLFedwc1+9kLp+vbbpoDh+6TklxBeAi9TL0taeWpdmZzQDry0AcO+jQ12RyohqqoYoo8RDwJrU+qXkjWtfi8Xxt58BdQuwQs9qC/afLwCw8tnQbqYAPsgxE1S6F3EAIXux2oQFKm0ihMsOF71dHYx+f3NND68ghCu1YIoePPQN1pGRABkJ6Bus96CutRZMydTl+TvuiRW1m3n0eDl0vRPcEysqdXn+jsQPsrHMquGeXEaY4Yk4wxWcY5V/9scqOMOVUFthatyTy8QyqwZ+kDURKoMWxNKr2EeqVKcTNOajqKoBgOE28U4tdQl5p5bwCw7BWquaZSzAPlwjlithJtp3pTImSqQRrb2Z8PHGigD4RZuNX6JYj6wj7O4TFLbCO/Mn/m8R+h6rYSUb3ekokRY6f/YukArN979jcW+V/S8g0eT/N3VN3kTqWbQ428m9/8k0P/1aIhF36PccEl6EhOcAUCrXKZXXWS3XKd2vc/TRBG9O5ELC17MmWubD2nKhUKZa26Ba2+D3P+4/MNCFwg59oWVeYhkzgN/JDR8deKBoD7Y+ljEjGZ0sosXVTvbc6RHirr2reNy1OXd6pJsQ+gqjk8VWFYmHrwBzW/n+uMPFiRwHB2I7ih8ciHFxIkd/3Omk5tCDV1t+2nNu5sxxpDFNx+huNhVT3/zMDz8usXC3ddaHBj1GHj/As08fwTS7Kt1HBTmyN29vdwAw+/wbwLVOJ3uAD1wi/dUH7Qei66PfyuRj4Ik9is+hglfbkbfR3cnZm7chlUWLdwmprtCohX4HUtlOcQjLYCu+fzGJH2QRKvP3UNz8bWk1qMxjGTOMThZ3kvgLI5AzFfo379UAAAAASUVORK5CYII="`, + `display.Image(b64decode(base64_data))` + ].join('\n'); + } + } else { + code = markdownCellContents; + } + const language = cellKind === NotebookCellKind.Code ? 'python' : 'markdown'; + cells.push(new NotebookCellData(cellKind, code, language)); + cellIndex += 1; + if (cellIndex >= cellCount) { + break; + } + } + + return new NotebookData(cells); + } +}); diff --git a/src/test/performance/notebookPerf.vscode.common.test.ts b/src/test/performance/notebookPerf.vscode.common.test.ts new file mode 100644 index 00000000000..bc397f7c91d --- /dev/null +++ b/src/test/performance/notebookPerf.vscode.common.test.ts @@ -0,0 +1,110 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import { + window, + extensions, + NotebookCellKind, + workspace, + NotebookCellData, + NotebookData, + commands, + type NotebookDocument, + WorkspaceEdit, + Range, + Position +} from 'vscode'; +import { JupyterNotebookView, Telemetry } from '../../platform/common/constants'; +import type { API } from '../vscode-notebook-perf/src/api'; +import { PerformanceExtensionId } from '../constants'; +import { StopWatch } from '../../platform/common/utils/stopWatch'; +import { sendTelemetryEvent } from '../../telemetry'; +import { countCells } from '../utils/notebook'; + +suite('Notebook Performance (@notebookPerformance)', function () { + let api: API; + let notebook: NotebookDocument; + this.timeout(120_000); + suiteSetup(async function () { + this.timeout(120_000); + const extension = extensions.getExtension(PerformanceExtensionId)!; + await extension.activate(); + api = extension.exports; + }); + setup(async () => { + await commands.executeCommand('workbench.action.revertAndCloseActiveEditor'); + await commands.executeCommand('workbench.action.closeAllEditors'); + + notebook = await workspace.openNotebookDocument(JupyterNotebookView, generateNotebookData(1_000)); + await window.showNotebookDocument(notebook); + await commands.executeCommand('outline.focus'); + await commands.executeCommand('notebook.selectKernel', { + id: 'perfController', + extension: PerformanceExtensionId + }); + }); + teardown(async () => { + await commands.executeCommand('workbench.action.revertAndCloseActiveEditor'); + await commands.executeCommand('workbench.action.closeAllEditors'); + }); + test('Text output', async () => runAndSendTelemetry('text')); + test('Html output', async () => runAndSendTelemetry('html')); + test('Image output', async () => runAndSendTelemetry('image')); + test('Editing code cell', async () => editAndSendTelemetry(NotebookCellKind.Code)); + test('Editing markdown cell', async () => editAndSendTelemetry(NotebookCellKind.Markup)); + + async function editAndSendTelemetry(cellKind: NotebookCellKind) { + const cell = notebook.getCells().find((cell) => cell.kind === cellKind); + if (!cell) { + throw new Error('No cell found'); + } + const rangeToClear = new Range(0, 0, cell.document.lineCount - 1, cell.document.lineAt(0).range.end.character); + const edit = new WorkspaceEdit(); + edit.replace(cell.document.uri, rangeToClear, ``); + await workspace.applyEdit(edit); + + const stopWatch = new StopWatch(); + const promises: Promise[] = []; + for (let i = 0; i < 500; i += 1) { + const edit = new WorkspaceEdit(); + edit.insert(cell.document.uri, new Position(0, i), i.toString()); + promises.push(Promise.resolve(workspace.applyEdit(edit))); + } + await Promise.all(promises); + const duration = stopWatch.elapsedTime; + const { codeCellCount, markdownCellCount } = countCells(notebook); + sendTelemetryEvent(Telemetry.NativeNotebookEditPerformance, { codeCellCount, markdownCellCount, duration }); + } + + async function runAndSendTelemetry(outputType: 'text' | 'html' | 'image') { + const metrics = await api.executeNotebook(outputType); + const { codeCellCount, markdownCellCount } = countCells(notebook); + sendTelemetryEvent( + Telemetry.NativeNotebookExecutionPerformance, + { ...metrics, codeCellCount, markdownCellCount }, + { outputType } + ); + } + + function generateNotebookData(cellCount: number) { + const markdownCellContents = ['# Header1', '## Header2', '### Header3', 'Hello World'].join('\n'); + let cellIndex = 0; + let codeCellNumber = 0; + const cells: NotebookCellData[] = []; + while (true) { + const cellKind = cellIndex % 2 === 0 ? NotebookCellKind.Code : NotebookCellKind.Markup; + if (cellKind === NotebookCellKind.Code) { + codeCellNumber += 1; + } + const code = cellKind === NotebookCellKind.Code ? `print(${codeCellNumber})` : markdownCellContents; + const language = cellKind === NotebookCellKind.Code ? 'python' : 'markdown'; + cells.push(new NotebookCellData(cellKind, code, language)); + cellIndex += 1; + if (cellIndex >= cellCount) { + break; + } + } + + return new NotebookData(cells); + } +}); diff --git a/src/test/standardTest.node.ts b/src/test/standardTest.node.ts index 708198a23ee..f8abf4be939 100644 --- a/src/test/standardTest.node.ts +++ b/src/test/standardTest.node.ts @@ -22,19 +22,36 @@ process.env.VSC_JUPYTER_CI_TEST = '1'; const workspacePath = process.env.CODE_TESTS_WORKSPACE ? process.env.CODE_TESTS_WORKSPACE : path.join(__dirname, '..', '..', 'src', 'test'); -const extensionDevelopmentPath = process.env.CODE_EXTENSIONS_PATH +const extensionDevelopmentPathForTestsWithJupyter = process.env.CODE_EXTENSIONS_PATH ? process.env.CODE_EXTENSIONS_PATH : EXTENSION_ROOT_DIR_FOR_TESTS; +const extensionDevelopmentPathForPerfTestsWithoutJupyter = path.join( + extensionDevelopmentPathForTestsWithJupyter, + 'src', + 'test', + 'vscode-notebook-perf' +); +const extensionDevelopmentPath = isNotebookPerfTestWithoutJupyter() + ? extensionDevelopmentPathForPerfTestsWithoutJupyter + : extensionDevelopmentPathForTestsWithJupyter; + const isRunningVSCodeTests = process.env.TEST_FILES_SUFFIX?.includes('vscode.test'); setTestExecution(true); function requiresPythonExtensionToBeInstalled() { - if (process.env.VSC_JUPYTER_CI_TEST_DO_NOT_INSTALL_PYTHON_EXT) { + if ( + process.env.VSC_JUPYTER_CI_TEST_DO_NOT_INSTALL_PYTHON_EXT && + process.env.VSC_JUPYTER_CI_TEST_DO_NOT_INSTALL_PYTHON_EXT !== 'false' + ) { return; } return isRunningVSCodeTests || IS_SMOKE_TEST() || IS_PERF_TEST(); } +function isNotebookPerfTestWithoutJupyter() { + return !!process.env.VSC_JUPYTER_NOTEBOOK_PERF_TEST; +} + const channel = (process.env.VSC_JUPYTER_CI_TEST_VSC_CHANNEL || '').toLowerCase().includes('insiders') ? 'insiders' : 'stable'; @@ -64,7 +81,7 @@ async function createTempDir() { * Smoke tests & tests running in VSCode require Python extension to be installed. */ async function installPythonExtension(vscodeExecutablePath: string, extensionsDir: string, platform: DownloadPlatform) { - if (!requiresPythonExtensionToBeInstalled()) { + if (!requiresPythonExtensionToBeInstalled() || isNotebookPerfTestWithoutJupyter()) { console.info('Python Extension not required'); return; } @@ -132,8 +149,11 @@ async function createSettings(): Promise { // New Kernel Picker. 'notebook.kernelPicker.type': 'mru', 'jupyter.experimental.dropCustomMetadata': true, // On CI always run without custom metadata. - 'jupyter.drop.custom.property': true // On CI always run without custom metadata. + 'jupyter.drop.custom.property': true, // On CI always run without custom metadata. + 'notebook.stickyScroll.enabled': true, // Required for perf tests + 'notebook.outline.showCodeCells': true // Required for perf tests }; + if (IS_SMOKE_TEST()) { defaultSettings['python.languageServer'] = 'None'; } @@ -165,6 +185,7 @@ async function start() { const baseLaunchArgs = requiresPythonExtensionToBeInstalled() ? [] : ['--disable-extensions']; const userDataDirectory = await createSettings(); const extensionsDir = await getExtensionsDir(); + console.error(`Using extensions development path: ${extensionDevelopmentPath}`); await installPythonExtension(vscodeExecutablePath, extensionsDir, platform); await runTests({ vscodeExecutablePath, diff --git a/src/test/utils/notebook.ts b/src/test/utils/notebook.ts new file mode 100644 index 00000000000..dd5a5c05759 --- /dev/null +++ b/src/test/utils/notebook.ts @@ -0,0 +1,59 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import { type NotebookDocument, NotebookCellKind, commands, extensions } from 'vscode'; +import type { Jupyter } from '../../api'; +import { StopWatch } from '../../platform/common/utils/stopWatch'; +import { JVSC_EXTENSION_ID_FOR_TESTS } from '../constants'; +import { sleep } from '../core'; + +export function countCells(notebook: NotebookDocument) { + let codeCellCount = 0; + let markdownCellCount = 0; + notebook.getCells().forEach((cell) => { + if (cell.kind === NotebookCellKind.Code) { + codeCellCount += 1; + } else { + markdownCellCount += 1; + } + }); + return { codeCellCount, markdownCellCount }; +} + +export async function startKernelUsingApiByRunningFirstAvailableCodeCell(notebook: NotebookDocument) { + // For some reason VS Code does not send the execution request to the extension host. + // Lets wait for at least 10s. + // UI seems to take a while to load the notebook document. + await sleep(10_000); + // Run at least 1 cell, we need to ensure we have started the kernel. + const codeCell = notebook.getCells().find((cell) => cell.kind === NotebookCellKind.Code); + const firstCellRange = { start: codeCell!.index, end: codeCell!.index + 1 }; + void commands.executeCommand('notebook.cell.execute', { + ranges: [firstCellRange], + document: notebook.uri + }); + + const api = extensions.getExtension(JVSC_EXTENSION_ID_FOR_TESTS)!.exports; + const stopWatch = new StopWatch(); + const hasKernelStartedAndIsIdle = async () => { + const kernel = await api.kernels.getKernel(notebook.uri); + if (!kernel) { + return false; + } + return kernel.status === 'idle'; + }; + + while (!(await hasKernelStartedAndIsIdle())) { + await sleep(1000); + + // Kernels can take a while to start (at least on windows) + // Possible its very slow on CI, hence lets ensure we wait for at least 20s. + if (stopWatch.elapsedTime > 20_000) { + stopWatch.reset(); + void commands.executeCommand('notebook.cell.execute', { + ranges: [firstCellRange], + document: notebook.uri + }); + } + } +} diff --git a/src/test/vscode-notebook-perf/.editorconfig b/src/test/vscode-notebook-perf/.editorconfig new file mode 100644 index 00000000000..4f92a467d0e --- /dev/null +++ b/src/test/vscode-notebook-perf/.editorconfig @@ -0,0 +1,17 @@ +# EditorConfig is awesome: http://EditorConfig.org + +# top-most EditorConfig file +root = true + +# Tab indentation +[*] +indent_style = space +indent_size = 4 +trim_trailing_whitespace = true +insert_final_newline = true + +# The indent size used in the `package.json` file cannot be changed +# https://github.com/npm/npm/pull/3180#issuecomment-16336516 +[{.travis.yml,npm-shrinkwrap.json,package.json}] +indent_style = space +indent_size = 4 diff --git a/src/test/vscode-notebook-perf/.gitattributes b/src/test/vscode-notebook-perf/.gitattributes new file mode 100644 index 00000000000..f36040d4363 --- /dev/null +++ b/src/test/vscode-notebook-perf/.gitattributes @@ -0,0 +1,2 @@ +package.json text eol=lf +package-lock.json text eol=lf diff --git a/src/test/vscode-notebook-perf/.gitignore b/src/test/vscode-notebook-perf/.gitignore new file mode 100644 index 00000000000..3f0d39d6a9d --- /dev/null +++ b/src/test/vscode-notebook-perf/.gitignore @@ -0,0 +1,17 @@ +.DS_Store +.huskyrc.json +out +**/node_modules +*.pyc +*.vsix +.vscode-test +__pycache__ +**/.vscode-test/** +**/.vscode test/** +**/.vscode-smoke/** +**/.venv*/ +bin/** +logs/** +obj/** +temp +tmp diff --git a/src/test/vscode-notebook-perf/.nvmrc b/src/test/vscode-notebook-perf/.nvmrc new file mode 100644 index 00000000000..e048c8ca198 --- /dev/null +++ b/src/test/vscode-notebook-perf/.nvmrc @@ -0,0 +1 @@ +v18.15.0 diff --git a/src/test/vscode-notebook-perf/.prettierrc.js b/src/test/vscode-notebook-perf/.prettierrc.js new file mode 100644 index 00000000000..8b6563478c4 --- /dev/null +++ b/src/test/vscode-notebook-perf/.prettierrc.js @@ -0,0 +1,15 @@ +module.exports = { + singleQuote: true, + printWidth: 120, + tabWidth: 4, + endOfLine: 'auto', + trailingComma: 'all', + overrides: [ + { + files: ['*.yml', '*.yaml'], + options: { + tabWidth: 2, + }, + }, + ], +}; diff --git a/src/test/vscode-notebook-perf/.vscode-test.mjs b/src/test/vscode-notebook-perf/.vscode-test.mjs new file mode 100644 index 00000000000..f869559585a --- /dev/null +++ b/src/test/vscode-notebook-perf/.vscode-test.mjs @@ -0,0 +1,34 @@ +//@ts-check + +import fs from 'fs'; +import { defineConfig } from '@vscode/test-cli'; +import { fileURLToPath } from 'url'; +import { dirname } from 'path'; +import { configureWorkspace } from './out/test/setup'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); + +async function generateConfig() { + const { workspaceFolder, userDataDir } = configureWorkspace(); + /** @type {import('@vscode/test-cli').TestConfiguration} */ + let config = { + label: 'Extension Test', + files: ['out/**/*.test.js'], + version: 'insiders', + srcDir: 'src', + workspaceFolder: `${__dirname}/src/test`, + launchArgs: [workspaceFolder, '--user-data-dir', userDataDir], + // installExtensions: ['ms-python.vscode-pylance@prerelease'] + }; + + config.mocha = { + ui: 'tdd', + color: true, + timeout: 120_000, + }; + + return config; +} + +export default defineConfig(generateConfig()); diff --git a/src/test/vscode-notebook-perf/.vscode/extensions.json b/src/test/vscode-notebook-perf/.vscode/extensions.json new file mode 100644 index 00000000000..dd01eb3555f --- /dev/null +++ b/src/test/vscode-notebook-perf/.vscode/extensions.json @@ -0,0 +1,5 @@ +{ + // See http://go.microsoft.com/fwlink/?LinkId=827846 + // for the documentation about the extensions.json format + "recommendations": ["dbaeumer.vscode-eslint", "amodio.tsl-problem-matcher", "ms-vscode.extension-test-runner"] +} diff --git a/src/test/vscode-notebook-perf/.vscode/launch.json b/src/test/vscode-notebook-perf/.vscode/launch.json new file mode 100644 index 00000000000..831469074e3 --- /dev/null +++ b/src/test/vscode-notebook-perf/.vscode/launch.json @@ -0,0 +1,37 @@ +// A launch configuration that compiles the extension and then opens it inside a new window +// Use IntelliSense to learn about possible attributes. +// Hover to view descriptions of existing attributes. +// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Run Extension", + "type": "extensionHost", + "request": "launch", + "args": [ + "--extensionDevelopmentPath=${workspaceFolder}" + ], + "outFiles": [ + "${workspaceFolder}/dist/**/*.js" + ], + "preLaunchTask": "${defaultBuildTask}" + }, + { + "name": "Run Performance Tests", + "type": "extensionHost", + "request": "launch", + "runtimeExecutable": "${execPath}", + "args": [ + "${workspaceFolder}/src/test", + "--extensionDevelopmentPath=${workspaceFolder}", + "--extensionTestsPath=${workspaceFolder}/out/test/index.node.js" + ], + "outFiles": [ + "${workspaceFolder}/out/**/*", + "!${workspaceFolder}/**/node_modules**/*" + ], + "preLaunchTask": "${defaultBuildTask}" + }, + ] +} diff --git a/src/test/vscode-notebook-perf/.vscode/settings.json b/src/test/vscode-notebook-perf/.vscode/settings.json new file mode 100644 index 00000000000..a3b712cae13 --- /dev/null +++ b/src/test/vscode-notebook-perf/.vscode/settings.json @@ -0,0 +1,54 @@ +// Place your settings in this file to overwrite default and user settings. +{ + "files.exclude": { + "out": false, + "dist": false, + "**/*.pyc": true, + ".nyc_output": true, + "obj": true, + "bin": true, + "**/__pycache__": true, + "**/node_modules": false, + ".vscode-test": true, + ".vscode test": true, + ".venv": true, + "**/.pytest_cache/**": true, + "languageServer.*/**": true, + "**/.mypy_cache/**": true, + "**/.ropeproject/**": true + }, + "search.exclude": { + "out": true, + "dist": true, + "**/node_modules": true, + "coverage": true, + "**/.venv/**": true, + "languageServer*/**": true, + ".vscode-test": true, + ".vscode test": true + }, + "editor.codeActionsOnSave": { + "source.fixAll.eslint": "explicit", + "source.fixAll.tslint": "explicit" + }, + "[python]": { + "editor.formatOnSave": true + }, + "[typescript]": { + "editor.formatOnSave": true, + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[javascript]": { + "editor.formatOnSave": true, + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "git.branchProtection": ["main", "release*"], + "git.branchProtectionPrompt": "alwaysCommitToNewBranch", + "javascript.preferences.quoteStyle": "single", + "typescript.tsdk": "./node_modules/typescript/lib", // we want to use the TS server from our node_modules folder to control its version + "typescript.preferences.quoteStyle": "single", + "typescript.preferences.importModuleSpecifier": "relative", + "prettier.printWidth": 120, + "prettier.singleQuote": true, + "testExplorer.useNativeTesting": true +} diff --git a/src/test/vscode-notebook-perf/.vscode/tasks.json b/src/test/vscode-notebook-perf/.vscode/tasks.json new file mode 100644 index 00000000000..01d10379656 --- /dev/null +++ b/src/test/vscode-notebook-perf/.vscode/tasks.json @@ -0,0 +1,21 @@ +// See https://go.microsoft.com/fwlink/?LinkId=733558 +// for the documentation about the tasks.json format +{ + "version": "2.0.0", + "tasks": [ + { + "type": "npm", + "script": "watch", + "problemMatcher": "$tsc-watch", + "isBackground": true, + "presentation": { + "reveal": "never", + "group": "watchers" + }, + "group": { + "kind": "build", + "isDefault": true + } + } + ] +} diff --git a/src/test/vscode-notebook-perf/package-lock.json b/src/test/vscode-notebook-perf/package-lock.json new file mode 100644 index 00000000000..84fddde9dda --- /dev/null +++ b/src/test/vscode-notebook-perf/package-lock.json @@ -0,0 +1,1823 @@ +{ + "name": "vscode-notebook-perf", + "version": "0.0.1", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "vscode-notebook-perf", + "version": "0.0.1", + "devDependencies": { + "@types/glob": "^5.0.35", + "@types/mocha": "^9.1.0", + "@types/node": "^18.15.0", + "@types/vscode": "^1.78.0", + "@vscode/test-cli": "^0.0.8", + "@vscode/test-electron": "^2.3.9", + "glob": "^7.1.2", + "mocha": "^10.2.0", + "typescript": "^5.3.3" + }, + "engines": { + "vscode": "^1.78.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@tootallnate/once": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@types/glob": { + "version": "5.0.38", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-5.0.38.tgz", + "integrity": "sha512-rTtf75rwyP9G2qO5yRpYtdJ6aU1QqEhWbtW55qEgquEDa6bXW0s2TWZfDm02GuppjEozOWG/F2UnPq5hAQb+gw==", + "dev": true, + "dependencies": { + "@types/minimatch": "*", + "@types/node": "*" + } + }, + "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 + }, + "node_modules/@types/minimatch": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz", + "integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==", + "dev": true + }, + "node_modules/@types/mocha": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.1.1.tgz", + "integrity": "sha512-Z61JK7DKDtdKTWwLeElSEBcWGRLY8g95ic5FoQqI9CMx0ns/Ghep3B4DfcEimiKMvtamNVULVNKEsiwV3aQmXw==", + "dev": true + }, + "node_modules/@types/node": { + "version": "18.19.31", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.31.tgz", + "integrity": "sha512-ArgCD39YpyyrtFKIqMDvjz79jto5fcI/SVUs2HwB+f0dAzq68yqOdyaSivLiLugSziTpNXLQrVb7RZFmdZzbhA==", + "dev": true, + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@types/vscode": { + "version": "1.88.0", + "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.88.0.tgz", + "integrity": "sha512-rWY+Bs6j/f1lvr8jqZTyp5arRMfovdxolcqGi+//+cPDOh8SBvzXH90e7BiSXct5HJ9HGW6jATchbRTpTJpEkw==", + "dev": true + }, + "node_modules/@vscode/test-cli": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/@vscode/test-cli/-/test-cli-0.0.8.tgz", + "integrity": "sha512-sBSMSDzJChiiDjmys2Q6uI4SIoUYf0t6oDsQO3ypaQ7udha9YD4e2On9e9VE5OBayZMpxbgJX+NudmCwJMdOIg==", + "dev": true, + "dependencies": { + "@types/mocha": "^10.0.2", + "c8": "^9.1.0", + "chokidar": "^3.5.3", + "enhanced-resolve": "^5.15.0", + "glob": "^10.3.10", + "minimatch": "^9.0.3", + "mocha": "^10.2.0", + "supports-color": "^9.4.0", + "yargs": "^17.7.2" + }, + "bin": { + "vscode-test": "out/bin.mjs" + } + }, + "node_modules/@vscode/test-cli/node_modules/@types/mocha": { + "version": "10.0.6", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.6.tgz", + "integrity": "sha512-dJvrYWxP/UcXm36Qn36fxhUKu8A/xMRXVT2cliFF1Z7UA9liG5Psj3ezNSZw+5puH2czDXRLcXQxf8JbJt0ejg==", + "dev": true + }, + "node_modules/@vscode/test-cli/node_modules/glob": { + "version": "10.3.12", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.12.tgz", + "integrity": "sha512-TCNv8vJ+xz4QiqTpfOJA7HvYv+tNIRHKfUWw/q+v2jdgN4ebz+KY9tGx5J4rHP0o84mNP+ApH66HRX8us3Khqg==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^2.3.6", + "minimatch": "^9.0.1", + "minipass": "^7.0.4", + "path-scurry": "^1.10.2" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@vscode/test-electron": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/@vscode/test-electron/-/test-electron-2.3.9.tgz", + "integrity": "sha512-z3eiChaCQXMqBnk2aHHSEkobmC2VRalFQN0ApOAtydL172zXGxTwGrRtviT5HnUB+Q+G3vtEYFtuQkYqBzYgMA==", + "dev": true, + "dependencies": { + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "jszip": "^3.10.1", + "semver": "^7.5.2" + }, + "engines": { + "node": ">=16" + } + }, + "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, + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, + "node_modules/c8": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/c8/-/c8-9.1.0.tgz", + "integrity": "sha512-mBWcT5iqNir1zIkzSPyI3NCR9EZCVI3WUD+AVO17MVWTSFNyUueXE82qTeampNtTr+ilN/5Ua3j24LgbCKjDVg==", + "dev": true, + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@istanbuljs/schema": "^0.1.3", + "find-up": "^5.0.0", + "foreground-child": "^3.1.1", + "istanbul-lib-coverage": "^3.2.0", + "istanbul-lib-report": "^3.0.1", + "istanbul-reports": "^3.1.6", + "test-exclude": "^6.0.0", + "v8-to-istanbul": "^9.0.0", + "yargs": "^17.7.2", + "yargs-parser": "^21.1.1" + }, + "bin": { + "c8": "bin/c8.js" + }, + "engines": { + "node": ">=14.14.0" + } + }, + "node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true + }, + "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/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/diff": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", + "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/enhanced-resolve": { + "version": "5.16.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.16.0.tgz", + "integrity": "sha512-O+QWCviPNSSLAD9Ucn8Awv+poAkqn3T1XY5/N7kR7rQO9yfSGWkYZDwpJ+iKF7B8rxaQKWngSqACpgzeapSyoA==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/escalade": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", + "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, + "bin": { + "flat": "cli.js" + } + }, + "node_modules/foreground-child": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", + "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "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==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "bin": { + "he": "bin/he" + } + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, + "node_modules/http-proxy-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "dev": true, + "dependencies": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "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, + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", + "dev": true + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-reports": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", + "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", + "dev": true, + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jackspeak": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", + "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", + "dev": true, + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jszip": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", + "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", + "dev": true, + "dependencies": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "setimmediate": "^1.0.5" + } + }, + "node_modules/lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "dev": true, + "dependencies": { + "immediate": "~3.0.5" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimatch": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz", + "integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minipass": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", + "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/mocha": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.4.0.tgz", + "integrity": "sha512-eqhGB8JKapEYcC4ytX/xrzKforgEc3j1pGlAXVy3eRwrtAy5/nIfT1SvgGzfN0XZZxeLq0aQWkOUAmqIJiv+bA==", + "dev": true, + "dependencies": { + "ansi-colors": "4.1.1", + "browser-stdout": "1.3.1", + "chokidar": "3.5.3", + "debug": "4.3.4", + "diff": "5.0.0", + "escape-string-regexp": "4.0.0", + "find-up": "5.0.0", + "glob": "8.1.0", + "he": "1.2.0", + "js-yaml": "4.1.0", + "log-symbols": "4.1.0", + "minimatch": "5.0.1", + "ms": "2.1.3", + "serialize-javascript": "6.0.0", + "strip-json-comments": "3.1.1", + "supports-color": "8.1.1", + "workerpool": "6.2.1", + "yargs": "16.2.0", + "yargs-parser": "20.2.4", + "yargs-unparser": "2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha.js" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/mocha/node_modules/chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/mocha/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/mocha/node_modules/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mocha/node_modules/minimatch": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", + "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mocha/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 + }, + "node_modules/mocha/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/mocha/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mocha/node_modules/yargs-parser": { + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-scurry": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.2.tgz", + "integrity": "sha512-7xTavNy5RQXnsjANvVvMkEjvloOinkAjv/Z6Ildz9v2RinZ4SBKTWFOVRbaF8p0vpHnyjV/UwNDdKuUv6M5qcA==", + "dev": true, + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz", + "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==", + "dev": true, + "engines": { + "node": "14 || >=16.14" + } + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/serialize-javascript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "dev": true, + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "dev": true + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "9.4.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-9.4.0.tgz", + "integrity": "sha512-VL+lNrEoIXww1coLPOmiEmK/0sGigko5COxI09KzHc2VJXJsQ37UaQ+8quuxjDeA7+KnLGTWRyOXSLLR2Wb4jw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/test-exclude/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/test-exclude/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/typescript": { + "version": "5.4.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", + "integrity": "sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, + "node_modules/v8-to-istanbul": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.2.0.tgz", + "integrity": "sha512-/EH/sDgxU2eGxajKdwLCDmQ4FWq+kpi3uCmBGpw1xJtnAxEjlD8j8PEiGWpCIMIs3ciNAgH0d3TTJiUkYzyZjA==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/workerpool": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", + "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==", + "dev": true + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dev": true, + "dependencies": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/src/test/vscode-notebook-perf/package.json b/src/test/vscode-notebook-perf/package.json new file mode 100644 index 00000000000..c89ad9d317a --- /dev/null +++ b/src/test/vscode-notebook-perf/package.json @@ -0,0 +1,53 @@ +{ + "activationEvents": [ + "onNotebook:jupyter-notebook" + ], + "author": { + "name": "Microsoft Corporation" + }, + "categories": [ + "Other" + ], + "contributes": { + "commands": [ + { + "command": "vscode-notebook-perf.runAllTextOutput", + "title": "Notebook Perf: Run all (text output)" + }, + { + "command": "vscode-notebook-perf.runAllHtmlOutput", + "title": "Notebook Perf: Run all (html output)" + }, + { + "command": "vscode-notebook-perf.runAllImageOutput", + "title": "Notebook Perf: Run all (image output)" + } + ] + }, + "description": "", + "devDependencies": { + "@types/glob": "^5.0.35", + "@types/mocha": "^9.1.0", + "@types/node": "^18.15.0", + "@types/vscode": "^1.78.0", + "@vscode/test-cli": "^0.0.8", + "@vscode/test-electron": "^2.3.9", + "glob": "^7.1.2", + "mocha": "^10.2.0", + "typescript": "^5.3.3" + }, + "displayName": "Notebook Performance Test", + "engines": { + "vscode": "^1.78.0" + }, + "main": "./out/extension.js", + "name": "vscode-notebook-perf", + "publisher": "ms-toolsai", + "scripts": { + "compile": "tsc -p .", + "pretest": "npm run compile", + "test": "node ./out/test/runTest.js", + "watch": "tsc -p . --watch" + }, + "version": "0.0.1" +} \ No newline at end of file diff --git a/src/test/vscode-notebook-perf/src/api.ts b/src/test/vscode-notebook-perf/src/api.ts new file mode 100644 index 00000000000..ecfd8e59721 --- /dev/null +++ b/src/test/vscode-notebook-perf/src/api.ts @@ -0,0 +1,12 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +export type Metrics = { + preExecuteDuration: number; + executeDuration: number; + postExecuteDuration: number; + duration: number; +}; +export type API = { + executeNotebook(outputType: 'text' | 'html' | 'image'): Promise; +}; diff --git a/src/test/vscode-notebook-perf/src/extension.ts b/src/test/vscode-notebook-perf/src/extension.ts new file mode 100644 index 00000000000..3f7d5a8e939 --- /dev/null +++ b/src/test/vscode-notebook-perf/src/extension.ts @@ -0,0 +1,206 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +import { + ExtensionContext, + commands, + notebooks, + window, + type NotebookController, + type NotebookCell, + type OutputChannel, + NotebookCellOutput, + NotebookCellOutputItem, + workspace, +} from 'vscode'; +import type { API, Metrics } from './api'; + +const timer = new (class { + public metrics: Metrics = { + preExecuteDuration: 0, + executeDuration: 0, + postExecuteDuration: 0, + duration: 0, + }; + private _startAt = Date.now(); + private _executionCompletedAt = 0; + private _executionStartedAt = 0; + private logger: OutputChannel; + private outputType = ''; + constructor() { + this.logger = window.createOutputChannel('Dummy Execution'); + } + get executionCompleted() { + return this._executionCompletedAt > 0; + } + dispose() { + this.logger.dispose(); + if (this._timer) { + this._timer = undefined; + clearTimeout(this._timer); + } + } + reset(outputType: string) { + this.metrics = { + preExecuteDuration: 0, + executeDuration: 0, + postExecuteDuration: 0, + duration: 0, + }; + this.outputType = outputType; + this.logger.replace(''); + this._startAt = Date.now(); + this._executionStartedAt = 0; + } + private _timer?: NodeJS.Timeout; + log() { + if (this._timer) { + clearTimeout(this._timer); + } + this._timer = setTimeout(() => { + this._timer = undefined; + this.logger.replace(this.getMessage()); + }, 1_000); + } + private getMessage() { + return [ + `Output type ${this.outputType}`, + `Time spent in VS Code before extension is notified: ${this.metrics.preExecuteDuration}`, + `Time spent in Extension Host running all cells: ${this.metrics.executeDuration}`, + `Time spent in VS Code updating UI after execution completed: ${this.metrics.postExecuteDuration}`, + `Time spent in users perspective: ${this.metrics.duration}`, + ].join('\n'); + } + startExecution() { + this.metrics.preExecuteDuration = Date.now() - this._startAt; + this._executionStartedAt = Date.now(); + this.log(); + } + endExecution() { + this._executionCompletedAt = Date.now(); + this.metrics.executeDuration = Date.now() - this._executionStartedAt; + this.log(); + } + detectNotebookChanges() { + this.metrics.duration = Date.now() - this._startAt; + this.metrics.postExecuteDuration = Date.now() - this._executionCompletedAt; + this.log(); + } +})(); + +class NotebookKernel { + public lastExecutionOrder = 0; + private readonly controller: NotebookController; + private expectedMimeType: 'text' | 'html' | 'image' = 'text'; + constructor() { + this.controller = notebooks.createNotebookController( + 'perfController', + 'jupyter-notebook', + 'Dummy Execution', + (cells) => { + timer.startExecution(); + this.executionHandler(cells); + timer.endExecution(); + }, + ); + this.controller.supportsExecutionOrder = true; + } + setMimeType(expectedMimeType: 'text' | 'html' | 'image' = 'text') { + this.expectedMimeType = expectedMimeType; + } + dispose() { + this.controller.dispose(); + } + private executionHandler(cells: NotebookCell[]) { + for (const cell of cells) { + const execution = this.controller.createNotebookCellExecution(cell); + const value = `${cell.document.getText()} => ${cell.index + 1}`; + execution.start(Date.now()); + void execution.clearOutput(); + this.lastExecutionOrder = this.lastExecutionOrder + 1; + execution.executionOrder = this.lastExecutionOrder; + const outputItems = [NotebookCellOutputItem.text(value)]; + if (this.expectedMimeType === 'html') { + outputItems.push(NotebookCellOutputItem.text('
Hello World
', 'text/html')); + } + if (this.expectedMimeType === 'image') { + outputItems.push( + new NotebookCellOutputItem( + base64ToUint8Array( + 'iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAApgAAAKYB3X3/OAAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAANCSURBVEiJtZZPbBtFFMZ/M7ubXdtdb1xSFyeilBapySVU8h8OoFaooFSqiihIVIpQBKci6KEg9Q6H9kovIHoCIVQJJCKE1ENFjnAgcaSGC6rEnxBwA04Tx43t2FnvDAfjkNibxgHxnWb2e/u992bee7tCa00YFsffekFY+nUzFtjW0LrvjRXrCDIAaPLlW0nHL0SsZtVoaF98mLrx3pdhOqLtYPHChahZcYYO7KvPFxvRl5XPp1sN3adWiD1ZAqD6XYK1b/dvE5IWryTt2udLFedwc1+9kLp+vbbpoDh+6TklxBeAi9TL0taeWpdmZzQDry0AcO+jQ12RyohqqoYoo8RDwJrU+qXkjWtfi8Xxt58BdQuwQs9qC/afLwCw8tnQbqYAPsgxE1S6F3EAIXux2oQFKm0ihMsOF71dHYx+f3NND68ghCu1YIoePPQN1pGRABkJ6Bus96CutRZMydTl+TvuiRW1m3n0eDl0vRPcEysqdXn+jsQPsrHMquGeXEaY4Yk4wxWcY5V/9scqOMOVUFthatyTy8QyqwZ+kDURKoMWxNKr2EeqVKcTNOajqKoBgOE28U4tdQl5p5bwCw7BWquaZSzAPlwjlithJtp3pTImSqQRrb2Z8PHGigD4RZuNX6JYj6wj7O4TFLbCO/Mn/m8R+h6rYSUb3ekokRY6f/YukArN979jcW+V/S8g0eT/N3VN3kTqWbQ428m9/8k0P/1aIhF36PccEl6EhOcAUCrXKZXXWS3XKd2vc/TRBG9O5ELC17MmWubD2nKhUKZa26Ba2+D3P+4/MNCFwg59oWVeYhkzgN/JDR8deKBoD7Y+ljEjGZ0sosXVTvbc6RHirr2reNy1OXd6pJsQ+gqjk8VWFYmHrwBzW/n+uMPFiRwHB2I7ih8ciHFxIkd/3Omk5tCDV1t+2nNu5sxxpDFNx+huNhVT3/zMDz8usXC3ddaHBj1GHj/As08fwTS7Kt1HBTmyN29vdwAw+/wbwLVOJ3uAD1wi/dUH7Qei66PfyuRj4Ik9is+hglfbkbfR3cnZm7chlUWLdwmprtCohX4HUtlOcQjLYCu+fzGJH2QRKvP3UNz8bWk1qMxjGTOMThZ3kvgLI5AzFfo379UAAAAASUVORK5CYII=', + ), + 'image/png', + ), + ); + } + void execution.appendOutput(new NotebookCellOutput(outputItems)); + execution.end(true, Date.now()); + } + } +} + +export function activate(context: ExtensionContext): API { + const controller = new NotebookKernel(); + context.subscriptions.push(timer); + context.subscriptions.push(controller); + const runAll = (outputType: 'text' | 'html' | 'image') => { + const notebook = window.activeNotebookEditor?.notebook; + if (!notebook) { + return; + } + + controller.lastExecutionOrder = 0; + controller.setMimeType(outputType); + timer.reset(outputType); + void commands.executeCommand('notebook.cell.execute', { + ranges: [{ start: 0, end: notebook.cellCount }], + document: notebook.uri, + }); + }; + context.subscriptions.push( + commands.registerCommand('vscode-notebook-dummy-execution.runAllTextOutput', () => runAll('text')), + commands.registerCommand('vscode-notebook-dummy-execution.runAllHtmlOutput', () => runAll('html')), + commands.registerCommand('vscode-notebook-dummy-execution.runAllImageOutput', () => runAll('image')), + ); + + let lastUpdateTime = Date.now(); + const getTimeElapsedSinceLastUpdate = () => Date.now() - lastUpdateTime; + context.subscriptions.push( + workspace.onDidChangeNotebookDocument(() => { + if (timer.executionCompleted) { + timer.detectNotebookChanges(); + lastUpdateTime = Date.now(); + } + }), + ); + + return { + executeNotebook: async (outputType: 'text' | 'html' | 'image') => { + runAll(outputType); + await new Promise(async (resolve) => { + while (getTimeElapsedSinceLastUpdate() < 10_000) { + await sleep(1000); + } + // Looks like no more updates are coming in. + return resolve(); + }); + + return timer.metrics; + }, + }; +} + +export function deactivate() { + // no-op +} +export function base64ToUint8Array(base64: string): Uint8Array { + if (typeof Buffer !== 'undefined' && typeof Buffer.from === 'function') { + return Buffer.from(base64, 'base64'); + } else { + return Uint8Array.from(atob(base64), (c) => c.charCodeAt(0)); + } +} + +function sleep(ms: number) { + return new Promise((resolve) => setTimeout(resolve, ms)); +} diff --git a/src/test/vscode-notebook-perf/tsconfig.json b/src/test/vscode-notebook-perf/tsconfig.json new file mode 100644 index 00000000000..3c836e4eaef --- /dev/null +++ b/src/test/vscode-notebook-perf/tsconfig.json @@ -0,0 +1,30 @@ +{ + "compilerOptions": { + "module": "Node16", + "target": "ES2022", + "lib": [ + "ES2022" + ], + "skipLibCheck": true, + "sourceMap": true, + "rootDir": "src", + "outDir": "out", + "forceConsistentCasingInFileNames": true, + "strict": true, /* enable all strict type-checking options */ + "paths": { + "vscode": [ + "../../../vscode.d.ts" + ] + }, + "typeRoots": [ + "../../../node_modules/@types", + ] + }, + "include": [ + "./src/**/*", + ] + /* Additional Checks */ + // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ + // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ + // "noUnusedParameters": true, /* Report errors on unused parameters. */ +} diff --git a/src/test/web/customReporter.ts b/src/test/web/customReporter.ts index 6e52198314b..2fa57e8f61d 100644 --- a/src/test/web/customReporter.ts +++ b/src/test/web/customReporter.ts @@ -7,7 +7,7 @@ import type * as mochaTypes from 'mocha'; import { env, extensions, UIKind, Uri } from 'vscode'; -import { JVSC_EXTENSION_ID_FOR_TESTS } from '../constants'; +import { JVSC_EXTENSION_ID_FOR_TESTS, PerformanceExtensionId } from '../constants'; import { format } from 'util'; import { registerLogger } from '../../platform/logging/index'; import { Arguments, ILogger } from '../../platform/logging/types'; @@ -99,14 +99,17 @@ function writeReportProgress(message: Message) { if (env.uiKind === UIKind.Desktop) { messages.push(message); if (message.event === constants.EVENT_RUN_END) { - const ext = extensions.getExtension(JVSC_EXTENSION_ID_FOR_TESTS)!.extensionUri; - const logFile = Uri.joinPath(ext, 'logs', 'testresults.json'); + const jupyterExtUri = extensions.getExtension(JVSC_EXTENSION_ID_FOR_TESTS)?.extensionUri; + const logDir = jupyterExtUri + ? Uri.joinPath(jupyterExtUri, 'logs') + : Uri.joinPath(extensions.getExtension(PerformanceExtensionId)!.extensionUri, '..', '..', '..', 'logs'); + const logFile = Uri.joinPath(logDir, 'testresults.json'); console.log(`Writing test results to ${logFile}`); const requireFunc: typeof require = typeof __webpack_require__ === 'function' ? __non_webpack_require__ : require; const fs: typeof import('fs-extra') = requireFunc('fs-extra'); // eslint-disable-next-line local-rules/dont-use-fspath - fs.ensureDirSync(Uri.joinPath(ext, 'logs').fsPath); + fs.ensureDirSync(logDir.fsPath); // eslint-disable-next-line local-rules/dont-use-fspath fs.writeFileSync(logFile.fsPath, JSON.stringify(messages)); }