From 2628bf0fa6544e73c844060f2066e81dc294f899 Mon Sep 17 00:00:00 2001 From: Christoph Pirkl Date: Thu, 17 Aug 2023 10:04:37 +0200 Subject: [PATCH 01/21] Fix typo in user guide --- doc/user_guide/user_guide.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/user_guide/user_guide.md b/doc/user_guide/user_guide.md index 4f9dd3d..508a3fe 100644 --- a/doc/user_guide/user_guide.md +++ b/doc/user_guide/user_guide.md @@ -393,7 +393,7 @@ CREATE VIRTUAL SCHEMA RLS_VIRTUAL_SCHEMA Remember that RLS is an additional layer of access control _on top_ of the measures built into the core database. So in order to read columns in an RLS Virtual Schema, users first need to be allowed to access that schema. -A word or warning before you start granting permissions. Make sure you grant only access to the RLS Virtual Schema to regular users and _not to the orignial_ schema. Otherwise, those users can simply bypass RLS protection by going to the source. +A word of warning before you start granting permissions. Make sure you grant only access to the RLS Virtual Schema to regular users and _not to the orignial_ schema. Otherwise, those users can simply bypass RLS protection by going to the source. Here is an example for allowing `SELECT` statements to a user. From ea00c4dc78e84950404ecde84b16cff536793b42 Mon Sep 17 00:00:00 2001 From: Christoph Pirkl Date: Thu, 17 Aug 2023 10:04:48 +0200 Subject: [PATCH 02/21] Trim trailing spaces in user guide --- doc/user_guide/user_guide.md | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/doc/user_guide/user_guide.md b/doc/user_guide/user_guide.md index 508a3fe..e69f774 100644 --- a/doc/user_guide/user_guide.md +++ b/doc/user_guide/user_guide.md @@ -155,13 +155,13 @@ In case you want to use role-based security, add a column called `EXA_ROW_ROLES For our example we will create very simple order item list as shown below. ```sql -CREATE OR REPLACE TABLE MY_SCHEMA.ORDER_ITEM -( - ORDER_ID DECIMAL(18,0), - CUSTOMER VARCHAR(50), - PRODUCT VARCHAR(100), +CREATE OR REPLACE TABLE MY_SCHEMA.ORDER_ITEM +( + ORDER_ID DECIMAL(18,0), + CUSTOMER VARCHAR(50), + PRODUCT VARCHAR(100), QUANTITY DECIMAL(18,0), - EXA_ROW_ROLES DECIMAL(20,0) + EXA_ROW_ROLES DECIMAL(20,0) ); ``` @@ -188,7 +188,7 @@ INSERT INTO MY_SCHEMA.ORDER_ITEM VALUES An example of updating the table using `ROLES_MASK` function: -```sql +```sql UPDATE ORDER_ITEM SET EXA_ROW_ROLES = (SELECT MY_SCHEMA.ROLES_MASK(ROLE_ID) FROM MY_SCHEMA.EXA_ROLES_MAPPING WHERE ROLE_NAME IN ('Sales', 'Development')) WHERE customer IN ('John Smith', 'Jane Doe'); @@ -240,7 +240,7 @@ Delete roles using `DELETE_RLS_ROLE(role_name)` script. The script removes the r Example: -```sql +```sql EXECUTE SCRIPT DELETE_RLS_ROLE('Sales'); ``` @@ -253,7 +253,7 @@ If you want to use tenant security, you must add an additional column `EXA_ROW_T Example: ```sql -CREATE OR REPLACE TABLE MY_SCHEMA.ORDER_ITEM_WITH_TENANT +CREATE OR REPLACE TABLE MY_SCHEMA.ORDER_ITEM_WITH_TENANT ( ORDER_ID DECIMAL(18,0), CUSTOMER VARCHAR(50), @@ -318,13 +318,13 @@ In case you want to use group-based security, add a column called `EXA_ROW_GROUP For our example we will create very simple order item list as shown below. ```sql -CREATE OR REPLACE TABLE MY_SCHEMA.ORDER_ITEM_GROUP -( - ORDER_ID DECIMAL(18,0), - CUSTOMER VARCHAR(50), - PRODUCT VARCHAR(100), +CREATE OR REPLACE TABLE MY_SCHEMA.ORDER_ITEM_GROUP +( + ORDER_ID DECIMAL(18,0), + CUSTOMER VARCHAR(50), + PRODUCT VARCHAR(100), QUANTITY DECIMAL(18,0), - EXA_ROW_GROUP VARCHAR(128) + EXA_ROW_GROUP VARCHAR(128) ); ``` @@ -374,7 +374,7 @@ CREATE OR REPLACE LUA ADAPTER SCRIPT RLS_SCHEMA.RLS_ADAPTER AS end end ) - + .lua here> / ; From d5c4cd3115b6a6d24ad6b276333d1b4b990228eb Mon Sep 17 00:00:00 2001 From: Christoph Pirkl Date: Fri, 25 Aug 2023 15:11:39 +0200 Subject: [PATCH 03/21] Increment version --- .settings/org.eclipse.jdt.core.prefs | 2 +- dependencies.md | 2 +- doc/changes/changelog.md | 1 + doc/changes/changes_1.5.0.md | 23 +++++++++++++++++++ pk_generated_parent.pom | 3 +-- pom.xml | 20 ++++++++-------- ...=> row-level-security-lua-1.5.0-1.rockspec | 2 +- src/main/lua/exasol/rls/RlsAdapter.lua | 2 +- 8 files changed, 39 insertions(+), 16 deletions(-) create mode 100644 doc/changes/changes_1.5.0.md rename row-level-security-lua-1.4.1-1.rockspec => row-level-security-lua-1.5.0-1.rockspec (99%) diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs index 8b5a9aa..bb40c3f 100644 --- a/.settings/org.eclipse.jdt.core.prefs +++ b/.settings/org.eclipse.jdt.core.prefs @@ -111,7 +111,7 @@ org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning org.eclipse.jdt.core.compiler.problem.unusedTypeParameter=ignore org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning -org.eclipse.jdt.core.compiler.processAnnotations=enabled +org.eclipse.jdt.core.compiler.processAnnotations=disabled org.eclipse.jdt.core.compiler.release=disabled org.eclipse.jdt.core.compiler.source=11 org.eclipse.jdt.core.formatter.align_assignment_statements_on_columns=false diff --git a/dependencies.md b/dependencies.md index 38e1924..b9fa8b1 100644 --- a/dependencies.md +++ b/dependencies.md @@ -49,7 +49,7 @@ [1]: https://repo1.maven.org/maven2/com/exasol/exasol-jdbc/7.1.20/exasol-jdbc-7.1.20-license.txt [2]: https://github.com/exasol/exasol-testcontainers/ [3]: https://github.com/exasol/exasol-testcontainers/blob/main/LICENSE -[4]: https://testcontainers.org +[4]: https://java.testcontainers.org [5]: http://opensource.org/licenses/MIT [6]: http://hamcrest.org/JavaHamcrest/ [7]: http://opensource.org/licenses/BSD-3-Clause diff --git a/doc/changes/changelog.md b/doc/changes/changelog.md index 2476a7f..14d3de6 100644 --- a/doc/changes/changelog.md +++ b/doc/changes/changelog.md @@ -1,5 +1,6 @@ # Changes +* [1.5.0](changes_1.5.0.md) * [1.4.1](changes_1.4.1.md) * [1.4.0](changes_1.4.0.md) * [1.3.0](changes_1.3.0.md) diff --git a/doc/changes/changes_1.5.0.md b/doc/changes/changes_1.5.0.md new file mode 100644 index 0000000..06cee87 --- /dev/null +++ b/doc/changes/changes_1.5.0.md @@ -0,0 +1,23 @@ +# Exasol Row Level Security (Lua) 1.5.0, released 2023-??-?? + +Code name: Add Extension + +## Summary + +This release adds support for the [Extension Manager](https://github.com/exasol/extension-manager/). + +## Feature + +* #138: Added support for Extension Manager + +## Dependency Updates + +### Test Dependency Updates + +* Updated `org.junit.jupiter:junit-jupiter-engine:5.9.3` to `5.10.0` +* Updated `org.junit.jupiter:junit-jupiter-params:5.9.3` to `5.10.0` +* Updated `org.testcontainers:junit-jupiter:1.18.3` to `1.19.0` + +### Plugin Dependency Updates + +* Updated `com.exasol:project-keeper-maven-plugin:2.9.9` to `2.9.10` diff --git a/pk_generated_parent.pom b/pk_generated_parent.pom index dd8aef8..bfc55c5 100644 --- a/pk_generated_parent.pom +++ b/pk_generated_parent.pom @@ -3,7 +3,7 @@ 4.0.0 com.exasol row-level-security-lua-generated-parent - 1.4.1 + 1.5.0 pom UTF-8 @@ -157,7 +157,6 @@ true true false - true true false diff --git a/pom.xml b/pom.xml index efe7e67..18e82bd 100644 --- a/pom.xml +++ b/pom.xml @@ -3,12 +3,12 @@ 4.0.0 com.exasol row-level-security-lua - 1.4.1 + 1.5.0 Exasol Row Level Security (Lua) This projects provides a plug-in to the Exasol database that adds per-row access control. https://github.com/exasol/row-level-security-lua/ - 5.9.3 + 5.10.0 target/site/jacoco/jacoco.xml,target/site/jacoco-it/jacoco.xml ${basedir}/src/main/lua ${basedir}/src/test/lua @@ -32,16 +32,16 @@ org.testcontainers junit-jupiter - 1.18.3 + 1.19.0 test - - + + org.hamcrest @@ -91,7 +91,7 @@ com.exasol project-keeper-maven-plugin - 2.9.9 + 2.9.10 @@ -240,7 +240,7 @@ net.sourceforge.plantuml plantuml - 1.2023.9 + 1.2023.10 @@ -279,7 +279,7 @@ row-level-security-lua-generated-parent com.exasol - 1.4.1 + 1.5.0 pk_generated_parent.pom diff --git a/row-level-security-lua-1.4.1-1.rockspec b/row-level-security-lua-1.5.0-1.rockspec similarity index 99% rename from row-level-security-lua-1.4.1-1.rockspec rename to row-level-security-lua-1.5.0-1.rockspec index 1c7519a..d34ecba 100644 --- a/row-level-security-lua-1.4.1-1.rockspec +++ b/row-level-security-lua-1.5.0-1.rockspec @@ -1,6 +1,6 @@ rockspec_format = "3.0" -local tag = "1.4.1" +local tag = "1.5.0" local project = "row-level-security-lua" local src = "src/main/lua" diff --git a/src/main/lua/exasol/rls/RlsAdapter.lua b/src/main/lua/exasol/rls/RlsAdapter.lua index fa02222..c2eff18 100644 --- a/src/main/lua/exasol/rls/RlsAdapter.lua +++ b/src/main/lua/exasol/rls/RlsAdapter.lua @@ -3,7 +3,7 @@ local RlsAdapter = {} RlsAdapter.__index = RlsAdapter local AbstractVirtualSchemaAdapter = require("exasol.vscl.AbstractVirtualSchemaAdapter") setmetatable(RlsAdapter, {__index = AbstractVirtualSchemaAdapter}) -local VERSION = "1.4.1" +local VERSION = "1.5.0" local adapter_capabilities = require("exasol.rls.adapter_capabilities") local QueryRewriter = require("exasol.rls.RlsQueryRewriter") From a646cf7d73309bb525ae2af5caa544ca59d5e411 Mon Sep 17 00:00:00 2001 From: Christoph Pirkl Date: Fri, 25 Aug 2023 15:12:58 +0200 Subject: [PATCH 04/21] Upgrade Exasol DB versions --- .github/workflows/ci-build.yml | 2 +- src/test/java/com/exasol/RlsTestConstants.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci-build.yml b/.github/workflows/ci-build.yml index ae37a14..5435e5a 100644 --- a/.github/workflows/ci-build.yml +++ b/.github/workflows/ci-build.yml @@ -13,7 +13,7 @@ jobs: fail-fast: true matrix: lua_version: [5.4] - docker_db_version: ["7.1.21", "8.20.0"] + docker_db_version: ["7.1.22", "8.21.0"] runs-on: ubuntu-22.04 concurrency: group: ${{ github.workflow }}-${{ github.ref }}-${{ matrix.lua_version }}-${{ matrix.docker_db_version }} diff --git a/src/test/java/com/exasol/RlsTestConstants.java b/src/test/java/com/exasol/RlsTestConstants.java index afc7b3b..ef4ad08 100644 --- a/src/test/java/com/exasol/RlsTestConstants.java +++ b/src/test/java/com/exasol/RlsTestConstants.java @@ -12,7 +12,7 @@ public final class RlsTestConstants { public static final String ROLE_MASK_TYPE = "DECIMAL(20,0)"; public static final String IDENTIFIER_TYPE = "VARCHAR(128)"; public static final int PUBLIC_ROLE_BIT_INDEX = 63; - public static final String DOCKER_DB = "exasol/docker-db:8.20.0"; + public static final String DOCKER_DB = "exasol/docker-db:8.21.0"; private RlsTestConstants() { // prevent instantiation From f9ba3e1081501c0734e8d353da9c2d581eee7958 Mon Sep 17 00:00:00 2001 From: Christoph Pirkl Date: Fri, 25 Aug 2023 15:43:55 +0200 Subject: [PATCH 05/21] Add extension project --- .gitattributes | 13 +- ...ase_droid_upload_github_release_assets.yml | 7 + .gitignore | 7 + extension/.eslintrc.cjs | 14 + extension/generate-description.sh | 45 + extension/jest.config.ts | 17 + extension/package-lock.json | 5272 +++++++++++++++++ extension/package.json | 31 + extension/src/extension.test.ts | 195 + extension/src/extension.ts | 52 + extension/src/test-utils.ts | 67 + extension/src/upgrade.test.ts | 50 + extension/tsconfig.json | 20 + pom.xml | 54 + 14 files changed, 5839 insertions(+), 5 deletions(-) create mode 100644 extension/.eslintrc.cjs create mode 100755 extension/generate-description.sh create mode 100644 extension/jest.config.ts create mode 100644 extension/package-lock.json create mode 100644 extension/package.json create mode 100644 extension/src/extension.test.ts create mode 100644 extension/src/extension.ts create mode 100644 extension/src/test-utils.ts create mode 100644 extension/src/upgrade.test.ts create mode 100644 extension/tsconfig.json diff --git a/.gitattributes b/.gitattributes index 6d13f80..28e910a 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,8 +1,11 @@ *.sh text eol=lf *.bat text eol=crlf -pk_generated_parent.pom linguist-generated=true -dependencies.md linguist-generated=true -doc/changes/changelog.md linguist-generated=true -.github/workflows/broken_links_checker.yml linguist-generated=true -.github/workflows/dependencies_check.yml linguist-generated=true +pk_generated_parent.pom linguist-generated=true +dependencies.md linguist-generated=true +doc/changes/changelog.md linguist-generated=true +.github/workflows/broken_links_checker.yml linguist-generated=true +.github/workflows/dependencies_check.yml linguist-generated=true +extension/package-lock.json linguist-generated=true +.settings/org.eclipse.jdt.core.prefs linguist-generated=true +.settings/org.eclipse.jdt.ui.prefs linguist-generated=true diff --git a/.github/workflows/release_droid_upload_github_release_assets.yml b/.github/workflows/release_droid_upload_github_release_assets.yml index c46fe52..bee0846 100644 --- a/.github/workflows/release_droid_upload_github_release_assets.yml +++ b/.github/workflows/release_droid_upload_github_release_assets.yml @@ -38,6 +38,13 @@ jobs: run: | cd target find . -maxdepth 1 \( -name '*.lua' -o -name '*.sql' \) -exec bash -c 'sha256sum {} > {}.sha256' \; + - name: Generate sha256sum files for extension + run: cd extension/dist/ && sha256sum row-level-security-extension.js > row-level-security-extension.js.sha256 + - name: Upload extension + uses: shogo82148/actions-upload-release-asset@v1 + with: + upload_url: ${{ github.event.inputs.upload_url }} + asset_path: extension/dist/row-level-security-extension.js* - name: Upload SQL scripts to the GitHub release draft uses: shogo82148/actions-upload-release-asset@v1 with: diff --git a/.gitignore b/.gitignore index a3ec5bd..3726461 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,10 @@ pom.xml.versionsBackup *.orig *.old *.flattened-pom.xml + +# Extension +/extension/node_modules/ +/extension/dist/ +/extension/src/extension-description.ts +/extension/coverage/ +/extension-test.properties diff --git a/extension/.eslintrc.cjs b/extension/.eslintrc.cjs new file mode 100644 index 0000000..bb3b040 --- /dev/null +++ b/extension/.eslintrc.cjs @@ -0,0 +1,14 @@ +module.exports = { + root: true, + parser: '@typescript-eslint/parser', + parserOptions: { + tsconfigRootDir: __dirname, + project: ['./tsconfig.json'], + }, + plugins: ['@typescript-eslint'], + extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended', 'plugin:@typescript-eslint/recommended-requiring-type-checking'], + ignorePatterns: ["src/**/*.test.ts"], + rules: { + "@typescript-eslint/no-unused-vars": ["error", { argsIgnorePattern: "^_" }] + } +}; diff --git a/extension/generate-description.sh b/extension/generate-description.sh new file mode 100755 index 0000000..8225403 --- /dev/null +++ b/extension/generate-description.sh @@ -0,0 +1,45 @@ +#!/bin/bash + +set -o errexit +set -o nounset +set -o pipefail + +project_dir="$( cd "$(dirname "$0")/.." >/dev/null 2>&1 ; pwd -P )" +readonly project_dir +readonly target_dir="$project_dir/target" +readonly extension_config="$project_dir/extension/src/extension-description.ts" + +readonly all_built_files=("$target_dir"/exasol-cloud-storage-extension-*.jar) +readonly file_count=${#all_built_files[@]} + +if [ "$file_count" -ne 1 ]; then + echo "ERROR: Expected exactly one jar but found $file_count in $target_dir: ${all_built_files[*]}" + exit 1 +fi + +readonly complete_path="${all_built_files[0]}" +file_name=$(basename "$complete_path") +readonly file_name +file_size_bytes=$(stat -c%s "$complete_path") +readonly file_size_bytes + +version= +if [[ $file_name =~ ^exasol-cloud-storage-extension-(.*).jar$ ]]; then + version=${BASH_REMATCH[1]} +else + echo "ERROR: Could not extract version from filename $file_name" + exit 1 +fi +readonly version + +cat > "$extension_config" <=0.10.0" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", + "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.10.tgz", + "integrity": "sha512-/KKIMG4UEL35WmI9OlvMhurwtytjvXoFcGNrOvyG9zIzA8YmPjVtIZUf7b05+TPO7G7/GEmLHDaoCgACHl9hhA==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.22.10", + "chalk": "^2.4.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/code-frame/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/code-frame/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/@babel/code-frame/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/code-frame/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.22.9", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.9.tgz", + "integrity": "sha512-5UamI7xkUcJ3i9qVDS+KFDEK8/7oJ55/sJMB1Ge7IEapr7KfdfV/HErR+koZwOfd+SgtFKOKRhRakdg++DcJpQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.10.tgz", + "integrity": "sha512-fTmqbbUBAwCcre6zPzNngvsI0aNrPZe77AeqvDxWM9Nm+04RrJ3CAmGHA9f7lJQY6ZMhRztNemy4uslDxTX4Qw==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.22.10", + "@babel/generator": "^7.22.10", + "@babel/helper-compilation-targets": "^7.22.10", + "@babel/helper-module-transforms": "^7.22.9", + "@babel/helpers": "^7.22.10", + "@babel/parser": "^7.22.10", + "@babel/template": "^7.22.5", + "@babel/traverse": "^7.22.10", + "@babel/types": "^7.22.10", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.2", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "dev": true + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.10.tgz", + "integrity": "sha512-79KIf7YiWjjdZ81JnLujDRApWtl7BxTqWD88+FFdQEIOG8LJ0etDOM7CXuIgGJa55sGOwZVwuEsaLEm0PJ5/+A==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.10", + "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", + "jsesc": "^2.5.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.10.tgz", + "integrity": "sha512-JMSwHD4J7SLod0idLq5PKgI+6g/hLD/iuWBq08ZX49xE14VpVEojJ5rHWptpirV2j020MvypRLAXAO50igCJ5Q==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.22.9", + "@babel/helper-validator-option": "^7.22.5", + "browserslist": "^4.21.9", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-environment-visitor": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz", + "integrity": "sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-function-name": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz", + "integrity": "sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ==", + "dev": true, + "dependencies": { + "@babel/template": "^7.22.5", + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-hoist-variables": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.5.tgz", + "integrity": "sha512-8Dl6+HD/cKifutF5qGd/8ZJi84QeAKh+CEe1sBzz8UayBBGg1dAIJrdHOcOM5b2MpzWL2yuotJTtGjETq0qjXg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.22.9", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.22.9.tgz", + "integrity": "sha512-t+WA2Xn5K+rTeGtC8jCsdAH52bjggG5TKRuRrAGNM/mjIbO4GxvlLMFOEz9wXY5I2XQ60PMFsAG2WIcG82dQMQ==", + "dev": true, + "dependencies": { + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-module-imports": "^7.22.5", + "@babel/helper-simple-access": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/helper-validator-identifier": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz", + "integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", + "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", + "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz", + "integrity": "sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.5.tgz", + "integrity": "sha512-R3oB6xlIVKUnxNUxbmgq7pKjxpru24zlimpE8WK47fACIlM0II/Hm1RS8IaOI7NgCr6LNS+jl5l75m20npAziw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.22.10.tgz", + "integrity": "sha512-a41J4NW8HyZa1I1vAndrraTlPZ/eZoga2ZgS7fEr0tZJGVU4xqdE80CEm0CcNjha5EZ8fTBYLKHF0kqDUuAwQw==", + "dev": true, + "dependencies": { + "@babel/template": "^7.22.5", + "@babel/traverse": "^7.22.10", + "@babel/types": "^7.22.10" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.10.tgz", + "integrity": "sha512-78aUtVcT7MUscr0K5mIEnkwxPE0MaxkR5RxRwuHaQ+JuU5AmTPhY+do2mdzVTnIJJpyBglql2pehuBIWHug+WQ==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.22.5", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/@babel/highlight/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/parser": { + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.10.tgz", + "integrity": "sha512-lNbdGsQb9ekfsnjFGhEiF4hfFqGgfOP3H3d27re3n+CGhNuTSUEQdfWk556sTLNTloczcdM5TYF2LhzmDQKyvQ==", + "dev": true, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.22.5.tgz", + "integrity": "sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.22.5.tgz", + "integrity": "sha512-1mS2o03i7t1c6VzH6fdQ3OA8tcEIxwG18zIPRp+UY1Ihv6W+XZzBCVxExF9upussPXJ0xE9XRHwMoNs1ep/nRQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.5.tgz", + "integrity": "sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.22.5", + "@babel/parser": "^7.22.5", + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.10.tgz", + "integrity": "sha512-Q/urqV4pRByiNNpb/f5OSv28ZlGJiFiiTh+GAHktbIrkPhPbl90+uW6SmpoLyZqutrg9AEaEf3Q/ZBRHBXgxig==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.22.10", + "@babel/generator": "^7.22.10", + "@babel/helper-environment-visitor": "^7.22.5", + "@babel/helper-function-name": "^7.22.5", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.22.10", + "@babel/types": "^7.22.10", + "debug": "^4.1.0", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/types": { + "version": "7.22.10", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.10.tgz", + "integrity": "sha512-obaoigiLrlDZ7TUQln/8m4mSqIW2QFeOrCQc9r+xsaHGNoplVNYlRVpsfE8Vj35GEm2ZH4ZhrNYogs/3fj85kg==", + "dev": true, + "dependencies": { + "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.5", + "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.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/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.2.tgz", + "integrity": "sha512-tM8yLeYVe7pRyAu9VMi/Q7aunpLwD139EY1S99xbQkT4/q2qa6eA4ige/WJQYdJ8GBL1K33pPFhPfPdJ/WzT8Q==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.2.tgz", + "integrity": "sha512-lsB65vAbe90I/Qe10OjkmrdxSX4UJDjosDgb8sZUKcg3oefEuW2OT2Vozz8ef7wrJbMcmhvCC+hciF8jY/uAkw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.2.tgz", + "integrity": "sha512-qK/TpmHt2M/Hg82WXHRc/W/2SGo/l1thtDHZWqFq7oi24AjZ4O/CpPSu6ZuYKFkEgmZlFoa7CooAyYmuvnaG8w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.2.tgz", + "integrity": "sha512-Ora8JokrvrzEPEpZO18ZYXkH4asCdc1DLdcVy8TGf5eWtPO1Ie4WroEJzwI52ZGtpODy3+m0a2yEX9l+KUn0tA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.2.tgz", + "integrity": "sha512-tP+B5UuIbbFMj2hQaUr6EALlHOIOmlLM2FK7jeFBobPy2ERdohI4Ka6ZFjZ1ZYsrHE/hZimGuU90jusRE0pwDw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.2.tgz", + "integrity": "sha512-YbPY2kc0acfzL1VPVK6EnAlig4f+l8xmq36OZkU0jzBVHcOTyQDhnKQaLzZudNJQyymd9OqQezeaBgkTGdTGeQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.2.tgz", + "integrity": "sha512-nSO5uZT2clM6hosjWHAsS15hLrwCvIWx+b2e3lZ3MwbYSaXwvfO528OF+dLjas1g3bZonciivI8qKR/Hm7IWGw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.2.tgz", + "integrity": "sha512-Odalh8hICg7SOD7XCj0YLpYCEc+6mkoq63UnExDCiRA2wXEmGlK5JVrW50vZR9Qz4qkvqnHcpH+OFEggO3PgTg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.2.tgz", + "integrity": "sha512-ig2P7GeG//zWlU0AggA3pV1h5gdix0MA3wgB+NsnBXViwiGgY77fuN9Wr5uoCrs2YzaYfogXgsWZbm+HGr09xg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.2.tgz", + "integrity": "sha512-mLfp0ziRPOLSTek0Gd9T5B8AtzKAkoZE70fneiiyPlSnUKKI4lp+mGEnQXcQEHLJAcIYDPSyBvsUbKUG2ri/XQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.2.tgz", + "integrity": "sha512-hn28+JNDTxxCpnYjdDYVMNTR3SKavyLlCHHkufHV91fkewpIyQchS1d8wSbmXhs1fiYDpNww8KTFlJ1dHsxeSw==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.2.tgz", + "integrity": "sha512-KbXaC0Sejt7vD2fEgPoIKb6nxkfYW9OmFUK9XQE4//PvGIxNIfPk1NmlHmMg6f25x57rpmEFrn1OotASYIAaTg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.2.tgz", + "integrity": "sha512-dJ0kE8KTqbiHtA3Fc/zn7lCd7pqVr4JcT0JqOnbj4LLzYnp+7h8Qi4yjfq42ZlHfhOCM42rBh0EwHYLL6LEzcw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.2.tgz", + "integrity": "sha512-7Z/jKNFufZ/bbu4INqqCN6DDlrmOTmdw6D0gH+6Y7auok2r02Ur661qPuXidPOJ+FSgbEeQnnAGgsVynfLuOEw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.2.tgz", + "integrity": "sha512-U+RinR6aXXABFCcAY4gSlv4CL1oOVvSSCdseQmGO66H+XyuQGZIUdhG56SZaDJQcLmrSfRmx5XZOWyCJPRqS7g==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.2.tgz", + "integrity": "sha512-oxzHTEv6VPm3XXNaHPyUTTte+3wGv7qVQtqaZCrgstI16gCuhNOtBXLEBkBREP57YTd68P0VgDgG73jSD8bwXQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.2.tgz", + "integrity": "sha512-WNa5zZk1XpTTwMDompZmvQLHszDDDN7lYjEHCUmAGB83Bgs20EMs7ICD+oKeT6xt4phV4NDdSi/8OfjPbSbZfQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.2.tgz", + "integrity": "sha512-S6kI1aT3S++Dedb7vxIuUOb3oAxqxk2Rh5rOXOTYnzN8JzW1VzBd+IqPiSpgitu45042SYD3HCoEyhLKQcDFDw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.2.tgz", + "integrity": "sha512-VXSSMsmb+Z8LbsQGcBMiM+fYObDNRm8p7tkUDMPG/g4fhFX5DEFmjxIEa3N8Zr96SjsJ1woAhF0DUnS3MF3ARw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.2.tgz", + "integrity": "sha512-5NayUlSAyb5PQYFAU9x3bHdsqB88RC3aM9lKDAz4X1mo/EchMIT1Q+pSeBXNgkfNmRecLXA0O8xP+x8V+g/LKg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.2.tgz", + "integrity": "sha512-47gL/ek1v36iN0wL9L4Q2MFdujR0poLZMJwhO2/N3gA89jgHp4MR8DKCmwYtGNksbfJb9JoTtbkoe6sDhg2QTA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.2.tgz", + "integrity": "sha512-tcuhV7ncXBqbt/Ybf0IyrMcwVOAPDckMK9rXNHtF17UTK18OKLpg08glminN06pt2WCoALhXdLfSPbVvK/6fxw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.7.0.tgz", + "integrity": "sha512-+HencqxU7CFJnQb7IKtuNBqS6Yx3Tz4kOL8BJXo+JyeiBm5MEX6pO8onXDkjrkCRlfYXS1Axro15ZjVFe9YgsA==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.2.tgz", + "integrity": "sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/js": { + "version": "8.47.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.47.0.tgz", + "integrity": "sha512-P6omY1zv5MItm93kLM8s2vr1HICJH8v0dvddDhysbIuZ+vcjOHg5Zbkf1mTkcmi2JA9oBG2anOkRnW8WJTS8Og==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@exasol/extension-manager-interface": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@exasol/extension-manager-interface/-/extension-manager-interface-0.3.1.tgz", + "integrity": "sha512-eiemC19v6wp9VCrL7w8SL6gBFAhkdwt2MoS19fcaQoHPOzH08jG9q4RuY43JQGa4PckCW3KO5lmn3P5XsIpbXg==" + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz", + "integrity": "sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true + }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "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/@jest/console": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.6.3.tgz", + "integrity": "sha512-ukZbHAdDH4ktZIOKvWs1juAXhiVAdvCyM8zv4S/7Ii3vJSDvMW5k+wOVGMQmHLHUFw3Ko63ZQNy7NI6PSlsD5w==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^29.6.3", + "jest-util": "^29.6.3", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/core": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.6.3.tgz", + "integrity": "sha512-skV1XrfNxfagmjRUrk2FyN5/2YwIzdWVVBa/orUfbLvQUANXxERq2pTvY0I+FinWHjDKB2HRmpveUiph4X0TJw==", + "dev": true, + "dependencies": { + "@jest/console": "^29.6.3", + "@jest/reporters": "^29.6.3", + "@jest/test-result": "^29.6.3", + "@jest/transform": "^29.6.3", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-changed-files": "^29.6.3", + "jest-config": "^29.6.3", + "jest-haste-map": "^29.6.3", + "jest-message-util": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.6.3", + "jest-resolve-dependencies": "^29.6.3", + "jest-runner": "^29.6.3", + "jest-runtime": "^29.6.3", + "jest-snapshot": "^29.6.3", + "jest-util": "^29.6.3", + "jest-validate": "^29.6.3", + "jest-watcher": "^29.6.3", + "micromatch": "^4.0.4", + "pretty-format": "^29.6.3", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/environment": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.6.3.tgz", + "integrity": "sha512-u/u3cCztYCfgBiGHsamqP5x+XvucftOGPbf5RJQxfpeC1y4AL8pCjKvPDA3oCmdhZYPgk5AE0VOD/flweR69WA==", + "dev": true, + "dependencies": { + "@jest/fake-timers": "^29.6.3", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.6.3.tgz", + "integrity": "sha512-Ic08XbI2jlg6rECy+CGwk/8NDa6VE7UmIG6++9OTPAMnQmNGY28hu69Nf629CWv6T7YMODLbONxDFKdmQeI9FA==", + "dev": true, + "dependencies": { + "expect": "^29.6.3", + "jest-snapshot": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect-utils": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.6.3.tgz", + "integrity": "sha512-nvOEW4YoqRKD9HBJ9OJ6przvIvP9qilp5nAn1462P5ZlL/MM9SgPEZFyjTGPfs7QkocdUsJa6KjHhyRn4ueItA==", + "dev": true, + "dependencies": { + "jest-get-type": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/fake-timers": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.6.3.tgz", + "integrity": "sha512-pa1wmqvbj6eX0nMvOM2VDAWvJOI5A/Mk3l8O7n7EsAh71sMZblaKO9iT4GjIj0LwwK3CP/Jp1ypEV0x3m89RvA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@sinonjs/fake-timers": "^10.0.2", + "@types/node": "*", + "jest-message-util": "^29.6.3", + "jest-mock": "^29.6.3", + "jest-util": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/globals": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.6.3.tgz", + "integrity": "sha512-RB+uI+CZMHntzlnOPlll5x/jgRff3LEPl/td/jzMXiIgR0iIhKq9qm1HLU+EC52NuoVy/1swit/sDGjVn4bc6A==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.6.3", + "@jest/expect": "^29.6.3", + "@jest/types": "^29.6.3", + "jest-mock": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/reporters": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.6.3.tgz", + "integrity": "sha512-kGz59zMi0GkVjD2CJeYWG9k6cvj7eBqt9aDAqo2rcCLRTYlvQ62Gu/n+tOmJMBHGjzeijjuCENjzTyYBgrtLUw==", + "dev": true, + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^29.6.3", + "@jest/test-result": "^29.6.3", + "@jest/transform": "^29.6.3", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^6.0.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "^29.6.3", + "jest-util": "^29.6.3", + "jest-worker": "^29.6.3", + "slash": "^3.0.0", + "string-length": "^4.0.1", + "strip-ansi": "^6.0.0", + "v8-to-istanbul": "^9.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dev": true, + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/source-map": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", + "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.18", + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-result": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.6.3.tgz", + "integrity": "sha512-k7ZZaNvOSMBHPZYiy0kuiaFoyansR5QnTwDux1EjK3kD5iWpRVyJIJ0RAIV39SThafchuW59vra7F8mdy5Hfgw==", + "dev": true, + "dependencies": { + "@jest/console": "^29.6.3", + "@jest/types": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-sequencer": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.6.3.tgz", + "integrity": "sha512-/SmijaAU2TY9ComFGIYa6Z+fmKqQMnqs2Nmwb0P/Z/tROdZ7M0iruES1EaaU9PBf8o9uED5xzaJ3YPFEIcDgAg==", + "dev": true, + "dependencies": { + "@jest/test-result": "^29.6.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.6.3", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/transform": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.6.3.tgz", + "integrity": "sha512-dPIc3DsvMZ/S8ut4L2ViCj265mKO0owB0wfzBv2oGzL9pQ+iRvJewHqLBmsGb7XFb5UotWIEtvY5A/lnylaIoQ==", + "dev": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.6.3", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.2" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", + "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "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.19", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.19.tgz", + "integrity": "sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "dev": true + }, + "node_modules/@sinonjs/commons": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz", + "integrity": "sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==", + "dev": true, + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", + "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^3.0.0" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", + "dev": true + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true + }, + "node_modules/@types/babel__core": { + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.1.tgz", + "integrity": "sha512-aACu/U/omhdk15O4Nfb+fHgH/z3QsfQzpnvRZhYhThms83ZnAOZz7zZAWO7mn2yyNQaA4xTO8GLK3uqFU4bYYw==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.6.4", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.4.tgz", + "integrity": "sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.1", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.1.tgz", + "integrity": "sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.1.tgz", + "integrity": "sha512-MitHFXnhtgwsGZWtT68URpOvLN4EREih1u3QtQiN4VdAxWKRVvGCSvw/Qth0M0Qq3pJpnGOu5JaM/ydK7OGbqg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.20.7" + } + }, + "node_modules/@types/graceful-fs": { + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.6.tgz", + "integrity": "sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", + "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", + "dev": true + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", + "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", + "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", + "dev": true, + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/json-schema": { + "version": "7.0.12", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.12.tgz", + "integrity": "sha512-Hr5Jfhc9eYOQNPYO5WLDq/n4jqijdHNlDXjuAQkkt+mWdQR+XJToOHrsD4cPaMXpn6KO7y2+wM8AZEs8VpBLVA==", + "dev": true + }, + "node_modules/@types/node": { + "version": "20.5.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.5.4.tgz", + "integrity": "sha512-Y9vbIAoM31djQZrPYjpTLo0XlaSwOIsrlfE3LpulZeRblttsLQRFRlBAppW0LOxyT3ALj2M5vU1ucQQayQH3jA==", + "dev": true + }, + "node_modules/@types/semver": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.0.tgz", + "integrity": "sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==", + "dev": true + }, + "node_modules/@types/stack-utils": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", + "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", + "dev": true + }, + "node_modules/@types/yargs": { + "version": "17.0.24", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", + "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", + "dev": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", + "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", + "dev": true + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.4.1.tgz", + "integrity": "sha512-3F5PtBzUW0dYlq77Lcqo13fv+58KDwUib3BddilE8ajPJT+faGgxmI9Sw+I8ZS22BYwoir9ZhNXcLi+S+I2bkw==", + "dev": true, + "dependencies": { + "@eslint-community/regexpp": "^4.5.1", + "@typescript-eslint/scope-manager": "6.4.1", + "@typescript-eslint/type-utils": "6.4.1", + "@typescript-eslint/utils": "6.4.1", + "@typescript-eslint/visitor-keys": "6.4.1", + "debug": "^4.3.4", + "graphemer": "^1.4.0", + "ignore": "^5.2.4", + "natural-compare": "^1.4.0", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.4.1.tgz", + "integrity": "sha512-610G6KHymg9V7EqOaNBMtD1GgpAmGROsmfHJPXNLCU9bfIuLrkdOygltK784F6Crboyd5tBFayPB7Sf0McrQwg==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "6.4.1", + "@typescript-eslint/types": "6.4.1", + "@typescript-eslint/typescript-estree": "6.4.1", + "@typescript-eslint/visitor-keys": "6.4.1", + "debug": "^4.3.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.4.1.tgz", + "integrity": "sha512-p/OavqOQfm4/Hdrr7kvacOSFjwQ2rrDVJRPxt/o0TOWdFnjJptnjnZ+sYDR7fi4OimvIuKp+2LCkc+rt9fIW+A==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.4.1", + "@typescript-eslint/visitor-keys": "6.4.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.4.1.tgz", + "integrity": "sha512-7ON8M8NXh73SGZ5XvIqWHjgX2f+vvaOarNliGhjrJnv1vdjG0LVIz+ToYfPirOoBi56jxAKLfsLm40+RvxVVXA==", + "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "6.4.1", + "@typescript-eslint/utils": "6.4.1", + "debug": "^4.3.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/types": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.4.1.tgz", + "integrity": "sha512-zAAopbNuYu++ijY1GV2ylCsQsi3B8QvfPHVqhGdDcbx/NK5lkqMnCGU53amAjccSpk+LfeONxwzUhDzArSfZJg==", + "dev": true, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.4.1.tgz", + "integrity": "sha512-xF6Y7SatVE/OyV93h1xGgfOkHr2iXuo8ip0gbfzaKeGGuKiAnzS+HtVhSPx8Www243bwlW8IF7X0/B62SzFftg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.4.1", + "@typescript-eslint/visitor-keys": "6.4.1", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.4.1.tgz", + "integrity": "sha512-F/6r2RieNeorU0zhqZNv89s9bDZSovv3bZQpUNOmmQK1L80/cV4KEu95YUJWi75u5PhboFoKUJBnZ4FQcoqhDw==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "6.4.1", + "@typescript-eslint/types": "6.4.1", + "@typescript-eslint/typescript-estree": "6.4.1", + "semver": "^7.5.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.4.1.tgz", + "integrity": "sha512-y/TyRJsbZPkJIZQXrHfdnxVnxyKegnpEvnRGNam7s3TRR2ykGefEWOhaef00/UUN3IZxizS7BTO3svd3lCOJRQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.4.1", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/acorn": { + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", + "integrity": "sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-escapes/node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, + "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/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-jest": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.6.3.tgz", + "integrity": "sha512-1Ne93zZZEy5XmTa4Q+W5+zxBrDpExX8E3iy+xJJ+24ewlfo/T3qHfQJCzi/MMVFmBQDNxtRR/Gfd2dwb/0yrQw==", + "dev": true, + "dependencies": { + "@jest/transform": "^29.6.3", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^29.6.3", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.8.0" + } + }, + "node_modules/babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "dev": true, + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-istanbul/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/babel-plugin-jest-hoist": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", + "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", + "dev": true, + "dependencies": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.1.14", + "@types/babel__traverse": "^7.0.6" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/babel-preset-current-node-syntax": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", + "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", + "dev": true, + "dependencies": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.8.3", + "@babel/plugin-syntax-import-meta": "^7.8.3", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.8.3", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-top-level-await": "^7.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/babel-preset-jest": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", + "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", + "dev": true, + "dependencies": { + "babel-plugin-jest-hoist": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "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/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/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/browserslist": { + "version": "4.21.10", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.10.tgz", + "integrity": "sha512-bipEBdZfVH5/pwrvqc+Ub0kUPVfGUhlKxbvfD+z1BDnPEO/X98ruXGA1WP5ASpAFKan7Qr6j736IacbZQuAlKQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001517", + "electron-to-chromium": "^1.4.477", + "node-releases": "^2.0.13", + "update-browserslist-db": "^1.0.11" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/bs-logger": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", + "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", + "dev": true, + "dependencies": { + "fast-json-stable-stringify": "2.x" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "dependencies": { + "node-int64": "^0.4.0" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001522", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001522.tgz", + "integrity": "sha512-TKiyTVZxJGhsTszLuzb+6vUZSjVOAhClszBr2Ta2k9IwtNBT/4dzmL6aywt0HCgEZlmwJzXJd8yNiob6HgwTRg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] + }, + "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/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/ci-info": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz", + "integrity": "sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "engines": { + "node": ">=8" + } + }, + "node_modules/cjs-module-lexer": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz", + "integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==", + "dev": true + }, + "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/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "dev": true, + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/collect-v8-coverage": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", + "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", + "dev": true + }, + "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/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, + "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/dedent": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.1.tgz", + "integrity": "sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg==", + "dev": true, + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" + }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/diff-sequences": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.4.500", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.500.tgz", + "integrity": "sha512-P38NO8eOuWOKY1sQk5yE0crNtrjgjJj6r3NrbIKtG18KzCHmHE2Bt+aQA7/y0w3uYsHWxDa6icOohzjLJ4vJ4A==", + "dev": true + }, + "node_modules/emittery": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", + "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "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/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/esbuild": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.2.tgz", + "integrity": "sha512-G6hPax8UbFakEj3hWO0Vs52LQ8k3lnBhxZWomUJDxfz3rZTLqF5k/FCzuNdLx2RbpBiQQF9H9onlDDH1lZsnjg==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/android-arm": "0.19.2", + "@esbuild/android-arm64": "0.19.2", + "@esbuild/android-x64": "0.19.2", + "@esbuild/darwin-arm64": "0.19.2", + "@esbuild/darwin-x64": "0.19.2", + "@esbuild/freebsd-arm64": "0.19.2", + "@esbuild/freebsd-x64": "0.19.2", + "@esbuild/linux-arm": "0.19.2", + "@esbuild/linux-arm64": "0.19.2", + "@esbuild/linux-ia32": "0.19.2", + "@esbuild/linux-loong64": "0.19.2", + "@esbuild/linux-mips64el": "0.19.2", + "@esbuild/linux-ppc64": "0.19.2", + "@esbuild/linux-riscv64": "0.19.2", + "@esbuild/linux-s390x": "0.19.2", + "@esbuild/linux-x64": "0.19.2", + "@esbuild/netbsd-x64": "0.19.2", + "@esbuild/openbsd-x64": "0.19.2", + "@esbuild/sunos-x64": "0.19.2", + "@esbuild/win32-arm64": "0.19.2", + "@esbuild/win32-ia32": "0.19.2", + "@esbuild/win32-x64": "0.19.2" + } + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "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/eslint": { + "version": "8.47.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.47.0.tgz", + "integrity": "sha512-spUQWrdPt+pRVP1TTJLmfRNJJHHZryFmptzcafwSvHsceV81djHOdnEeDmkdotZyLNjDhrOasNK8nikkoG1O8Q==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.2", + "@eslint/js": "^8.47.0", + "@humanwhocodes/config-array": "^0.11.10", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/esquery": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/expect": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.6.3.tgz", + "integrity": "sha512-x1vY4LlEMWUYVZQrFi4ZANXFwqYbJ/JNQspLVvzhW2BNY28aNcXMQH6imBbt+RBf5sVRTodYHXtSP/TLEU0Dxw==", + "dev": true, + "dependencies": { + "@jest/expect-utils": "^29.6.3", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.6.3", + "jest-message-util": "^29.6.3", + "jest-util": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-glob": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", + "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "node_modules/fastq": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", + "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "dev": true, + "dependencies": { + "bser": "2.1.1" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "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-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "dependencies": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", + "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", + "dev": true + }, + "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/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "engines": { + "node": ">=6.9.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/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "13.21.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.21.0.tgz", + "integrity": "sha512-ybyme3s4yy/t/3s35bewwXKOf7cvzfreG2lH0lZl0JB7I4GxRP2ghxOK/Nb9EkRXdbBXZLfq/p/0W2JUONB/Gg==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "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/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/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "engines": { + "node": ">=10.17.0" + } + }, + "node_modules/ignore": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/import-local": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", + "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", + "dev": true, + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "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-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true + }, + "node_modules/is-core-module": { + "version": "2.13.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.0.tgz", + "integrity": "sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==", + "dev": true, + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "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-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "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-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", + "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.0.tgz", + "integrity": "sha512-x58orMzEVfzPUKqlbLd1hXCnySCxKdDKa6Rjg97CwuLLRI4g3FHTdnExu1OqffVFay6zeMW+T6/DowFLndWnIw==", + "dev": true, + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=10" + } + }, + "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-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-reports": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.6.tgz", + "integrity": "sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg==", + "dev": true, + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest/-/jest-29.6.3.tgz", + "integrity": "sha512-alueLuoPCDNHFcFGmgETR4KpQ+0ff3qVaiJwxQM4B5sC0CvXcgg4PEi7xrDkxuItDmdz/FVc7SSit4KEu8GRvw==", + "dev": true, + "dependencies": { + "@jest/core": "^29.6.3", + "@jest/types": "^29.6.3", + "import-local": "^3.0.2", + "jest-cli": "^29.6.3" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-changed-files": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.6.3.tgz", + "integrity": "sha512-G5wDnElqLa4/c66ma5PG9eRjE342lIbF6SUnTJi26C3J28Fv2TVY2rOyKB9YGbSA5ogwevgmxc4j4aVjrEK6Yg==", + "dev": true, + "dependencies": { + "execa": "^5.0.0", + "jest-util": "^29.6.3", + "p-limit": "^3.1.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-circus": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.6.3.tgz", + "integrity": "sha512-p0R5YqZEMnOpHqHLWRSjm2z/0p6RNsrNE/GRRT3eli8QGOAozj6Ys/3Tv+Ej+IfltJoSPwcQ6/hOCRkNlxLLCw==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.6.3", + "@jest/expect": "^29.6.3", + "@jest/test-result": "^29.6.3", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^1.0.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^29.6.3", + "jest-matcher-utils": "^29.6.3", + "jest-message-util": "^29.6.3", + "jest-runtime": "^29.6.3", + "jest-snapshot": "^29.6.3", + "jest-util": "^29.6.3", + "p-limit": "^3.1.0", + "pretty-format": "^29.6.3", + "pure-rand": "^6.0.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-cli": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.6.3.tgz", + "integrity": "sha512-KuPdXUPXQIf0t6DvmG8MV4QyhcjR1a6ruKl3YL7aGn/AQ8JkROwFkWzEpDIpt11Qy188dHbRm8WjwMsV/4nmnQ==", + "dev": true, + "dependencies": { + "@jest/core": "^29.6.3", + "@jest/test-result": "^29.6.3", + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "import-local": "^3.0.2", + "jest-config": "^29.6.3", + "jest-util": "^29.6.3", + "jest-validate": "^29.6.3", + "prompts": "^2.0.1", + "yargs": "^17.3.1" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-config": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.6.3.tgz", + "integrity": "sha512-nb9bOq2aEqogbyL4F9mLkAeQGAgNt7Uz6U59YtQDIxFPiL7Ejgq0YIrp78oyEHD6H4CIV/k7mFrK7eFDzUJ69w==", + "dev": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/test-sequencer": "^29.6.3", + "@jest/types": "^29.6.3", + "babel-jest": "^29.6.3", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-circus": "^29.6.3", + "jest-environment-node": "^29.6.3", + "jest-get-type": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.6.3", + "jest-runner": "^29.6.3", + "jest-util": "^29.6.3", + "jest-validate": "^29.6.3", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^29.6.3", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@types/node": "*", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/jest-diff": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.6.3.tgz", + "integrity": "sha512-3sw+AdWnwH9sSNohMRKA7JiYUJSRr/WS6+sEFfBuhxU5V5GlEVKfvUn8JuMHE0wqKowemR1C2aHy8VtXbaV8dQ==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-docblock": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.6.3.tgz", + "integrity": "sha512-2+H+GOTQBEm2+qFSQ7Ma+BvyV+waiIFxmZF5LdpBsAEjWX8QYjSCa4FrkIYtbfXUJJJnFCYrOtt6TZ+IAiTjBQ==", + "dev": true, + "dependencies": { + "detect-newline": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-each": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.6.3.tgz", + "integrity": "sha512-KoXfJ42k8cqbkfshW7sSHcdfnv5agDdHCPA87ZBdmHP+zJstTJc0ttQaJ/x7zK6noAL76hOuTIJ6ZkQRS5dcyg==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "jest-util": "^29.6.3", + "pretty-format": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-environment-node": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.6.3.tgz", + "integrity": "sha512-PKl7upfPJXMYbWpD+60o4HP86KvFO2c9dZ+Zr6wUzsG5xcPx/65o3ArNgHW5M0RFvLYdW4/aieR4JSooD0a2ew==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.6.3", + "@jest/fake-timers": "^29.6.3", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.6.3", + "jest-util": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-get-type": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-haste-map": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.6.3.tgz", + "integrity": "sha512-GecR5YavfjkhOytEFHAeI6aWWG3f/cOKNB1YJvj/B76xAmeVjy4zJUYobGF030cRmKaO1FBw3V8CZZ6KVh9ZSw==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.6.3", + "jest-worker": "^29.6.3", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + } + }, + "node_modules/jest-leak-detector": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.6.3.tgz", + "integrity": "sha512-0kfbESIHXYdhAdpLsW7xdwmYhLf1BRu4AA118/OxFm0Ho1b2RcTmO4oF6aAMaxpxdxnJ3zve2rgwzNBD4Zbm7Q==", + "dev": true, + "dependencies": { + "jest-get-type": "^29.6.3", + "pretty-format": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-matcher-utils": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.6.3.tgz", + "integrity": "sha512-6ZrMYINZdwduSt5Xu18/n49O1IgXdjsfG7NEZaQws9k69eTKWKcVbJBw/MZsjOZe2sSyJFmuzh8042XWwl54Zg==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-message-util": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.6.3.tgz", + "integrity": "sha512-FtzaEEHzjDpQp51HX4UMkPZjy46ati4T5pEMyM6Ik48ztu4T9LQplZ6OsimHx7EuM9dfEh5HJa6D3trEftu3dA==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.6.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.6.3", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-mock": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.6.3.tgz", + "integrity": "sha512-Z7Gs/mOyTSR4yPsaZ72a/MtuK6RnC3JYqWONe48oLaoEcYwEDxqvbXz85G4SJrm2Z5Ar9zp6MiHF4AlFlRM4Pg==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-util": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-pnp-resolver": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", + "dev": true, + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "jest-resolve": "*" + }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } + } + }, + "node_modules/jest-regex-util": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", + "dev": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.6.3.tgz", + "integrity": "sha512-WMXwxhvzDeA/J+9jz1i8ZKGmbw/n+s988EiUvRI4egM+eTn31Hb5v10Re3slG3/qxntkBt2/6GkQVDGu6Bwyhw==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.6.3", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^29.6.3", + "jest-validate": "^29.6.3", + "resolve": "^1.20.0", + "resolve.exports": "^2.0.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve-dependencies": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.6.3.tgz", + "integrity": "sha512-iah5nhSPTwtUV7yzpTc9xGg8gP3Ch2VNsuFMsKoCkNCrQSbFtx5KRPemmPJ32AUhTSDqJXB6djPN6zAaUGV53g==", + "dev": true, + "dependencies": { + "jest-regex-util": "^29.6.3", + "jest-snapshot": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runner": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.6.3.tgz", + "integrity": "sha512-E4zsMhQnjhirFPhDTJgoLMWUrVCDij/KGzWlbslDHGuO8Hl2pVUfOiygMzVZtZq+BzmlqwEr7LYmW+WFLlmX8w==", + "dev": true, + "dependencies": { + "@jest/console": "^29.6.3", + "@jest/environment": "^29.6.3", + "@jest/test-result": "^29.6.3", + "@jest/transform": "^29.6.3", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "graceful-fs": "^4.2.9", + "jest-docblock": "^29.6.3", + "jest-environment-node": "^29.6.3", + "jest-haste-map": "^29.6.3", + "jest-leak-detector": "^29.6.3", + "jest-message-util": "^29.6.3", + "jest-resolve": "^29.6.3", + "jest-runtime": "^29.6.3", + "jest-util": "^29.6.3", + "jest-watcher": "^29.6.3", + "jest-worker": "^29.6.3", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runtime": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.6.3.tgz", + "integrity": "sha512-VM0Z3a9xaqizGpEKwCOIhImkrINYzxgwk8oQAvrmAiXX8LNrJrRjyva30RkuRY0ETAotHLlUcd2moviCA1hgsQ==", + "dev": true, + "dependencies": { + "@jest/environment": "^29.6.3", + "@jest/fake-timers": "^29.6.3", + "@jest/globals": "^29.6.3", + "@jest/source-map": "^29.6.3", + "@jest/test-result": "^29.6.3", + "@jest/transform": "^29.6.3", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.6.3", + "jest-message-util": "^29.6.3", + "jest-mock": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.6.3", + "jest-snapshot": "^29.6.3", + "jest-util": "^29.6.3", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-snapshot": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.6.3.tgz", + "integrity": "sha512-66Iu7H1ojiveQMGFnKecHIZPPPBjZwfQEnF6wxqpxGf57sV3YSUtAb5/sTKM5TPa3OndyxZp1wxHFbmgVhc53w==", + "dev": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-jsx": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/types": "^7.3.3", + "@jest/expect-utils": "^29.6.3", + "@jest/transform": "^29.6.3", + "@jest/types": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^29.6.3", + "graceful-fs": "^4.2.9", + "jest-diff": "^29.6.3", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.6.3", + "jest-message-util": "^29.6.3", + "jest-util": "^29.6.3", + "natural-compare": "^1.4.0", + "pretty-format": "^29.6.3", + "semver": "^7.5.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-util": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.6.3.tgz", + "integrity": "sha512-QUjna/xSy4B32fzcKTSz1w7YYzgiHrjjJjevdRf61HYk998R5vVMMNmrHESYZVDS5DSWs+1srPLPKxXPkeSDOA==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.6.3.tgz", + "integrity": "sha512-e7KWZcAIX+2W1o3cHfnqpGajdCs1jSM3DkXjGeLSNmCazv1EeI1ggTeK5wdZhF+7N+g44JI2Od3veojoaumlfg==", + "dev": true, + "dependencies": { + "@jest/types": "^29.6.3", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "leven": "^3.1.0", + "pretty-format": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate/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/jest-watcher": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.6.3.tgz", + "integrity": "sha512-NgpFjZ2U2MKusjidbi4Oiu7tfs+nrgdIxIEVROvH1cFmOei9Uj25lwkMsakqLnH/s0nEcvxO1ck77FiRlcnpZg==", + "dev": true, + "dependencies": { + "@jest/test-result": "^29.6.3", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "jest-util": "^29.6.3", + "string-length": "^4.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.6.3.tgz", + "integrity": "sha512-wacANXecZ/GbQakpf2CClrqrlwsYYDSXFd4fIGdL+dXpM2GWoJ+6bhQ7vR3TKi3+gkSfBkjy1/khH/WrYS4Q6g==", + "dev": true, + "dependencies": { + "@types/node": "*", + "jest-util": "^29.6.3", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker/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/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "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/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "dev": true + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true + }, + "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/lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", + "dev": true + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "dependencies": { + "yallist": "^3.0.2" + } + }, + "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/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dev": true, + "dependencies": { + "tmpl": "1.0.5" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "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/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/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true + }, + "node_modules/node-releases": { + "version": "2.0.13", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.13.tgz", + "integrity": "sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==", + "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/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "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/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/optionator": { + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", + "dev": true, + "dependencies": { + "@aashutoshrathi/word-wrap": "^1.2.3", + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "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/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, + "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/pirates": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", + "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pkg-dir/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pkg-dir/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/pretty-format": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.6.3.tgz", + "integrity": "sha512-ZsBgjVhFAj5KeK+nHfF1305/By3lechHQSMWCTl8iHSbfOm2TN5nHEtFc/+W7fAyUeCs2n5iow72gld4gW0xDw==", + "dev": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/punycode": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", + "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/pure-rand": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.0.2.tgz", + "integrity": "sha512-6Yg0ekpKICSjPswYOuC5sku/TSWaRYlA0qsXqJgM/d/4pLPHPuTxK7Nbf7jFKzAeedUhR8C7K9Uv63FBsSo8xQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ] + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true + }, + "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/resolve": { + "version": "1.22.4", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.4.tgz", + "integrity": "sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg==", + "dev": true, + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-cwd/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/resolve.exports": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", + "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver/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/semver/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/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": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true + }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/stack-utils/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dev": true, + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "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/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-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "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": "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/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "node_modules/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "dev": true + }, + "node_modules/to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "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/ts-api-utils": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.2.tgz", + "integrity": "sha512-Cbu4nIqnEdd+THNEsBdkolnOXhg0I8XteoHaEKgvsxpsbWda4IsUut2c187HxywQCvveojow0Dgw/amxtSKVkQ==", + "dev": true, + "engines": { + "node": ">=16.13.0" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, + "node_modules/ts-jest": { + "version": "29.1.1", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.1.1.tgz", + "integrity": "sha512-D6xjnnbP17cC85nliwGiL+tpoKN0StpgE0TeOjXQTU6MVCfsB4v7aW05CgQ/1OywGb0x/oy9hHFnN+sczTiRaA==", + "dev": true, + "dependencies": { + "bs-logger": "0.x", + "fast-json-stable-stringify": "2.x", + "jest-util": "^29.0.0", + "json5": "^2.2.3", + "lodash.memoize": "4.x", + "make-error": "1.x", + "semver": "^7.5.3", + "yargs-parser": "^21.0.1" + }, + "bin": { + "ts-jest": "cli.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.0 <8", + "@jest/types": "^29.0.0", + "babel-jest": "^29.0.0", + "jest": "^29.0.0", + "typescript": ">=4.3 <6" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "@jest/types": { + "optional": true + }, + "babel-jest": { + "optional": true + }, + "esbuild": { + "optional": true + } + } + }, + "node_modules/ts-node": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", + "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", + "dev": true, + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typescript": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz", + "integrity": "sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz", + "integrity": "sha512-dCwEFf0/oT85M1fHBg4F0jtLwJrutGoHSQXCh7u4o2t1drG+c0a9Flnqww6XUKSfQMPpJBRjU8d4RXB09qtvaA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true + }, + "node_modules/v8-to-istanbul": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.1.0.tgz", + "integrity": "sha512-6z3GW9x8G1gd+JIIgQQQxXuiJtCXeAjp6RaPEPLv62mH3iPHPxV6W3robxtCzNErRo6ZwTmzWhsbNvjyEBKzKA==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^1.6.0" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/v8-to-istanbul/node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "dev": true + }, + "node_modules/walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "dev": true, + "dependencies": { + "makeerror": "1.0.12" + } + }, + "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/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/write-file-atomic": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", + "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "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": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "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/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/extension/package.json b/extension/package.json new file mode 100644 index 0000000..4ba4199 --- /dev/null +++ b/extension/package.json @@ -0,0 +1,31 @@ +{ + "name": "row-level-security-extension", + "version": "0.0.0", + "main": "dist/extension.js", + "description": "Row Level Security Extension", + "license": "MIT", + "scripts": { + "build": "npm run generate && npm run compile", + "compile": "tsc --build && esbuild dist/extension.js --bundle --outfile=dist/row-level-security-extension.js --target=es6", + "generate": "bash generate-description.sh", + "lint": "eslint ./src/", + "clean": "rm -rf dist/", + "test": "jest --silent", + "test-watch": "jest --watch" + }, + "dependencies": { + "@exasol/extension-manager-interface": "0.3.1" + }, + "devDependencies": { + "@jest/globals": "^29.6.3", + "@types/node": "^20.5.4", + "@typescript-eslint/eslint-plugin": "^6.4.1", + "@typescript-eslint/parser": "^6.4.1", + "esbuild": "^0.19.2", + "eslint": "^8.47.0", + "jest": "29.6.3", + "ts-jest": "^29.1.1", + "ts-node": "^10.9.1", + "typescript": "^5.1.6" + } +} \ No newline at end of file diff --git a/extension/src/extension.test.ts b/extension/src/extension.test.ts new file mode 100644 index 0000000..289ef1b --- /dev/null +++ b/extension/src/extension.test.ts @@ -0,0 +1,195 @@ +import { ExaMetadata, Installation, PreconditionFailedError } from '@exasol/extension-manager-interface'; +import { ExaScriptsRow } from '@exasol/extension-manager-interface/dist/exasolSchema'; +import { describe, expect, it } from '@jest/globals'; +import { createExtension } from "./extension"; +import { EXTENSION_DESCRIPTION } from './extension-description'; +import { createMockContext, getInstalledExtension } from './test-utils'; + +const currentVersion = EXTENSION_DESCRIPTION.version + +describe("Cloud Storage Extension", () => { + + describe("installableVersions", () => { + it("contains exactly one 'latest', non deprecated version", () => { + const latestVersions = createExtension().installableVersions.filter(version => version.latest) + expect(latestVersions).toHaveLength(1) + expect(latestVersions[0].deprecated).toEqual(false) + }) + }) + + describe("extension registration", () => { + it("creates an extension", () => { + const ext = createExtension(); + expect(ext).not.toBeNull() + }) + + it("creates a new object for every call", () => { + const ext1 = createExtension(); + const ext2 = createExtension(); + expect(ext1).not.toBe(ext2) + }) + + it("registers when loaded", () => { + const installedExtension = getInstalledExtension(); + expect(installedExtension.extension).not.toBeNull() + expect(typeof installedExtension.apiVersion).toBe('string'); + expect(installedExtension.apiVersion).not.toBe(''); + }) + }) + + describe("findInstallations()", () => { + function findInstallations(allScripts: ExaScriptsRow[]): Installation[] { + const metadata: ExaMetadata = { + allScripts: { rows: allScripts }, + virtualSchemaProperties: { rows: [] }, + virtualSchemas: { rows: [] } + } + const installations = createExtension().findInstallations(createMockContext(), metadata) + expect(installations).toBeDefined() + return installations + } + + function text(name: string, className: string, version: string): string { + return `CREATE ${name} ... + %scriptclass ${className}; + %jar /buckets/bfsdefault/default/exasol-cloud-storage-extension-${version}.jar;` + } + function script({ schema = "schema", name = "name", inputType, resultType = "EMITS", type = "UDF", text = "", comment }: Partial): ExaScriptsRow { + return { schema, name, inputType, resultType, type, text, comment } + } + function setScript(name: string, className: string, version = EXTENSION_DESCRIPTION.version): ExaScriptsRow { + return script({ name, inputType: "SET", text: text(name, className, version) }) + } + function scalarScript(name: string, className: string, version = EXTENSION_DESCRIPTION.version): ExaScriptsRow { + return script({ name, inputType: "SCALAR", text: text(name, className, version) }) + } + + it("returns empty list when no adapter script is available", () => { + expect(findInstallations([])).toHaveLength(0) + }) + + it("returns single item when all scripts are available", () => { + const scripts: ExaScriptsRow[] = [ + setScript("EXPORT_PATH", "com.exasol.cloudetl.scriptclasses.TableExportQueryGenerator"), + setScript("EXPORT_TABLE", "com.exasol.cloudetl.scriptclasses.TableDataExporter"), + setScript("IMPORT_FILES", "com.exasol.cloudetl.scriptclasses.FilesDataImporter"), + scalarScript("IMPORT_METADATA", "com.exasol.cloudetl.scriptclasses.FilesMetadataReader"), + setScript("IMPORT_PATH", "com.exasol.cloudetl.scriptclasses.FilesImportQueryGenerator") + ] + expect(findInstallations(scripts)).toStrictEqual([{ name: "Cloud Storage Extension", version: EXTENSION_DESCRIPTION.version }]) + }) + + it("fails for inconsistent version", () => { + const scripts: ExaScriptsRow[] = [ + setScript("EXPORT_PATH", "com.exasol.cloudetl.scriptclasses.TableExportQueryGenerator"), + setScript("EXPORT_TABLE", "com.exasol.cloudetl.scriptclasses.TableDataExporter"), + setScript("IMPORT_FILES", "com.exasol.cloudetl.scriptclasses.FilesDataImporter"), + scalarScript("IMPORT_METADATA", "com.exasol.cloudetl.scriptclasses.FilesMetadataReader", "0.0.0"), + setScript("IMPORT_PATH", "com.exasol.cloudetl.scriptclasses.FilesImportQueryGenerator") + ] + expect(() => findInstallations(scripts)).toThrowError(new PreconditionFailedError(`Not all scripts use the same version. Found 2 different versions: '${currentVersion}, 0.0.0'`)) + }) + + describe("returns expected installations", () => { + }) + }) + + describe("install()", () => { + it("executes expected statements", () => { + const context = createMockContext(); + createExtension().install(context, EXTENSION_DESCRIPTION.version); + const executeCalls = context.mocks.sqlExecute.mock.calls + expect(executeCalls.length).toBe(10) + + const expectedScriptNames = ["IMPORT_PATH", "IMPORT_METADATA", "IMPORT_FILES", "EXPORT_PATH", "EXPORT_TABLE"] + + const createScriptStatements = executeCalls.slice(0, 5).map(args => args[0]) + const createCommentStatements = executeCalls.slice(5, 10).map(args => args[0]) + + expect(createScriptStatements).toHaveLength(5) + expect(createCommentStatements).toHaveLength(5) + + const expectedComment = `Created by Extension Manager for Cloud Storage Extension ${EXTENSION_DESCRIPTION.version}` + for (let i = 0; i < expectedScriptNames.length; i++) { + const name = expectedScriptNames[i]; + expect(createScriptStatements[i]).toContain(`CREATE OR REPLACE JAVA`) + expect(createScriptStatements[i]).toContain(`SCRIPT "ext-schema"."${name}"`) + expect(createScriptStatements[i]).toContain(`%scriptclass com.exasol.cloudetl.scriptclasses.`) + expect(createCommentStatements[i]).toEqual(`COMMENT ON SCRIPT "ext-schema"."${name}" IS '${expectedComment}'`) + } + }) + it("fails for wrong version", () => { + expect(() => { createExtension().install(createMockContext(), "wrongVersion") }) + .toThrow(`Installing version 'wrongVersion' not supported, try '${EXTENSION_DESCRIPTION.version}'.`) + }) + }) + + describe("uninstall()", () => { + it("executes query to check if schema exists", () => { + const context = createMockContext() + context.mocks.sqlQuery.mockReturnValue({ columns: [], rows: [] }); + createExtension().uninstall(context, EXTENSION_DESCRIPTION.version) + const calls = context.mocks.sqlQuery.mock.calls + expect(calls.length).toEqual(1) + expect(calls[0]).toEqual(["SELECT 1 FROM SYS.EXA_ALL_SCHEMAS WHERE SCHEMA_NAME=?", "ext-schema"]) + }) + it("skips drop statements when schema does not exist", () => { + const context = createMockContext() + context.mocks.sqlQuery.mockReturnValue({ columns: [], rows: [] }); + createExtension().uninstall(context, EXTENSION_DESCRIPTION.version) + expect(context.mocks.sqlExecute.mock.calls.length).toEqual(0) + }) + it("executes expected statements", () => { + const context = createMockContext() + context.mocks.sqlQuery.mockReturnValue({ columns: [], rows: [[1]] }); + createExtension().uninstall(context, EXTENSION_DESCRIPTION.version) + const calls = context.mocks.sqlExecute.mock.calls + const expectedScriptNames = ["IMPORT_PATH", "IMPORT_METADATA", "IMPORT_FILES", "EXPORT_PATH", "EXPORT_TABLE"] + expect(calls.length).toEqual(expectedScriptNames.length) + for (let i = 0; i < expectedScriptNames.length; i++) { + expect(calls[i]).toEqual([`DROP SCRIPT "ext-schema"."${expectedScriptNames[i]}"`]) + } + }) + it("fails for wrong version", () => { + expect(() => { createExtension().uninstall(createMockContext(), "wrongVersion") }) + .toThrow(`Uninstalling version 'wrongVersion' not supported, try '${EXTENSION_DESCRIPTION.version}'.`) + }) + }) + + + describe("getInstanceParameters()", () => { + it("is not supported", () => { + expect(() => { createExtension().getInstanceParameters(createMockContext(), "version") }) + .toThrow("Creating instances not supported") + }) + }) + + describe("addInstance()", () => { + it("is not supported", () => { + expect(() => { createExtension().addInstance(createMockContext(), "version", { values: [] }) }) + .toThrow("Creating instances not supported") + }) + }) + + describe("findInstances()", () => { + it("is not supported", () => { + expect(() => { createExtension().findInstances(createMockContext(), "version") }) + .toThrow("Finding instances not supported") + }) + }) + + describe("deleteInstance()", () => { + it("is not supported", () => { + expect(() => { createExtension().deleteInstance(createMockContext(), "version", "instId") }) + .toThrow("Deleting instances not supported") + }) + }) + + describe("readInstanceParameterValues()", () => { + it("is not supported", () => { + expect(() => { createExtension().readInstanceParameterValues(createMockContext(), "version", "instId") }) + .toThrow("Reading instance parameter values not supported") + }) + }) +}) + diff --git a/extension/src/extension.ts b/extension/src/extension.ts new file mode 100644 index 0000000..0b7d434 --- /dev/null +++ b/extension/src/extension.ts @@ -0,0 +1,52 @@ +import { ExasolExtension, registerExtension } from "@exasol/extension-manager-interface"; +import { JavaBaseExtension, ScriptDefinition, convertBaseExtension, jarFileVersionExtractor } from "@exasol/extension-manager-interface/dist/base"; +import { EXTENSION_DESCRIPTION } from "./extension-description"; + +/** Script definitions for the required scripts. */ +const SCRIPTS: ScriptDefinition[] = [ + { + name: "IMPORT_PATH", + type: "SET", + args: "...", + scriptClass: "com.exasol.cloudetl.scriptclasses.FilesImportQueryGenerator" + }, + { + name: "IMPORT_METADATA", + type: "SCALAR", + args: `filename VARCHAR(2000), partition_index VARCHAR(100), start_index DECIMAL(36, 0), end_index DECIMAL(36, 0)`, + scriptClass: "com.exasol.cloudetl.scriptclasses.FilesMetadataReader" + }, + { + name: "IMPORT_FILES", + type: "SET", + args: "...", + scriptClass: "com.exasol.cloudetl.scriptclasses.FilesDataImporter" + }, + { + name: "EXPORT_PATH", + type: "SET", + args: "...", + scriptClass: "com.exasol.cloudetl.scriptclasses.TableExportQueryGenerator" + }, + { + name: "EXPORT_TABLE", + type: "SET", + args: "ROWS_AFFECTED INT", + scriptClass: "com.exasol.cloudetl.scriptclasses.TableDataExporter" + } +] + +export function createExtension(): ExasolExtension { + const baseExtension: JavaBaseExtension = { + name: "Cloud Storage Extension", + description: "Access data formatted with Avro, Orc and Parquet on public cloud storage systems", + category: "cloud-storage-importer", + version: EXTENSION_DESCRIPTION.version, + file: { name: EXTENSION_DESCRIPTION.fileName, size: EXTENSION_DESCRIPTION.fileSizeBytes }, + scripts: SCRIPTS, + scriptVersionExtractor: jarFileVersionExtractor(/exasol-cloud-storage-extension-(\d+\.\d+\.\d+).jar/) + } + return convertBaseExtension(baseExtension) +} + +registerExtension(createExtension()) \ No newline at end of file diff --git a/extension/src/test-utils.ts b/extension/src/test-utils.ts new file mode 100644 index 0000000..3f24dbd --- /dev/null +++ b/extension/src/test-utils.ts @@ -0,0 +1,67 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import { Context, QueryResult, SqlClient } from '@exasol/extension-manager-interface'; +import { ExaScriptsRow } from '@exasol/extension-manager-interface/dist/exasolSchema'; +import * as jestMock from "jest-mock"; + +const EXTENSION_SCHEMA_NAME = "ext-schema" + +export function getInstalledExtension(): any { + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + return (global as any).installedExtension +} + +export type ContextMock = Context & { + mocks: { + sqlExecute: jestMock.Mock<(query: string, ...args: any) => void>, + sqlQuery: jestMock.Mock<(query: string, ...args: any) => QueryResult> + getScriptByName: jestMock.Mock<(scriptName: string) => ExaScriptsRow | null> + simulateScripts: (scripts: ExaScriptsRow[]) => void + } +} + +export function createMockContext(): ContextMock { + const mockedScripts: Map = new Map() + const execute = jestMock.fn<(query: string, ...args: any) => void>().mockName("sqlClient.execute()") + const query = jestMock.fn<(query: string, ...args: any) => QueryResult>().mockName("sqlClient.query()") + const getScriptByName = jestMock.fn<(scriptName: string) => ExaScriptsRow | null>().mockName("metadata.getScriptByName()") + getScriptByName.mockImplementation((scriptName) => mockedScripts.get(scriptName) ?? null) + const sqlClient: SqlClient = { + execute: execute, + query: query + } + + return { + extensionSchemaName: EXTENSION_SCHEMA_NAME, + sqlClient, + bucketFs: { + resolvePath(fileName: string) { + return "/bucketfs/" + fileName; + }, + }, + metadata: { + getScriptByName + }, + mocks: { + sqlExecute: execute, + sqlQuery: query, + getScriptByName: getScriptByName, + simulateScripts(scripts: ExaScriptsRow[]) { + mockedScripts.clear() + scripts.forEach(script => mockedScripts.set(script.name, script)) + }, + } + } +} + +export function script({ schema = "schema", name = "name", inputType, resultType, type = "", text = "", comment }: Partial): ExaScriptsRow { + return { schema, name, inputType, resultType, type, text, comment } +} +export function adapterScript({ name = "S3_FILES_ADAPTER", type = "ADAPTER", text = "adapter script" }: Partial): ExaScriptsRow { + return script({ name, type, text }) +} +export function importScript({ name = "IMPORT_FROM_S3_DOCUMENT_FILES", type = "UDF", inputType = "SET", resultType = "EMITS" }: Partial): ExaScriptsRow { + return script({ name, type, inputType, resultType }) +} +export function scriptWithVersion(name: string, version: string): ExaScriptsRow { + return script({ name, text: `CREATE ... %jar /path/to/exasol-cloud-storage-extension-${version}.jar; more text` }) +} diff --git a/extension/src/upgrade.test.ts b/extension/src/upgrade.test.ts new file mode 100644 index 0000000..8b54631 --- /dev/null +++ b/extension/src/upgrade.test.ts @@ -0,0 +1,50 @@ +import { ExaScriptsRow, PreconditionFailedError } from '@exasol/extension-manager-interface'; +import { describe, expect, it } from '@jest/globals'; +import { createExtension } from './extension'; +import { EXTENSION_DESCRIPTION } from './extension-description'; +import { createMockContext, scriptWithVersion } from './test-utils'; + +const currentVersion = EXTENSION_DESCRIPTION.version + +describe("upgrade()", () => { + const version = "1.2.3" + const importPath = scriptWithVersion("IMPORT_PATH", version) + const importMetadata = scriptWithVersion("IMPORT_METADATA", version) + const importFiles = scriptWithVersion("IMPORT_FILES", version) + const exportPath = scriptWithVersion("EXPORT_PATH", version) + const exportTable = scriptWithVersion("EXPORT_TABLE", version) + const allScripts = [importPath, importMetadata, importFiles, exportPath, exportTable] + + describe("validateInstalledScripts()", () => { + it("success", () => { + const context = createMockContext() + context.mocks.simulateScripts(allScripts) + expect(createExtension().upgrade(context)).toStrictEqual({ + previousVersion: version, newVersion: currentVersion + }) + const executeCalls = context.mocks.sqlExecute.mock.calls + expect(executeCalls.length).toBe(10) + }) + describe("failure", () => { + const tests: { name: string; scripts: ExaScriptsRow[], expectedMessage: string }[] = [ + { name: "no script", scripts: [], expectedMessage: "Not all required scripts are installed: Validation failed: Script 'IMPORT_PATH' is missing, Script 'IMPORT_METADATA' is missing, Script 'IMPORT_FILES' is missing, Script 'EXPORT_PATH' is missing, Script 'EXPORT_TABLE' is missing" }, + { name: "one missing script", scripts: [importPath, importMetadata, importFiles, exportPath], expectedMessage: "Not all required scripts are installed: Validation failed: Script 'EXPORT_TABLE' is missing" }, + { name: "inconsistent versions", scripts: [importPath, importMetadata, importFiles, exportPath, scriptWithVersion("EXPORT_TABLE", "1.2.4")], expectedMessage: "Failed to validate script versions: Not all scripts use the same version. Found 2 different versions: '1.2.3, 1.2.4'" }, + { + name: "version already up-to-date", scripts: [ + scriptWithVersion("IMPORT_PATH", currentVersion), scriptWithVersion("IMPORT_METADATA", currentVersion), + scriptWithVersion("IMPORT_FILES", currentVersion), scriptWithVersion("EXPORT_PATH", currentVersion), scriptWithVersion("EXPORT_TABLE", currentVersion) + ], + expectedMessage: `Extension is already installed in latest version ${currentVersion}` + }, + ] + tests.forEach(test => it(test.name, () => { + const context = createMockContext() + context.mocks.simulateScripts(test.scripts) + expect(() => createExtension().upgrade(context)).toThrowError(new PreconditionFailedError(test.expectedMessage)) + const executeCalls = context.mocks.sqlExecute.mock.calls + expect(executeCalls.length).toBe(0) + })) + }) + }) +}) diff --git a/extension/tsconfig.json b/extension/tsconfig.json new file mode 100644 index 0000000..41a0fe6 --- /dev/null +++ b/extension/tsconfig.json @@ -0,0 +1,20 @@ +{ + "compilerOptions": { + "noImplicitAny": true, + "noEmitOnError": true, + "removeComments": false, + "sourceMap": true, + "target": "es6", + "outDir": "dist/", + "declaration": true, + "moduleResolution": "node", + "esModuleInterop": true, + "strict": true, + "noFallthroughCasesInSwitch": true, + "noImplicitOverride": true, + "noImplicitReturns": true, + "noUnusedLocals": true + }, + "include": ["src/**/*"], + "exclude": ["src/**/*.test.ts"] +} diff --git a/pom.xml b/pom.xml index 18e82bd..09df35d 100644 --- a/pom.xml +++ b/pom.xml @@ -105,6 +105,7 @@ exec-maven-plugin 3.1.0 + unit-test test @@ -166,6 +167,59 @@ + + + npm-ci + process-resources + + exec + + + extension/ + npm + ci + false + + + + build-extension + package + + exec + + + extension/ + npm + run build + false + + + + unit-test-extension + verify + + exec + + + extension/ + npm + test + false + + + + lint-extension + verify + + exec + + + extension/ + npm + run lint + false + + From eea09831971e01baf817a7dedf3d6b81c9a11b02 Mon Sep 17 00:00:00 2001 From: Christoph Pirkl Date: Fri, 25 Aug 2023 16:20:34 +0200 Subject: [PATCH 06/21] Generate extension info incl. file content --- extension/generate-description.sh | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/extension/generate-description.sh b/extension/generate-description.sh index 8225403..c9dadb4 100755 --- a/extension/generate-description.sh +++ b/extension/generate-description.sh @@ -9,7 +9,7 @@ readonly project_dir readonly target_dir="$project_dir/target" readonly extension_config="$project_dir/extension/src/extension-description.ts" -readonly all_built_files=("$target_dir"/exasol-cloud-storage-extension-*.jar) +readonly all_built_files=("$target_dir"/row-level-security-dist-*.lua) readonly file_count=${#all_built_files[@]} if [ "$file_count" -ne 1 ]; then @@ -24,7 +24,7 @@ file_size_bytes=$(stat -c%s "$complete_path") readonly file_size_bytes version= -if [[ $file_name =~ ^exasol-cloud-storage-extension-(.*).jar$ ]]; then +if [[ $file_name =~ ^row-level-security-dist-(.*).lua$ ]]; then version=${BASH_REMATCH[1]} else echo "ERROR: Could not extract version from filename $file_name" @@ -32,12 +32,21 @@ else fi readonly version +file_content=$(<"$target_dir/$file_name") +readonly file_content + +# Lua code contains backicks in comments. We need to escape them with \ +# shellcheck disable=SC2016 +escaped_content=$(echo "$file_content" | sed -r 's/`/\\`/g') +readonly escaped_content + cat > "$extension_config" < Date: Mon, 4 Sep 2023 16:05:39 +0200 Subject: [PATCH 07/21] #138 Create instance --- extension/generate-description.sh | 8 +- extension/src/addInstance.ts | 54 ++++++++ extension/src/common.ts | 11 ++ extension/src/extension.test.ts | 48 ++++++- extension/src/extension.ts | 97 +++++++------- extension/src/install.ts | 24 ++++ extension/src/parameterDefinitions.ts | 26 ++++ extension/src/uninstall.ts | 5 + extension/src/upgrade.test.ts | 50 -------- pom.xml | 6 + .../com/exasol/rls/extension/ExtensionIT.java | 121 ++++++++++++++++++ 11 files changed, 350 insertions(+), 100 deletions(-) create mode 100644 extension/src/addInstance.ts create mode 100644 extension/src/common.ts create mode 100644 extension/src/install.ts create mode 100644 extension/src/parameterDefinitions.ts create mode 100644 extension/src/uninstall.ts delete mode 100644 extension/src/upgrade.test.ts create mode 100644 src/test/java/com/exasol/rls/extension/ExtensionIT.java diff --git a/extension/generate-description.sh b/extension/generate-description.sh index c9dadb4..595286b 100755 --- a/extension/generate-description.sh +++ b/extension/generate-description.sh @@ -35,16 +35,16 @@ readonly version file_content=$(<"$target_dir/$file_name") readonly file_content -# Lua code contains backicks in comments. We need to escape them with \ +# First escape \ to allow using \n in Lua strings. Then escape backticks ` used in Lua comments +# because we store the script content in a backtick-quoted TypeScript string. # shellcheck disable=SC2016 -escaped_content=$(echo "$file_content" | sed -r 's/`/\\`/g') +escaped_content=$(echo "$file_content" | sed -r 's/\\/\\\\/g' | sed -r 's/`/\\`/g' ) readonly escaped_content cat > "$extension_config" < { +describe("Row Level Security Lua", () => { describe("installableVersions", () => { it("contains exactly one 'latest', non deprecated version", () => { @@ -191,5 +191,49 @@ describe("Cloud Storage Extension", () => { .toThrow("Reading instance parameter values not supported") }) }) + + + describe("upgrade()", () => { + const version = "1.2.3" + const importPath = scriptWithVersion("IMPORT_PATH", version) + const importMetadata = scriptWithVersion("IMPORT_METADATA", version) + const importFiles = scriptWithVersion("IMPORT_FILES", version) + const exportPath = scriptWithVersion("EXPORT_PATH", version) + const exportTable = scriptWithVersion("EXPORT_TABLE", version) + const allScripts = [importPath, importMetadata, importFiles, exportPath, exportTable] + + describe("validateInstalledScripts()", () => { + it("success", () => { + const context = createMockContext() + context.mocks.simulateScripts(allScripts) + expect(createExtension().upgrade(context)).toStrictEqual({ + previousVersion: version, newVersion: currentVersion + }) + const executeCalls = context.mocks.sqlExecute.mock.calls + expect(executeCalls.length).toBe(10) + }) + describe("failure", () => { + const tests: { name: string; scripts: ExaScriptsRow[], expectedMessage: string }[] = [ + { name: "no script", scripts: [], expectedMessage: "Not all required scripts are installed: Validation failed: Script 'IMPORT_PATH' is missing, Script 'IMPORT_METADATA' is missing, Script 'IMPORT_FILES' is missing, Script 'EXPORT_PATH' is missing, Script 'EXPORT_TABLE' is missing" }, + { name: "one missing script", scripts: [importPath, importMetadata, importFiles, exportPath], expectedMessage: "Not all required scripts are installed: Validation failed: Script 'EXPORT_TABLE' is missing" }, + { name: "inconsistent versions", scripts: [importPath, importMetadata, importFiles, exportPath, scriptWithVersion("EXPORT_TABLE", "1.2.4")], expectedMessage: "Failed to validate script versions: Not all scripts use the same version. Found 2 different versions: '1.2.3, 1.2.4'" }, + { + name: "version already up-to-date", scripts: [ + scriptWithVersion("IMPORT_PATH", currentVersion), scriptWithVersion("IMPORT_METADATA", currentVersion), + scriptWithVersion("IMPORT_FILES", currentVersion), scriptWithVersion("EXPORT_PATH", currentVersion), scriptWithVersion("EXPORT_TABLE", currentVersion) + ], + expectedMessage: `Extension is already installed in latest version ${currentVersion}` + }, + ] + tests.forEach(test => it(test.name, () => { + const context = createMockContext() + context.mocks.simulateScripts(test.scripts) + expect(() => createExtension().upgrade(context)).toThrowError(new PreconditionFailedError(test.expectedMessage)) + const executeCalls = context.mocks.sqlExecute.mock.calls + expect(executeCalls.length).toBe(0) + })) + }) + }) + }) }) diff --git a/extension/src/extension.ts b/extension/src/extension.ts index 0b7d434..750a2a1 100644 --- a/extension/src/extension.ts +++ b/extension/src/extension.ts @@ -1,52 +1,61 @@ -import { ExasolExtension, registerExtension } from "@exasol/extension-manager-interface"; -import { JavaBaseExtension, ScriptDefinition, convertBaseExtension, jarFileVersionExtractor } from "@exasol/extension-manager-interface/dist/base"; +import { Context, ExasolExtension, NotFoundError, registerExtension } from "@exasol/extension-manager-interface"; import { EXTENSION_DESCRIPTION } from "./extension-description"; +import { install } from "./install"; +import { createInstanceParameters } from "./parameterDefinitions"; +import { uninstall } from "./uninstall"; +import { addInstance } from "./addInstance"; -/** Script definitions for the required scripts. */ -const SCRIPTS: ScriptDefinition[] = [ - { - name: "IMPORT_PATH", - type: "SET", - args: "...", - scriptClass: "com.exasol.cloudetl.scriptclasses.FilesImportQueryGenerator" - }, - { - name: "IMPORT_METADATA", - type: "SCALAR", - args: `filename VARCHAR(2000), partition_index VARCHAR(100), start_index DECIMAL(36, 0), end_index DECIMAL(36, 0)`, - scriptClass: "com.exasol.cloudetl.scriptclasses.FilesMetadataReader" - }, - { - name: "IMPORT_FILES", - type: "SET", - args: "...", - scriptClass: "com.exasol.cloudetl.scriptclasses.FilesDataImporter" - }, - { - name: "EXPORT_PATH", - type: "SET", - args: "...", - scriptClass: "com.exasol.cloudetl.scriptclasses.TableExportQueryGenerator" - }, - { - name: "EXPORT_TABLE", - type: "SET", - args: "ROWS_AFFECTED INT", - scriptClass: "com.exasol.cloudetl.scriptclasses.TableDataExporter" - } -] +export type ExtendedContext = Context & { + version: string, + luaScriptContent: string +} -export function createExtension(): ExasolExtension { - const baseExtension: JavaBaseExtension = { - name: "Cloud Storage Extension", - description: "Access data formatted with Avro, Orc and Parquet on public cloud storage systems", - category: "cloud-storage-importer", +function extendContext(context: Context): ExtendedContext { + return { + ...context, version: EXTENSION_DESCRIPTION.version, - file: { name: EXTENSION_DESCRIPTION.fileName, size: EXTENSION_DESCRIPTION.fileSizeBytes }, - scripts: SCRIPTS, - scriptVersionExtractor: jarFileVersionExtractor(/exasol-cloud-storage-extension-(\d+\.\d+\.\d+).jar/) + luaScriptContent: EXTENSION_DESCRIPTION.content + } +} +export function createExtension(): ExasolExtension { + const baseExtension: ExasolExtension = { + name: "Row Level Security Lua", + description: "Lua implementation of Exasol's row-level-security", + category: "security", + bucketFsUploads: [], + installableVersions: [{ name: EXTENSION_DESCRIPTION.version, deprecated: false, latest: true }], + findInstallations(context, metadata) { + return [] + }, + install(context, versionToInstall) { + install(extendContext(context), versionToInstall) + }, + uninstall(context, versionToUninstall) { + uninstall(extendContext(context), versionToUninstall) + }, + upgrade(context) { + return { previousVersion: "", newVersion: "" } + }, + findInstances(context, version) { + return [] + }, + getInstanceParameters(context, version) { + if (EXTENSION_DESCRIPTION.version !== version) { + throw new NotFoundError(`Version '${version}' not supported, can only use '${EXTENSION_DESCRIPTION.version}'.`) + } + return createInstanceParameters() + }, + addInstance(context, version, params) { + return addInstance(extendContext(context), version, params); + }, + deleteInstance(context, extensionVersion, instanceId) { + + }, + readInstanceParameterValues(context, extensionVersion, instanceId) { + return { values: [] } + }, } - return convertBaseExtension(baseExtension) + return baseExtension } registerExtension(createExtension()) \ No newline at end of file diff --git a/extension/src/install.ts b/extension/src/install.ts new file mode 100644 index 0000000..1fe0464 --- /dev/null +++ b/extension/src/install.ts @@ -0,0 +1,24 @@ +import { BadRequestError } from "@exasol/extension-manager-interface"; +import { ADAPTER_SCRIPT_NAME } from "./common"; +import { ExtendedContext } from "./extension"; + +export function install(context: ExtendedContext, versionToInstall: string) { + if (context.version !== versionToInstall) { + throw new BadRequestError(`Installing version '${versionToInstall}' not supported, try '${context.version}'.`) + } + const qualifiedScriptName = `"${context.extensionSchemaName}"."${ADAPTER_SCRIPT_NAME}"` + const luaModuleLoadingPreamble = `table.insert(package.searchers, + function (module_name) + local loader = package.preload[module_name] + if(loader == nil) then + error("Module " .. module_name .. " not found in package.preload.") + else + return loader + end + end + )` + const createScriptCommand = `CREATE OR REPLACE LUA ADAPTER SCRIPT ${qualifiedScriptName} AS\n${luaModuleLoadingPreamble}\n${context.luaScriptContent}\n/`; + const createCommentCommand = `COMMENT ON SCRIPT ${qualifiedScriptName} IS 'Created by extension manager for Row Level Security Lua ${context.version}'`; + context.sqlClient.execute(createScriptCommand) + context.sqlClient.execute(createCommentCommand); +} diff --git a/extension/src/parameterDefinitions.ts b/extension/src/parameterDefinitions.ts new file mode 100644 index 0000000..cbab440 --- /dev/null +++ b/extension/src/parameterDefinitions.ts @@ -0,0 +1,26 @@ +import { Parameter } from "@exasol/extension-manager-interface"; + +export type ScopedParameter = Parameter & { scope: "general" | "vs" } +export type ScopedParameters = { [key: string]: ScopedParameter } + +const allParams: ScopedParameters = { + virtualSchemaName: { scope: "general", id: "virtualSchemaName", name: "Name of the new virtual schema", type: "string", required: true }, + + // Virtual Schema parameters + schemaName: { scope: "vs", id: "SCHEMA_NAME", name: "Name of the schema for which to apply row-level security", type: "string", required: true, multiline: false }, + excludedCapabilities: { scope: "vs", id: "EXCLUDED_CAPABILITIES", name: "Comma-separated list of capabilities that should not be pushed-down, e.g. 'SELECTLIST_PROJECTION, ORDER_BY_COLUMN'", type: "string", required: false, multiline: false }, + tableFilter: { scope: "vs", id: "TABLE_FILTER", name: "Comma-separated list of tables that should be added to the schema, e.g. 'ORDERS, ORDER_ITEMS, PRODUCTS'. If this is empty, all tables are added.", type: "string", required: false, multiline: false }, +}; + +export function getAllParameterDefinitions(): ScopedParameters { + return allParams; +} + +export function createInstanceParameters(): Parameter[] { + return [ + allParams.virtualSchemaName, + allParams.schemaName, + allParams.excludedCapabilities, + allParams.tableFilter, + ]; +} \ No newline at end of file diff --git a/extension/src/uninstall.ts b/extension/src/uninstall.ts new file mode 100644 index 0000000..0639113 --- /dev/null +++ b/extension/src/uninstall.ts @@ -0,0 +1,5 @@ +import { ExtendedContext } from "./extension"; + +export function uninstall(context: ExtendedContext, versionToUninstall: string) { + +} \ No newline at end of file diff --git a/extension/src/upgrade.test.ts b/extension/src/upgrade.test.ts deleted file mode 100644 index 8b54631..0000000 --- a/extension/src/upgrade.test.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { ExaScriptsRow, PreconditionFailedError } from '@exasol/extension-manager-interface'; -import { describe, expect, it } from '@jest/globals'; -import { createExtension } from './extension'; -import { EXTENSION_DESCRIPTION } from './extension-description'; -import { createMockContext, scriptWithVersion } from './test-utils'; - -const currentVersion = EXTENSION_DESCRIPTION.version - -describe("upgrade()", () => { - const version = "1.2.3" - const importPath = scriptWithVersion("IMPORT_PATH", version) - const importMetadata = scriptWithVersion("IMPORT_METADATA", version) - const importFiles = scriptWithVersion("IMPORT_FILES", version) - const exportPath = scriptWithVersion("EXPORT_PATH", version) - const exportTable = scriptWithVersion("EXPORT_TABLE", version) - const allScripts = [importPath, importMetadata, importFiles, exportPath, exportTable] - - describe("validateInstalledScripts()", () => { - it("success", () => { - const context = createMockContext() - context.mocks.simulateScripts(allScripts) - expect(createExtension().upgrade(context)).toStrictEqual({ - previousVersion: version, newVersion: currentVersion - }) - const executeCalls = context.mocks.sqlExecute.mock.calls - expect(executeCalls.length).toBe(10) - }) - describe("failure", () => { - const tests: { name: string; scripts: ExaScriptsRow[], expectedMessage: string }[] = [ - { name: "no script", scripts: [], expectedMessage: "Not all required scripts are installed: Validation failed: Script 'IMPORT_PATH' is missing, Script 'IMPORT_METADATA' is missing, Script 'IMPORT_FILES' is missing, Script 'EXPORT_PATH' is missing, Script 'EXPORT_TABLE' is missing" }, - { name: "one missing script", scripts: [importPath, importMetadata, importFiles, exportPath], expectedMessage: "Not all required scripts are installed: Validation failed: Script 'EXPORT_TABLE' is missing" }, - { name: "inconsistent versions", scripts: [importPath, importMetadata, importFiles, exportPath, scriptWithVersion("EXPORT_TABLE", "1.2.4")], expectedMessage: "Failed to validate script versions: Not all scripts use the same version. Found 2 different versions: '1.2.3, 1.2.4'" }, - { - name: "version already up-to-date", scripts: [ - scriptWithVersion("IMPORT_PATH", currentVersion), scriptWithVersion("IMPORT_METADATA", currentVersion), - scriptWithVersion("IMPORT_FILES", currentVersion), scriptWithVersion("EXPORT_PATH", currentVersion), scriptWithVersion("EXPORT_TABLE", currentVersion) - ], - expectedMessage: `Extension is already installed in latest version ${currentVersion}` - }, - ] - tests.forEach(test => it(test.name, () => { - const context = createMockContext() - context.mocks.simulateScripts(test.scripts) - expect(() => createExtension().upgrade(context)).toThrowError(new PreconditionFailedError(test.expectedMessage)) - const executeCalls = context.mocks.sqlExecute.mock.calls - expect(executeCalls.length).toBe(0) - })) - }) - }) -}) diff --git a/pom.xml b/pom.xml index 09df35d..6047e93 100644 --- a/pom.xml +++ b/pom.xml @@ -85,6 +85,12 @@ 1.2.0 test + + com.exasol + extension-manager-integration-test-java + 0.5.0 + test + diff --git a/src/test/java/com/exasol/rls/extension/ExtensionIT.java b/src/test/java/com/exasol/rls/extension/ExtensionIT.java new file mode 100644 index 0000000..e23e9f4 --- /dev/null +++ b/src/test/java/com/exasol/rls/extension/ExtensionIT.java @@ -0,0 +1,121 @@ +package com.exasol.rls.extension; + +import static com.exasol.matcher.ResultSetStructureMatcher.table; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.*; +import static org.junit.jupiter.api.Assertions.assertAll; + +import java.io.FileNotFoundException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.sql.*; +import java.util.List; +import java.util.concurrent.TimeoutException; + +import org.hamcrest.Matcher; +import org.junit.jupiter.api.*; + +import com.exasol.bucketfs.BucketAccessException; +import com.exasol.dbbuilder.dialects.Table; +import com.exasol.dbbuilder.dialects.exasol.ExasolObjectFactory; +import com.exasol.dbbuilder.dialects.exasol.ExasolSchema; +import com.exasol.exasoltestsetup.ExasolTestSetup; +import com.exasol.exasoltestsetup.ExasolTestSetupFactory; +import com.exasol.extensionmanager.client.model.*; +import com.exasol.extensionmanager.itest.ExtensionManagerSetup; +import com.exasol.extensionmanager.itest.builder.ExtensionBuilder; +import com.exasol.mavenprojectversiongetter.MavenProjectVersionGetter; + +class ExtensionIT { + private static final String PREVIOUS_VERSION = "2.6.2"; + private static final Path EXTENSION_SOURCE_DIR = Paths.get("extension").toAbsolutePath(); + private static final String EXTENSION_ID = "row-level-security-extension.js"; + private static final int EXPECTED_PARAMETER_COUNT = 10; + private static final String PROJECT_VERSION = MavenProjectVersionGetter.getCurrentProjectVersion(); + private static ExasolTestSetup exasolTestSetup; + private static ExtensionManagerSetup setup; + private Connection connection; + private ExasolObjectFactory dbObjectFactory; + + @BeforeAll + static void setup() throws FileNotFoundException, BucketAccessException, TimeoutException { + exasolTestSetup = new ExasolTestSetupFactory(Path.of("no-such-file")).getTestSetup(); + setup = ExtensionManagerSetup.create(exasolTestSetup, ExtensionBuilder.createDefaultNpmBuilder( + EXTENSION_SOURCE_DIR, EXTENSION_SOURCE_DIR.resolve("dist").resolve(EXTENSION_ID))); + } + + @BeforeEach + void setupTest() throws SQLException { + connection = exasolTestSetup.createConnection(); + dbObjectFactory = new ExasolObjectFactory(exasolTestSetup.createConnection()); + } + + @AfterAll + static void teardown() throws Exception { + if (setup != null) { + setup.close(); + } + exasolTestSetup.close(); + } + + @AfterEach + void cleanup() throws SQLException { + connection.close(); + setup.cleanup(); + } + + @Test + void listExtensions() { + final List extensions = setup.client().getExtensions(); + assertAll(() -> assertThat(extensions, hasSize(1)), // + () -> assertThat(extensions.get(0).getName(), equalTo("Row Level Security Lua")), + () -> assertThat(extensions.get(0).getInstallableVersions().get(0).getName(), equalTo(PROJECT_VERSION)), + () -> assertThat("isLatest", extensions.get(0).getInstallableVersions().get(0).isLatest(), is(true)), + () -> assertThat("isDeprecated", extensions.get(0).getInstallableVersions().get(0).isDeprecated(), + is(false)), + () -> assertThat(extensions.get(0).getDescription(), + equalTo("Lua implementation of Exasol's row-level-security"))); + } + + @Test + void listInstallations() { + assertThat(setup.client().getInstallations(), emptyIterable()); + } + + @Test + void installWrongVersionFails() { + setup.client().assertRequestFails(() -> setup.client().install("0.0.0"), + "Installing version '0.0.0' not supported, try '" + PROJECT_VERSION + "'.", 400); + } + + @Test + void installExtensions() { + setup.client().install(); + assertThat(setup.client().getInstallations(), + contains(new InstallationsResponseInstallation().name(EXTENSION_ID).version(PROJECT_VERSION))); + } + + @Test + void createInstance() throws SQLException { + setup.client().install(); + final String virtualSchemaName = "RLS_SCHEMA"; + setup.addVirtualSchemaToCleanupQueue(virtualSchemaName); + final ExasolSchema schema = this.dbObjectFactory.createSchema("BASE_SCHEMA"); + final Table table = schema.createTable("TAB", "ID", "SMALLINT", "NAME", "varchar(10)").insert(1, "a") + .insert(2, "b").insert(3, "c"); + setup.client().createInstance( + List.of(param("virtualSchemaName", virtualSchemaName), param("SCHEMA_NAME", schema.getName()))); + assertResult("select * from " + virtualSchemaName + "." + table.getName() + " order by id", + table("INTEGER", "VARCHAR").row(1, "a").row(2, "b").row(3, "c").matches()); + } + + private void assertResult(final String sql, final Matcher matcher) throws SQLException { + try (Statement statement = connection.createStatement()) { + assertThat(statement.executeQuery(sql), matcher); + } + } + + private ParameterValue param(final String name, final String value) { + return new ParameterValue().name(name).value(value); + } +} From c13c65c38c384be92761831d37df30fe3debd007 Mon Sep 17 00:00:00 2001 From: Christoph Pirkl Date: Mon, 4 Sep 2023 16:05:47 +0200 Subject: [PATCH 08/21] Fix formatting of user guide --- doc/user_guide/user_guide.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/user_guide/user_guide.md b/doc/user_guide/user_guide.md index e69f774..eaee815 100644 --- a/doc/user_guide/user_guide.md +++ b/doc/user_guide/user_guide.md @@ -380,6 +380,7 @@ CREATE OR REPLACE LUA ADAPTER SCRIPT RLS_SCHEMA.RLS_ADAPTER AS ; ``` The first fixed part is a module loading preamble that is required with 7.1.0. Later versions will make this unnecessary, the user guide will be updated accordingly if an Exasol release is available that incorporates that module loading feature by default. + ### Creating Virtual Schema ```sql From f944033871e4e4ac06b362de709bbb89b97e30d9 Mon Sep 17 00:00:00 2001 From: Christoph Pirkl Date: Tue, 5 Sep 2023 08:35:48 +0200 Subject: [PATCH 09/21] Upgrade test dependencies --- dependencies.md | 119 ++++++++++++++++++----------------- doc/changes/changes_1.5.0.md | 6 +- pk_generated_parent.pom | 2 +- pom.xml | 6 +- 4 files changed, 70 insertions(+), 63 deletions(-) diff --git a/dependencies.md b/dependencies.md index b9fa8b1..fe5a5e1 100644 --- a/dependencies.md +++ b/dependencies.md @@ -12,38 +12,39 @@ | [Matcher for SQL Result Sets][8] | [MIT License][9] | | [JUnit Jupiter Engine][10] | [Eclipse Public License v2.0][11] | | [JUnit Jupiter Params][10] | [Eclipse Public License v2.0][11] | -| [SLF4J JDK14 Binding][12] | [MIT License][13] | +| [SLF4J JDK14 Provider][12] | [MIT License][13] | | [Test Database Builder for Java][14] | [MIT License][15] | | [Maven Project Version Getter][16] | [MIT License][17] | +| [Extension integration tests library][18] | [MIT License][19] | ## Plugin Dependencies | Dependency | License | | ------------------------------------------------------- | -------------------------------------------------------------- | -| [SonarQube Scanner for Maven][18] | [GNU LGPL 3][19] | -| [Apache Maven Compiler Plugin][20] | [Apache-2.0][21] | -| [Apache Maven Enforcer Plugin][22] | [Apache-2.0][21] | -| [Maven Flatten Plugin][23] | [Apache Software Licenese][21] | -| [org.sonatype.ossindex.maven:ossindex-maven-plugin][24] | [ASL2][25] | -| [Maven Surefire Plugin][26] | [Apache-2.0][21] | -| [Versions Maven Plugin][27] | [Apache License, Version 2.0][21] | -| [duplicate-finder-maven-plugin Maven Mojo][28] | [Apache License 2.0][29] | -| [Maven Failsafe Plugin][30] | [Apache-2.0][21] | -| [JaCoCo :: Maven Plugin][31] | [Eclipse Public License 2.0][32] | -| [Project keeper maven plugin][33] | [The MIT License][34] | -| [Exec Maven Plugin][35] | [Apache License 2][21] | -| [OpenFastTrace Maven Plugin][36] | [GNU General Public License v3.0][37] | -| [Build Helper Maven Plugin][38] | [The MIT License][39] | -| [error-code-crawler-maven-plugin][40] | [MIT License][41] | -| [Reproducible Build Maven Plugin][42] | [Apache 2.0][25] | -| [Apache Maven JAR Plugin][43] | [Apache License, Version 2.0][21] | -| [Maven PlantUML plugin][44] | [Apache License - Version 2.0, January 2004][45] | -| [Maven Clean Plugin][46] | [The Apache Software License, Version 2.0][25] | -| [Maven Resources Plugin][47] | [The Apache Software License, Version 2.0][25] | -| [Maven Install Plugin][48] | [The Apache Software License, Version 2.0][25] | -| [Maven Deploy Plugin][49] | [The Apache Software License, Version 2.0][25] | -| [Maven Site Plugin 3][50] | [The Apache Software License, Version 2.0][25] | +| [SonarQube Scanner for Maven][20] | [GNU LGPL 3][21] | +| [Apache Maven Compiler Plugin][22] | [Apache-2.0][23] | +| [Apache Maven Enforcer Plugin][24] | [Apache-2.0][23] | +| [Maven Flatten Plugin][25] | [Apache Software Licenese][23] | +| [org.sonatype.ossindex.maven:ossindex-maven-plugin][26] | [ASL2][27] | +| [Maven Surefire Plugin][28] | [Apache-2.0][23] | +| [Versions Maven Plugin][29] | [Apache License, Version 2.0][23] | +| [duplicate-finder-maven-plugin Maven Mojo][30] | [Apache License 2.0][31] | +| [Maven Failsafe Plugin][32] | [Apache-2.0][23] | +| [JaCoCo :: Maven Plugin][33] | [Eclipse Public License 2.0][34] | +| [Project keeper maven plugin][35] | [The MIT License][36] | +| [Exec Maven Plugin][37] | [Apache License 2][23] | +| [OpenFastTrace Maven Plugin][38] | [GNU General Public License v3.0][39] | +| [Build Helper Maven Plugin][40] | [The MIT License][41] | +| [error-code-crawler-maven-plugin][42] | [MIT License][43] | +| [Reproducible Build Maven Plugin][44] | [Apache 2.0][27] | +| [Apache Maven JAR Plugin][45] | [Apache License, Version 2.0][23] | +| [Maven PlantUML plugin][46] | [Apache License + Version 2.0, January 2004][47] | +| [Maven Clean Plugin][48] | [The Apache Software License, Version 2.0][27] | +| [Maven Resources Plugin][49] | [The Apache Software License, Version 2.0][27] | +| [Maven Install Plugin][50] | [The Apache Software License, Version 2.0][27] | +| [Maven Deploy Plugin][51] | [The Apache Software License, Version 2.0][27] | +| [Maven Site Plugin 3][52] | [The Apache Software License, Version 2.0][27] | [0]: http://www.exasol.com [1]: https://repo1.maven.org/maven2/com/exasol/exasol-jdbc/7.1.20/exasol-jdbc-7.1.20-license.txt @@ -63,36 +64,38 @@ [15]: https://github.com/exasol/test-db-builder-java/blob/main/LICENSE [16]: https://github.com/exasol/maven-project-version-getter/ [17]: https://github.com/exasol/maven-project-version-getter/blob/main/LICENSE -[18]: http://sonarsource.github.io/sonar-scanner-maven/ -[19]: http://www.gnu.org/licenses/lgpl.txt -[20]: https://maven.apache.org/plugins/maven-compiler-plugin/ -[21]: https://www.apache.org/licenses/LICENSE-2.0.txt -[22]: https://maven.apache.org/enforcer/maven-enforcer-plugin/ -[23]: https://www.mojohaus.org/flatten-maven-plugin/ -[24]: https://sonatype.github.io/ossindex-maven/maven-plugin/ -[25]: http://www.apache.org/licenses/LICENSE-2.0.txt -[26]: https://maven.apache.org/surefire/maven-surefire-plugin/ -[27]: https://www.mojohaus.org/versions/versions-maven-plugin/ -[28]: https://basepom.github.io/duplicate-finder-maven-plugin -[29]: http://www.apache.org/licenses/LICENSE-2.0.html -[30]: https://maven.apache.org/surefire/maven-failsafe-plugin/ -[31]: https://www.jacoco.org/jacoco/trunk/doc/maven.html -[32]: https://www.eclipse.org/legal/epl-2.0/ -[33]: https://github.com/exasol/project-keeper/ -[34]: https://github.com/exasol/project-keeper/blob/main/LICENSE -[35]: https://www.mojohaus.org/exec-maven-plugin -[36]: https://github.com/itsallcode/openfasttrace-maven-plugin -[37]: https://www.gnu.org/licenses/gpl-3.0.html -[38]: https://www.mojohaus.org/build-helper-maven-plugin/ -[39]: https://spdx.org/licenses/MIT.txt -[40]: https://github.com/exasol/error-code-crawler-maven-plugin/ -[41]: https://github.com/exasol/error-code-crawler-maven-plugin/blob/main/LICENSE -[42]: http://zlika.github.io/reproducible-build-maven-plugin -[43]: https://maven.apache.org/plugins/maven-jar-plugin/ -[44]: https://github.com/Huluvu424242/plantuml-maven-plugin -[45]: https://www.apache.org/licenses/LICENSE-2.0 -[46]: http://maven.apache.org/plugins/maven-clean-plugin/ -[47]: http://maven.apache.org/plugins/maven-resources-plugin/ -[48]: http://maven.apache.org/plugins/maven-install-plugin/ -[49]: http://maven.apache.org/plugins/maven-deploy-plugin/ -[50]: http://maven.apache.org/plugins/maven-site-plugin/ +[18]: https://github.com/exasol/extension-manager/ +[19]: https://github.com/exasol/extension-manager/blob/main/LICENSE +[20]: http://sonarsource.github.io/sonar-scanner-maven/ +[21]: http://www.gnu.org/licenses/lgpl.txt +[22]: https://maven.apache.org/plugins/maven-compiler-plugin/ +[23]: https://www.apache.org/licenses/LICENSE-2.0.txt +[24]: https://maven.apache.org/enforcer/maven-enforcer-plugin/ +[25]: https://www.mojohaus.org/flatten-maven-plugin/ +[26]: https://sonatype.github.io/ossindex-maven/maven-plugin/ +[27]: http://www.apache.org/licenses/LICENSE-2.0.txt +[28]: https://maven.apache.org/surefire/maven-surefire-plugin/ +[29]: https://www.mojohaus.org/versions/versions-maven-plugin/ +[30]: https://basepom.github.io/duplicate-finder-maven-plugin +[31]: http://www.apache.org/licenses/LICENSE-2.0.html +[32]: https://maven.apache.org/surefire/maven-failsafe-plugin/ +[33]: https://www.jacoco.org/jacoco/trunk/doc/maven.html +[34]: https://www.eclipse.org/legal/epl-2.0/ +[35]: https://github.com/exasol/project-keeper/ +[36]: https://github.com/exasol/project-keeper/blob/main/LICENSE +[37]: https://www.mojohaus.org/exec-maven-plugin +[38]: https://github.com/itsallcode/openfasttrace-maven-plugin +[39]: https://www.gnu.org/licenses/gpl-3.0.html +[40]: https://www.mojohaus.org/build-helper-maven-plugin/ +[41]: https://spdx.org/licenses/MIT.txt +[42]: https://github.com/exasol/error-code-crawler-maven-plugin/ +[43]: https://github.com/exasol/error-code-crawler-maven-plugin/blob/main/LICENSE +[44]: http://zlika.github.io/reproducible-build-maven-plugin +[45]: https://maven.apache.org/plugins/maven-jar-plugin/ +[46]: https://github.com/Huluvu424242/plantuml-maven-plugin +[47]: https://www.apache.org/licenses/LICENSE-2.0 +[48]: http://maven.apache.org/plugins/maven-clean-plugin/ +[49]: http://maven.apache.org/plugins/maven-resources-plugin/ +[50]: http://maven.apache.org/plugins/maven-install-plugin/ +[51]: http://maven.apache.org/plugins/maven-deploy-plugin/ +[52]: http://maven.apache.org/plugins/maven-site-plugin/ diff --git a/doc/changes/changes_1.5.0.md b/doc/changes/changes_1.5.0.md index 06cee87..28e3051 100644 --- a/doc/changes/changes_1.5.0.md +++ b/doc/changes/changes_1.5.0.md @@ -14,10 +14,14 @@ This release adds support for the [Extension Manager](https://github.com/exasol/ ### Test Dependency Updates +* Added `com.exasol:extension-manager-integration-test-java:0.5.0` +* Updated `com.exasol:test-db-builder-java:3.4.2` to `3.5.0` * Updated `org.junit.jupiter:junit-jupiter-engine:5.9.3` to `5.10.0` * Updated `org.junit.jupiter:junit-jupiter-params:5.9.3` to `5.10.0` +* Updated `org.slf4j:slf4j-jdk14:2.0.7` to `2.0.9` * Updated `org.testcontainers:junit-jupiter:1.18.3` to `1.19.0` ### Plugin Dependency Updates -* Updated `com.exasol:project-keeper-maven-plugin:2.9.9` to `2.9.10` +* Updated `com.exasol:project-keeper-maven-plugin:2.9.9` to `2.9.11` +* Updated `org.apache.maven.plugins:maven-enforcer-plugin:3.3.0` to `3.4.0` diff --git a/pk_generated_parent.pom b/pk_generated_parent.pom index bfc55c5..cb5c0a5 100644 --- a/pk_generated_parent.pom +++ b/pk_generated_parent.pom @@ -51,7 +51,7 @@ org.apache.maven.plugins maven-enforcer-plugin - 3.3.0 + 3.4.0 enforce-maven diff --git a/pom.xml b/pom.xml index 6047e93..029560f 100644 --- a/pom.xml +++ b/pom.xml @@ -70,13 +70,13 @@ org.slf4j slf4j-jdk14 - 2.0.7 + 2.0.9 test com.exasol test-db-builder-java - 3.4.2 + 3.5.0 test @@ -97,7 +97,7 @@ com.exasol project-keeper-maven-plugin - 2.9.10 + 2.9.11 From df9f7650b60fc728a96b702ef4e8520a300a9a7c Mon Sep 17 00:00:00 2001 From: Christoph Pirkl Date: Tue, 5 Sep 2023 11:27:33 +0200 Subject: [PATCH 10/21] Add parameters for debug_address & log_level --- extension/src/addInstance.ts | 53 +++++++++++++------ extension/src/parameterDefinitions.ts | 32 +++++++---- .../com/exasol/rls/extension/ExtensionIT.java | 4 ++ 3 files changed, 64 insertions(+), 25 deletions(-) diff --git a/extension/src/addInstance.ts b/extension/src/addInstance.ts index bc7711d..2f1bceb 100644 --- a/extension/src/addInstance.ts +++ b/extension/src/addInstance.ts @@ -3,22 +3,37 @@ import { ADAPTER_SCRIPT_NAME, convertSchemaNameToInstanceId } from "./common"; import { ExtendedContext } from "./extension"; import { getAllParameterDefinitions } from "./parameterDefinitions"; +interface VirtualSchemaConfig { + virtualSchemaName: string + baseSchemaName: string + excludedCapabilities: string | undefined + tableFilter: string | undefined + debugAddress: string | undefined + logLevel: string | undefined +} + export function addInstance(context: ExtendedContext, version: string, paramValues: ParameterValues): Instance { if (context.version !== version) { throw new NotFoundError(`Version '${version}' not supported, can only use '${context.version}'.`) } + const config = buildVirtualSchemaConfig(paramValues) + const createVirtualSchemaStmt = createVirtualSchemaStatement(context.extensionSchemaName, config); + context.sqlClient.execute(createVirtualSchemaStmt); + const comment = `Created by extension manager for row-level-security-lua ${escapeSingleQuotes(config.virtualSchemaName)} (version ${context.version})`; + context.sqlClient.execute(`COMMENT ON SCHEMA "${config.virtualSchemaName}" IS '${comment}'`); + return { id: convertSchemaNameToInstanceId(config.virtualSchemaName), name: config.virtualSchemaName } +} +function buildVirtualSchemaConfig(paramValues: ParameterValues): VirtualSchemaConfig { const allParams = getAllParameterDefinitions(); - const virtualSchemaName = getParameterValue(paramValues, allParams.virtualSchemaName); - const schemaName = getParameterValue(paramValues, allParams.schemaName); - const excludedCapabilities = getOptionalParameterValue(paramValues, allParams.excludedCapabilities); - const tableFilter = getOptionalParameterValue(paramValues, allParams.tableFilter); - - const createVirtualSchemaStmt = createVirtualSchemaStatement(virtualSchemaName, context.extensionSchemaName, schemaName, excludedCapabilities, tableFilter); - context.sqlClient.execute(createVirtualSchemaStmt); - const comment = `Created by extension manager for row-level-security-lua ${escapeSingleQuotes(virtualSchemaName)} (version ${context.version})`; - context.sqlClient.execute(`COMMENT ON SCHEMA "${virtualSchemaName}" IS '${comment}'`); - return { id: convertSchemaNameToInstanceId(virtualSchemaName), name: virtualSchemaName } + return { + virtualSchemaName: getParameterValue(paramValues, allParams.virtualSchemaName), + baseSchemaName: getParameterValue(paramValues, allParams.schemaName), + tableFilter: getOptionalParameterValue(paramValues, allParams.tableFilter), + excludedCapabilities: getOptionalParameterValue(paramValues, allParams.excludedCapabilities), + debugAddress: getOptionalParameterValue(paramValues, allParams.debugAddress), + logLevel: getOptionalParameterValue(paramValues, allParams.logLevel) + } } function getParameterValue(paramValues: ParameterValues, definition: Parameter): string { @@ -38,13 +53,19 @@ function getOptionalParameterValue(paramValues: ParameterValues, definition: Par return undefined } -function createVirtualSchemaStatement(name: string, adapterSchema: string, baseSchemaName: string, excludedCapabilities: string | undefined, tableFilter: string | undefined): string { - let stmt = `CREATE VIRTUAL SCHEMA "${name}" USING "${adapterSchema}"."${ADAPTER_SCRIPT_NAME}" WITH SCHEMA_NAME = '${baseSchemaName}'` - if (excludedCapabilities) { - stmt += ` EXCLUDED_CAPABILITIES='${excludedCapabilities}'` +function createVirtualSchemaStatement(adapterSchema: string, config: VirtualSchemaConfig): string { + let stmt = `CREATE VIRTUAL SCHEMA "${config.virtualSchemaName}" USING "${adapterSchema}"."${ADAPTER_SCRIPT_NAME}" WITH SCHEMA_NAME = '${config.baseSchemaName}'` + if (config.excludedCapabilities) { + stmt += ` EXCLUDED_CAPABILITIES='${config.excludedCapabilities}'` + } + if (config.excludedCapabilities) { + stmt += ` TABLE_FILTER='${config.tableFilter}'` + } + if (config.debugAddress) { + stmt += ` DEBUG_ADDRESS='${config.debugAddress}'` } - if (excludedCapabilities) { - stmt += ` TABLE_FILTER='${tableFilter}'` + if (config.logLevel) { + stmt += ` LOG_LEVEL='${config.logLevel}'` } return stmt; } diff --git a/extension/src/parameterDefinitions.ts b/extension/src/parameterDefinitions.ts index cbab440..236750a 100644 --- a/extension/src/parameterDefinitions.ts +++ b/extension/src/parameterDefinitions.ts @@ -1,18 +1,30 @@ -import { Parameter } from "@exasol/extension-manager-interface"; +import { Parameter, SelectOption } from "@exasol/extension-manager-interface"; -export type ScopedParameter = Parameter & { scope: "general" | "vs" } -export type ScopedParameters = { [key: string]: ScopedParameter } -const allParams: ScopedParameters = { - virtualSchemaName: { scope: "general", id: "virtualSchemaName", name: "Name of the new virtual schema", type: "string", required: true }, +const REMOTELOG_LUA_LOG_LEVELS: string[] = ["NONE", "FATAL", "ERROR", "WARN", "INFO", "CONFIG", "DEBUG", "TRACE"]; +const LOG_LEVEL_OPTIONS: SelectOption[] = REMOTELOG_LUA_LOG_LEVELS.map(level => { return { id: level, name: level } }) + +interface AllParameters { + virtualSchemaName: Parameter + schemaName: Parameter + excludedCapabilities: Parameter + tableFilter: Parameter + debugAddress: Parameter + logLevel: Parameter +} + +const allParams: AllParameters = { + virtualSchemaName: { id: "virtualSchemaName", name: "Name of the new virtual schema", type: "string", required: true }, // Virtual Schema parameters - schemaName: { scope: "vs", id: "SCHEMA_NAME", name: "Name of the schema for which to apply row-level security", type: "string", required: true, multiline: false }, - excludedCapabilities: { scope: "vs", id: "EXCLUDED_CAPABILITIES", name: "Comma-separated list of capabilities that should not be pushed-down, e.g. 'SELECTLIST_PROJECTION, ORDER_BY_COLUMN'", type: "string", required: false, multiline: false }, - tableFilter: { scope: "vs", id: "TABLE_FILTER", name: "Comma-separated list of tables that should be added to the schema, e.g. 'ORDERS, ORDER_ITEMS, PRODUCTS'. If this is empty, all tables are added.", type: "string", required: false, multiline: false }, + schemaName: { id: "SCHEMA_NAME", name: "Name of the schema for which to apply row-level security", type: "string", required: true, multiline: false }, + excludedCapabilities: { id: "EXCLUDED_CAPABILITIES", name: "Comma-separated list of capabilities that should not be pushed-down, e.g. 'SELECTLIST_PROJECTION, ORDER_BY_COLUMN'", type: "string", required: false, multiline: false }, + tableFilter: { id: "TABLE_FILTER", name: "Comma-separated list of tables that should be added to the schema, e.g. 'ORDERS, ORDER_ITEMS, PRODUCTS'. If this is empty, all tables are added.", type: "string", required: false, multiline: false }, + debugAddress: { id: "DEBUG_ADDRESS", name: "Network address and port to which to send debug output, e.g. '192.168.179.38:3000'", type: "string", required: false, multiline: false }, + logLevel: { id: "LOG_LEVEL", name: "Log level for debug output", type: "select", required: false, options: LOG_LEVEL_OPTIONS }, }; -export function getAllParameterDefinitions(): ScopedParameters { +export function getAllParameterDefinitions(): AllParameters { return allParams; } @@ -22,5 +34,7 @@ export function createInstanceParameters(): Parameter[] { allParams.schemaName, allParams.excludedCapabilities, allParams.tableFilter, + allParams.debugAddress, + allParams.logLevel, ]; } \ No newline at end of file diff --git a/src/test/java/com/exasol/rls/extension/ExtensionIT.java b/src/test/java/com/exasol/rls/extension/ExtensionIT.java index e23e9f4..6f04acd 100644 --- a/src/test/java/com/exasol/rls/extension/ExtensionIT.java +++ b/src/test/java/com/exasol/rls/extension/ExtensionIT.java @@ -105,6 +105,10 @@ void createInstance() throws SQLException { .insert(2, "b").insert(3, "c"); setup.client().createInstance( List.of(param("virtualSchemaName", virtualSchemaName), param("SCHEMA_NAME", schema.getName()))); + extracted(virtualSchemaName, table); + } + + private void extracted(final String virtualSchemaName, final Table table) throws SQLException { assertResult("select * from " + virtualSchemaName + "." + table.getName() + " order by id", table("INTEGER", "VARCHAR").row(1, "a").row(2, "b").row(3, "c").matches()); } From 625f1adafedf4b3b71f6020c77c412319d01b5c2 Mon Sep 17 00:00:00 2001 From: Christoph Pirkl Date: Tue, 5 Sep 2023 15:29:36 +0200 Subject: [PATCH 11/21] Code cleanup --- extension/generate-description.sh | 2 ++ extension/src/parameterDefinitions.ts | 11 +++++++---- extension/tsconfig.json | 4 ++-- pom.xml | 8 -------- 4 files changed, 11 insertions(+), 14 deletions(-) diff --git a/extension/generate-description.sh b/extension/generate-description.sh index 595286b..52bdb4a 100755 --- a/extension/generate-description.sh +++ b/extension/generate-description.sh @@ -1,5 +1,7 @@ #!/bin/bash +# This generates file src/extension-description.ts which describes the Lua virtual schema adapter script. + set -o errexit set -o nounset set -o pipefail diff --git a/extension/src/parameterDefinitions.ts b/extension/src/parameterDefinitions.ts index 236750a..be16def 100644 --- a/extension/src/parameterDefinitions.ts +++ b/extension/src/parameterDefinitions.ts @@ -1,9 +1,8 @@ import { Parameter, SelectOption } from "@exasol/extension-manager-interface"; - -const REMOTELOG_LUA_LOG_LEVELS: string[] = ["NONE", "FATAL", "ERROR", "WARN", "INFO", "CONFIG", "DEBUG", "TRACE"]; -const LOG_LEVEL_OPTIONS: SelectOption[] = REMOTELOG_LUA_LOG_LEVELS.map(level => { return { id: level, name: level } }) - +/** + * This describes all mandatory and optional parameters for creating an instance of this extension. + */ interface AllParameters { virtualSchemaName: Parameter schemaName: Parameter @@ -13,6 +12,9 @@ interface AllParameters { logLevel: Parameter } +const REMOTELOG_LUA_LOG_LEVELS: string[] = ["NONE", "FATAL", "ERROR", "WARN", "INFO", "CONFIG", "DEBUG", "TRACE"]; +const LOG_LEVEL_OPTIONS: SelectOption[] = REMOTELOG_LUA_LOG_LEVELS.map(level => { return { id: level, name: level } }) + const allParams: AllParameters = { virtualSchemaName: { id: "virtualSchemaName", name: "Name of the new virtual schema", type: "string", required: true }, @@ -24,6 +26,7 @@ const allParams: AllParameters = { logLevel: { id: "LOG_LEVEL", name: "Log level for debug output", type: "select", required: false, options: LOG_LEVEL_OPTIONS }, }; + export function getAllParameterDefinitions(): AllParameters { return allParams; } diff --git a/extension/tsconfig.json b/extension/tsconfig.json index 41a0fe6..7f2aca0 100644 --- a/extension/tsconfig.json +++ b/extension/tsconfig.json @@ -1,7 +1,5 @@ { "compilerOptions": { - "noImplicitAny": true, - "noEmitOnError": true, "removeComments": false, "sourceMap": true, "target": "es6", @@ -10,6 +8,8 @@ "moduleResolution": "node", "esModuleInterop": true, "strict": true, + "noImplicitAny": true, + "noEmitOnError": true, "noFallthroughCasesInSwitch": true, "noImplicitOverride": true, "noImplicitReturns": true, diff --git a/pom.xml b/pom.xml index 029560f..b6f05cf 100644 --- a/pom.xml +++ b/pom.xml @@ -34,14 +34,6 @@ junit-jupiter 1.19.0 test - - org.hamcrest From 7804ba2d2cec0c5b70f303b2cf8698d2f81f4182 Mon Sep 17 00:00:00 2001 From: Christoph Pirkl Date: Tue, 5 Sep 2023 15:29:44 +0200 Subject: [PATCH 12/21] Upgrade to latest exasol version --- .github/workflows/ci-build.yml | 2 +- src/test/java/com/exasol/RlsTestConstants.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci-build.yml b/.github/workflows/ci-build.yml index 5435e5a..4bafd03 100644 --- a/.github/workflows/ci-build.yml +++ b/.github/workflows/ci-build.yml @@ -13,7 +13,7 @@ jobs: fail-fast: true matrix: lua_version: [5.4] - docker_db_version: ["7.1.22", "8.21.0"] + docker_db_version: ["7.1.22", "8.22.0"] runs-on: ubuntu-22.04 concurrency: group: ${{ github.workflow }}-${{ github.ref }}-${{ matrix.lua_version }}-${{ matrix.docker_db_version }} diff --git a/src/test/java/com/exasol/RlsTestConstants.java b/src/test/java/com/exasol/RlsTestConstants.java index ef4ad08..ecdfc43 100644 --- a/src/test/java/com/exasol/RlsTestConstants.java +++ b/src/test/java/com/exasol/RlsTestConstants.java @@ -12,7 +12,7 @@ public final class RlsTestConstants { public static final String ROLE_MASK_TYPE = "DECIMAL(20,0)"; public static final String IDENTIFIER_TYPE = "VARCHAR(128)"; public static final int PUBLIC_ROLE_BIT_INDEX = 63; - public static final String DOCKER_DB = "exasol/docker-db:8.21.0"; + public static final String DOCKER_DB = "exasol/docker-db:8.22.0"; private RlsTestConstants() { // prevent instantiation From a0ced3b7d78dfec5ff7c3d3ee5b38ac4c65d486a Mon Sep 17 00:00:00 2001 From: Christoph Pirkl Date: Tue, 5 Sep 2023 15:33:41 +0200 Subject: [PATCH 13/21] Exclude vulnerabilities --- pom.xml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/pom.xml b/pom.xml index b6f05cf..29289bf 100644 --- a/pom.xml +++ b/pom.xml @@ -296,6 +296,20 @@ + + org.sonatype.ossindex.maven + ossindex-maven-plugin + + + + CVE-2020-36641 + + CVE-2023-4586 + + + From ac08c7e5ee0511bbf50dbf45c96b4f4385aaff37 Mon Sep 17 00:00:00 2001 From: Christoph Pirkl Date: Tue, 5 Sep 2023 15:38:41 +0200 Subject: [PATCH 14/21] Run pk fix --- dependencies.md | 84 ++++++++++++++++++++++++------------------------- 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/dependencies.md b/dependencies.md index fe5a5e1..10f0054 100644 --- a/dependencies.md +++ b/dependencies.md @@ -25,26 +25,26 @@ | [Apache Maven Compiler Plugin][22] | [Apache-2.0][23] | | [Apache Maven Enforcer Plugin][24] | [Apache-2.0][23] | | [Maven Flatten Plugin][25] | [Apache Software Licenese][23] | -| [org.sonatype.ossindex.maven:ossindex-maven-plugin][26] | [ASL2][27] | -| [Maven Surefire Plugin][28] | [Apache-2.0][23] | -| [Versions Maven Plugin][29] | [Apache License, Version 2.0][23] | -| [duplicate-finder-maven-plugin Maven Mojo][30] | [Apache License 2.0][31] | -| [Maven Failsafe Plugin][32] | [Apache-2.0][23] | -| [JaCoCo :: Maven Plugin][33] | [Eclipse Public License 2.0][34] | -| [Project keeper maven plugin][35] | [The MIT License][36] | -| [Exec Maven Plugin][37] | [Apache License 2][23] | -| [OpenFastTrace Maven Plugin][38] | [GNU General Public License v3.0][39] | -| [Build Helper Maven Plugin][40] | [The MIT License][41] | -| [error-code-crawler-maven-plugin][42] | [MIT License][43] | -| [Reproducible Build Maven Plugin][44] | [Apache 2.0][27] | -| [Apache Maven JAR Plugin][45] | [Apache License, Version 2.0][23] | -| [Maven PlantUML plugin][46] | [Apache License - Version 2.0, January 2004][47] | -| [Maven Clean Plugin][48] | [The Apache Software License, Version 2.0][27] | -| [Maven Resources Plugin][49] | [The Apache Software License, Version 2.0][27] | -| [Maven Install Plugin][50] | [The Apache Software License, Version 2.0][27] | -| [Maven Deploy Plugin][51] | [The Apache Software License, Version 2.0][27] | -| [Maven Site Plugin 3][52] | [The Apache Software License, Version 2.0][27] | +| [Apache Maven JAR Plugin][26] | [Apache License, Version 2.0][23] | +| [Maven PlantUML plugin][27] | [Apache License + Version 2.0, January 2004][28] | +| [org.sonatype.ossindex.maven:ossindex-maven-plugin][29] | [ASL2][30] | +| [Maven Surefire Plugin][31] | [Apache-2.0][23] | +| [Versions Maven Plugin][32] | [Apache License, Version 2.0][23] | +| [duplicate-finder-maven-plugin Maven Mojo][33] | [Apache License 2.0][34] | +| [Maven Failsafe Plugin][35] | [Apache-2.0][23] | +| [JaCoCo :: Maven Plugin][36] | [Eclipse Public License 2.0][37] | +| [Project keeper maven plugin][38] | [The MIT License][39] | +| [Exec Maven Plugin][40] | [Apache License 2][23] | +| [OpenFastTrace Maven Plugin][41] | [GNU General Public License v3.0][42] | +| [Build Helper Maven Plugin][43] | [The MIT License][44] | +| [error-code-crawler-maven-plugin][45] | [MIT License][46] | +| [Reproducible Build Maven Plugin][47] | [Apache 2.0][30] | +| [Maven Clean Plugin][48] | [The Apache Software License, Version 2.0][30] | +| [Maven Resources Plugin][49] | [The Apache Software License, Version 2.0][30] | +| [Maven Install Plugin][50] | [The Apache Software License, Version 2.0][30] | +| [Maven Deploy Plugin][51] | [The Apache Software License, Version 2.0][30] | +| [Maven Site Plugin 3][52] | [The Apache Software License, Version 2.0][30] | [0]: http://www.exasol.com [1]: https://repo1.maven.org/maven2/com/exasol/exasol-jdbc/7.1.20/exasol-jdbc-7.1.20-license.txt @@ -72,28 +72,28 @@ [23]: https://www.apache.org/licenses/LICENSE-2.0.txt [24]: https://maven.apache.org/enforcer/maven-enforcer-plugin/ [25]: https://www.mojohaus.org/flatten-maven-plugin/ -[26]: https://sonatype.github.io/ossindex-maven/maven-plugin/ -[27]: http://www.apache.org/licenses/LICENSE-2.0.txt -[28]: https://maven.apache.org/surefire/maven-surefire-plugin/ -[29]: https://www.mojohaus.org/versions/versions-maven-plugin/ -[30]: https://basepom.github.io/duplicate-finder-maven-plugin -[31]: http://www.apache.org/licenses/LICENSE-2.0.html -[32]: https://maven.apache.org/surefire/maven-failsafe-plugin/ -[33]: https://www.jacoco.org/jacoco/trunk/doc/maven.html -[34]: https://www.eclipse.org/legal/epl-2.0/ -[35]: https://github.com/exasol/project-keeper/ -[36]: https://github.com/exasol/project-keeper/blob/main/LICENSE -[37]: https://www.mojohaus.org/exec-maven-plugin -[38]: https://github.com/itsallcode/openfasttrace-maven-plugin -[39]: https://www.gnu.org/licenses/gpl-3.0.html -[40]: https://www.mojohaus.org/build-helper-maven-plugin/ -[41]: https://spdx.org/licenses/MIT.txt -[42]: https://github.com/exasol/error-code-crawler-maven-plugin/ -[43]: https://github.com/exasol/error-code-crawler-maven-plugin/blob/main/LICENSE -[44]: http://zlika.github.io/reproducible-build-maven-plugin -[45]: https://maven.apache.org/plugins/maven-jar-plugin/ -[46]: https://github.com/Huluvu424242/plantuml-maven-plugin -[47]: https://www.apache.org/licenses/LICENSE-2.0 +[26]: https://maven.apache.org/plugins/maven-jar-plugin/ +[27]: https://github.com/Huluvu424242/plantuml-maven-plugin +[28]: https://www.apache.org/licenses/LICENSE-2.0 +[29]: https://sonatype.github.io/ossindex-maven/maven-plugin/ +[30]: http://www.apache.org/licenses/LICENSE-2.0.txt +[31]: https://maven.apache.org/surefire/maven-surefire-plugin/ +[32]: https://www.mojohaus.org/versions/versions-maven-plugin/ +[33]: https://basepom.github.io/duplicate-finder-maven-plugin +[34]: http://www.apache.org/licenses/LICENSE-2.0.html +[35]: https://maven.apache.org/surefire/maven-failsafe-plugin/ +[36]: https://www.jacoco.org/jacoco/trunk/doc/maven.html +[37]: https://www.eclipse.org/legal/epl-2.0/ +[38]: https://github.com/exasol/project-keeper/ +[39]: https://github.com/exasol/project-keeper/blob/main/LICENSE +[40]: https://www.mojohaus.org/exec-maven-plugin +[41]: https://github.com/itsallcode/openfasttrace-maven-plugin +[42]: https://www.gnu.org/licenses/gpl-3.0.html +[43]: https://www.mojohaus.org/build-helper-maven-plugin/ +[44]: https://spdx.org/licenses/MIT.txt +[45]: https://github.com/exasol/error-code-crawler-maven-plugin/ +[46]: https://github.com/exasol/error-code-crawler-maven-plugin/blob/main/LICENSE +[47]: http://zlika.github.io/reproducible-build-maven-plugin [48]: http://maven.apache.org/plugins/maven-clean-plugin/ [49]: http://maven.apache.org/plugins/maven-resources-plugin/ [50]: http://maven.apache.org/plugins/maven-install-plugin/ From faedc14fd067557429b366e7df93f7fb0585bffd Mon Sep 17 00:00:00 2001 From: Christoph Pirkl Date: Wed, 6 Sep 2023 09:25:04 +0200 Subject: [PATCH 15/21] Implement extension --- extension/src/common.ts | 14 ++ extension/src/extension.test.ts | 116 +++++----- extension/src/extension.ts | 5 +- extension/src/findInstallations.ts | 14 ++ extension/src/install.ts | 37 +-- extension/src/parameterDefinitions.ts | 8 +- extension/src/uninstall.ts | 13 ++ .../com/exasol/rls/extension/ExtensionIT.java | 218 ++++++++++++++++-- 8 files changed, 334 insertions(+), 91 deletions(-) create mode 100644 extension/src/findInstallations.ts diff --git a/extension/src/common.ts b/extension/src/common.ts index b14895c..52f2b9e 100644 --- a/extension/src/common.ts +++ b/extension/src/common.ts @@ -1,3 +1,5 @@ +import { Result, failureResult, successResult } from "@exasol/extension-manager-interface/dist/base/common"; +import { EXTENSION_DESCRIPTION } from "./extension-description"; export const ADAPTER_SCRIPT_NAME = "RLS_ADAPTER" @@ -9,3 +11,15 @@ function identity(arg: string): string { export const convertInstanceIdToSchemaName = identity export const convertSchemaNameToInstanceId = identity +const versionCommentRegexp = new RegExp("^\\s*-- RLS Lua version (.*?)\\s*$", "m") +export function extractVersion(scriptText: string): Result { + const match = versionCommentRegexp.exec(scriptText) + if (!match) { + return failureResult(`version not found in script text '${scriptText}'`) + } + return successResult(match[1]) +} + +export function getScriptVersionComment(): string { + return `-- RLS Lua version ${EXTENSION_DESCRIPTION.version}` +} \ No newline at end of file diff --git a/extension/src/extension.test.ts b/extension/src/extension.test.ts index 3a0368a..1ec320f 100644 --- a/extension/src/extension.test.ts +++ b/extension/src/extension.test.ts @@ -1,8 +1,11 @@ -import { ExaMetadata, Installation, PreconditionFailedError } from '@exasol/extension-manager-interface'; +import { ExaMetadata, Installation, NotFoundError, PreconditionFailedError } from '@exasol/extension-manager-interface'; +import { failureResult, successResult } from '@exasol/extension-manager-interface/dist/base/common'; import { ExaScriptsRow } from '@exasol/extension-manager-interface/dist/exasolSchema'; import { describe, expect, it } from '@jest/globals'; +import { ADAPTER_SCRIPT_NAME, extractVersion } from './common'; import { createExtension } from "./extension"; import { EXTENSION_DESCRIPTION } from './extension-description'; +import { buildCreateScriptCommand } from './install'; import { createMockContext, getInstalledExtension, scriptWithVersion } from './test-utils'; const currentVersion = EXTENSION_DESCRIPTION.version @@ -14,6 +17,7 @@ describe("Row Level Security Lua", () => { const latestVersions = createExtension().installableVersions.filter(version => version.latest) expect(latestVersions).toHaveLength(1) expect(latestVersions[0].deprecated).toEqual(false) + expect(latestVersions[0].name).toEqual(currentVersion) }) }) @@ -37,6 +41,28 @@ describe("Row Level Security Lua", () => { }) }) + describe("extractVersion()", () => { + it("extracts version from script", () => { + expect(extractVersion('CREATE LUA ADAPTER SCRIPT "RLS_ADAPTER" AS\n-- RLS Lua version version number\nmore content')).toStrictEqual(successResult("version number")) + }) + it("extracts version from script with whitespace", () => { + expect(extractVersion('CREATE LUA ADAPTER SCRIPT "RLS_ADAPTER" AS\n -- RLS Lua version version number \n more content')).toStrictEqual(successResult("version number")) + }) + it("extracts version from script with dummy content", () => { + expect(extractVersion('dummy\n-- RLS Lua version version number\ndummy')).toStrictEqual(successResult("version number")) + }) + it("extracts version from script with only version comment", () => { + expect(extractVersion('-- RLS Lua version version number')).toStrictEqual(successResult("version number")) + }) + it("fails to extracts version from script", () => { + expect(extractVersion('invalid script')).toStrictEqual(failureResult("version not found in script text 'invalid script'")) + }) + it("recognizes it's own version tag", () => { + const script = buildCreateScriptCommand("scriptName", "luaContent") + expect(extractVersion(script)).toStrictEqual(successResult(currentVersion)) + }) + }) + describe("findInstallations()", () => { function findInstallations(allScripts: ExaScriptsRow[]): Installation[] { const metadata: ExaMetadata = { @@ -49,48 +75,22 @@ describe("Row Level Security Lua", () => { return installations } - function text(name: string, className: string, version: string): string { - return `CREATE ${name} ... - %scriptclass ${className}; - %jar /buckets/bfsdefault/default/exasol-cloud-storage-extension-${version}.jar;` - } - function script({ schema = "schema", name = "name", inputType, resultType = "EMITS", type = "UDF", text = "", comment }: Partial): ExaScriptsRow { + function script({ schema = "schema", name = "name", inputType, resultType, type = "ADAPTER", text = "", comment }: Partial): ExaScriptsRow { return { schema, name, inputType, resultType, type, text, comment } } - function setScript(name: string, className: string, version = EXTENSION_DESCRIPTION.version): ExaScriptsRow { - return script({ name, inputType: "SET", text: text(name, className, version) }) - } - function scalarScript(name: string, className: string, version = EXTENSION_DESCRIPTION.version): ExaScriptsRow { - return script({ name, inputType: "SCALAR", text: text(name, className, version) }) - } it("returns empty list when no adapter script is available", () => { expect(findInstallations([])).toHaveLength(0) }) - it("returns single item when all scripts are available", () => { - const scripts: ExaScriptsRow[] = [ - setScript("EXPORT_PATH", "com.exasol.cloudetl.scriptclasses.TableExportQueryGenerator"), - setScript("EXPORT_TABLE", "com.exasol.cloudetl.scriptclasses.TableDataExporter"), - setScript("IMPORT_FILES", "com.exasol.cloudetl.scriptclasses.FilesDataImporter"), - scalarScript("IMPORT_METADATA", "com.exasol.cloudetl.scriptclasses.FilesMetadataReader"), - setScript("IMPORT_PATH", "com.exasol.cloudetl.scriptclasses.FilesImportQueryGenerator") - ] - expect(findInstallations(scripts)).toStrictEqual([{ name: "Cloud Storage Extension", version: EXTENSION_DESCRIPTION.version }]) - }) - - it("fails for inconsistent version", () => { - const scripts: ExaScriptsRow[] = [ - setScript("EXPORT_PATH", "com.exasol.cloudetl.scriptclasses.TableExportQueryGenerator"), - setScript("EXPORT_TABLE", "com.exasol.cloudetl.scriptclasses.TableDataExporter"), - setScript("IMPORT_FILES", "com.exasol.cloudetl.scriptclasses.FilesDataImporter"), - scalarScript("IMPORT_METADATA", "com.exasol.cloudetl.scriptclasses.FilesMetadataReader", "0.0.0"), - setScript("IMPORT_PATH", "com.exasol.cloudetl.scriptclasses.FilesImportQueryGenerator") - ] - expect(() => findInstallations(scripts)).toThrowError(new PreconditionFailedError(`Not all scripts use the same version. Found 2 different versions: '${currentVersion}, 0.0.0'`)) + it("returns single item when script is available", () => { + const scripts: ExaScriptsRow[] = [script({ name: "RLS_ADAPTER", text: "-- RLS Lua version version" })] + expect(findInstallations(scripts)).toStrictEqual([{ name: "schema.RLS_ADAPTER", version: "version" }]) }) - describe("returns expected installations", () => { + it("returns unknown version for invalid script", () => { + const scripts: ExaScriptsRow[] = [script({ name: "RLS_ADAPTER", text: "invalid" })] + expect(findInstallations(scripts)).toStrictEqual([{ name: "schema.RLS_ADAPTER", version: "(unknown)" }]) }) }) @@ -99,24 +99,16 @@ describe("Row Level Security Lua", () => { const context = createMockContext(); createExtension().install(context, EXTENSION_DESCRIPTION.version); const executeCalls = context.mocks.sqlExecute.mock.calls - expect(executeCalls.length).toBe(10) - - const expectedScriptNames = ["IMPORT_PATH", "IMPORT_METADATA", "IMPORT_FILES", "EXPORT_PATH", "EXPORT_TABLE"] - - const createScriptStatements = executeCalls.slice(0, 5).map(args => args[0]) - const createCommentStatements = executeCalls.slice(5, 10).map(args => args[0]) + expect(executeCalls.length).toBe(2) - expect(createScriptStatements).toHaveLength(5) - expect(createCommentStatements).toHaveLength(5) + const createScriptCommand = executeCalls[0][0] + const createCommentCommand = executeCalls[1][0] - const expectedComment = `Created by Extension Manager for Cloud Storage Extension ${EXTENSION_DESCRIPTION.version}` - for (let i = 0; i < expectedScriptNames.length; i++) { - const name = expectedScriptNames[i]; - expect(createScriptStatements[i]).toContain(`CREATE OR REPLACE JAVA`) - expect(createScriptStatements[i]).toContain(`SCRIPT "ext-schema"."${name}"`) - expect(createScriptStatements[i]).toContain(`%scriptclass com.exasol.cloudetl.scriptclasses.`) - expect(createCommentStatements[i]).toEqual(`COMMENT ON SCRIPT "ext-schema"."${name}" IS '${expectedComment}'`) - } + expect(createScriptCommand).toContain(`CREATE OR REPLACE LUA ADAPTER SCRIPT \"ext-schema\".\"RLS_ADAPTER\" AS +-- RLS Lua version 1.5.0 +table.insert(package.searchers,`) + const expectedComment = `Created by Extension Manager for Row Level Security Lua ${EXTENSION_DESCRIPTION.version}` + expect(createCommentCommand).toEqual(`COMMENT ON SCRIPT "ext-schema"."${ADAPTER_SCRIPT_NAME}" IS '${expectedComment}'`) }) it("fails for wrong version", () => { expect(() => { createExtension().install(createMockContext(), "wrongVersion") }) @@ -144,11 +136,8 @@ describe("Row Level Security Lua", () => { context.mocks.sqlQuery.mockReturnValue({ columns: [], rows: [[1]] }); createExtension().uninstall(context, EXTENSION_DESCRIPTION.version) const calls = context.mocks.sqlExecute.mock.calls - const expectedScriptNames = ["IMPORT_PATH", "IMPORT_METADATA", "IMPORT_FILES", "EXPORT_PATH", "EXPORT_TABLE"] - expect(calls.length).toEqual(expectedScriptNames.length) - for (let i = 0; i < expectedScriptNames.length; i++) { - expect(calls[i]).toEqual([`DROP SCRIPT "ext-schema"."${expectedScriptNames[i]}"`]) - } + expect(calls.length).toEqual(1) + expect(calls[0]).toEqual([`DROP ADAPTER SCRIPT "ext-schema"."${ADAPTER_SCRIPT_NAME}"`]) }) it("fails for wrong version", () => { expect(() => { createExtension().uninstall(createMockContext(), "wrongVersion") }) @@ -156,11 +145,20 @@ describe("Row Level Security Lua", () => { }) }) - describe("getInstanceParameters()", () => { - it("is not supported", () => { - expect(() => { createExtension().getInstanceParameters(createMockContext(), "version") }) - .toThrow("Creating instances not supported") + it("fails for wrong version", () => { + expect(() => { createExtension().getInstanceParameters(createMockContext(), "wrongVersion") }) + .toThrowError(new NotFoundError(`Version 'wrongVersion' not supported, can only use '${EXTENSION_DESCRIPTION.version}'.`)) + }) + it("returns expected parameters", () => { + const actual = createExtension().getInstanceParameters(createMockContext(), EXTENSION_DESCRIPTION.version) + expect(actual).toHaveLength(6) + expect(actual[0]).toStrictEqual({ + id: "virtualSchemaName", name: "Name of the new virtual schema", required: true, type: "string" + }) + expect(actual[1]).toStrictEqual({ + id: "SCHEMA_NAME", name: "Name of the schema for which to apply row-level security", required: true, type: "string" + }) }) }) diff --git a/extension/src/extension.ts b/extension/src/extension.ts index 750a2a1..9021c7c 100644 --- a/extension/src/extension.ts +++ b/extension/src/extension.ts @@ -1,9 +1,10 @@ import { Context, ExasolExtension, NotFoundError, registerExtension } from "@exasol/extension-manager-interface"; +import { addInstance } from "./addInstance"; import { EXTENSION_DESCRIPTION } from "./extension-description"; +import { findInstallations } from "./findInstallations"; import { install } from "./install"; import { createInstanceParameters } from "./parameterDefinitions"; import { uninstall } from "./uninstall"; -import { addInstance } from "./addInstance"; export type ExtendedContext = Context & { version: string, @@ -25,7 +26,7 @@ export function createExtension(): ExasolExtension { bucketFsUploads: [], installableVersions: [{ name: EXTENSION_DESCRIPTION.version, deprecated: false, latest: true }], findInstallations(context, metadata) { - return [] + return findInstallations(extendContext(context), metadata.allScripts.rows) }, install(context, versionToInstall) { install(extendContext(context), versionToInstall) diff --git a/extension/src/findInstallations.ts b/extension/src/findInstallations.ts new file mode 100644 index 0000000..581f20d --- /dev/null +++ b/extension/src/findInstallations.ts @@ -0,0 +1,14 @@ +import { ExaScriptsRow, Installation } from "@exasol/extension-manager-interface"; +import { ADAPTER_SCRIPT_NAME, extractVersion } from "./common"; +import { ExtendedContext } from "./extension"; + +export function findInstallations(context: ExtendedContext, scripts: ExaScriptsRow[]): Installation[] { + const adapterScript = scripts.find(script => script.name === ADAPTER_SCRIPT_NAME); + if (!adapterScript) { + return [] + } + const name = `${adapterScript.schema}.${adapterScript.name}` + const versionResult = extractVersion(adapterScript.text) + const version = versionResult.type === "success" ? versionResult.result : "(unknown)" + return [{ name, version }]; +} diff --git a/extension/src/install.ts b/extension/src/install.ts index 1fe0464..c5688ce 100644 --- a/extension/src/install.ts +++ b/extension/src/install.ts @@ -1,5 +1,5 @@ import { BadRequestError } from "@exasol/extension-manager-interface"; -import { ADAPTER_SCRIPT_NAME } from "./common"; +import { ADAPTER_SCRIPT_NAME, getScriptVersionComment } from "./common"; import { ExtendedContext } from "./extension"; export function install(context: ExtendedContext, versionToInstall: string) { @@ -7,18 +7,29 @@ export function install(context: ExtendedContext, versionToInstall: string) { throw new BadRequestError(`Installing version '${versionToInstall}' not supported, try '${context.version}'.`) } const qualifiedScriptName = `"${context.extensionSchemaName}"."${ADAPTER_SCRIPT_NAME}"` - const luaModuleLoadingPreamble = `table.insert(package.searchers, - function (module_name) - local loader = package.preload[module_name] - if(loader == nil) then - error("Module " .. module_name .. " not found in package.preload.") - else - return loader - end - end - )` - const createScriptCommand = `CREATE OR REPLACE LUA ADAPTER SCRIPT ${qualifiedScriptName} AS\n${luaModuleLoadingPreamble}\n${context.luaScriptContent}\n/`; - const createCommentCommand = `COMMENT ON SCRIPT ${qualifiedScriptName} IS 'Created by extension manager for Row Level Security Lua ${context.version}'`; + const createScriptCommand = buildCreateScriptCommand(qualifiedScriptName, context.luaScriptContent); + const createCommentCommand = `COMMENT ON SCRIPT ${qualifiedScriptName} IS 'Created by Extension Manager for Row Level Security Lua ${context.version}'`; context.sqlClient.execute(createScriptCommand) context.sqlClient.execute(createCommentCommand); } + +const luaModuleLoadingPreamble = `table.insert(package.searchers, + function (module_name) + local loader = package.preload[module_name] + if(loader == nil) then + error("Module " .. module_name .. " not found in package.preload.") + else + return loader + end + end +)`; + +export function buildCreateScriptCommand(qualifiedScriptName: string, luaScriptContent: string) { + const createScriptCommand = `CREATE OR REPLACE LUA ADAPTER SCRIPT ${qualifiedScriptName} AS +${getScriptVersionComment()} +${luaModuleLoadingPreamble} +${luaScriptContent} +/`; + return createScriptCommand; +} + diff --git a/extension/src/parameterDefinitions.ts b/extension/src/parameterDefinitions.ts index be16def..51b1f30 100644 --- a/extension/src/parameterDefinitions.ts +++ b/extension/src/parameterDefinitions.ts @@ -19,10 +19,10 @@ const allParams: AllParameters = { virtualSchemaName: { id: "virtualSchemaName", name: "Name of the new virtual schema", type: "string", required: true }, // Virtual Schema parameters - schemaName: { id: "SCHEMA_NAME", name: "Name of the schema for which to apply row-level security", type: "string", required: true, multiline: false }, - excludedCapabilities: { id: "EXCLUDED_CAPABILITIES", name: "Comma-separated list of capabilities that should not be pushed-down, e.g. 'SELECTLIST_PROJECTION, ORDER_BY_COLUMN'", type: "string", required: false, multiline: false }, - tableFilter: { id: "TABLE_FILTER", name: "Comma-separated list of tables that should be added to the schema, e.g. 'ORDERS, ORDER_ITEMS, PRODUCTS'. If this is empty, all tables are added.", type: "string", required: false, multiline: false }, - debugAddress: { id: "DEBUG_ADDRESS", name: "Network address and port to which to send debug output, e.g. '192.168.179.38:3000'", type: "string", required: false, multiline: false }, + schemaName: { id: "SCHEMA_NAME", name: "Name of the schema for which to apply row-level security", type: "string", required: true }, + excludedCapabilities: { id: "EXCLUDED_CAPABILITIES", name: "Comma-separated list of capabilities that should not be pushed-down, e.g. 'SELECTLIST_PROJECTION, ORDER_BY_COLUMN'", type: "string", required: false }, + tableFilter: { id: "TABLE_FILTER", name: "Comma-separated list of tables that should be added to the schema, e.g. 'ORDERS, ORDER_ITEMS, PRODUCTS'. If this is empty, all tables are added.", type: "string", required: false }, + debugAddress: { id: "DEBUG_ADDRESS", name: "Network address and port to which to send debug output, e.g. '192.168.179.38:3000'", type: "string", required: false }, logLevel: { id: "LOG_LEVEL", name: "Log level for debug output", type: "select", required: false, options: LOG_LEVEL_OPTIONS }, }; diff --git a/extension/src/uninstall.ts b/extension/src/uninstall.ts index 0639113..2d161f0 100644 --- a/extension/src/uninstall.ts +++ b/extension/src/uninstall.ts @@ -1,5 +1,18 @@ +import { NotFoundError } from "@exasol/extension-manager-interface"; +import { ADAPTER_SCRIPT_NAME } from "./common"; import { ExtendedContext } from "./extension"; export function uninstall(context: ExtendedContext, versionToUninstall: string) { + if (context.version !== versionToUninstall) { + throw new NotFoundError(`Uninstalling version '${versionToUninstall}' not supported, try '${context.version}'.`) + } + function extensionSchemaExists(): boolean { + const result = context.sqlClient.query("SELECT 1 FROM SYS.EXA_ALL_SCHEMAS WHERE SCHEMA_NAME=?", context.extensionSchemaName) + return result.rows.length > 0 + } + + if (extensionSchemaExists()) { // Drop commands fail when schema does not exist. + context.sqlClient.execute(`DROP ADAPTER SCRIPT "${context.extensionSchemaName}"."${ADAPTER_SCRIPT_NAME}"`) + } } \ No newline at end of file diff --git a/src/test/java/com/exasol/rls/extension/ExtensionIT.java b/src/test/java/com/exasol/rls/extension/ExtensionIT.java index 6f04acd..fb4e2f6 100644 --- a/src/test/java/com/exasol/rls/extension/ExtensionIT.java +++ b/src/test/java/com/exasol/rls/extension/ExtensionIT.java @@ -4,12 +4,15 @@ import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.*; import static org.junit.jupiter.api.Assertions.assertAll; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; import java.io.FileNotFoundException; +import java.net.URISyntaxException; import java.nio.file.Path; import java.nio.file.Paths; import java.sql.*; import java.util.List; +import java.util.Map; import java.util.concurrent.TimeoutException; import org.hamcrest.Matcher; @@ -17,12 +20,13 @@ import com.exasol.bucketfs.BucketAccessException; import com.exasol.dbbuilder.dialects.Table; +import com.exasol.dbbuilder.dialects.exasol.AdapterScript.Language; import com.exasol.dbbuilder.dialects.exasol.ExasolObjectFactory; import com.exasol.dbbuilder.dialects.exasol.ExasolSchema; import com.exasol.exasoltestsetup.ExasolTestSetup; import com.exasol.exasoltestsetup.ExasolTestSetupFactory; import com.exasol.extensionmanager.client.model.*; -import com.exasol.extensionmanager.itest.ExtensionManagerSetup; +import com.exasol.extensionmanager.itest.*; import com.exasol.extensionmanager.itest.builder.ExtensionBuilder; import com.exasol.mavenprojectversiongetter.MavenProjectVersionGetter; @@ -30,7 +34,7 @@ class ExtensionIT { private static final String PREVIOUS_VERSION = "2.6.2"; private static final Path EXTENSION_SOURCE_DIR = Paths.get("extension").toAbsolutePath(); private static final String EXTENSION_ID = "row-level-security-extension.js"; - private static final int EXPECTED_PARAMETER_COUNT = 10; + private static final int EXPECTED_PARAMETER_COUNT = 6; private static final String PROJECT_VERSION = MavenProjectVersionGetter.getCurrentProjectVersion(); private static ExasolTestSetup exasolTestSetup; private static ExtensionManagerSetup setup; @@ -78,10 +82,79 @@ void listExtensions() { } @Test - void listInstallations() { + void listInstallationsEmpty() { assertThat(setup.client().getInstallations(), emptyIterable()); } + @Test + void listInstallations_ignoresWrongScriptNames() { + createAdapter("wrong_adapter_name"); + final List installations = setup.client().getInstallations(); + assertThat(installations, hasSize(0)); + } + + @Test + void listInstallations_findsMatchingScripts() { + createAdapter("RLS_ADAPTER"); + final List installations = setup.client().getInstallations(); + assertAll(() -> assertThat(installations, hasSize(1)), // + () -> assertThat(installations.get(0).getName(), + equalTo(ExtensionManagerSetup.EXTENSION_SCHEMA_NAME + ".RLS_ADAPTER")), + () -> assertThat(installations.get(0).getVersion(), equalTo("dummy.version"))); + } + + @Test + void listInstallations_findsOwnInstallation() { + setup.client().install(); + final List installations = setup.client().getInstallations(); + assertAll(() -> assertThat(installations, hasSize(1)), // + () -> assertThat(installations.get(0).getName(), + equalTo(ExtensionManagerSetup.EXTENSION_SCHEMA_NAME + ".RLS_ADAPTER")), + () -> assertThat(installations.get(0).getVersion(), equalTo(PROJECT_VERSION))); + } + + @Test + void install_createsScripts() { + setup.client().install(); + assertScriptsExist(); + } + + @Test + void install_worksIfCalledTwice() { + setup.client().install(); + setup.client().install(); + assertScriptsExist(); + } + + @Test + void install_failsForUnsupportedVersion() { + final ExtensionManagerClient client = setup.client(); + client.assertRequestFails(() -> client.install("unsupported"), + equalTo("Installing version 'unsupported' not supported, try '" + PROJECT_VERSION + "'."), + equalTo(400)); + setup.exasolMetadata().assertNoScripts(); + } + + @Test + void getExtensionDetailsFailsForUnknownVersion() { + setup.client().assertRequestFails(() -> setup.client().getExtensionDetails("unknownVersion"), + equalTo("Version 'unknownVersion' not supported, can only use '" + PROJECT_VERSION + "'."), + equalTo(404)); + } + + @Test + void getExtensionDetailsSuccess() { + final ExtensionDetailsResponse extensionDetails = setup.client().getExtensionDetails(PROJECT_VERSION); + final List parameters = extensionDetails.getParameterDefinitions(); + final ParamDefinition param1 = new ParamDefinition().id("virtualSchemaName") + .name("Name of the new virtual schema").definition(Map.of("id", "virtualSchemaName", "name", + "Name of the new virtual schema", "required", true, "type", "string")); + assertAll(() -> assertThat(extensionDetails.getId(), equalTo(EXTENSION_ID)), + () -> assertThat(extensionDetails.getVersion(), equalTo(PROJECT_VERSION)), + () -> assertThat(parameters, hasSize(EXPECTED_PARAMETER_COUNT)), + () -> assertThat(parameters.get(0), equalTo(param1))); + } + @Test void installWrongVersionFails() { setup.client().assertRequestFails(() -> setup.client().install("0.0.0"), @@ -96,30 +169,149 @@ void installExtensions() { } @Test - void createInstance() throws SQLException { + void createInstanceFailsWithoutRequiredParameters() { + final ExtensionManagerClient client = setup.client(); + client.install(); + client.assertRequestFails(() -> client.createInstance(List.of()), startsWith( + "invalid parameters: Failed to validate parameter 'Name of the new virtual schema': This is a required parameter."), + equalTo(400)); + } + + @Test + void uninstall_failsForUnknownVersion() { + setup.client().assertRequestFails(() -> setup.client().uninstall("unknownVersion"), + equalTo("Uninstalling version 'unknownVersion' not supported, try '" + PROJECT_VERSION + "'."), + equalTo(404)); + } + + @Test + void uninstall_succeedsForNonExistingInstallation() { + assertDoesNotThrow(() -> setup.client().uninstall()); + } + + @Test + void uninstall_removesAdapters() { + setup.client().install(); + assertAll(() -> assertScriptsExist(), // + () -> assertThat(setup.client().getInstallations(), hasSize(1))); + setup.client().uninstall(PROJECT_VERSION); + assertAll(() -> assertThat(setup.client().getInstallations(), is(empty())), + () -> setup.exasolMetadata().assertNoScripts()); + } + + @Test + void upgradeFailsWhenNotInstalled() { + setup.client().assertRequestFails(() -> setup.client().upgrade(), + "extension is not installed, the following scripts are missing: S3_FILES_ADAPTER, IMPORT_FROM_S3_DOCUMENT_FILES", + 404); + } + + @Test + void upgradeFailsWhenAlreadyUpToDate() { + setup.client().install(); + setup.client().assertRequestFails(() -> setup.client().upgrade(), + "Extension is already installed in latest version " + PROJECT_VERSION, 412); + } + + @Test + void upgradeFromPreviousVersion() throws InterruptedException, BucketAccessException, TimeoutException, + FileNotFoundException, URISyntaxException { + final PreviousExtensionVersion previousVersion = createPreviousVersion(); + previousVersion.prepare(); + previousVersion.install(); + final String virtualTable = createVirtualSchema(previousVersion.getExtensionId(), PREVIOUS_VERSION); + verifyVirtualTableContainsData(virtualTable); + assertInstalledVersion("EXA_EXTENSIONS.RLS_ADAPTER", PREVIOUS_VERSION); + previousVersion.upgrade(); + assertInstalledVersion("EXA_EXTENSIONS.RLS_ADAPTER", PROJECT_VERSION); + verifyVirtualTableContainsData(virtualTable); + } + + private PreviousExtensionVersion createPreviousVersion() { + return setup.previousVersionManager().newVersion().currentVersion(PROJECT_VERSION) // + .previousVersion(PREVIOUS_VERSION) // + .extensionFileName(EXTENSION_ID) // + .project("row-level-security-lua") // + .build(); + } + + private void assertInstalledVersion(final String expectedName, final String expectedVersion) { + final List installations = setup.client().getInstallations(); + final InstallationsResponseInstallation expectedInstallation = new InstallationsResponseInstallation() + .name(expectedName).version(expectedVersion); + // The extension is installed twice (previous and current version), so each one returns the same installation. + assertAll(() -> assertThat(installations, hasSize(2)), + () -> assertThat(installations.get(0), equalTo(expectedInstallation)), + () -> assertThat(installations.get(1), equalTo(expectedInstallation))); + } + + @Test + void createInstance() { setup.client().install(); + final String virtualTableName = createVirtualSchema(); + verifyVirtualTableContainsData(virtualTableName); + } + + private String createVirtualSchema() { + return createVirtualSchema(EXTENSION_ID, PROJECT_VERSION); + } + + private String createVirtualSchema(final String extensionId, final String extensionVersion) { final String virtualSchemaName = "RLS_SCHEMA"; + final Table baseTable = createBaseTable(); + createInstance(virtualSchemaName, baseTable); + return virtualSchemaName + "." + baseTable.getName(); + } + + private Table createBaseTable() { + final ExasolSchema baseSchema = this.dbObjectFactory.createSchema("BASE_SCHEMA"); + return baseSchema.createTable("TAB", "ID", "SMALLINT", "NAME", "varchar(10)").insert(1, "a").insert(2, "b") + .insert(3, "c"); + } + + private void createInstance(final String virtualSchemaName, final Table baseTable) { + createInstance(EXTENSION_ID, PROJECT_VERSION, virtualSchemaName, baseTable); + } + + private void createInstance(final String extensionId, final String extensionVersion, final String virtualSchemaName, + final Table baseTable) { setup.addVirtualSchemaToCleanupQueue(virtualSchemaName); - final ExasolSchema schema = this.dbObjectFactory.createSchema("BASE_SCHEMA"); - final Table table = schema.createTable("TAB", "ID", "SMALLINT", "NAME", "varchar(10)").insert(1, "a") - .insert(2, "b").insert(3, "c"); - setup.client().createInstance( - List.of(param("virtualSchemaName", virtualSchemaName), param("SCHEMA_NAME", schema.getName()))); - extracted(virtualSchemaName, table); + final String instanceName = setup.client().createInstance(List.of(param("virtualSchemaName", virtualSchemaName), + param("SCHEMA_NAME", baseTable.getParent().getName()))); + assertThat(instanceName, equalTo(virtualSchemaName)); } - private void extracted(final String virtualSchemaName, final Table table) throws SQLException { - assertResult("select * from " + virtualSchemaName + "." + table.getName() + " order by id", + private void verifyVirtualTableContainsData(final String tableName) { + assertResult("select * from " + tableName + " order by id", table("INTEGER", "VARCHAR").row(1, "a").row(2, "b").row(3, "c").matches()); } - private void assertResult(final String sql, final Matcher matcher) throws SQLException { + private void assertResult(final String sql, final Matcher matcher) { try (Statement statement = connection.createStatement()) { assertThat(statement.executeQuery(sql), matcher); + } catch (final SQLException exception) { + throw new AssertionError("Failed to execute query '" + sql + "': " + exception.getMessage(), exception); } } private ParameterValue param(final String name, final String value) { return new ParameterValue().name(name).value(value); } + + private void assertScriptsExist() { + final String comment = "Created by Extension Manager for Row Level Security Lua " + PROJECT_VERSION; + setup.exasolMetadata() + .assertScript(table() + .row("RLS_ADAPTER", "ADAPTER", null, null, + allOf(containsString("CREATE LUA ADAPTER SCRIPT \"RLS_ADAPTER\" AS"), // + containsString("-- RLS Lua version " + PROJECT_VERSION)), + comment) // + .matches()); + } + + private void createAdapter(final String adapterScriptName) { + final ExasolSchema schema = setup.createExtensionSchema(); + schema.createAdapterScriptBuilder(adapterScriptName).language(Language.LUA) + .content("-- RLS Lua version dummy.version").build(); + } } From 9d5f5e27caa9fa10e7989618aac64c56e4c1e461 Mon Sep 17 00:00:00 2001 From: Christoph Pirkl Date: Wed, 6 Sep 2023 14:51:41 +0200 Subject: [PATCH 16/21] Implement delete instance & upgrade --- extension/src/addInstance.ts | 14 +- extension/src/common.ts | 2 +- extension/src/deleteInstance.ts | 15 ++ extension/src/extension.test.ts | 179 +++++++++++++----- extension/src/extension.ts | 20 +- extension/src/findInstallations.ts | 3 + extension/src/findInstances.ts | 13 ++ extension/src/install.ts | 4 +- extension/src/test-utils.ts | 9 - extension/src/upgrade.ts | 25 +++ pom.xml | 2 +- .../com/exasol/rls/extension/ExtensionIT.java | 46 ++++- 12 files changed, 245 insertions(+), 87 deletions(-) create mode 100644 extension/src/deleteInstance.ts create mode 100644 extension/src/findInstances.ts create mode 100644 extension/src/upgrade.ts diff --git a/extension/src/addInstance.ts b/extension/src/addInstance.ts index 2f1bceb..0531955 100644 --- a/extension/src/addInstance.ts +++ b/extension/src/addInstance.ts @@ -1,5 +1,5 @@ -import { Instance, NotFoundError, Parameter, ParameterValues } from "@exasol/extension-manager-interface"; -import { ADAPTER_SCRIPT_NAME, convertSchemaNameToInstanceId } from "./common"; +import { BadRequestError, Instance, NotFoundError, Parameter, ParameterValues } from "@exasol/extension-manager-interface"; +import { ADAPTER_SCRIPT_NAME, EXTENSION_NAME, convertSchemaNameToInstanceId } from "./common"; import { ExtendedContext } from "./extension"; import { getAllParameterDefinitions } from "./parameterDefinitions"; @@ -19,7 +19,7 @@ export function addInstance(context: ExtendedContext, version: string, paramValu const config = buildVirtualSchemaConfig(paramValues) const createVirtualSchemaStmt = createVirtualSchemaStatement(context.extensionSchemaName, config); context.sqlClient.execute(createVirtualSchemaStmt); - const comment = `Created by extension manager for row-level-security-lua ${escapeSingleQuotes(config.virtualSchemaName)} (version ${context.version})`; + const comment = `Created by Extension Manager for ${EXTENSION_NAME} version ${context.version}`; context.sqlClient.execute(`COMMENT ON SCHEMA "${config.virtualSchemaName}" IS '${comment}'`); return { id: convertSchemaNameToInstanceId(config.virtualSchemaName), name: config.virtualSchemaName } } @@ -41,7 +41,7 @@ function getParameterValue(paramValues: ParameterValues, definition: Parameter): if (value) { return value } - throw new Error(`Missing parameter "${definition.id}"`) + throw new BadRequestError(`Missing parameter "${definition.id}"`) } function getOptionalParameterValue(paramValues: ParameterValues, definition: Parameter): string | undefined { @@ -58,7 +58,7 @@ function createVirtualSchemaStatement(adapterSchema: string, config: VirtualSche if (config.excludedCapabilities) { stmt += ` EXCLUDED_CAPABILITIES='${config.excludedCapabilities}'` } - if (config.excludedCapabilities) { + if (config.tableFilter) { stmt += ` TABLE_FILTER='${config.tableFilter}'` } if (config.debugAddress) { @@ -69,7 +69,3 @@ function createVirtualSchemaStatement(adapterSchema: string, config: VirtualSche } return stmt; } - -function escapeSingleQuotes(value: string): string { - return value.replace(/'/g, "''") -} diff --git a/extension/src/common.ts b/extension/src/common.ts index 52f2b9e..ca371a3 100644 --- a/extension/src/common.ts +++ b/extension/src/common.ts @@ -2,7 +2,7 @@ import { Result, failureResult, successResult } from "@exasol/extension-manager- import { EXTENSION_DESCRIPTION } from "./extension-description"; export const ADAPTER_SCRIPT_NAME = "RLS_ADAPTER" - +export const EXTENSION_NAME = "Row Level Security Lua" function identity(arg: string): string { return arg; diff --git a/extension/src/deleteInstance.ts b/extension/src/deleteInstance.ts new file mode 100644 index 0000000..d731dd5 --- /dev/null +++ b/extension/src/deleteInstance.ts @@ -0,0 +1,15 @@ +import { NotFoundError } from "@exasol/extension-manager-interface"; +import { convertInstanceIdToSchemaName } from "./common"; +import { ExtendedContext } from "./extension"; + +export function deleteInstance(context: ExtendedContext, version: string, instanceId: string): void { + if (context.version !== version) { + throw new NotFoundError(`Version '${version}' not supported, can only use '${context.version}'.`) + } + const schemaName = convertInstanceIdToSchemaName(instanceId); + context.sqlClient.execute(dropVirtualSchemaStatement(schemaName)); +} + +function dropVirtualSchemaStatement(schemaName: string): string { + return `DROP VIRTUAL SCHEMA IF EXISTS "${schemaName}" CASCADE`; +} diff --git a/extension/src/extension.test.ts b/extension/src/extension.test.ts index 1ec320f..d2badab 100644 --- a/extension/src/extension.test.ts +++ b/extension/src/extension.test.ts @@ -1,12 +1,12 @@ -import { ExaMetadata, Installation, NotFoundError, PreconditionFailedError } from '@exasol/extension-manager-interface'; +import { BadRequestError, ExaMetadata, Installation, Instance, NotFoundError, ParameterValue, PreconditionFailedError, Row } from '@exasol/extension-manager-interface'; import { failureResult, successResult } from '@exasol/extension-manager-interface/dist/base/common'; import { ExaScriptsRow } from '@exasol/extension-manager-interface/dist/exasolSchema'; import { describe, expect, it } from '@jest/globals'; -import { ADAPTER_SCRIPT_NAME, extractVersion } from './common'; +import { ADAPTER_SCRIPT_NAME, EXTENSION_NAME, extractVersion } from './common'; import { createExtension } from "./extension"; import { EXTENSION_DESCRIPTION } from './extension-description'; import { buildCreateScriptCommand } from './install'; -import { createMockContext, getInstalledExtension, scriptWithVersion } from './test-utils'; +import { createMockContext, getInstalledExtension, script } from './test-utils'; const currentVersion = EXTENSION_DESCRIPTION.version @@ -107,7 +107,7 @@ describe("Row Level Security Lua", () => { expect(createScriptCommand).toContain(`CREATE OR REPLACE LUA ADAPTER SCRIPT \"ext-schema\".\"RLS_ADAPTER\" AS -- RLS Lua version 1.5.0 table.insert(package.searchers,`) - const expectedComment = `Created by Extension Manager for Row Level Security Lua ${EXTENSION_DESCRIPTION.version}` + const expectedComment = `Created by Extension Manager for Row Level Security Lua version ${EXTENSION_DESCRIPTION.version}` expect(createCommentCommand).toEqual(`COMMENT ON SCRIPT "ext-schema"."${ADAPTER_SCRIPT_NAME}" IS '${expectedComment}'`) }) it("fails for wrong version", () => { @@ -163,74 +163,153 @@ table.insert(package.searchers,`) }) describe("addInstance()", () => { - it("is not supported", () => { - expect(() => { createExtension().addInstance(createMockContext(), "version", { values: [] }) }) - .toThrow("Creating instances not supported") + it("fails for missing schema name", () => { + expect(() => createExtension().addInstance(createMockContext(), currentVersion, { values: [] })) + .toThrowError(new BadRequestError(`Missing parameter "virtualSchemaName"`)) + }) + it("fails for missing base schema name", () => { + expect(() => createExtension().addInstance(createMockContext(), currentVersion, { values: [{ name: "virtualSchemaName", value: "new_vs" }] })) + .toThrowError(new BadRequestError(`Missing parameter "SCHEMA_NAME"`)) + }) + + it("executes expected statements", () => { + const context = createMockContext(); + const parameters = [{ name: "virtualSchemaName", value: "NEW_RLS_VS" }, { name: "SCHEMA_NAME", value: "baseSchema" }] + const instance = createExtension().addInstance(context, currentVersion, { values: parameters }); + expect(instance.name).toBe("NEW_RLS_VS") + const calls = context.mocks.sqlExecute.mock.calls + expect(calls.length).toBe(2) + + expect(calls[0]).toEqual([`CREATE VIRTUAL SCHEMA "NEW_RLS_VS" USING "ext-schema"."RLS_ADAPTER" WITH SCHEMA_NAME = 'baseSchema'`]) + const comment = `Created by Extension Manager for ${EXTENSION_NAME} version ${currentVersion}` + expect(calls[1]).toEqual([`COMMENT ON SCHEMA "NEW_RLS_VS" IS '${comment}'`]) + }) + + describe("uses optional parameters", () => { + const tests: { name: string, params: ParameterValue[], expectedScript: string }[] = [ + { name: "no optional param", params: [], expectedScript: "" }, + { name: "excluded capabilities", params: [{ name: "EXCLUDED_CAPABILITIES", value: "cap1, cap2" }], expectedScript: " EXCLUDED_CAPABILITIES='cap1, cap2'" }, + { name: "table filter", params: [{ name: "TABLE_FILTER", value: "tab1, tab2" }], expectedScript: " TABLE_FILTER='tab1, tab2'" }, + { name: "quoted table filter", params: [{ name: "TABLE_FILTER", value: `"tab1", tab2` }], expectedScript: ` TABLE_FILTER='"tab1", tab2'` }, + { name: "debug address", params: [{ name: "DEBUG_ADDRESS", value: "123.45.6.78:3000" }], expectedScript: " DEBUG_ADDRESS='123.45.6.78:3000'" }, + { name: "log level", params: [{ name: "LOG_LEVEL", value: "TRACE" }], expectedScript: " LOG_LEVEL='TRACE'" }, + { + name: "all optional params", params: [{ name: "EXCLUDED_CAPABILITIES", value: "cap1, cap2" }, { name: "TABLE_FILTER", value: "tab1, tab2" }, + { name: "DEBUG_ADDRESS", value: "123.45.6.78:3000" }, { name: "LOG_LEVEL", value: "TRACE" }], + expectedScript: " EXCLUDED_CAPABILITIES='cap1, cap2' TABLE_FILTER='tab1, tab2' DEBUG_ADDRESS='123.45.6.78:3000' LOG_LEVEL='TRACE'" + }, + ] + tests.forEach(test => it(test.name, () => { + const context = createMockContext(); + const parameters = [{ name: "virtualSchemaName", value: "NEW_RLS_VS" }, { name: "SCHEMA_NAME", value: "baseSchema" }] + test.params.forEach(p => parameters.push(p)) + const instance = createExtension().addInstance(context, currentVersion, { values: parameters }); + expect(instance.name).toBe("NEW_RLS_VS") + const calls = context.mocks.sqlExecute.mock.calls + expect(calls.length).toBe(2) + expect(calls[0]).toEqual([`CREATE VIRTUAL SCHEMA "NEW_RLS_VS" USING "ext-schema"."RLS_ADAPTER" WITH SCHEMA_NAME = 'baseSchema'` + test.expectedScript]) + })) + }) + + it("returns id and name", () => { + const context = createMockContext(); + const parameters = [{ name: "virtualSchemaName", value: "NEW_RLS_VS" }, { name: "SCHEMA_NAME", value: "baseSchema" }] + const instance = createExtension().addInstance(context, currentVersion, { values: parameters }); + expect(instance).toStrictEqual({ id: "NEW_RLS_VS", name: "NEW_RLS_VS" }) + }) + + it("fails for wrong version", () => { + expect(() => { createExtension().addInstance(createMockContext(), "wrongVersion", { values: [] }) }) + .toThrow(`Version 'wrongVersion' not supported, can only use '${currentVersion}'.`) }) }) describe("findInstances()", () => { - it("is not supported", () => { - expect(() => { createExtension().findInstances(createMockContext(), "version") }) - .toThrow("Finding instances not supported") + function findInstances(rows: Row[]): Instance[] { + const context = createMockContext(); + context.mocks.sqlQuery.mockReturnValue({ columns: [], rows }); + return createExtension().findInstances(context, "version") + } + it("returns empty list for empty metadata", () => { + expect(findInstances([])).toEqual([]) + }) + it("returns single instance", () => { + expect(findInstances([["rls_vs"]])) + .toEqual([{ id: "rls_vs", name: "rls_vs" }]) + }) + it("returns multiple instance", () => { + expect(findInstances([["vs1"], ["vs2"], ["vs3"]])) + .toEqual([{ id: "vs1", name: "vs1" }, { id: "vs2", name: "vs2" }, { id: "vs3", name: "vs3" }]) + }) + it("filters by schema and script name", () => { + const context = createMockContext(); + context.mocks.sqlQuery.mockReturnValue({ columns: [], rows: [] }); + createExtension().findInstances(context, "version") + const queryCalls = context.mocks.sqlQuery.mock.calls + expect(queryCalls.length).toEqual(1) + expect(queryCalls[0]).toEqual(["SELECT SCHEMA_NAME FROM SYS.EXA_ALL_VIRTUAL_SCHEMAS WHERE ADAPTER_SCRIPT = ?||'.'||? ORDER BY SCHEMA_NAME", "ext-schema", "RLS_ADAPTER"]) }) }) describe("deleteInstance()", () => { - it("is not supported", () => { - expect(() => { createExtension().deleteInstance(createMockContext(), "version", "instId") }) - .toThrow("Deleting instances not supported") + describe("deleteInstance()", () => { + it("drops connection and virtual schema", () => { + const context = createMockContext(); + createExtension().deleteInstance(context, currentVersion, "instId") + const executeCalls = context.mocks.sqlExecute.mock.calls + expect(executeCalls.length).toEqual(1) + expect(executeCalls[0]).toEqual([`DROP VIRTUAL SCHEMA IF EXISTS "instId" CASCADE`]) + }) + it("fails for wrong version", () => { + expect(() => { createExtension().deleteInstance(createMockContext(), "wrongVersion", "instId") }) + .toThrow(`Version 'wrongVersion' not supported, can only use '${currentVersion}'.`) + }) }) }) describe("readInstanceParameterValues()", () => { it("is not supported", () => { expect(() => { createExtension().readInstanceParameterValues(createMockContext(), "version", "instId") }) - .toThrow("Reading instance parameter values not supported") + .toThrowError(new NotFoundError("Reading instance parameter values not supported")) }) }) describe("upgrade()", () => { + + function scriptWithVersion(name: string, version: string): ExaScriptsRow { + return script({ name, text: "-- RLS Lua version " + version }) + } + const version = "1.2.3" - const importPath = scriptWithVersion("IMPORT_PATH", version) - const importMetadata = scriptWithVersion("IMPORT_METADATA", version) - const importFiles = scriptWithVersion("IMPORT_FILES", version) - const exportPath = scriptWithVersion("EXPORT_PATH", version) - const exportTable = scriptWithVersion("EXPORT_TABLE", version) - const allScripts = [importPath, importMetadata, importFiles, exportPath, exportTable] - - describe("validateInstalledScripts()", () => { - it("success", () => { + const adapterScript = scriptWithVersion("RLS_ADAPTER", version) + + it("success", () => { + const context = createMockContext() + context.mocks.simulateScripts([adapterScript]) + expect(createExtension().upgrade(context)).toStrictEqual({ previousVersion: version, newVersion: currentVersion }) + const executeCalls = context.mocks.sqlExecute.mock.calls + expect(executeCalls.length).toBe(2) + }) + describe("failure", () => { + const tests: { name: string; scripts: ExaScriptsRow[], expectedMessage: string }[] = [ + { name: "no script", scripts: [], expectedMessage: "Adapter script 'RLS_ADAPTER' is not installed" }, + { + name: "invalid version", scripts: [script({ name: "RLS_ADAPTER", text: "invalid script" })], + expectedMessage: `Failed to extract version from adapter script schema.RLS_ADAPTER: version not found in script text 'invalid script'` + }, + { + name: "version already up-to-date", scripts: [scriptWithVersion("RLS_ADAPTER", currentVersion)], + expectedMessage: `Extension is already installed in latest version ${currentVersion}` + }, + ] + tests.forEach(test => it(test.name, () => { const context = createMockContext() - context.mocks.simulateScripts(allScripts) - expect(createExtension().upgrade(context)).toStrictEqual({ - previousVersion: version, newVersion: currentVersion - }) + context.mocks.simulateScripts(test.scripts) + expect(() => createExtension().upgrade(context)).toThrowError(new PreconditionFailedError(test.expectedMessage)) const executeCalls = context.mocks.sqlExecute.mock.calls - expect(executeCalls.length).toBe(10) - }) - describe("failure", () => { - const tests: { name: string; scripts: ExaScriptsRow[], expectedMessage: string }[] = [ - { name: "no script", scripts: [], expectedMessage: "Not all required scripts are installed: Validation failed: Script 'IMPORT_PATH' is missing, Script 'IMPORT_METADATA' is missing, Script 'IMPORT_FILES' is missing, Script 'EXPORT_PATH' is missing, Script 'EXPORT_TABLE' is missing" }, - { name: "one missing script", scripts: [importPath, importMetadata, importFiles, exportPath], expectedMessage: "Not all required scripts are installed: Validation failed: Script 'EXPORT_TABLE' is missing" }, - { name: "inconsistent versions", scripts: [importPath, importMetadata, importFiles, exportPath, scriptWithVersion("EXPORT_TABLE", "1.2.4")], expectedMessage: "Failed to validate script versions: Not all scripts use the same version. Found 2 different versions: '1.2.3, 1.2.4'" }, - { - name: "version already up-to-date", scripts: [ - scriptWithVersion("IMPORT_PATH", currentVersion), scriptWithVersion("IMPORT_METADATA", currentVersion), - scriptWithVersion("IMPORT_FILES", currentVersion), scriptWithVersion("EXPORT_PATH", currentVersion), scriptWithVersion("EXPORT_TABLE", currentVersion) - ], - expectedMessage: `Extension is already installed in latest version ${currentVersion}` - }, - ] - tests.forEach(test => it(test.name, () => { - const context = createMockContext() - context.mocks.simulateScripts(test.scripts) - expect(() => createExtension().upgrade(context)).toThrowError(new PreconditionFailedError(test.expectedMessage)) - const executeCalls = context.mocks.sqlExecute.mock.calls - expect(executeCalls.length).toBe(0) - })) - }) + expect(executeCalls.length).toBe(0) + })) }) }) }) diff --git a/extension/src/extension.ts b/extension/src/extension.ts index 9021c7c..a214ee7 100644 --- a/extension/src/extension.ts +++ b/extension/src/extension.ts @@ -1,10 +1,14 @@ import { Context, ExasolExtension, NotFoundError, registerExtension } from "@exasol/extension-manager-interface"; import { addInstance } from "./addInstance"; +import { EXTENSION_NAME } from "./common"; +import { deleteInstance } from "./deleteInstance"; import { EXTENSION_DESCRIPTION } from "./extension-description"; import { findInstallations } from "./findInstallations"; +import { findInstances } from "./findInstances"; import { install } from "./install"; import { createInstanceParameters } from "./parameterDefinitions"; import { uninstall } from "./uninstall"; +import { upgrade } from "./upgrade"; export type ExtendedContext = Context & { version: string, @@ -20,7 +24,7 @@ function extendContext(context: Context): ExtendedContext { } export function createExtension(): ExasolExtension { const baseExtension: ExasolExtension = { - name: "Row Level Security Lua", + name: EXTENSION_NAME, description: "Lua implementation of Exasol's row-level-security", category: "security", bucketFsUploads: [], @@ -35,10 +39,10 @@ export function createExtension(): ExasolExtension { uninstall(extendContext(context), versionToUninstall) }, upgrade(context) { - return { previousVersion: "", newVersion: "" } + return upgrade(extendContext(context)) }, - findInstances(context, version) { - return [] + findInstances(context, _version) { + return findInstances(extendContext(context)) }, getInstanceParameters(context, version) { if (EXTENSION_DESCRIPTION.version !== version) { @@ -49,11 +53,11 @@ export function createExtension(): ExasolExtension { addInstance(context, version, params) { return addInstance(extendContext(context), version, params); }, - deleteInstance(context, extensionVersion, instanceId) { - + deleteInstance(context, version, instanceId) { + deleteInstance(extendContext(context), version, instanceId) }, - readInstanceParameterValues(context, extensionVersion, instanceId) { - return { values: [] } + readInstanceParameterValues(_context, _version, _instanceId) { + throw new NotFoundError("Reading instance parameter values not supported") }, } return baseExtension diff --git a/extension/src/findInstallations.ts b/extension/src/findInstallations.ts index 581f20d..44ba6f9 100644 --- a/extension/src/findInstallations.ts +++ b/extension/src/findInstallations.ts @@ -9,6 +9,9 @@ export function findInstallations(context: ExtendedContext, scripts: ExaScriptsR } const name = `${adapterScript.schema}.${adapterScript.name}` const versionResult = extractVersion(adapterScript.text) + if (versionResult.type === "failure") { + console.warn(versionResult.message) + } const version = versionResult.type === "success" ? versionResult.result : "(unknown)" return [{ name, version }]; } diff --git a/extension/src/findInstances.ts b/extension/src/findInstances.ts new file mode 100644 index 0000000..19075bd --- /dev/null +++ b/extension/src/findInstances.ts @@ -0,0 +1,13 @@ +import { Instance } from "@exasol/extension-manager-interface"; +import { ADAPTER_SCRIPT_NAME } from "./common"; +import { ExtendedContext } from "./extension"; + +export function findInstances(context: ExtendedContext): Instance[] { + const result = context.sqlClient.query("SELECT SCHEMA_NAME FROM SYS.EXA_ALL_VIRTUAL_SCHEMAS" + + " WHERE ADAPTER_SCRIPT = ?||'.'||? " + + " ORDER BY SCHEMA_NAME", context.extensionSchemaName, ADAPTER_SCRIPT_NAME) + return result.rows.map(row => { + const schemaName = row[0]; + return { id: schemaName, name: schemaName } + }) +} diff --git a/extension/src/install.ts b/extension/src/install.ts index c5688ce..324b29f 100644 --- a/extension/src/install.ts +++ b/extension/src/install.ts @@ -1,5 +1,5 @@ import { BadRequestError } from "@exasol/extension-manager-interface"; -import { ADAPTER_SCRIPT_NAME, getScriptVersionComment } from "./common"; +import { ADAPTER_SCRIPT_NAME, EXTENSION_NAME, getScriptVersionComment } from "./common"; import { ExtendedContext } from "./extension"; export function install(context: ExtendedContext, versionToInstall: string) { @@ -8,7 +8,7 @@ export function install(context: ExtendedContext, versionToInstall: string) { } const qualifiedScriptName = `"${context.extensionSchemaName}"."${ADAPTER_SCRIPT_NAME}"` const createScriptCommand = buildCreateScriptCommand(qualifiedScriptName, context.luaScriptContent); - const createCommentCommand = `COMMENT ON SCRIPT ${qualifiedScriptName} IS 'Created by Extension Manager for Row Level Security Lua ${context.version}'`; + const createCommentCommand = `COMMENT ON SCRIPT ${qualifiedScriptName} IS 'Created by Extension Manager for ${EXTENSION_NAME} version ${context.version}'`; context.sqlClient.execute(createScriptCommand) context.sqlClient.execute(createCommentCommand); } diff --git a/extension/src/test-utils.ts b/extension/src/test-utils.ts index 3f24dbd..637e768 100644 --- a/extension/src/test-utils.ts +++ b/extension/src/test-utils.ts @@ -56,12 +56,3 @@ export function createMockContext(): ContextMock { export function script({ schema = "schema", name = "name", inputType, resultType, type = "", text = "", comment }: Partial): ExaScriptsRow { return { schema, name, inputType, resultType, type, text, comment } } -export function adapterScript({ name = "S3_FILES_ADAPTER", type = "ADAPTER", text = "adapter script" }: Partial): ExaScriptsRow { - return script({ name, type, text }) -} -export function importScript({ name = "IMPORT_FROM_S3_DOCUMENT_FILES", type = "UDF", inputType = "SET", resultType = "EMITS" }: Partial): ExaScriptsRow { - return script({ name, type, inputType, resultType }) -} -export function scriptWithVersion(name: string, version: string): ExaScriptsRow { - return script({ name, text: `CREATE ... %jar /path/to/exasol-cloud-storage-extension-${version}.jar; more text` }) -} diff --git a/extension/src/upgrade.ts b/extension/src/upgrade.ts new file mode 100644 index 0000000..3e992a3 --- /dev/null +++ b/extension/src/upgrade.ts @@ -0,0 +1,25 @@ +import { PreconditionFailedError, UpgradeResult } from "@exasol/extension-manager-interface"; +import { ADAPTER_SCRIPT_NAME, extractVersion } from "./common"; +import { ExtendedContext } from "./extension"; +import { install } from "./install"; + +export function upgrade(context: ExtendedContext): UpgradeResult { + const previousVersion = getAdapterVersion(context) + const newVersion = context.version + install(context, newVersion) + return { previousVersion, newVersion }; +} + +function getAdapterVersion(context: ExtendedContext): string { + const adapterScript = context.metadata.getScriptByName(ADAPTER_SCRIPT_NAME) + if (!adapterScript) { + throw new PreconditionFailedError(`Adapter script '${ADAPTER_SCRIPT_NAME}' is not installed`) + } + const version = extractVersion(adapterScript.text) + if (version.type === "failure") { + throw new PreconditionFailedError(`Failed to extract version from adapter script ${adapterScript.schema}.${adapterScript.name}: ${version.message}`) + } else if (version.result === context.version) { + throw new PreconditionFailedError(`Extension is already installed in latest version ${context.version}`) + } + return version.result +} diff --git a/pom.xml b/pom.xml index 29289bf..7bef345 100644 --- a/pom.xml +++ b/pom.xml @@ -201,7 +201,7 @@ extension/ npm - test + run test false diff --git a/src/test/java/com/exasol/rls/extension/ExtensionIT.java b/src/test/java/com/exasol/rls/extension/ExtensionIT.java index fb4e2f6..e2f3bf4 100644 --- a/src/test/java/com/exasol/rls/extension/ExtensionIT.java +++ b/src/test/java/com/exasol/rls/extension/ExtensionIT.java @@ -36,6 +36,7 @@ class ExtensionIT { private static final String EXTENSION_ID = "row-level-security-extension.js"; private static final int EXPECTED_PARAMETER_COUNT = 6; private static final String PROJECT_VERSION = MavenProjectVersionGetter.getCurrentProjectVersion(); + private static final String BASE_SCHEMA_NAME = "BASE_SCHEMA"; private static ExasolTestSetup exasolTestSetup; private static ExtensionManagerSetup setup; private Connection connection; @@ -64,6 +65,7 @@ static void teardown() throws Exception { @AfterEach void cleanup() throws SQLException { + connection.createStatement().execute("drop schema if exists " + BASE_SCHEMA_NAME + " cascade"); connection.close(); setup.cleanup(); } @@ -164,8 +166,8 @@ void installWrongVersionFails() { @Test void installExtensions() { setup.client().install(); - assertThat(setup.client().getInstallations(), - contains(new InstallationsResponseInstallation().name(EXTENSION_ID).version(PROJECT_VERSION))); + assertThat(setup.client().getInstallations(), contains( + new InstallationsResponseInstallation().name("EXA_EXTENSIONS.RLS_ADAPTER").version(PROJECT_VERSION))); } @Test @@ -202,8 +204,7 @@ void uninstall_removesAdapters() { @Test void upgradeFailsWhenNotInstalled() { setup.client().assertRequestFails(() -> setup.client().upgrade(), - "extension is not installed, the following scripts are missing: S3_FILES_ADAPTER, IMPORT_FROM_S3_DOCUMENT_FILES", - 404); + "Adapter script 'RLS_ADAPTER' is not installed", 412); } @Test @@ -213,6 +214,7 @@ void upgradeFailsWhenAlreadyUpToDate() { "Extension is already installed in latest version " + PROJECT_VERSION, 412); } + @Disabled("No previous version exists for upgrading") @Test void upgradeFromPreviousVersion() throws InterruptedException, BucketAccessException, TimeoutException, FileNotFoundException, URISyntaxException { @@ -228,7 +230,8 @@ void upgradeFromPreviousVersion() throws InterruptedException, BucketAccessExcep } private PreviousExtensionVersion createPreviousVersion() { - return setup.previousVersionManager().newVersion().currentVersion(PROJECT_VERSION) // + return setup.previousVersionManager().newVersion() // + .currentVersion(PROJECT_VERSION) // .previousVersion(PREVIOUS_VERSION) // .extensionFileName(EXTENSION_ID) // .project("row-level-security-lua") // @@ -252,6 +255,35 @@ void createInstance() { verifyVirtualTableContainsData(virtualTableName); } + @Test + void findInstance_notInstalled() { + assertThat(setup.client().listInstances("ignoredVersion"), emptyIterable()); + } + + @Test + void findInstance_noInstance() { + setup.client().install(); + assertThat(setup.client().listInstances("ignoredVersion"), emptyIterable()); + } + + @Test + void findInstance() { + setup.client().install(); + createVirtualSchema(); + assertThat(setup.client().listInstances("ignoredVersion"), + contains(new Instance().id("RLS_SCHEMA").name("RLS_SCHEMA"))); + } + + @Test + void deleteInstance() { + setup.client().install(); + createVirtualSchema(); + setup.exasolMetadata().assertVirtualSchema( + table().row("RLS_SCHEMA", "SYS", "EXA_EXTENSIONS.RLS_ADAPTER", not(emptyOrNullString())).matches()); + setup.client().deleteInstance(PROJECT_VERSION, "RLS_SCHEMA"); + setup.exasolMetadata().assertNoVirtualSchema(); + } + private String createVirtualSchema() { return createVirtualSchema(EXTENSION_ID, PROJECT_VERSION); } @@ -264,7 +296,7 @@ private String createVirtualSchema(final String extensionId, final String extens } private Table createBaseTable() { - final ExasolSchema baseSchema = this.dbObjectFactory.createSchema("BASE_SCHEMA"); + final ExasolSchema baseSchema = this.dbObjectFactory.createSchema(BASE_SCHEMA_NAME); return baseSchema.createTable("TAB", "ID", "SMALLINT", "NAME", "varchar(10)").insert(1, "a").insert(2, "b") .insert(3, "c"); } @@ -299,7 +331,7 @@ private ParameterValue param(final String name, final String value) { } private void assertScriptsExist() { - final String comment = "Created by Extension Manager for Row Level Security Lua " + PROJECT_VERSION; + final String comment = "Created by Extension Manager for Row Level Security Lua version " + PROJECT_VERSION; setup.exasolMetadata() .assertScript(table() .row("RLS_ADAPTER", "ADAPTER", null, null, From 5c7e2089a8d75f1103e5d437aece947c6ffd3bd8 Mon Sep 17 00:00:00 2001 From: Christoph Pirkl Date: Wed, 6 Sep 2023 15:15:42 +0200 Subject: [PATCH 17/21] Skip extension integration tests for Exasol 8 --- .../com/exasol/rls/extension/ExtensionIT.java | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/test/java/com/exasol/rls/extension/ExtensionIT.java b/src/test/java/com/exasol/rls/extension/ExtensionIT.java index e2f3bf4..35e45ce 100644 --- a/src/test/java/com/exasol/rls/extension/ExtensionIT.java +++ b/src/test/java/com/exasol/rls/extension/ExtensionIT.java @@ -5,6 +5,7 @@ import static org.hamcrest.Matchers.*; import static org.junit.jupiter.api.Assertions.assertAll; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assumptions.assumeFalse; import java.io.FileNotFoundException; import java.net.URISyntaxException; @@ -45,6 +46,7 @@ class ExtensionIT { @BeforeAll static void setup() throws FileNotFoundException, BucketAccessException, TimeoutException { exasolTestSetup = new ExasolTestSetupFactory(Path.of("no-such-file")).getTestSetup(); + assumePythonUdfSupported(); setup = ExtensionManagerSetup.create(exasolTestSetup, ExtensionBuilder.createDefaultNpmBuilder( EXTENSION_SOURCE_DIR, EXTENSION_SOURCE_DIR.resolve("dist").resolve(EXTENSION_ID))); } @@ -55,6 +57,22 @@ void setupTest() throws SQLException { dbObjectFactory = new ExasolObjectFactory(exasolTestSetup.createConnection()); } + static void assumePythonUdfSupported() { + final String dbVersion = readDbVersion(); + assumeFalse("8.22.0".equals(dbVersion), "Assume DB version != 8.22.0, actual: " + dbVersion); + } + + private static String readDbVersion() { + try (Statement stmt = exasolTestSetup.createConnection().createStatement()) { + final ResultSet result = stmt + .executeQuery("select PARAM_VALUE from SYS.EXA_METADATA WHERE PARAM_NAME='databaseProductVersion'"); + result.next(); + return result.getString(1); + } catch (final SQLException exception) { + throw new AssertionError("Failed to read database version", exception); + } + } + @AfterAll static void teardown() throws Exception { if (setup != null) { From df6aadb338adb6d29978b4964e5d2c00995d3a30 Mon Sep 17 00:00:00 2001 From: Christoph Pirkl Date: Thu, 7 Sep 2023 08:49:26 +0200 Subject: [PATCH 18/21] Update release date --- doc/changes/changes_1.5.0.md | 2 +- extension/src/extension.ts | 2 +- src/test/java/com/exasol/rls/extension/ExtensionIT.java | 8 ++++++-- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/doc/changes/changes_1.5.0.md b/doc/changes/changes_1.5.0.md index 28e3051..66f3b48 100644 --- a/doc/changes/changes_1.5.0.md +++ b/doc/changes/changes_1.5.0.md @@ -1,4 +1,4 @@ -# Exasol Row Level Security (Lua) 1.5.0, released 2023-??-?? +# Exasol Row Level Security (Lua) 1.5.0, released 2023-09-07 Code name: Add Extension diff --git a/extension/src/extension.ts b/extension/src/extension.ts index a214ee7..299a411 100644 --- a/extension/src/extension.ts +++ b/extension/src/extension.ts @@ -63,4 +63,4 @@ export function createExtension(): ExasolExtension { return baseExtension } -registerExtension(createExtension()) \ No newline at end of file +registerExtension(createExtension()) diff --git a/src/test/java/com/exasol/rls/extension/ExtensionIT.java b/src/test/java/com/exasol/rls/extension/ExtensionIT.java index 35e45ce..906d8ea 100644 --- a/src/test/java/com/exasol/rls/extension/ExtensionIT.java +++ b/src/test/java/com/exasol/rls/extension/ExtensionIT.java @@ -296,8 +296,6 @@ void findInstance() { void deleteInstance() { setup.client().install(); createVirtualSchema(); - setup.exasolMetadata().assertVirtualSchema( - table().row("RLS_SCHEMA", "SYS", "EXA_EXTENSIONS.RLS_ADAPTER", not(emptyOrNullString())).matches()); setup.client().deleteInstance(PROJECT_VERSION, "RLS_SCHEMA"); setup.exasolMetadata().assertNoVirtualSchema(); } @@ -329,6 +327,12 @@ private void createInstance(final String extensionId, final String extensionVers final String instanceName = setup.client().createInstance(List.of(param("virtualSchemaName", virtualSchemaName), param("SCHEMA_NAME", baseTable.getParent().getName()))); assertThat(instanceName, equalTo(virtualSchemaName)); + verifyVirtualSchemaExists(virtualSchemaName); + } + + private void verifyVirtualSchemaExists(final String virtualSchemaName) { + setup.exasolMetadata().assertVirtualSchema(table() + .row(virtualSchemaName, "SYS", "EXA_EXTENSIONS.RLS_ADAPTER", not(emptyOrNullString())).matches()); } private void verifyVirtualTableContainsData(final String tableName) { From de00cd0194fca458767ceb0d0b0b47cb3bc4be94 Mon Sep 17 00:00:00 2001 From: Christoph Pirkl Date: Thu, 7 Sep 2023 12:58:07 +0200 Subject: [PATCH 19/21] Free up disk space during build --- .github/workflows/ci-build.yml | 4 ++++ .github/workflows/release_droid_prepare_original_checksum.yml | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/.github/workflows/ci-build.yml b/.github/workflows/ci-build.yml index 4bafd03..45cf9bf 100644 --- a/.github/workflows/ci-build.yml +++ b/.github/workflows/ci-build.yml @@ -19,6 +19,10 @@ jobs: group: ${{ github.workflow }}-${{ github.ref }}-${{ matrix.lua_version }}-${{ matrix.docker_db_version }} cancel-in-progress: true steps: + - name: Free Disk Space + run: | + sudo rm -rf /usr/local/lib/android + sudo rm -rf /usr/share/dotnet - name: Checkout the repository uses: actions/checkout@v3 with: diff --git a/.github/workflows/release_droid_prepare_original_checksum.yml b/.github/workflows/release_droid_prepare_original_checksum.yml index 7e13596..79b49da 100644 --- a/.github/workflows/release_droid_prepare_original_checksum.yml +++ b/.github/workflows/release_droid_prepare_original_checksum.yml @@ -7,6 +7,10 @@ jobs: build: runs-on: ubuntu-latest steps: + - name: Free Disk Space + run: | + sudo rm -rf /usr/local/lib/android + sudo rm -rf /usr/share/dotnet - name: Checkout the repository uses: actions/checkout@v3 with: From 04156be0bbd33a9e1ae15f001a0ac016fa15bfc9 Mon Sep 17 00:00:00 2001 From: Christoph Pirkl Date: Thu, 7 Sep 2023 13:01:02 +0200 Subject: [PATCH 20/21] Apply suggestions from code review Co-authored-by: Christoph Kuhnke --- extension/src/uninstall.ts | 2 +- .../com/exasol/rls/extension/ExtensionIT.java | 15 +++++++++------ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/extension/src/uninstall.ts b/extension/src/uninstall.ts index 2d161f0..3b0e480 100644 --- a/extension/src/uninstall.ts +++ b/extension/src/uninstall.ts @@ -12,7 +12,7 @@ export function uninstall(context: ExtendedContext, versionToUninstall: string) return result.rows.length > 0 } - if (extensionSchemaExists()) { // Drop commands fail when schema does not exist. + if (extensionSchemaExists()) { // Drop command fails when schema does not exist. context.sqlClient.execute(`DROP ADAPTER SCRIPT "${context.extensionSchemaName}"."${ADAPTER_SCRIPT_NAME}"`) } } \ No newline at end of file diff --git a/src/test/java/com/exasol/rls/extension/ExtensionIT.java b/src/test/java/com/exasol/rls/extension/ExtensionIT.java index 906d8ea..d6908cc 100644 --- a/src/test/java/com/exasol/rls/extension/ExtensionIT.java +++ b/src/test/java/com/exasol/rls/extension/ExtensionIT.java @@ -32,7 +32,7 @@ import com.exasol.mavenprojectversiongetter.MavenProjectVersionGetter; class ExtensionIT { - private static final String PREVIOUS_VERSION = "2.6.2"; + private static final String PREVIOUS_VERSION = "1.5.0"; private static final Path EXTENSION_SOURCE_DIR = Paths.get("extension").toAbsolutePath(); private static final String EXTENSION_ID = "row-level-security-extension.js"; private static final int EXPECTED_PARAMETER_COUNT = 6; @@ -166,15 +166,18 @@ void getExtensionDetailsFailsForUnknownVersion() { void getExtensionDetailsSuccess() { final ExtensionDetailsResponse extensionDetails = setup.client().getExtensionDetails(PROJECT_VERSION); final List parameters = extensionDetails.getParameterDefinitions(); - final ParamDefinition param1 = new ParamDefinition().id("virtualSchemaName") - .name("Name of the new virtual schema").definition(Map.of("id", "virtualSchemaName", "name", - "Name of the new virtual schema", "required", true, "type", "string")); + final ParamDefinition param1 = paramDef("virtualSchemaName", "Name of the new virtual schema"); assertAll(() -> assertThat(extensionDetails.getId(), equalTo(EXTENSION_ID)), () -> assertThat(extensionDetails.getVersion(), equalTo(PROJECT_VERSION)), () -> assertThat(parameters, hasSize(EXPECTED_PARAMETER_COUNT)), () -> assertThat(parameters.get(0), equalTo(param1))); } - +private ParamDefinition paramDef(String id, String name, ) { + return new ParamDefinition() // + .id(id) // + .name(name) // + .definition(Map.of( "id", id, "name", name, "required", true, "type", "string" )); +} @Test void installWrongVersionFails() { setup.client().assertRequestFails(() -> setup.client().install("0.0.0"), @@ -232,7 +235,7 @@ void upgradeFailsWhenAlreadyUpToDate() { "Extension is already installed in latest version " + PROJECT_VERSION, 412); } - @Disabled("No previous version exists for upgrading") + @Disabled("This is the initial version of RLS_LUA, there is no previous version to be upgraded") @Test void upgradeFromPreviousVersion() throws InterruptedException, BucketAccessException, TimeoutException, FileNotFoundException, URISyntaxException { From 48acc80e357b987b2c8b9eb71bc0181416b3c21e Mon Sep 17 00:00:00 2001 From: Christoph Pirkl Date: Thu, 7 Sep 2023 13:09:21 +0200 Subject: [PATCH 21/21] Implement review findings by @ckunki --- extension/generate-description.sh | 2 +- extension/src/extension.test.ts | 24 +++++------ extension/src/install.ts | 4 +- .../com/exasol/rls/extension/ExtensionIT.java | 42 +++++++++---------- 4 files changed, 32 insertions(+), 40 deletions(-) diff --git a/extension/generate-description.sh b/extension/generate-description.sh index 52bdb4a..3990511 100755 --- a/extension/generate-description.sh +++ b/extension/generate-description.sh @@ -15,7 +15,7 @@ readonly all_built_files=("$target_dir"/row-level-security-dist-*.lua) readonly file_count=${#all_built_files[@]} if [ "$file_count" -ne 1 ]; then - echo "ERROR: Expected exactly one jar but found $file_count in $target_dir: ${all_built_files[*]}" + echo "ERROR: Expected exactly one lua file but found $file_count in $target_dir: ${all_built_files[*]}" exit 1 fi diff --git a/extension/src/extension.test.ts b/extension/src/extension.test.ts index d2badab..e10d145 100644 --- a/extension/src/extension.test.ts +++ b/extension/src/extension.test.ts @@ -75,10 +75,6 @@ describe("Row Level Security Lua", () => { return installations } - function script({ schema = "schema", name = "name", inputType, resultType, type = "ADAPTER", text = "", comment }: Partial): ExaScriptsRow { - return { schema, name, inputType, resultType, type, text, comment } - } - it("returns empty list when no adapter script is available", () => { expect(findInstallations([])).toHaveLength(0) }) @@ -97,7 +93,7 @@ describe("Row Level Security Lua", () => { describe("install()", () => { it("executes expected statements", () => { const context = createMockContext(); - createExtension().install(context, EXTENSION_DESCRIPTION.version); + createExtension().install(context, currentVersion); const executeCalls = context.mocks.sqlExecute.mock.calls expect(executeCalls.length).toBe(2) @@ -105,14 +101,14 @@ describe("Row Level Security Lua", () => { const createCommentCommand = executeCalls[1][0] expect(createScriptCommand).toContain(`CREATE OR REPLACE LUA ADAPTER SCRIPT \"ext-schema\".\"RLS_ADAPTER\" AS --- RLS Lua version 1.5.0 +-- RLS Lua version ${currentVersion} table.insert(package.searchers,`) - const expectedComment = `Created by Extension Manager for Row Level Security Lua version ${EXTENSION_DESCRIPTION.version}` + const expectedComment = `Created by Extension Manager for Row Level Security Lua version ${currentVersion}` expect(createCommentCommand).toEqual(`COMMENT ON SCRIPT "ext-schema"."${ADAPTER_SCRIPT_NAME}" IS '${expectedComment}'`) }) it("fails for wrong version", () => { expect(() => { createExtension().install(createMockContext(), "wrongVersion") }) - .toThrow(`Installing version 'wrongVersion' not supported, try '${EXTENSION_DESCRIPTION.version}'.`) + .toThrow(`Installing version 'wrongVersion' not supported, try '${currentVersion}'.`) }) }) @@ -120,7 +116,7 @@ table.insert(package.searchers,`) it("executes query to check if schema exists", () => { const context = createMockContext() context.mocks.sqlQuery.mockReturnValue({ columns: [], rows: [] }); - createExtension().uninstall(context, EXTENSION_DESCRIPTION.version) + createExtension().uninstall(context, currentVersion) const calls = context.mocks.sqlQuery.mock.calls expect(calls.length).toEqual(1) expect(calls[0]).toEqual(["SELECT 1 FROM SYS.EXA_ALL_SCHEMAS WHERE SCHEMA_NAME=?", "ext-schema"]) @@ -128,30 +124,30 @@ table.insert(package.searchers,`) it("skips drop statements when schema does not exist", () => { const context = createMockContext() context.mocks.sqlQuery.mockReturnValue({ columns: [], rows: [] }); - createExtension().uninstall(context, EXTENSION_DESCRIPTION.version) + createExtension().uninstall(context, currentVersion) expect(context.mocks.sqlExecute.mock.calls.length).toEqual(0) }) it("executes expected statements", () => { const context = createMockContext() context.mocks.sqlQuery.mockReturnValue({ columns: [], rows: [[1]] }); - createExtension().uninstall(context, EXTENSION_DESCRIPTION.version) + createExtension().uninstall(context, currentVersion) const calls = context.mocks.sqlExecute.mock.calls expect(calls.length).toEqual(1) expect(calls[0]).toEqual([`DROP ADAPTER SCRIPT "ext-schema"."${ADAPTER_SCRIPT_NAME}"`]) }) it("fails for wrong version", () => { expect(() => { createExtension().uninstall(createMockContext(), "wrongVersion") }) - .toThrow(`Uninstalling version 'wrongVersion' not supported, try '${EXTENSION_DESCRIPTION.version}'.`) + .toThrow(`Uninstalling version 'wrongVersion' not supported, try '${currentVersion}'.`) }) }) describe("getInstanceParameters()", () => { it("fails for wrong version", () => { expect(() => { createExtension().getInstanceParameters(createMockContext(), "wrongVersion") }) - .toThrowError(new NotFoundError(`Version 'wrongVersion' not supported, can only use '${EXTENSION_DESCRIPTION.version}'.`)) + .toThrowError(new NotFoundError(`Version 'wrongVersion' not supported, can only use '${currentVersion}'.`)) }) it("returns expected parameters", () => { - const actual = createExtension().getInstanceParameters(createMockContext(), EXTENSION_DESCRIPTION.version) + const actual = createExtension().getInstanceParameters(createMockContext(), currentVersion) expect(actual).toHaveLength(6) expect(actual[0]).toStrictEqual({ id: "virtualSchemaName", name: "Name of the new virtual schema", required: true, type: "string" diff --git a/extension/src/install.ts b/extension/src/install.ts index 324b29f..ad39648 100644 --- a/extension/src/install.ts +++ b/extension/src/install.ts @@ -25,11 +25,9 @@ const luaModuleLoadingPreamble = `table.insert(package.searchers, )`; export function buildCreateScriptCommand(qualifiedScriptName: string, luaScriptContent: string) { - const createScriptCommand = `CREATE OR REPLACE LUA ADAPTER SCRIPT ${qualifiedScriptName} AS + return `CREATE OR REPLACE LUA ADAPTER SCRIPT ${qualifiedScriptName} AS ${getScriptVersionComment()} ${luaModuleLoadingPreamble} ${luaScriptContent} /`; - return createScriptCommand; } - diff --git a/src/test/java/com/exasol/rls/extension/ExtensionIT.java b/src/test/java/com/exasol/rls/extension/ExtensionIT.java index d6908cc..cced2b9 100644 --- a/src/test/java/com/exasol/rls/extension/ExtensionIT.java +++ b/src/test/java/com/exasol/rls/extension/ExtensionIT.java @@ -117,20 +117,16 @@ void listInstallations_ignoresWrongScriptNames() { void listInstallations_findsMatchingScripts() { createAdapter("RLS_ADAPTER"); final List installations = setup.client().getInstallations(); - assertAll(() -> assertThat(installations, hasSize(1)), // - () -> assertThat(installations.get(0).getName(), - equalTo(ExtensionManagerSetup.EXTENSION_SCHEMA_NAME + ".RLS_ADAPTER")), - () -> assertThat(installations.get(0).getVersion(), equalTo("dummy.version"))); + assertThat(installations, contains(new InstallationsResponseInstallation() + .name(ExtensionManagerSetup.EXTENSION_SCHEMA_NAME + ".RLS_ADAPTER").version("dummy.version"))); } @Test void listInstallations_findsOwnInstallation() { setup.client().install(); final List installations = setup.client().getInstallations(); - assertAll(() -> assertThat(installations, hasSize(1)), // - () -> assertThat(installations.get(0).getName(), - equalTo(ExtensionManagerSetup.EXTENSION_SCHEMA_NAME + ".RLS_ADAPTER")), - () -> assertThat(installations.get(0).getVersion(), equalTo(PROJECT_VERSION))); + assertThat(installations, contains(new InstallationsResponseInstallation() + .name(ExtensionManagerSetup.EXTENSION_SCHEMA_NAME + ".RLS_ADAPTER").version(PROJECT_VERSION))); } @Test @@ -172,12 +168,14 @@ void getExtensionDetailsSuccess() { () -> assertThat(parameters, hasSize(EXPECTED_PARAMETER_COUNT)), () -> assertThat(parameters.get(0), equalTo(param1))); } -private ParamDefinition paramDef(String id, String name, ) { - return new ParamDefinition() // - .id(id) // - .name(name) // - .definition(Map.of( "id", id, "name", name, "required", true, "type", "string" )); -} + + private ParamDefinition paramDef(final String id, final String name) { + return new ParamDefinition() // + .id(id) // + .name(name) // + .definition(Map.of("id", id, "name", name, "required", true, "type", "string")); + } + @Test void installWrongVersionFails() { setup.client().assertRequestFails(() -> setup.client().install("0.0.0"), @@ -242,7 +240,7 @@ void upgradeFromPreviousVersion() throws InterruptedException, BucketAccessExcep final PreviousExtensionVersion previousVersion = createPreviousVersion(); previousVersion.prepare(); previousVersion.install(); - final String virtualTable = createVirtualSchema(previousVersion.getExtensionId(), PREVIOUS_VERSION); + final String virtualTable = createInstance(previousVersion.getExtensionId(), PREVIOUS_VERSION); verifyVirtualTableContainsData(virtualTable); assertInstalledVersion("EXA_EXTENSIONS.RLS_ADAPTER", PREVIOUS_VERSION); previousVersion.upgrade(); @@ -270,9 +268,9 @@ private void assertInstalledVersion(final String expectedName, final String expe } @Test - void createInstance() { + void createInstance_success() { setup.client().install(); - final String virtualTableName = createVirtualSchema(); + final String virtualTableName = createInstance(); verifyVirtualTableContainsData(virtualTableName); } @@ -290,7 +288,7 @@ void findInstance_noInstance() { @Test void findInstance() { setup.client().install(); - createVirtualSchema(); + createInstance(); assertThat(setup.client().listInstances("ignoredVersion"), contains(new Instance().id("RLS_SCHEMA").name("RLS_SCHEMA"))); } @@ -298,16 +296,16 @@ void findInstance() { @Test void deleteInstance() { setup.client().install(); - createVirtualSchema(); + createInstance(); setup.client().deleteInstance(PROJECT_VERSION, "RLS_SCHEMA"); setup.exasolMetadata().assertNoVirtualSchema(); } - private String createVirtualSchema() { - return createVirtualSchema(EXTENSION_ID, PROJECT_VERSION); + private String createInstance() { + return createInstance(EXTENSION_ID, PROJECT_VERSION); } - private String createVirtualSchema(final String extensionId, final String extensionVersion) { + private String createInstance(final String extensionId, final String extensionVersion) { final String virtualSchemaName = "RLS_SCHEMA"; final Table baseTable = createBaseTable(); createInstance(virtualSchemaName, baseTable);