From 26c45843d24c5a9ffab32737f2d9c192591a1670 Mon Sep 17 00:00:00 2001 From: Bob den Os <108393871+BobdenOs@users.noreply.github.com> Date: Wed, 9 Oct 2024 16:00:37 +0200 Subject: [PATCH 1/4] fix: Improved behavioral consistency between the database services (#673) This PR contains a few fixes that where required to make the new compliance tests to pass: - Decimal precision behavior is aligned between the databases when defined - `@cap-js/hana` now properly supports scalar `SELECT` queries in the columns The goal of this PR is to remove as much dependencies as possible now that `cds@8` provides the `cds-test` command. - Removing `jest` by switching to `cds-test` - Removed `jest.config.js` files - Removing `chai` by switching to `cds-test` - Adjust all tests to fit with the `cds-test` `expect` pattern - Removing `HANA` test action - Move `HXE` Github action steps into a reuse file - Added a lock for `HANA` database creation - Added a fallback for `Postgres` during database creation - Removed compliance import tests - Added symbolic link to the compliance test folder --------- Co-authored-by: Daniel Hutzel Co-authored-by: Johannes Vogel <31311694+johannes-vogel@users.noreply.github.com> --- .github/actions/hxe/action.yml | 55 + .github/workflows/hana.yml | 84 - .github/workflows/release-please.yml | 49 +- .github/workflows/release.yml | 4 +- .github/workflows/test.yml | 17 +- db-service/lib/SQLService.js | 5 + db-service/lib/cql-functions.js | 4 +- db-service/lib/cqn2sql.js | 21 +- db-service/package.json | 2 +- .../cqn2sql/__snapshots__/create.test.js.snap | 7 - .../cqn2sql/__snapshots__/delete.test.js.snap | 15 - .../cqn2sql/__snapshots__/drop.test.js.snap | 13 - .../__snapshots__/expression.test.js.snap | 77 - .../__snapshots__/function.test.js.snap | 32 - .../cqn2sql/__snapshots__/insert.test.js.snap | 56 - .../cqn2sql/__snapshots__/select.test.js.snap | 206 - .../cqn2sql/__snapshots__/update.test.js.snap | 107 - .../cqn2sql/__snapshots__/upsert.test.js.snap | 34 - db-service/test/cqn2sql/cqn.js | 498 -- db-service/test/cqn2sql/create.test.js | 47 - db-service/test/cqn2sql/delete.test.js | 339 -- db-service/test/cqn2sql/drop.test.js | 69 - db-service/test/cqn2sql/expression.test.js | 385 -- db-service/test/cqn2sql/function.test.js | 256 - db-service/test/cqn2sql/insert.test.js | 122 - db-service/test/cqn2sql/select.test.js | 514 -- db-service/test/cqn2sql/testModel.cds | 100 - db-service/test/cqn2sql/update.test.js | 144 - db-service/test/cqn2sql/upsert.test.js | 55 - db-service/test/cqn4sql/assocs2joins.test.js | 2 +- db-service/test/cqn4sql/not-persisted.test.js | 269 +- db-service/test/deep/deep.test.js | 30 +- hana/jest.config.js | 1 - hana/lib/HANAService.js | 68 +- hana/lib/cql-functions.js | 5 +- hana/lib/scripts/container-database.sql | 2 + hana/package.json | 8 +- hana/test/bookshop.test.js | 3 - hana/test/compliance | 1 + hana/test/compliance.test.js | 3 - hana/test/lean-draft.test.js | 3 - hana/test/run.test.js | 41 +- hana/test/sflight.test.js | 3 - hana/test/stream.test.js | 1 + jest.config.js | 2 - package-lock.json | 5082 +++-------------- package.json | 12 +- postgres/jest.config.js | 10 - postgres/lib/PostgresService.js | 69 +- postgres/lib/cql-functions.js | 20 + postgres/package.json | 2 +- .../__snapshots__/csn2postgres.test.js.snap | 218 - postgres/test/bookshop.test.js | 3 - postgres/test/compliance | 1 + postgres/test/compliance.test.js | 3 - postgres/test/connect.test.js | 5 +- postgres/test/csn2postgres.test.js | 20 - postgres/test/service-types.test.js | 3 +- postgres/test/service.test.js | 2 +- postgres/test/sflight.test.js | 3 - postgres/test/streaming.compat.test.js | 1 + postgres/test/streaming.test.js | 1 + sqlite/lib/SQLiteService.js | 5 +- sqlite/package.json | 2 +- sqlite/test/bookshop.test.js | 3 - sqlite/test/compliance | 1 + sqlite/test/compliance.test.js | 3 - sqlite/test/deep/deepInputProcessing.test.js | 52 +- sqlite/test/deep/deepPersistenceSkip.test.js | 41 +- sqlite/test/general/delete-rename.test.js | 8 +- sqlite/test/general/insert-as-select.test.js | 4 +- sqlite/test/general/localized.test.js | 15 +- sqlite/test/general/managed.test.js | 52 +- sqlite/test/general/stream.compat.test.js | 57 +- sqlite/test/general/stream.test.js | 82 +- sqlite/test/general/temporal.test.js | 14 +- sqlite/test/general/uuid.test.js | 18 +- sqlite/test/sflight.test.js | 3 - test/cds.js | 18 +- test/compliance/CREATE.test.js | 54 +- test/compliance/DELETE.test.js | 14 +- test/compliance/DROP.test.js | 1 + test/compliance/INSERT.test.js | 2 + test/compliance/SELECT.test.js | 1122 +++- test/compliance/UPDATE.test.js | 215 +- test/compliance/api.test.js | 12 +- test/compliance/definitions.test.js | 2 + test/compliance/keywords.test.js | 9 +- test/compliance/literals.test.js | 2 + .../basic/literals/basic.literals.number.js | 4 +- .../resources/db/basic/projection.cds | 272 + test/compliance/strictMode.test.js | 6 +- test/compliance/timestamps.test.js | 34 +- test/find | 2 + test/scenarios/sflight/integration.test.js | 11 +- .../scenarios/sflight}/lean-draft.test.js | 31 +- test/scenarios/sflight/read.test.js | 9 - 97 files changed, 2699 insertions(+), 8695 deletions(-) create mode 100644 .github/actions/hxe/action.yml delete mode 100644 .github/workflows/hana.yml delete mode 100644 db-service/test/cqn2sql/__snapshots__/create.test.js.snap delete mode 100644 db-service/test/cqn2sql/__snapshots__/delete.test.js.snap delete mode 100644 db-service/test/cqn2sql/__snapshots__/drop.test.js.snap delete mode 100644 db-service/test/cqn2sql/__snapshots__/expression.test.js.snap delete mode 100644 db-service/test/cqn2sql/__snapshots__/function.test.js.snap delete mode 100644 db-service/test/cqn2sql/__snapshots__/insert.test.js.snap delete mode 100644 db-service/test/cqn2sql/__snapshots__/select.test.js.snap delete mode 100644 db-service/test/cqn2sql/__snapshots__/update.test.js.snap delete mode 100644 db-service/test/cqn2sql/__snapshots__/upsert.test.js.snap delete mode 100644 db-service/test/cqn2sql/cqn.js delete mode 100644 db-service/test/cqn2sql/create.test.js delete mode 100644 db-service/test/cqn2sql/delete.test.js delete mode 100644 db-service/test/cqn2sql/drop.test.js delete mode 100644 db-service/test/cqn2sql/expression.test.js delete mode 100644 db-service/test/cqn2sql/function.test.js delete mode 100644 db-service/test/cqn2sql/insert.test.js delete mode 100644 db-service/test/cqn2sql/select.test.js delete mode 100644 db-service/test/cqn2sql/testModel.cds delete mode 100644 db-service/test/cqn2sql/update.test.js delete mode 100644 db-service/test/cqn2sql/upsert.test.js delete mode 100644 hana/jest.config.js delete mode 100644 hana/test/bookshop.test.js create mode 120000 hana/test/compliance delete mode 100644 hana/test/compliance.test.js delete mode 100644 hana/test/lean-draft.test.js delete mode 100644 hana/test/sflight.test.js delete mode 100644 jest.config.js delete mode 100644 postgres/jest.config.js delete mode 100644 postgres/test/__snapshots__/csn2postgres.test.js.snap delete mode 100644 postgres/test/bookshop.test.js create mode 120000 postgres/test/compliance delete mode 100644 postgres/test/compliance.test.js delete mode 100644 postgres/test/csn2postgres.test.js delete mode 100644 postgres/test/sflight.test.js delete mode 100644 sqlite/test/bookshop.test.js create mode 120000 sqlite/test/compliance delete mode 100644 sqlite/test/compliance.test.js delete mode 100644 sqlite/test/sflight.test.js create mode 100755 test/find rename {sqlite/test => test/scenarios/sflight}/lean-draft.test.js (99%) diff --git a/.github/actions/hxe/action.yml b/.github/actions/hxe/action.yml new file mode 100644 index 000000000..83d757814 --- /dev/null +++ b/.github/actions/hxe/action.yml @@ -0,0 +1,55 @@ +name: 'Start HANA' +description: 'Starts an local HANA Express instance for isolated testing' +inputs: + GITHUB_TOKEN: + description: 'Derivative token for using the GitHub REST API' + required: true +outputs: + TAG: + description: "The Image Tag" + value: ${{ steps.find-hxe.outputs.TAG }} + IMAGE_ID: + description: "The " + value: ${{ steps.find-hxe.outputs.IMAGE_ID }} +runs: + using: "composite" + steps: + - name: Find HXE image + id: find-hxe + shell: bash + # TODO: replace hana/tools/docker/hxe/* with ${{ github.action_path }} + run: | + TAG="$(sha1sum hana/tools/docker/hxe/* | sha1sum --tag | grep '[^ ]*$' -o)"; + IMAGE_ID=ghcr.io/${{ github.repository_owner }}/hanaexpress; + IMAGE_ID=$(echo $IMAGE_ID | tr '[A-Z]' '[a-z]'); + echo "TAG=${TAG}" >> $GITHUB_OUTPUT; + echo "IMAGE_ID=${IMAGE_ID}" >> $GITHUB_OUTPUT; + GHCR_TOKEN=$(echo ${{ inputs.GITHUB_TOKEN }} | base64); + if + curl -H "Authorization: Bearer ${GHCR_TOKEN}" https://ghcr.io/v2/${{ github.repository_owner }}/hanaexpress/manifests/$TAG | grep "MANIFEST_UNKNOWN"; + then + echo "BUILD_HXE=true" >> $GITHUB_OUTPUT + else + echo "BUILD_HXE=false" >> $GITHUB_OUTPUT + fi; + - name: Set up Docker Buildx + if: ${{ steps.find-hxe.outputs.BUILD_HXE == 'true' }} + uses: docker/setup-buildx-action@v3 + - name: Build HXE image + if: ${{ steps.find-hxe.outputs.BUILD_HXE == 'true' }} + shell: bash + run: | + echo "${{ inputs.GITHUB_TOKEN }}" | docker login ghcr.io -u $ --password-stdin; + DOCKER_BUILDKIT=1 docker build -t $IMAGE_ID:$TAG ./hana/tools/docker/hxe; + docker push $IMAGE_ID:$TAG; + env: + TAG: ${{ steps.find-hxe.outputs.TAG }} + IMAGE_ID: ${{ steps.find-hxe.outputs.IMAGE_ID }} + - name: Start HXE image + shell: bash + run: | + echo "${{ inputs.GITHUB_TOKEN }}" | docker login ghcr.io -u $ --password-stdin; + { npm start -w hana; } & + env: + TAG: ${{ steps.find-hxe.outputs.TAG }} + IMAGE_ID: ${{ steps.find-hxe.outputs.IMAGE_ID }} diff --git a/.github/workflows/hana.yml b/.github/workflows/hana.yml deleted file mode 100644 index 1b169eb3d..000000000 --- a/.github/workflows/hana.yml +++ /dev/null @@ -1,84 +0,0 @@ -name: Tests HANA - -on: - push: - branches: [main] - pull_request: - types: [opened, synchronize, reopened, auto_merge_enabled] - -# Allow parallel jobs on `main`, so that each commit is tested. For PRs, run only the latest commit. -concurrency: - group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} - cancel-in-progress: true - -jobs: - test: - runs-on: ubuntu-latest - timeout-minutes: 20 - name: HANA Node.js ${{ matrix.node }} - permissions: - packages: write - - strategy: - fail-fast: true - matrix: - node: [18] - - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 - with: - node-version: ${{ matrix.node }} - cache: 'npm' - - - run: npm ci - - run: npm run lint - - # TODO: move out of this repository - # testing setup - # Search the github repository for the image with the current hxe/* contents - - name: Find HXE image - id: find-hxe - run: | - TAG="$(sha1sum hana/tools/docker/hxe/* | sha1sum --tag | grep '[^ ]*$' -o)"; - IMAGE_ID=ghcr.io/${{ github.repository_owner }}/hanaexpress; - IMAGE_ID=$(echo $IMAGE_ID | tr '[A-Z]' '[a-z]'); - echo "TAG=${TAG}" >> $GITHUB_OUTPUT; - echo "IMAGE_ID=${IMAGE_ID}" >> $GITHUB_OUTPUT; - GHCR_TOKEN=$(echo ${{ secrets.GITHUB_TOKEN }} | base64); - if - curl -H "Authorization: Bearer ${GHCR_TOKEN}" https://ghcr.io/v2/${{ github.repository_owner }}/hanaexpress/manifests/$TAG | grep "MANIFEST_UNKNOWN"; - then - echo "BUILD_HXE=true" >> $GITHUB_OUTPUT - else - echo "BUILD_HXE=false" >> $GITHUB_OUTPUT - fi; - # Add docker buildx tools to use the new docker builder - - name: Set up Docker Buildx - if: ${{ steps.find-hxe.outputs.BUILD_HXE == 'true' }} - uses: docker/setup-buildx-action@v3 - # Build and push the latest HXE pre initialized docker image - - name: Build HXE image - if: ${{ steps.find-hxe.outputs.BUILD_HXE == 'true' }} - run: | - echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u $ --password-stdin; - DOCKER_BUILDKIT=1 docker build -t $IMAGE_ID:$TAG ./hana/tools/docker/hxe; - docker push $IMAGE_ID:$TAG; - env: - TAG: ${{ steps.find-hxe.outputs.TAG }} - IMAGE_ID: ${{ steps.find-hxe.outputs.IMAGE_ID }} - # Star the latest HXE image in the background - - name: Start HXE image - run: | - echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u $ --password-stdin; - { npm start -w hana; } & - env: - TAG: ${{ steps.find-hxe.outputs.TAG }} - IMAGE_ID: ${{ steps.find-hxe.outputs.IMAGE_ID }} - - # testing - - run: npm test -w hana -- --maxWorkers=1 - env: - FORCE_COLOR: true - TAG: ${{ steps.find-hxe.outputs.TAG }} - IMAGE_ID: ${{ steps.find-hxe.outputs.IMAGE_ID }} diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml index 6e7f31f23..8eeec2bd4 100644 --- a/.github/workflows/release-please.yml +++ b/.github/workflows/release-please.yml @@ -19,56 +19,25 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: - node-version: 18 + node-version: 20 registry-url: 'https://registry.npmjs.org' # Run tests - run: npm ci if: ${{ steps.release.outputs.releases_created }} # test sqlite/postgres/db-service - - run: npm test -w db-service -w sqlite -w postgres -- --maxWorkers=1 + - run: npm test -w db-service -w sqlite -w postgres if: ${{ steps.release.outputs.releases_created }} # test hana - # TODO: Factor this setup script out - - name: Find HXE image + - id: hxe if: ${{ steps.release.outputs.releases_created }} - id: find-hxe - run: | - TAG="$(sha1sum hana/tools/docker/hxe/* | sha1sum --tag | grep '[^ ]*$' -o)"; - IMAGE_ID=ghcr.io/${{ github.repository_owner }}/hanaexpress; - IMAGE_ID=$(echo $IMAGE_ID | tr '[A-Z]' '[a-z]'); - echo "TAG=${TAG}" >> $GITHUB_OUTPUT; - echo "IMAGE_ID=${IMAGE_ID}" >> $GITHUB_OUTPUT; - GHCR_TOKEN=$(echo ${{ secrets.GITHUB_TOKEN }} | base64); - if - curl -H "Authorization: Bearer ${GHCR_TOKEN}" https://ghcr.io/v2/${{ github.repository_owner }}/hanaexpress/manifests/$TAG | grep "MANIFEST_UNKNOWN"; - then - echo "BUILD_HXE=true" >> $GITHUB_OUTPUT - else - echo "BUILD_HXE=false" >> $GITHUB_OUTPUT - fi; - - name: Set up Docker Buildx - if: ${{ steps.release.outputs.releases_created && steps.find-hxe.outputs.BUILD_HXE == 'true' }} - uses: docker/setup-buildx-action@v3 - - name: Build HXE image - if: ${{ steps.release.outputs.releases_created && steps.find-hxe.outputs.BUILD_HXE == 'true' }} - run: | - echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u $ --password-stdin; - DOCKER_BUILDKIT=1 docker build -t $IMAGE_ID:$TAG ./hana/tools/docker/hxe; - docker push $IMAGE_ID:$TAG; - env: - TAG: ${{ steps.find-hxe.outputs.TAG }} - IMAGE_ID: ${{ steps.find-hxe.outputs.IMAGE_ID }} - - name: Start HXE image - if: ${{ steps.release.outputs.releases_created }} - run: | - echo "${{ secrets.GITHUB_TOKEN }}" | docker login ghcr.io -u $ --password-stdin; - { npm start -w hana; } & - env: - TAG: ${{ steps.find-hxe.outputs.TAG }} - IMAGE_ID: ${{ steps.find-hxe.outputs.IMAGE_ID }} - - run: npm test -w hana -- --maxWorkers=1 + uses: ./.github/actions/hxe + with: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - run: npm test -w hana if: ${{ steps.release.outputs.releases_created }} + TAG: ${{ steps.hxe.outputs.TAG }} + IMAGE_ID: ${{ steps.hxe.outputs.IMAGE_ID }} # Publish packages - name: Publish db-service diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 136e2a895..67907b778 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -17,10 +17,10 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: - node-version: 18 + node-version: 20 registry-url: https://registry.npmjs.org/ - run: npm i - - run: npm test -w db-service -w sqlite -w postgres -- --maxWorkers=1 + - run: npm test -w db-service -w sqlite -w postgres - name: get-version # this takes the version of the monorepo root id: package-version # v1.3.1 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 8d6b5c7b5..80f3feaac 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -14,13 +14,15 @@ concurrency: jobs: test: runs-on: ubuntu-latest - timeout-minutes: 5 - name: Node.js ${{ matrix.node }} + timeout-minutes: 20 + name: Tests + permissions: + packages: write strategy: fail-fast: true matrix: - node: [18] + node: [22] steps: - uses: actions/checkout@v4 @@ -31,6 +33,13 @@ jobs: - run: npm ci - run: npm run lint - - run: npm test -w db-service -w sqlite -w postgres -- --maxWorkers=1 + - id: hxe + uses: ./.github/actions/hxe + with: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # testing + - run: npm test -ws env: FORCE_COLOR: true + TAG: ${{ steps.hxe.outputs.TAG }} + IMAGE_ID: ${{ steps.hxe.outputs.IMAGE_ID }} diff --git a/db-service/lib/SQLService.js b/db-service/lib/SQLService.js index 117a5fe5b..c96d21749 100644 --- a/db-service/lib/SQLService.js +++ b/db-service/lib/SQLService.js @@ -367,6 +367,7 @@ class SQLService extends DatabaseService { */ cqn4sql(q) { if ( + (!q.target || q.target._unresolved) && !cds.env.features.db_strict && !q.SELECT?.from?.join && !q.SELECT?.from?.SELECT && @@ -466,6 +467,10 @@ const sqls = new (class extends SQLService { get factory() { return null } + + get model() { + return cds.model + } })() cds.extend(cds.ql.Query).with( class { diff --git a/db-service/lib/cql-functions.js b/db-service/lib/cql-functions.js index 2ced052cc..0165278e5 100644 --- a/db-service/lib/cql-functions.js +++ b/db-service/lib/cql-functions.js @@ -1,3 +1,5 @@ +const cds = require("@sap/cds") + const StandardFunctions = { // OData: https://docs.oasis-open.org/odata/odata/v4.01/odata-v4.01-part2-url-conventions.html#sec_CanonicalFunctions @@ -59,7 +61,7 @@ const StandardFunctions = { * @param {string} x * @returns {string} */ - countdistinct: x => `count(distinct ${x || '*'})`, + countdistinct: x => `count(distinct ${x || cds.error`countdistinct requires a ref to be counted`})`, /** * Generates SQL statement that produces the index of the first occurrence of the second string in the first string * @param {string} x diff --git a/db-service/lib/cqn2sql.js b/db-service/lib/cqn2sql.js index 9a6440184..e99c613eb 100644 --- a/db-service/lib/cqn2sql.js +++ b/db-service/lib/cqn2sql.js @@ -18,8 +18,8 @@ const DEBUG = (() => { return cds.debug('sql|sqlite') //if (DEBUG) { // return DEBUG - // (sql, ...more) => DEBUG (sql.replace(/(?:SELECT[\n\r\s]+(json_group_array\()?[\n\r\s]*json_insert\((\n|\r|.)*?\)[\n\r\s]*\)?[\n\r\s]+as[\n\r\s]+_json_[\n\r\s]+FROM[\n\r\s]*\(|\)[\n\r\s]*(\)[\n\r\s]+AS )|\)$)/gim,(a,b,c,d) => d || ''), ...more) - // FIXME: looses closing ) on INSERT queries + // (sql, ...more) => DEBUG (sql.replace(/(?:SELECT[\n\r\s]+(json_group_array\()?[\n\r\s]*json_insert\((\n|\r|.)*?\)[\n\r\s]*\)?[\n\r\s]+as[\n\r\s]+_json_[\n\r\s]+FROM[\n\r\s]*\(|\)[\n\r\s]*(\)[\n\r\s]+AS )|\)$)/gim,(a,b,c,d) => d || ''), ...more) + // FIXME: looses closing ) on INSERT queries //} })() @@ -88,6 +88,7 @@ class CQN2SQLRenderer { this.values = [] // prepare values, filled in by subroutines this[kind]((this.cqn = q)) // actual sql rendering happens here if (vars?.length && !this.values?.length) this.values = vars + if (vars && Object.keys(vars).length && !this.values?.length) this.values = vars const sanitize_values = process.env.NODE_ENV === 'production' && cds.env.log.sanitize_values !== false DEBUG?.( this.sql, @@ -116,8 +117,13 @@ class CQN2SQLRenderer { * @param {import('./infer/cqn').CREATE} q */ CREATE(q) { - const { target } = q, - { query } = target + let { target } = q + let query = target?.query || q.CREATE.as + if (!target || target._unresolved) { + const entity = q.CREATE.entity + target = typeof entity === 'string' ? { name: entity } : q.CREATE.entity + } + const name = this.name(target.name) // Don't allow place holders inside views delete this.values @@ -203,8 +209,9 @@ class CQN2SQLRenderer { */ DROP(q) { const { target } = q - const isView = target.query || target.projection - return (this.sql = `DROP ${isView ? 'VIEW' : 'TABLE'} IF EXISTS ${this.quote(this.name(target.name))}`) + const isView = target?.query || target?.projection || q.DROP.view + const name = target?.name || q.DROP.table?.ref?.[0] || q.DROP.view?.ref?.[0] + return (this.sql = `DROP ${isView ? 'VIEW' : 'TABLE'} IF EXISTS ${this.quote(this.name(name))}`) } // SELECT Statements ------------------------------------------------ @@ -223,7 +230,7 @@ class CQN2SQLRenderer { // REVISIT: When selecting from an entity that is not in the model the from.where are not normalized (as cqn4sql is skipped) if (!where && from?.ref?.length === 1 && from.ref[0]?.where) where = from.ref[0]?.where - let columns = this.SELECT_columns(q) + const columns = this.SELECT_columns(q) let sql = `SELECT` if (distinct) sql += ` DISTINCT` if (!_empty(columns)) sql += ` ${columns}` diff --git a/db-service/package.json b/db-service/package.json index 7749fdf77..72d51e3b7 100644 --- a/db-service/package.json +++ b/db-service/package.json @@ -22,7 +22,7 @@ "CHANGELOG.md" ], "scripts": { - "test": "jest --silent" + "test": "cds-test" }, "dependencies": { "generic-pool": "^3.9.0" diff --git a/db-service/test/cqn2sql/__snapshots__/create.test.js.snap b/db-service/test/cqn2sql/__snapshots__/create.test.js.snap deleted file mode 100644 index 94592f9d8..000000000 --- a/db-service/test/cqn2sql/__snapshots__/create.test.js.snap +++ /dev/null @@ -1,7 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`create with select statements Generate SQL from CREATE stmt with entity name 1`] = ` -{ - "sql": "CREATE TABLE Foo ( ID INT, a NVARCHAR(5000), b NVARCHAR(5000), c NVARCHAR(5000), x INT )", -} -`; diff --git a/db-service/test/cqn2sql/__snapshots__/delete.test.js.snap b/db-service/test/cqn2sql/__snapshots__/delete.test.js.snap deleted file mode 100644 index 98c13e2e9..000000000 --- a/db-service/test/cqn2sql/__snapshots__/delete.test.js.snap +++ /dev/null @@ -1,15 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`delete test complex cascade delete for entity with 'not exists' 1`] = `"DELETE FROM Books as Books WHERE ( Books.author_id is not null or Books.author_version is not null ) and not exists (SELECT 1 as _exists FROM Author as Author left JOIN Books as parent ON parent.ID = Author.parent_ID WHERE Author.parent_ID = parent.code and parent.descr = Author.version)"`; - -exports[`delete test simple cascade delete for entity with 'not exists' 1`] = `"DELETE FROM Books as Books WHERE ( Books.author_id is not null ) and not exists (SELECT 1 as _exists FROM Author as Author WHERE Author.id = Author.parent_ID)"`; - -exports[`delete test simple cascade delete for entity with 'not in' 1`] = `"DELETE FROM Foo as Foo WHERE Foo.x not in (SELECT Foo2.a FROM Foo2 as Foo2)"`; - -exports[`delete test with from entity 1`] = `"DELETE FROM Foo as Foo"`; - -exports[`delete test with from ref 1`] = `"DELETE FROM Foo as Foo"`; - -exports[`delete test with from ref and alias 1`] = `"DELETE FROM Foo as lala"`; - -exports[`delete test with from string and where clause 1`] = `"DELETE FROM Foo as Foo WHERE Foo.x < ?"`; diff --git a/db-service/test/cqn2sql/__snapshots__/drop.test.js.snap b/db-service/test/cqn2sql/__snapshots__/drop.test.js.snap deleted file mode 100644 index 134039c8c..000000000 --- a/db-service/test/cqn2sql/__snapshots__/drop.test.js.snap +++ /dev/null @@ -1,13 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`drop test drop table with string entity 1`] = ` -{ - "sql": "DROP TABLE IF EXISTS Foo", -} -`; - -exports[`drop test drop table with string table 1`] = ` -{ - "sql": "DROP TABLE IF EXISTS Foo", -} -`; diff --git a/db-service/test/cqn2sql/__snapshots__/expression.test.js.snap b/db-service/test/cqn2sql/__snapshots__/expression.test.js.snap deleted file mode 100644 index 9f56aeb01..000000000 --- a/db-service/test/cqn2sql/__snapshots__/expression.test.js.snap +++ /dev/null @@ -1,77 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`expressions ref is between two range 1`] = ` -{ - "sql": "SELECT Foo.ID,Foo.a,Foo.b,Foo.c,Foo.x FROM Foo as Foo WHERE Foo.x regexp ?", - "values": [ - "/\\d/", - ], -} -`; - -exports[`expressions ref is in list of sub select 1`] = ` -{ - "sql": "SELECT Foo.ID,Foo.a,Foo.b,Foo.c,Foo.x FROM Foo as Foo WHERE Foo.x IN (SELECT Foo2.name FROM Foo2 as Foo2)", - "values": [], -} -`; - -exports[`expressions ref is like pattern 1`] = ` -{ - "sql": "SELECT Foo.ID,Foo.a,Foo.b,Foo.c,Foo.x FROM Foo as Foo WHERE Foo.x like ?", - "values": [ - "%123", - ], -} -`; - -exports[`expressions ref is regular expression 1`] = ` -{ - "sql": "SELECT Foo.ID,Foo.a,Foo.b,Foo.c,Foo.x FROM Foo as Foo WHERE Foo.x between ? and ?", - "values": [ - 1, - 20, - ], -} -`; - -exports[`expressions ref list with multiple refs is in list of sub select 1`] = ` -{ - "sql": "SELECT Foo.ID,Foo.a,Foo.b,Foo.c,Foo.x FROM Foo as Foo WHERE (Foo.x,Foo.b) IN (SELECT Foo2.ID,Foo2.name FROM Foo2 as Foo2)", - "values": [], -} -`; - -exports[`expressions ref list with one ref is in list of sub select 1`] = ` -{ - "sql": "SELECT Foo.ID,Foo.a,Foo.b,Foo.c,Foo.x FROM Foo as Foo WHERE (Foo.x) IN (SELECT Foo2.name FROM Foo2 as Foo2)", - "values": [], -} -`; - -exports[`expressions with complex xpr 1`] = ` -{ - "sql": "SELECT Foo.ID,Foo.a,Foo.b,Foo.c,Foo.x FROM Foo as Foo WHERE (Foo.x < ?) AND (Foo.x > ?)", - "values": [ - 9, - 1, - ], -} -`; - -exports[`expressions with exists 1`] = ` -{ - "sql": "SELECT Foo.ID,Foo.a,Foo.b,Foo.c,Foo.x FROM Foo as Foo WHERE exists (SELECT Foo2.name FROM Foo2 as Foo2) or not exists (SELECT Foo22.name FROM Foo2 as Foo22)", - "values": [], -} -`; - -exports[`expressions with long xpr 1`] = ` -{ - "sql": "SELECT Foo.ID,Foo.a,Foo.b,Foo.c,Foo.x FROM Foo as Foo WHERE Foo.x < ? AND Foo.x > ?", - "values": [ - 9, - 1, - ], -} -`; diff --git a/db-service/test/cqn2sql/__snapshots__/function.test.js.snap b/db-service/test/cqn2sql/__snapshots__/function.test.js.snap deleted file mode 100644 index bad98b4f0..000000000 --- a/db-service/test/cqn2sql/__snapshots__/function.test.js.snap +++ /dev/null @@ -1,32 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`function contains complex 1`] = ` -{ - "sql": "SELECT Foo.ID,Foo.a,Foo.b,Foo.c,Foo.x FROM Foo as Foo WHERE ifnull(instr((Foo.a,Foo.b),?),0)", - "values": [ - "5", - ], -} -`; - -exports[`function fn with .xpr as argument 1`] = ` -{ - "sql": "SELECT Foo.ID,Foo.a,Foo.b,Foo.c,Foo.x FROM Foo as Foo WHERE round(Foo.x - ?,?)", - "values": [ - 100, - 3, - ], -} -`; - -exports[`function wrap xpr in concat functions in parentheses 1`] = ` -{ - "sql": "SELECT ? || (? * ? - ?) as something FROM Foo as Foo", - "values": [ - 8, - 2, - 0, - 2023, - ], -} -`; diff --git a/db-service/test/cqn2sql/__snapshots__/insert.test.js.snap b/db-service/test/cqn2sql/__snapshots__/insert.test.js.snap deleted file mode 100644 index b696fad0a..000000000 --- a/db-service/test/cqn2sql/__snapshots__/insert.test.js.snap +++ /dev/null @@ -1,56 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`insert insert only test filter in insert rows into columns with not existing column 1`] = ` -{ - "entries": [ - [ - "[[1,"'asd'",2],[9,"mmm'",77]]", - ], - ], - "sql": "INSERT INTO Foo2 (ID,not_existing,something) SELECT value->>'$[0]',value->>'$[1]',value->>'$[2]' FROM json_each(?)", -} -`; - -exports[`insert insert only test with insert entries 1`] = ` -{ - "entries": [ - [ - "[{"ID":1,"name":null,"a":2},{"ID":null,"name":"'asd'","a":6}]", - ], - ], - "sql": "INSERT INTO Foo2 (ID,name,a) SELECT value->>'$."ID"',value->>'$."name"',value->>'$."a"' FROM json_each(?)", -} -`; - -exports[`insert insert only test with insert rows into columns 1`] = ` -{ - "entries": [ - [ - "[[1,"'asd'",2],[9,"mmm'",77]]", - ], - ], - "sql": "INSERT INTO Foo (ID,b,a) SELECT value->>'$[0]',value->>'$[1]',value->>'$[2]' FROM json_each(?)", -} -`; - -exports[`insert insert only test with insert values into columns 1`] = ` -{ - "entries": [ - [ - "[[1,"'asd'",2]]", - ], - ], - "sql": "INSERT INTO Foo (ID,b,x) SELECT value->>'$[0]',value->>'$[1]',value->>'$[2]' FROM json_each(?)", -} -`; - -exports[`insert insert only test with insert with alias 1`] = ` -{ - "entries": [ - [ - "[{"ID":1,"name":null,"a":2},{"ID":null,"name":"'asd'","a":6}]", - ], - ], - "sql": "INSERT INTO Foo2 as Fooooo2 (ID,name,a) SELECT value->>'$."ID"',value->>'$."name"',value->>'$."a"' FROM json_each(?)", -} -`; diff --git a/db-service/test/cqn2sql/__snapshots__/select.test.js.snap b/db-service/test/cqn2sql/__snapshots__/select.test.js.snap deleted file mode 100644 index 6751746a9..000000000 --- a/db-service/test/cqn2sql/__snapshots__/select.test.js.snap +++ /dev/null @@ -1,206 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`cqn2sql GROUP BY GROUP BY two columns 1`] = `"SELECT Foo.a,Foo.b FROM Foo as Foo GROUP BY Foo.x,Foo.c"`; - -exports[`cqn2sql HAVING clauses with select specific elements with from type string with having clause 1`] = `"SELECT Foo.a,Foo.b,Foo.c FROM Foo as Foo HAVING Foo.x < ?"`; - -exports[`cqn2sql LIMIT with limit and offset 1`] = `"SELECT Foo.a,Foo.b,Foo.c FROM Foo as Foo LIMIT ? OFFSET ?"`; - -exports[`cqn2sql LIMIT with limit without offset 1`] = `"SELECT Foo.a,Foo.b,Foo.c FROM Foo as Foo LIMIT ?"`; - -exports[`cqn2sql ONE one results in limit 1 1`] = `"SELECT Foo.a,Foo.b,Foo.c FROM Foo as Foo LIMIT ?"`; - -exports[`cqn2sql ORDER BY ORDER BY alias 1`] = `"SELECT Foo.a,Foo.b,count(Foo.x) as count1 FROM Foo as Foo ORDER BY count1 ASC"`; - -exports[`cqn2sql ORDER BY ORDER BY with @cds.collate false 1`] = `"SELECT FooCollate.ID,FooCollate.collateString,FooCollate.nonCollateString FROM FooCollate as FooCollate ORDER BY FooCollate.collateString COLLATE NOCASE ASC,FooCollate.nonCollateString ASC"`; - -exports[`cqn2sql WHERE EXISTS with nested EXISTS 1`] = `"SELECT T2.ID,T2.a,T2.b,T2.c,T2.x FROM Foo as T2 WHERE exists (SELECT 1 as "1" FROM Books as T1 WHERE T1.ID = ? and exists (SELECT 1 as "1" FROM Foo2 as T0 WHERE T0.ID = ? and T1.ID = T0.a) and T2.ID = T1.ID)"`; - -exports[`cqn2sql WHERE entries where one column holds entries smaller than 9 1`] = `"SELECT Foo.ID,Foo.a,Foo.b,Foo.c,Foo.x FROM Foo as Foo WHERE Foo.x < ?"`; - -exports[`cqn2sql WHERE entries where one column holds entries which are in list 1`] = `"SELECT Foo.ID,Foo.a,Foo.b,Foo.c,Foo.x FROM Foo as Foo WHERE Foo.x IN (?,?,?)"`; - -exports[`cqn2sql WHERE entries where with int reference and param true 1`] = ` -{ - "sql": "SELECT Foo.ID,Foo.a,Foo.b,Foo.c,Foo.x FROM Foo as Foo WHERE Foo.x = :7", - "values": [], -} -`; - -exports[`cqn2sql WHERE entries where with place holder 1`] = ` -{ - "sql": "SELECT Foo.ID,Foo.a,Foo.b,Foo.c,Foo.x FROM Foo as Foo WHERE Foo.ID = ?", - "values": [], -} -`; - -exports[`cqn2sql WHERE select with a nested select in a complex where 1`] = `"SELECT Foo.a,Foo.b,Foo.c FROM Foo as Foo WHERE ( Foo.x + ? ) < ? AND Foo.x IN (SELECT Foo2.a FROM Foo as Foo2 WHERE Foo2.x < ?)"`; - -exports[`cqn2sql WHERE select with a nested select in where 1`] = `"SELECT Foo.a,Foo.b,Foo.c FROM Foo as Foo WHERE Foo.x IN (SELECT Foo2.a FROM Foo as Foo2 WHERE Foo2.x < ?)"`; - -exports[`cqn2sql WHERE where with partial cqn 1`] = `"SELECT Foo.ID,Foo.a,Foo.b,Foo.c,Foo.x FROM Foo as Foo WHERE (Foo.x = ?)"`; - -exports[`cqn2sql WHERE where with two partial cqn 1`] = `"SELECT Foo.ID,Foo.a,Foo.b,Foo.c,Foo.x FROM Foo as Foo WHERE (Foo.x + ?) = ?"`; - -exports[`cqn2sql WHERE with contains with multiple arguments 1`] = `"SELECT Foo.ID,Foo.a,Foo.b,Foo.c,Foo.x FROM Foo as Foo WHERE Foo.a = ? and ifnull(instr((Foo.a,Foo.b,Foo.c,Foo.x),?),0)"`; - -exports[`cqn2sql WHERE with contains with one column in where clause 1`] = ` -{ - "sql": "SELECT Foo.ID,Foo.a,Foo.b,Foo.c,Foo.x FROM Foo as Foo WHERE ifnull(instr((Foo.b),?),0)", - "values": [ - "5", - ], -} -`; - -exports[`cqn2sql WHERE with list of values 1`] = ` -{ - "sql": "SELECT Foo.ID,Foo.a,Foo.b,Foo.c,Foo.x FROM Foo as Foo WHERE (Foo.a,Foo.b,?) = (Foo.c,?,Foo.x)", - "values": [ - 1, - "d", - ], -} -`; - -exports[`cqn2sql WHERE with select with exist in where condition 1`] = `"SELECT T1.ID,T1.a,T1.b,T1.c,T1.x FROM Foo as T1 WHERE exists (SELECT Foo2.ID,Foo2.name,Foo2.a FROM Foo2 as Foo2)"`; - -exports[`cqn2sql aggregation functions with select with count(1) 1`] = `"SELECT count(?) as count FROM Foo as Foo"`; - -exports[`cqn2sql aggregation functions with select with different functions without alias in elements 1`] = `"SELECT min(Foo.x) as min,count(?) as count,sum(Foo.x) as sum FROM Foo as Foo"`; - -exports[`cqn2sql aggregation functions with select with functions in elements new notation 1`] = `"SELECT min(Foo.x) as foo1,Foo.a,count(*) as foo2,count(?) as foo3,sum(Foo.x) as foo4 FROM Foo as Foo"`; - -exports[`cqn2sql aggregation functions with select with functions in where clause new notation 1`] = `"SELECT Foo.a,Foo.b,Foo.c FROM Foo as Foo WHERE max(Foo.x) < ?"`; - -exports[`cqn2sql complex combinations AS, sub query 1`] = `"SELECT Foo.a,Foo.b as B,1 as C,Foo.x + ? as D,(SELECT Foo2.ID,Foo2.a,Foo2.b,Foo2.c,Foo2.x FROM Foo as Foo2) as E FROM Foo as Foo"`; - -exports[`cqn2sql complex combinations Exists in object mode in complex where 1`] = ` -{ - "sql": "SELECT Foo.ID,Foo.a,Foo.b,Foo.c,Foo.x FROM Foo as Foo WHERE Foo.ID = ? and ( exists (SELECT Author.id FROM Author as Author WHERE Author.id <> ?) or exists (SELECT Foo2.ID FROM Foo2 as Foo2 WHERE Foo2.name is distinct from ?) )", - "values": [ - "123", - "", - "", - ], -} -`; - -exports[`cqn2sql complex combinations WHERE, GROUP BY, HAVING, ORDER BY, LIMIT, OFFSET 1`] = `"SELECT Foo.x + ? as foo1,Foo.b,Foo.c FROM Foo as Foo WHERE Foo.ID = ? GROUP BY Foo.x HAVING Foo.x < ? ORDER BY c ASC LIMIT ? OFFSET ?"`; - -exports[`cqn2sql functions new notation function with multiple xpr 1`] = `"SELECT replace_regexpr(Foo.a,?,? flag ? in ? with ?) as replaced FROM Foo as Foo"`; - -exports[`cqn2sql functions new notation function with multiple xpr 2`] = ` -[ - 5, - "A", - "i", - "ABC-abc-AAA-aaa", - "B", -] -`; - -exports[`cqn2sql functions new notation function with xpr 1`] = `"SELECT replace_regexpr(? flag ? in ? with ?) as replaced FROM Foo as Foo"`; - -exports[`cqn2sql functions new notation function with xpr 2`] = ` -[ - "A", - "i", - "ABC-abc-AAA-aaa", - "B", -] -`; - -exports[`cqn2sql functions new notation in filter with 1 arg new notation 1`] = ` -{ - "sql": "SELECT Foo.ID,Foo.a,Foo.b,Foo.c,Foo.x FROM Foo as Foo WHERE lower(Foo.c) = ?", - "values": [ - "name", - ], -} -`; - -exports[`cqn2sql functions new notation in filter with 2 arg new notation 1`] = `"SELECT Foo.ID,Foo.a,Foo.b,Foo.c,Foo.x FROM Foo as Foo WHERE Foo.c = Foo.a || Foo.b"`; - -exports[`cqn2sql functions new notation in filter with 3 arg new notation 1`] = ` -{ - "sql": "SELECT Foo.ID,Foo.a,Foo.b,Foo.c,Foo.x FROM Foo as Foo WHERE Foo.c = ? || Foo.a || ?", - "values": [ - "Existing", - "!", - ], -} -`; - -exports[`cqn2sql functions new notation in filter with asterisk as arg new notation 1`] = `"SELECT Foo.ID,Foo.a,Foo.b,Foo.c,Foo.x FROM Foo as Foo HAVING count(*) > ?"`; - -exports[`cqn2sql functions new notation in filter with nested functions new notation 1`] = ` -{ - "sql": "SELECT Foo.ID,Foo.a,Foo.b,Foo.c,Foo.x FROM Foo as Foo WHERE lower(Foo.a) = lower(upper(trim(?))) and length(trim(?)) = Foo.b", - "values": [ - " existing name ", - " name", - ], -} -`; - -exports[`cqn2sql functions new notation in filter with subselect as function param 1`] = `"SELECT Foo.ID,Foo.a,Foo.b,Foo.c,Foo.x FROM Foo as Foo WHERE Foo.ID = any((SELECT Foo2.ID FROM Foo2 as Foo2 WHERE Foo2.a = ?))"`; - -exports[`cqn2sql functions new notation in orderby with 1 arg new notation 1`] = `"SELECT Foo.ID,Foo.a,Foo.b,Foo.c,Foo.x FROM Foo as Foo ORDER BY lower(Foo.c) DESC"`; - -exports[`cqn2sql quoted column aliases select with simple subselect and column aliases 1`] = `"SELECT Foo.a,Foo.b as B,1 as C,Foo.x + ? as D,(SELECT Foo2.a,Foo2.b as B,false as False,Foo2.x + ? as Xpr FROM Foo as Foo2) as E FROM Foo as Foo"`; - -exports[`cqn2sql quoted column aliases select with subselect in exists and column aliases 1`] = `"SELECT T2.id,T2.version,T2.parent_ID FROM Author as T2 WHERE exists (SELECT 1 as One,T1.code as Xpr1 FROM Books as T1 WHERE T1.ID = ? and exists (SELECT 3 as Three,T0.x + ? as Xpr2 FROM Foo as T0 WHERE T0.ID = ? and T1.ID = T0.b))"`; - -exports[`cqn2sql quoted column aliases select with subselect with in and column aliases 1`] = ` -{ - "sql": "SELECT Foo.a as A,? as ABC,Foo.x + ? as Xpr1 FROM Foo as Foo WHERE ( Foo.x + ? ) < ? AND Foo.x IN (SELECT Foo2.a as B,Foo2.x - ? as Xpr2 FROM Foo as Foo2 WHERE Foo2.x < ?)", - "values": [ - "abc", - 1, - 1, - 9, - 4, - 9, - ], -} -`; - -exports[`cqn2sql quoted column aliases simple select with column aliases 1`] = ` -{ - "sql": "SELECT T.a as A,true as True,false as False,? as Null,count(*) as CountFunc FROM Foo as T", - "values": [ - null, - ], -} -`; - -exports[`cqn2sql selection of columns of one table select distinct 1`] = `"SELECT DISTINCT Foo.a,Foo.b,Foo.c FROM Foo as Foo"`; - -exports[`cqn2sql selection of columns of one table select with static values 1`] = ` -{ - "sql": "SELECT 5 as ID,? as a,3.14 as pi,cast(3.1415 as DECIMAL) as pid,cast(? as NCLOB) as stringl,cast(true as BOOLEAN) as boolt,cast(? as DATE) as date,cast(? as TIME) as time,cast(? as DATETIME) as datetime,cast(? as TIMESTAMP) as timestamp FROM Foo as Foo", - "values": [ - "simple string", - "large string", - "1970-01-01", - "00:00:00", - "1970-01-01 00:00:00", - "1970-01-01 00:00:00.000", - ], -} -`; - -exports[`cqn2sql selection of columns of one table with select from non existent entity with star wildcard 1`] = `"SELECT * FROM "¿HoWdIdYoUmAnAgeToCaLaNeNtItyThIsNaMe?""`; - -exports[`cqn2sql selection of columns of one table with select specific elements with from ref 1`] = `"SELECT Foo.a,Foo.b,Foo.c FROM Foo as Foo"`; - -exports[`cqn2sql selection of columns of one table with select that has (faked) reflection model 1`] = `"SELECT T1.ID,T1.a,T1.b,T1.c,T1.x FROM Foo as T1"`; - -exports[`cqn2sql selection of columns of one table with select with asterisk in columns 1`] = `"SELECT Foo.ID,Foo.a,Foo.b,Foo.c,Foo.x FROM Foo as Foo"`; - -exports[`cqn2sql selection of columns of one table with select with empty orderBy and specific elements with from type string 1`] = `"SELECT Foo.a,Foo.b,Foo.c FROM Foo as Foo"`; - -exports[`cqn2sql selection of columns of one table with select with from ref and elements = undefined 1`] = `"SELECT Foo.ID,Foo.a,Foo.b,Foo.c,Foo.x FROM Foo as Foo"`; - -exports[`cqn2sql selection of columns of one table with select with from ref and elements as empty array 1`] = `"SELECT FROM Foo as Foo"`; diff --git a/db-service/test/cqn2sql/__snapshots__/update.test.js.snap b/db-service/test/cqn2sql/__snapshots__/update.test.js.snap deleted file mode 100644 index a861c8389..000000000 --- a/db-service/test/cqn2sql/__snapshots__/update.test.js.snap +++ /dev/null @@ -1,107 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`.update data alone still works 1`] = ` -{ - "sql": "UPDATE Foo2 AS Foo2 SET ID=?,name=?,a=?", - "values": [ - 1, - null, - null, - ], -} -`; - -exports[`.update set enhances data 1`] = ` -{ - "sql": "UPDATE Foo2 AS Foo2 SET a=?,ID=?,name=?", - "values": [ - 2, - 1, - "'asd'", - ], -} -`; - -exports[`.update set overwrites data 1`] = ` -{ - "sql": "UPDATE Foo2 AS Foo2 SET a=?,ID=?,name=?,a=?", - "values": [ - 2, - 1, - "'asd'", - 6, - ], -} -`; - -exports[`.update test with entity and values with operators 1`] = ` -{ - "sql": "UPDATE Foo2 AS Foo2 SET ID=?,name=?,a=Foo2.a - ?", - "values": [ - 42, - "'asd'", - 1, - ], -} -`; - -exports[`.update test with entity of type string 1`] = ` -{ - "sql": "UPDATE Foo2 AS Foo2 SET ID=?,name=?,a=?", - "values": [ - 1, - "'asd'", - 2, - ], -} -`; - -exports[`.update test with entity of type string and where clause 1`] = ` -{ - "sql": "UPDATE Foo2 AS Foo2 SET ID=?,name=?,a=? WHERE Foo2.a < ?", - "values": [ - 1, - "'asd'", - 2, - 9, - ], -} -`; - -exports[`.update test with setting a value to null 1`] = ` -{ - "sql": "UPDATE Foo2 AS Foo2 SET ID=?,name=?,a=? WHERE Foo2.a < ?", - "values": [ - 1, - null, - 2, - 9, - ], -} -`; - -exports[`.update test with subselect - sflight example 1`] = ` -{ - "sql": "UPDATE Travel AS Travel SET TotalPrice=coalesce(BookingFee,0) + (SELECT coalesce(sum(FlightPrice + (SELECT coalesce(sum(Price),0) as coalesce FROM BookingSupplement WHERE to_Booking_BookingUUID = BookingUUID)),0) as coalesce FROM Booking WHERE to_Travel_TravelUUID = TravelUUID)", - "values": [], -} -`; - -exports[`.update virtual and non-existing fields filtered out from with 1`] = ` -{ - "sql": "UPDATE Foo2 AS Foo2 SET ID=?,name=?", - "values": [ - 1, - "'asd'", - ], -} -`; - -exports[`.update virtual and non-existing filtered out from data 1`] = ` -{ - "sql": "UPDATE Foo2 AS Foo2 SET ID=?", - "values": [ - 1, - ], -} -`; diff --git a/db-service/test/cqn2sql/__snapshots__/upsert.test.js.snap b/db-service/test/cqn2sql/__snapshots__/upsert.test.js.snap deleted file mode 100644 index 2ad78d252..000000000 --- a/db-service/test/cqn2sql/__snapshots__/upsert.test.js.snap +++ /dev/null @@ -1,34 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`upsert test with entries 1`] = ` -{ - "entries": [ - [ - "[{"ID":1,"name":null,"a":2},{"ID":null,"name":"'asd'","a":6}]", - ], - ], - "sql": "INSERT INTO Foo2 (ID,name,a) SELECT value->>'$."ID"',value->>'$."name"',value->>'$."a"' FROM json_each(?) WHERE true ON CONFLICT(ID) DO UPDATE SET name = excluded.name,a = excluded.a", -} -`; - -exports[`upsert test with keys only 1`] = ` -{ - "entries": [ - [ - "[[1],[9]]", - ], - ], - "sql": "INSERT INTO Foo2 (ID) SELECT value->>'$[0]' FROM json_each(?) WHERE true ON CONFLICT(ID) DO NOTHING", -} -`; - -exports[`upsert test with rows (quoted) 1`] = ` -{ - "entries": [ - [ - "[[1,null,2]]", - ], - ], - "sql": "INSERT INTO """Foo2Quoted""" ("""ID""","""name""","""a""") SELECT value->>'$[0]',value->>'$[1]',value->>'$[2]' FROM json_each(?) WHERE true ON CONFLICT("""ID""") DO UPDATE SET """name""" = excluded."""name""","""a""" = excluded."""a"""", -} -`; diff --git a/db-service/test/cqn2sql/cqn.js b/db-service/test/cqn2sql/cqn.js deleted file mode 100644 index a3b7f9b57..000000000 --- a/db-service/test/cqn2sql/cqn.js +++ /dev/null @@ -1,498 +0,0 @@ -module.exports.select = { - SELECT: { - from: { ref: ['Foo'] }, - }, -} -module.exports.selectF = { - SELECT: { - from: { ref: ['Foo2'] }, - }, -} -module.exports.selectContainsOneColumn = { - SELECT: { - from: { ref: ['Foo'] }, - where: [ - { - func: 'contains', - args: [{ list: [{ ref: ['b'] }] }, { val: '5' }], - }, - ], - }, -} - -module.exports.selectWithColumns = { - SELECT: { - from: { ref: ['Foo'] }, - columns: [{ ref: ['a'] }, { ref: ['b'] }, { ref: ['c'] }], - }, -} -module.exports.selectWithColumnsEmptyOrderBy = { - SELECT: { - from: { ref: ['Foo'] }, - columns: [{ ref: ['a'] }, { ref: ['b'] }, { ref: ['c'] }], - orderBy: [], - }, -} -module.exports.selectWithColumnsWithAsterisk = { - SELECT: { - from: { ref: ['Foo'] }, - columns: ['*'], - }, -} -module.exports.orderByWithAlias = { - SELECT: { - from: { ref: ['Foo'] }, - columns: [{ ref: ['a'] }, { ref: ['b'] }, { func: 'count', args: [{ ref: ['x'] }], as: 'count1' }], - orderBy: [{ ref: ['count1'], sort: 'asc' }], - }, -} -module.exports.orderByCollations = { - SELECT: { - from: { ref: ['FooCollate'] }, - orderBy: [{ ref: ['collateString'], sort: 'asc' }, { ref: ['nonCollateString'], sort: 'asc' }], - localized: true - }, -} -module.exports.selectDistinct = { - SELECT: { - distinct: true, - from: { ref: ['Foo'] }, - columns: [{ ref: ['a'] }, { ref: ['b'] }, { ref: ['c'] }], - }, -} -module.exports.selectWithCSN = { - SELECT: { - from: { ref: ['Foo'], as: 'T1' }, - }, - [Symbol.for('sap.cds.model')]: { test: 'model' }, -} -module.exports.selectNonExistent = { - SELECT: { - columns: ['*'], - from: { ref: ['¿HoWdIdYoUmAnAgeToCaLaNeNtItyThIsNaMe?'] }, - }, -} - -module.exports.selectWhereCqn = { - SELECT: { - from: { ref: ['Foo'] }, - where: [{ xpr: [{ ref: ['x'] }, '=', { val: 9 }] }], - }, -} -module.exports.selectWhereWithOnePlaceholderCqn = { - SELECT: { - from: { ref: ['Foo'] }, - where: [{ ref: ['ID'] }, '=', { ref: ['?'], param: true }], - }, -} -module.exports.selectWhereWithRefStringParamTrueCqn = { - SELECT: { - from: { ref: ['Foo'] }, - where: [{ ref: ['x'] }, '=', { ref: ['y'], param: true }], - }, -} -module.exports.selectWhereWithRefIntParamTrueCqn = { - SELECT: { - from: { ref: ['Foo'] }, - where: [{ ref: ['x'] }, '=', { ref: [7], param: true }], - }, -} -module.exports.selectWhereTwoCqn = { - SELECT: { - from: { ref: ['Foo'] }, - where: [{ xpr: [{ ref: ['x'] }, '+', { val: 9 }] }, '=', { val: 9 }], - }, -} -module.exports.selectWhereList = { - SELECT: { - from: { ref: ['Foo'] }, - where: [{ ref: ['x'] }, 'IN', { list: [{ val: 1 }, { val: 2 }, { val: 3 }] }], - }, -} -module.exports.selectWhereSelect = { - SELECT: { - from: { ref: ['Foo'] }, - columns: [{ ref: ['a'] }, { ref: ['b'] }, { ref: ['c'] }], - where: [ - { ref: ['x'] }, - 'IN', - { - SELECT: { - from: { ref: ['Foo'] }, - columns: [{ ref: ['a'] }], - where: [{ ref: ['x'] }, '<', { val: 9 }], - }, - }, - ], - }, -} -module.exports.selectComplexWhere = { - SELECT: { - from: { ref: ['Foo'] }, - columns: [{ ref: ['a'] }, { ref: ['b'] }, { ref: ['c'] }], - where: [ - '(', - { ref: ['x'] }, - '+', - { val: 1 }, - ')', - '<', - { val: 9 }, - 'AND', - { ref: ['x'] }, - 'IN', - { - SELECT: { - from: { ref: ['Foo'] }, - columns: [{ ref: ['a'] }], - where: [{ ref: ['x'] }, '<', { val: 9 }], - }, - }, - ], - }, -} -module.exports.selectWhereExists = { - SELECT: { - from: { ref: ['Foo'], as: 'T1' }, - where: [ - 'exists', - { - SELECT: { - from: { ref: ['Foo2'] }, - }, - }, - ], - }, -} -module.exports.selectWhereNestedExists = { - SELECT: { - from: { ref: ['Foo'], as: 'T2' }, - where: [ - 'exists', - { - SELECT: { - from: { ref: ['Books'], as: 'T1' }, - columns: [{ val: 1 }], - where: [ - { ref: ['ID'] }, - '=', - { val: 1 }, - 'and', - 'exists', - { - SELECT: { - from: { ref: ['Foo2'], as: 'T0' }, - columns: [{ val: 1 }], - where: [ - { ref: ['ID'] }, - '=', - { val: 11 }, - 'and', - { ref: ['T1', 'ID'] }, - '=', - { - ref: ['a'], - }, - ], - }, - }, - 'and', - { ref: ['T2', 'ID'] }, - '=', - { - ref: ['ID'], - }, - ], - }, - }, - ], - }, -} -module.exports.selectHaving = { - SELECT: { - from: { ref: ['Foo'] }, - columns: [{ ref: ['a'] }, { ref: ['b'] }, { ref: ['c'] }], - having: [{ ref: ['x'] }, '<', { val: 9 }], - }, -} -module.exports.selectFuncitonWithoutAlias = { - SELECT: { - from: { ref: ['Foo'] }, - columns: [{ xpr: [{ ref: ['x'] }, '+', { val: 1 }] }], - }, -} -module.exports.selectAggregationLimitOrder = { - SELECT: { - from: { ref: ['Foo'] }, - columns: [{ xpr: [{ ref: ['x'] }, '+', { val: 1 }], as: 'foo1' }, { ref: ['b'] }, { ref: ['c'] }], - where: [{ ref: ['ID'] }, '=', { val: 111 }], - groupBy: [{ ref: ['x'] }], - having: [{ ref: ['x'] }, '<', { val: 9 }], - orderBy: [{ ref: ['c'], sort: 'asc' }], - limit: { rows: { val: 11 }, offset: { val: 22 } }, - }, -} -module.exports.selectSubSelect = { - SELECT: { - from: { ref: ['Foo'] }, - columns: [ - { ref: ['a'] }, - { ref: ['b'], as: 'B' }, - { val: 1, as: 'C' }, - { xpr: [{ ref: ['x'] }, '+', { val: 2 }], as: 'D' }, - { - SELECT: { - from: { ref: ['Foo'] }, - }, - as: 'E', - }, - ], - }, -} -module.exports.groupBy = { - SELECT: { - from: { ref: ['Foo'] }, - columns: [{ ref: ['a'] }, { ref: ['b'] }], - groupBy: [{ ref: ['x'] }, { ref: ['c'] }], - }, -} - -module.exports.one = { - SELECT: { - from: { ref: ['Foo'] }, - columns: [{ ref: ['a'] }, { ref: ['b'] }, { ref: ['c'] }], - one: true, - }, -} -module.exports.oneWithLimit = { - SELECT: { - from: { ref: ['Foo'] }, - columns: [{ ref: ['a'] }, { ref: ['b'] }, { ref: ['c'] }], - one: true, - limit: { rows: { val: 2 }, offset: { val: 5 } }, - }, -} -module.exports.limit = { - SELECT: { - from: { ref: ['Foo'] }, - columns: [{ ref: ['a'] }, { ref: ['b'] }, { ref: ['c'] }], - limit: { rows: { val: 1 } }, - }, -} -module.exports.limitOffset = { - SELECT: { - from: { ref: ['Foo'] }, - columns: [{ ref: ['a'] }, { ref: ['b'] }, { ref: ['c'] }], - limit: { rows: { val: 1 }, offset: { val: 2 } }, - }, -} - -module.exports.selectWithFunctionWithoutAlias = { - SELECT: { - from: { ref: ['Foo'] }, - columns: [{ func: 'min', args: [{ ref: ['x'] }] }], - }, -} - -module.exports.selectWithFunctionsWithoutAlias = { - SELECT: { - from: { ref: ['Foo'] }, - columns: [ - { func: 'min', args: [{ ref: ['x'] }] }, - { func: 'count', args: [{ val: 1 }] }, - { func: 'sum', args: [{ ref: ['x'] }] }, - ], - }, -} - -module.exports.selectWithSameFunctionsWithoutAlias = { - SELECT: { - from: { ref: ['Foo'] }, - columns: [ - { func: 'count', args: ['*'] }, - { func: 'count', args: [{ val: 1 }] }, - { func: 'count', args: ['*'] }, - ], - }, -} - -module.exports.selectWithAggregationNew = { - SELECT: { - from: { ref: ['Foo'] }, - columns: [ - { func: 'min', args: [{ ref: ['x'] }], as: 'foo1' }, - { ref: ['a'] }, - { func: 'count', args: ['*'], as: 'foo2' }, - { func: 'count', args: [{ val: 1 }], as: 'foo3' }, - { func: 'sum', args: [{ ref: ['x'] }], as: 'foo4' }, - ], - }, -} - -module.exports.selectWithCountOne = { - SELECT: { - from: { ref: ['Foo'] }, - columns: [{ func: 'count', args: [{ val: 1 }] }], - }, -} - -module.exports.selectWhereAggregationNew = { - SELECT: { - from: { ref: ['Foo'] }, - columns: [{ ref: ['a'] }, { ref: ['b'] }, { ref: ['c'] }], - where: [{ func: 'max', args: [{ ref: ['x'] }] }, '<', { val: 9 }], - }, -} -module.exports.aliasWithInSubSelect = { - SELECT: { - from: { ref: ['Foo'] }, - columns: [ - { ref: ['a'], as: 'A' }, - { val: 'abc', as: 'ABC' }, - { - xpr: [{ ref: ['x'] }, '+', { val: 1 }], - as: 'Xpr1', - }, - ], - where: [ - '(', - { ref: ['x'] }, - '+', - { val: 1 }, - ')', - '<', - { val: 9 }, - 'AND', - { ref: ['x'] }, - 'IN', - { - SELECT: { - from: { ref: ['Foo'] }, - columns: [ - { ref: ['a'], as: 'B' }, - { xpr: [{ ref: ['x'] }, '-', { val: 4 }], as: 'Xpr2' }, - ], - where: [{ ref: ['x'] }, '<', { val: 9 }], - }, - }, - ], - }, -} - -module.exports.aliasWithNestedExists = { - SELECT: { - from: { ref: ['Author'], as: 'T2' }, - where: [ - 'exists', - { - SELECT: { - from: { ref: ['Books'], as: 'T1' }, - columns: [ - { val: 1, as: 'One' }, - { xpr: [{ ref: ['code'] }], as: 'Xpr1' }, - ], - where: [ - { ref: ['ID'] }, - '=', - { val: 1 }, - 'and', - 'exists', - { - SELECT: { - from: { ref: ['Foo'], as: 'T0' }, - columns: [ - { val: 3, as: 'Three' }, - { xpr: [{ ref: ['x'] }, '+', { val: 1 }], as: 'Xpr2' }, - ], - where: [ - { ref: ['ID'] }, - '=', - { val: 11 }, - 'and', - { ref: ['T1', 'ID'] }, - '=', - { - ref: ['b'], - }, - ], - }, - }, - ], - }, - }, - ], - }, -} - -module.exports.aliasWithSubSelect = { - SELECT: { - from: { ref: ['Foo'] }, - columns: [ - { ref: ['a'] }, - { ref: ['b'], as: 'B' }, - { val: 1, as: 'C' }, - { xpr: [{ ref: ['x'] }, '+', { val: 2 }], as: 'D' }, - { - SELECT: { - from: { ref: ['Foo'] }, - columns: [ - { ref: ['a'] }, - { ref: ['b'], as: 'B' }, - { val: false, as: 'False' }, - { xpr: [{ ref: ['x'] }, '+', { val: 2 }], as: 'Xpr' }, - ], - }, - as: 'E', - }, - ], - }, -} -module.exports.selectWhereSmaller = { - SELECT: { - from: { ref: ['Foo'] }, - where: [{ ref: ['x'] }, '<', { val: 9 }], - }, -} -module.exports.join = { - SELECT: { - from: { - join: 'left', - args: [ - { ref: ['Foo'], as: 'A' }, - { ref: ['Foo2'], as: 'B' }, - ], - on: [{ ref: ['A', 'x'] }, '=', { ref: ['B', 'a'] }, 'AND', { ref: ['x'] }, '=', { val: 1 }], - }, - columns: [{ ref: ['c'] }, { ref: ['b'] }, { ref: ['name'] }], - }, -} -module.exports.selectComplexWhereWithExists = { - SELECT: { - from: { ref: ['Foo'] }, - where: [ - { ref: ['ID'] }, - '=', - { val: '123' }, - 'and', - '(', - 'exists', - { - SELECT: { - columns: [{ ref: ['id'] }], - from: { ref: ['Author'] }, - where: [{ ref: ['id'] }, '!=', { val: '' }], - }, - }, - 'or', - 'exists', - { - SELECT: { - columns: [{ ref: ['ID'] }], - from: { ref: ['Foo2'] }, - where: [{ ref: ['name'] }, '!=', { val: '' }], - }, - }, - ')', - ], - }, -} diff --git a/db-service/test/cqn2sql/create.test.js b/db-service/test/cqn2sql/create.test.js deleted file mode 100644 index 73cea65e2..000000000 --- a/db-service/test/cqn2sql/create.test.js +++ /dev/null @@ -1,47 +0,0 @@ -'use strict' -const cds = require('@sap/cds') -const _cqn2sql = require('../../lib/cqn2sql') -function cqn2sql(q, m = cds.model) { - return _cqn2sql(q, m) -} -const cqn = require('./cqn.js') - -beforeAll(async () => { - cds.model = await cds.load(__dirname + '/testModel').then(cds.linked) - //csn = await getTestModel('testModel') -}) - -describe('create with select statements', () => { - xtest('Generate SQL from CREATE stmt with entity name + as SELECT stmt', () => { - const cqnCreate = { - CREATE: { - entity: 'Foo', - as: cqn.select, - }, - } - const { sql } = cqn2sql(cqnCreate) - expect({ sql }).toMatchSnapshot() - // as SELECT is missing in sql - }) - - test('Generate SQL from CREATE stmt with entity name ', () => { - const cqnCreate = { - CREATE: { - entity: 'Foo', - }, - } - const { sql } = cqn2sql(cqnCreate) - expect({ sql }).toMatchSnapshot() - }) - - xtest('Generate SQL from CREATE stmt with CSN entity', () => { - const cqnCreate = { - CREATE: { - entity: cds.model.definitions['Books'], - }, - } - const { sql } = cqn2sql(cqnCreate) - expect({ sql }).toMatchSnapshot() - //TypeError: Cannot read properties of undefined (reading 'length') - }) -}) diff --git a/db-service/test/cqn2sql/delete.test.js b/db-service/test/cqn2sql/delete.test.js deleted file mode 100644 index 2b89725e7..000000000 --- a/db-service/test/cqn2sql/delete.test.js +++ /dev/null @@ -1,339 +0,0 @@ -'use strict' -const cds = require('@sap/cds') -const _cqn2sql = require('../../lib/cqn2sql') -function cqn2sql(q, m = cds.model) { - return _cqn2sql(q, m) -} - -beforeAll(async () => { - cds.model = await cds.load(__dirname + '/testModel').then(cds.linked) -}) -describe('delete', () => { - test('test with from entity', () => { - const cqnDelete = DELETE.from(cds.model.definitions.Foo) - const { sql } = cqn2sql(cqnDelete) - expect(sql).toMatchSnapshot() - }) - - test('test with from ref', () => { - const cqnDelete = { - DELETE: { - from: { ref: ['Foo'] }, - }, - } - const { sql } = cqn2sql(cqnDelete) - expect(sql).toMatchSnapshot() - }) - - test('test with from ref and alias', () => { - const cqnDelete = { - DELETE: { - from: { ref: ['Foo'], as: 'lala' }, - }, - } - const { sql } = cqn2sql(cqnDelete) - expect(sql).toMatchSnapshot() - }) - - test('test with from string and where clause', () => { - const cqnDelete = { - DELETE: { - from: 'Foo', - where: [{ ref: ['x'] }, '<', { val: 9 }], - }, - } - const { sql } = cqn2sql(cqnDelete) - expect(sql).toMatchSnapshot() - }) - - test("test simple cascade delete for entity with 'not in'", () => { - const cqnDelete = { - DELETE: { - from: 'Foo', - where: [ - { - ref: ['x'], - }, - 'not in', - { - SELECT: { - columns: [ - { - ref: ['a'], - }, - ], - from: { ref: ['Foo2'] }, - }, - }, - ], - }, - } - const { sql } = cqn2sql(cqnDelete) - expect(sql).toMatchSnapshot() - }) - - test("test simple cascade delete for entity with 'not exists'", () => { - const cqnDelete = { - DELETE: { - from: 'Books', - where: [ - '(', - { - ref: ['author', 'id'], - }, - 'is not null', - ')', - 'and', - 'not exists', - { - SELECT: { - columns: [ - { - val: 1, - as: '_exists', - }, - ], - from: { - ref: ['Author'], - }, - where: [ - { - ref: ['id'], - }, - '=', - { - ref: ['parent', 'ID'], - }, - ], - }, - }, - ], - }, - } - const { sql } = cqn2sql(cqnDelete) - expect(sql).toMatchSnapshot() - }) - - test("test complex cascade delete for entity with 'not exists'", () => { - const cqnDelete = { - DELETE: { - from: 'Books', - where: [ - '(', - { - ref: ['author', 'id'], - }, - 'is not null', - 'or', - { - ref: ['author', 'version'], - }, - 'is not null', - ')', - 'and', - 'not exists', - { - SELECT: { - columns: [ - { - val: 1, - as: '_exists', - }, - ], - from: { - ref: ['Author'], - }, - where: [ - { - ref: ['parent', 'ID'], - }, - '=', - { - ref: ['parent', 'code'], - }, - 'and', - { - ref: ['parent', 'descr'], - }, - '=', - { - ref: ['Author', 'version'], - }, - ], - }, - }, - ], - }, - } - const { sql } = cqn2sql(cqnDelete) - expect(sql).toMatchSnapshot() - }) - // Do we need this test ? - xtest("test complex reverse cascade delete for entity with 'exists'", () => { - const cqnDelete = { - DELETE: { - from: 'sub1_1_1', - where: [ - '(', - { - ref: ['sub1_1_1', 'sub1_1_parentForeignKey1_1_1'], - }, - 'is not null', - 'or', - { - ref: ['sub1_1_1', 'sub1_1_parentForeignVersion1_1_1'], - }, - 'is not null', - ')', - 'and', - 'exists', - { - SELECT: { - columns: [ - { - val: 1, - as: '_exists', - }, - ], - from: { - ref: ['sub1_1'], - }, - where: [ - { - ref: ['sub1_1_1', 'sub1_1_parentForeignKey1_1_1'], - }, - '=', - { - ref: ['sub1_1', 'key1_1'], - }, - 'and', - { - ref: ['sub1_1_1', 'sub1_1_parentForeignVersion1_1_1'], - }, - '=', - { - ref: ['sub1_1', 'version1_1'], - }, - 'and', - '(', - '(', - { - ref: ['sub1_1', 'sub1_parentForeignKey1_1'], - }, - 'is not null', - 'or', - { - ref: ['sub1_1', 'sub1_parentForeignVersion1_1'], - }, - 'is not null', - ')', - 'and', - 'exists', - { - SELECT: { - columns: [ - { - val: 1, - as: '_exists', - }, - ], - from: { - ref: ['sub1'], - }, - where: [ - { - ref: ['sub1_1', 'sub1_parentForeignKey1_1'], - }, - '=', - { - ref: ['sub1', 'key1'], - }, - 'and', - { - ref: ['sub1_1', 'sub1_parentForeignVersion1_1'], - }, - '=', - { - ref: ['sub1', 'version1'], - }, - 'and', - '(', - '(', - { - ref: ['sub1', 'root_rootForeignKey1'], - }, - 'is not null', - 'or', - { - ref: ['sub1', 'root_rootForeignVersion1'], - }, - 'is not null', - ')', - 'and', - 'exists', - { - SELECT: { - columns: [ - { - val: 1, - as: '_exists', - }, - ], - from: { - ref: ['root'], - }, - where: [ - { - ref: ['sub1', 'root_rootForeignKey1'], - }, - '=', - { - ref: ['root', 'rootKey'], - }, - 'and', - { - ref: ['sub1', 'root_rootForeignVersion1'], - }, - '=', - { - ref: ['root', 'rootVersion'], - }, - 'and', - '(', - { - ref: ['rootKey'], - }, - '=', - { - val: 1, - }, - 'and', - { - ref: ['rootVersion'], - }, - '=', - { - val: 'active', - }, - ')', - ], - }, - }, - ')', - ], - }, - }, - ')', - ], - }, - }, - ], - }, - } - const expected = { - sql: 'DELETE FROM sub1_1_1 WHERE ( sub1_1_1.sub1_1_parentForeignKey1_1_1 IS NOT NULL OR sub1_1_1.sub1_1_parentForeignVersion1_1_1 IS NOT NULL ) AND EXISTS ( SELECT 1 AS _exists FROM sub1_1 WHERE sub1_1_1.sub1_1_parentForeignKey1_1_1 = sub1_1.key1_1 AND sub1_1_1.sub1_1_parentForeignVersion1_1_1 = sub1_1.version1_1 AND ( ( sub1_1.sub1_parentForeignKey1_1 IS NOT NULL OR sub1_1.sub1_parentForeignVersion1_1 IS NOT NULL ) AND EXISTS ( SELECT 1 AS _exists FROM sub1 WHERE sub1_1.sub1_parentForeignKey1_1 = sub1.key1 AND sub1_1.sub1_parentForeignVersion1_1 = sub1.version1 AND ( ( sub1.root_rootForeignKey1 IS NOT NULL OR sub1.root_rootForeignVersion1 IS NOT NULL ) AND EXISTS ( SELECT 1 AS _exists FROM root WHERE sub1.root_rootForeignKey1 = root.rootKey AND sub1.root_rootForeignVersion1 = root.rootVersion AND ( rootKey = 1 AND rootVersion = ? ) ) ) ) ) )', - values: ['active'], - } - expect(cqn2sql(cqnDelete)).toEqual(expected) - }) -}) diff --git a/db-service/test/cqn2sql/drop.test.js b/db-service/test/cqn2sql/drop.test.js deleted file mode 100644 index c974bf19a..000000000 --- a/db-service/test/cqn2sql/drop.test.js +++ /dev/null @@ -1,69 +0,0 @@ -'use strict' -const cds = require('@sap/cds') -const _cqn2sql = require('../../lib/cqn2sql') -function cqn2sql(q, m = cds.model) { - return _cqn2sql(q, m) -} - -beforeAll(async () => { - cds.model = await cds.load(__dirname + '/testModel').then(cds.linked) -}) -describe('drop', () => { - test('test drop table with string entity', () => { - const { sql } = cqn2sql({ - DROP: { - entity: 'Foo', - }, - }) - expect({ sql }).toMatchSnapshot() - }) - test('test drop table with string table', () => { - const { sql } = cqn2sql({ - DROP: { - entity: 'Foo', - table: 'Foo', - }, - }) - expect({ sql }).toMatchSnapshot() - }) - - xtest('test drop table with ref', () => { - const { sql } = cqn2sql({ - DROP: { - table: { ref: ['Foo'] }, - }, - }) - expect({ sql }).toMatchSnapshot() - // Cannot destructure property 'ref' of 'from' as it is undefined. - }) - - xtest('test drop table with ref', () => { - const { sql } = cqn2sql({ - DROP: { - view: { ref: ['Foo'] }, - }, - }) - expect({ sql }).toMatchSnapshot() - // Cannot destructure property 'ref' of 'from' as it is undefined. - }) - - xtest('test drop view with string view', () => { - const { sql } = cqn2sql({ - DROP: { - view: 'Foo', - }, - }) - expect({ sql }).toMatchSnapshot() - //TypeError: Cannot destructure property 'ref' of 'from' as it is undefined. - }) - - xtest('test drop table with CQN entity', () => { - const { sql } = cqn2sql({ - DROP: { - entity: { ref: 'Books' }, - }, - }) - expect({ sql }).toMatchSnapshot() - //"B" not found in the definitions of your model - }) -}) diff --git a/db-service/test/cqn2sql/expression.test.js b/db-service/test/cqn2sql/expression.test.js deleted file mode 100644 index 2093690f5..000000000 --- a/db-service/test/cqn2sql/expression.test.js +++ /dev/null @@ -1,385 +0,0 @@ -'use strict' -const cds = require('@sap/cds') -const _cqn2sql = require('../../lib/cqn2sql') -function cqn2sql(q, m = cds.model) { - return _cqn2sql(q, m) -} - -beforeAll(async () => { - cds.model = await cds.load(__dirname + '/testModel').then(cds.linked) -}) - -describe('expressions', () => { - // xtest('parameterized numbers', () => { - // const opts = { parameterized_numbers: true } - // const xpr = [{ ref: ['x'] }, '<', { val: 9 }] - // const expected = { sql: 'x < ?', values: [9] } - // const result = new ExpressionBuilder(xpr, opts).build() - // const { sql, entries } = cqn2sql(cqnInsert) - // expect({ sql, entries }).toMatchSnapshot() - // expect(result).toEqual(expected) - // }) - - xtest('ref, String operator and value', () => { - const cqn = { - SELECT: { - from: { ref: ['Foo'] }, - where: [{ ref: ['x'] }, new String('<'), { val: 9 }], - }, - } - const { sql, values } = cqn2sql(cqn) - expect({ sql, values }).toMatchSnapshot() - }) - - test('ref = null', () => { - const cqn = { - SELECT: { - from: { ref: ['Foo'] }, - where: [{ ref: ['x'] }, '=', { val: null }], - }, - } - const { sql, values } = cqn2sql(cqn) - expect(sql).toMatch(/SELECT Foo.ID,Foo.a,Foo.b,Foo.c,Foo.x FROM Foo as Foo WHERE Foo.x IS NULL/i) - expect(values).toEqual([]) - }) - - // We should never have supported that! - test('null = ref', () => { - const cqn = { - SELECT: { - from: { ref: ['Foo'] }, - where: [{ val: null }, '=', { ref: ['x'] }], - }, - } - const { sql, values } = cqn2sql(cqn) - expect(sql).toMatch(/SELECT Foo.ID,Foo.a,Foo.b,Foo.c,Foo.x FROM Foo as Foo WHERE \? = Foo.x/i) - expect(values).toEqual([null]) - }) - - // We should never have supported that! - test('null = null', () => { - const cqn = { - SELECT: { - from: { ref: ['Foo'] }, - where: [{ val: null }, '=', { val: null }], - }, - } - const { sql, values } = cqn2sql(cqn) - expect(sql).toMatch(/SELECT Foo.ID,Foo.a,Foo.b,Foo.c,Foo.x FROM Foo as Foo WHERE \? IS NULL/i) - expect(values).toEqual([null]) - }) - - test('ref != null', () => { - const cqn = { - SELECT: { - from: { ref: ['Foo'] }, - where: [{ ref: ['x'] }, '!=', { val: null }], - }, - } - const { sql, values } = cqn2sql(cqn) - expect(sql).toMatch(/SELECT Foo.ID,Foo.a,Foo.b,Foo.c,Foo.x FROM Foo as Foo WHERE Foo.x IS NOT NULL/i) - expect(values).toEqual([]) - }) - - test('val != val', () => { - const cqn = { - SELECT: { - from: { ref: ['Foo'] }, - where: [{ val: 5 }, '!=', { val: 6 }], - }, - } - const { sql, values } = cqn2sql(cqn) - expect(sql).toMatch(/SELECT Foo.ID,Foo.a,Foo.b,Foo.c,Foo.x FROM Foo as Foo WHERE \? <> \?/i) - expect(values).toEqual([5, 6]) - }) - - test('ref != ref', () => { - const cqn = { - SELECT: { - from: { ref: ['Foo'] }, - where: [{ ref: ['x'] }, '!=', { ref: ['a'] }], - }, - } - const { sql } = cqn2sql(cqn) - expect(sql).toMatch(/SELECT Foo.ID,Foo.a,Foo.b,Foo.c,Foo.x FROM Foo as Foo WHERE Foo.x is distinct from Foo.a/i) - // Note: test before was that, which is wrong: - // sql: 'SELECT Foo.ID,Foo.a,Foo.b,Foo.c,Foo.x FROM Foo as Foo WHERE Foo.x != Foo.a', - }) - - // We should never have supported that! - test('null != ref', () => { - const cqn = { - SELECT: { - from: { ref: ['Foo'] }, - where: [{ val: null }, '!=', { ref: ['x'] }], - }, - } - const { sql, values } = cqn2sql(cqn) - expect(sql).toMatch(/SELECT Foo.ID,Foo.a,Foo.b,Foo.c,Foo.x FROM Foo as Foo WHERE \? is distinct from Foo.x/i) - expect(values).toEqual([null]) - }) - - test('ref != 5', () => { - const cqn = { - SELECT: { - from: { ref: ['Foo'] }, - where: [{ ref: ['x'] }, '!=', { val: 5 }], - }, - } - const { sql, values } = cqn2sql(cqn) - expect(sql).toMatch(/SELECT Foo.ID,Foo.a,Foo.b,Foo.c,Foo.x FROM Foo as Foo WHERE Foo.x is distinct from \?/i) - expect(values).toEqual([5]) - }) - - test('ref <> 5', () => { - const cqn = { - SELECT: { - from: { ref: ['Foo'] }, - where: [{ ref: ['x'] }, '<>', { val: 5 }], - }, - } - const { sql, values } = cqn2sql(cqn) - expect(sql).toMatch(/SELECT Foo.ID,Foo.a,Foo.b,Foo.c,Foo.x FROM Foo as Foo WHERE Foo.x <> \?/i) - expect(values).toEqual([5]) - }) - - test('ref != 5 and more', () => { - const cqn = { - SELECT: { - from: { ref: ['Foo'] }, - where: [{ ref: ['x'] }, '=', { val: 7 }, 'or', { ref: ['x'] }, '!=', { val: 5 }], - }, - } - const { sql, values } = cqn2sql(cqn) - expect(sql).toMatch( - /SELECT Foo.ID,Foo.a,Foo.b,Foo.c,Foo.x FROM Foo as Foo WHERE Foo.x = \? or Foo.x is distinct from \?/i, - ) - expect(values).toEqual([7, 5]) - }) - - // We don't have to support that - test('5 != ref', () => { - const cqn = { - SELECT: { - from: { ref: ['Foo'] }, - where: [{ val: 5 }, '!=', { ref: ['x'] }], - }, - } - const { sql, values } = cqn2sql(cqn) - expect({ sql, values }).toEqual({ - sql: 'SELECT Foo.ID,Foo.a,Foo.b,Foo.c,Foo.x FROM Foo as Foo WHERE ? is distinct from Foo.x', - values: [5], - }) - }) - - test('nested 5 != ref', () => { - const cqn = { - SELECT: { - from: { ref: ['Foo'] }, - where: [ - { xpr: [{ ref: ['x'] }, '!=', { val: 5 }] }, - 'or', - { xpr: [{ ref: ['x'] }, '=', { val: null }] }, - // We should never have supported that! - // 'or', - // { xpr: [{ val: null }, '=', { ref: ['x'] }] } - ], - }, - } - const { sql, values } = cqn2sql(cqn) - expect({ sql, values }).toEqual({ - sql: 'SELECT Foo.ID,Foo.a,Foo.b,Foo.c,Foo.x FROM Foo as Foo WHERE (Foo.x is distinct from ?) or (Foo.x is NULL)', - values: [5] - }) - }) - - test('ref is like pattern', () => { - const cqn = { - SELECT: { - from: { ref: ['Foo'] }, - where: [{ ref: ['x'] }, 'like', { val: '%123' }], - }, - } - const { sql, values } = cqn2sql(cqn) - expect({ sql, values }).toMatchSnapshot() - }) - - test('ref is regular expression', () => { - const cqn = { - SELECT: { - from: { ref: ['Foo'] }, - where: [{ ref: ['x'] }, 'between', { val: 1 }, 'and', { val: 20 }], - }, - } - const { sql, values } = cqn2sql(cqn) - expect({ sql, values }).toMatchSnapshot() - }) - - test('ref is between two range', () => { - const cqn = { - SELECT: { - from: { ref: ['Foo'] }, - where: [{ ref: ['x'] }, 'regexp', { val: '/\\d/' }], - }, - } - const { sql, values } = cqn2sql(cqn) - expect({ sql, values }).toMatchSnapshot() - }) - - xtest('ref is placeholder with param', () => { - const cqn = { - SELECT: { - from: { ref: ['Foo'] }, - where: [{ ref: ['x'] }, '>', { param: true, ref: ['abc'] }], - }, - } - const { sql, values } = cqn2sql(cqn) - expect({ sql, values }).toMatchSnapshot() - /*const expected = { - sql: 'x > ?', - values: ['abc'] - }*/ - }) - - xtest('ref is placeholder without param', () => { - const cqn = { - SELECT: { - from: { ref: ['Foo'] }, - where: [{ ref: ['x'] }, '>', { param: true, ref: ['?'] }], - }, - } - const { sql, values } = cqn2sql(cqn) - expect({ sql, values }).toMatchSnapshot() - /*const expected = { - sql: 'x > ?', - values: [] - }*/ - }) - - test('ref is in list of sub select', () => { - const cqn = { - SELECT: { - from: { ref: ['Foo'] }, - where: [ - { ref: ['x'] }, - 'IN', - { - SELECT: { - from: { ref: ['Foo2'] }, - columns: [{ ref: ['name'] }], - }, - }, - ], - }, - } - const { sql, values } = cqn2sql(cqn) - expect({ sql, values }).toMatchSnapshot() - }) - - test('ref list with one ref is in list of sub select', () => { - const cqn = { - SELECT: { - from: { ref: ['Foo'] }, - where: [ - { list: [{ ref: ['x'] }] }, - 'IN', - { - SELECT: { - from: { ref: ['Foo2'] }, - columns: [{ ref: ['name'] }], - }, - }, - ], - }, - } - const { sql, values } = cqn2sql(cqn) - expect({ sql, values }).toMatchSnapshot() - }) - - test('ref list with multiple refs is in list of sub select', () => { - const cqn = { - SELECT: { - from: { ref: ['Foo'] }, - where: [ - { list: [{ ref: ['x'] }, { ref: ['b'] }] }, - 'IN', - { - SELECT: { - from: { ref: ['Foo2'] }, - columns: [{ ref: ['ID'] }, { ref: ['name'] }], - }, - }, - ], - }, - } - const { sql, values } = cqn2sql(cqn) - expect({ sql, values }).toMatchSnapshot() - }) - - test('with complex xpr', () => { - const cqn = { - SELECT: { - from: { ref: ['Foo'] }, - where: [{ xpr: [{ ref: ['x'] }, '<', { val: 9 }] }, 'AND', { xpr: [{ ref: ['x'] }, '>', { val: 1 }] }], - }, - } - const { sql, values } = cqn2sql(cqn) - expect({ sql, values }).toMatchSnapshot() - }) - - test('with long xpr', () => { - const cqn = { - SELECT: { - from: { ref: ['Foo'] }, - where: [{ ref: ['x'] }, '<', { val: 9 }, 'AND', { ref: ['x'] }, '>', { val: 1 }], - }, - } - const { sql, values } = cqn2sql(cqn) - expect({ sql, values }).toMatchSnapshot() - }) - - test('with exists', () => { - const cqn = { - SELECT: { - from: { ref: ['Foo'] }, - where: [ - 'exists', - { - SELECT: { - from: { ref: ['Foo2'] }, - columns: [{ ref: ['name'] }], - }, - }, - 'or not exists', - { - SELECT: { - from: { ref: ['Foo2'] }, - columns: [{ ref: ['name'] }], - }, - }, - ], - }, - } - const { sql, values } = cqn2sql(cqn) - expect({ sql, values }).toMatchSnapshot() - }) - - test('window function ROW_NUMBER over partition', () => { - const cqn = { - SELECT: { - from: { ref: ['Foo'] }, - where: [ - { func: 'ROW_NUMBER', args: [{ val: 1 }] }, - 'OVER', - { xpr: ['PARTITION BY', { ref: ['b'] }, 'ORDER BY', { ref: ['x'] }, 'desc'] }, - ], - }, - } - - const { sql, values } = cqn2sql(cqn) - expect({ sql, values }).toEqual({ - sql: 'SELECT Foo.ID,Foo.a,Foo.b,Foo.c,Foo.x FROM Foo as Foo WHERE ROW_NUMBER(?) OVER (PARTITION BY Foo.b ORDER BY Foo.x desc)', - values: [1] - }) - }) -}) diff --git a/db-service/test/cqn2sql/function.test.js b/db-service/test/cqn2sql/function.test.js deleted file mode 100644 index ccb4177b0..000000000 --- a/db-service/test/cqn2sql/function.test.js +++ /dev/null @@ -1,256 +0,0 @@ -const cds = require('@sap/cds') -const _cqn2sql = require('../../lib/cqn2sql') -function cqn2sql(q, m = cds.model) { - return _cqn2sql(q, m) -} - -beforeAll(async () => { - cds.model = await cds.load(__dirname + '/testModel').then(cds.linked) -}) -describe('function', () => { - test('contains complex', () => { - const cqn = { - SELECT: { - from: { ref: ['Foo'] }, - where: [ - { - func: 'contains', - args: [{ list: [{ ref: ['a'] }, { ref: ['b'] }] }, { val: '5' }], - }, - ], - }, - } - const { sql, values } = cqn2sql(cqn) - expect({ sql, values }).toMatchSnapshot() - // Hana syntax expected -> "( ID LIKE ( '%' || ? || '%' ) ESCAPE '^' OR AGE LIKE ( '%' || ? || '%' ) ESCAPE '^' )" - // result -> instr((Foo.a,Foo.b),?)" - }) - - test('wrap xpr in concat functions in parentheses', () => { - const cqn = { - SELECT: { - from: { ref: ['Foo'] }, - columns: [ - { - func: 'concat', - args: [ - { val: 2023 }, - { - xpr: [{ val: 8 }, '*', { val: 2 }, '-', { val: 0 }], - }, - ], - as: 'something', - }, - ], - }, - } - const { sql, values } = cqn2sql(cqn) - expect({ sql, values }).toMatchSnapshot() - }) - - xtest('contains complex', () => { - const cqn = { - SELECT: { - from: { ref: ['Foo'] }, - where: [ - { - func: 'contains', - args: [{ list: [{ ref: ['a'] }] }, { val: 'abc' }, 'and', { val: 'bcd' }, 'or', { val: 'efg' }], - }, - ], - }, - } - const { sql, values } = cqn2sql(cqn) - expect({ sql, values }).toMatchSnapshot() - // Hana syntax expected ->"( a LIKE ( '%' || ? || '%' ) ESCAPE '^' ) and ( a LIKE ( '%' || ? || '%' ) ESCAPE '^' ) or ( a LIKE ( '%' || ? || '%' ) ESCAPE '^' )" - // requires adjustment of instr to support logical operators - }) - - xtest('contains complex new notation', () => { - const cqn = { - SELECT: { - from: { ref: ['Foo'] }, - where: [ - { - func: 'contains', - args: [ - { - list: [{ ref: ['ID'] }, { ref: ['x'] }], - }, - { val: '5' }, - 'and', - 'not', - { val: '3' }, - ], - }, - ], - }, - } - const { sql, values } = cqn2sql(cqn) - expect({ sql, values }).toMatchSnapshot() - // "( ID LIKE ( '%' || ? || '%' ) ESCAPE '^' OR x LIKE ( '%' || ? || '%' ) ESCAPE '^' ) and not ( ID LIKE ( '%' || ? || '%' ) ESCAPE '^' OR x LIKE ( '%' || ? || '%' ) ESCAPE '^' )" - // result -> Unsupported expr: and - }) - - test('not contains', () => { - const cqn = { - SELECT: { - from: { ref: ['Foo'] }, - where: [ - 'not', - { - func: 'contains', - args: [{ list: [{ ref: ['b'] }] }, { val: '5' }], - }, - ], - }, - } - const { sql, values } = cqn2sql(cqn) - expect({ sql, values }).toEqual({ - sql: `SELECT Foo.ID,Foo.a,Foo.b,Foo.c,Foo.x FROM Foo as Foo WHERE not ifnull(instr((Foo.b),?),0)`, - values: ['5'], - }) - }) - - xtest('not contains complex', () => { - const cqn = { - SELECT: { - from: { ref: ['Foo'] }, - where: [ - 'not', - { - func: 'contains', - args: [{ list: [{ ref: ['ID'] }, { ref: ['a'] }] }, { val: '5' }], - }, - ], - }, - } - const { sql, values } = cqn2sql(cqn) - expect({ sql, values }).toEqual({ - sql: 'SELECT Foo.ID,Foo.a,Foo.b,Foo.c,Foo.x FROM Foo as Foo WHERE not (instr((Foo.ID),?) and instr((Foo.a),?))', - values: ['5', '5'], - }) - }) - - xtest('contains values with wildcards/escape characters', () => { - const getExprWithVal = val => { - return { - SELECT: { - from: { ref: ['Foo'] }, - where: [ - 'not', - { - func: 'contains', - args: [{ list: [{ ref: ['a'] }] }, { val }], - }, - ], - }, - } - } - // Input values should be escaped with ^ (on HANA) - const { values } = cqn2sql(getExprWithVal('Te%st')) - expect({ values }).toMatchSnapshot() - - const { values1 } = cqn2sql(getExprWithVal('Te_st')) - expect({ values1 }).toMatchSnapshot() - - const { values2 } = cqn2sql(getExprWithVal('Te^st')) - expect({ values2 }).toMatchSnapshot() - - const { values3 } = cqn2sql(getExprWithVal('Te^^st')) - expect({ values3 }).toMatchSnapshot() - - const { values4 } = cqn2sql(getExprWithVal('Te^^st')) - expect({ values4 }).toMatchSnapshot() - - // expected 'Te^%st' , result 'Te%st' - }) - - xtest('contains will not modify the original object', () => { - const cqn = { - SELECT: { - from: { ref: ['Foo'] }, - where: [ - { - func: 'contains', - args: [{ list: [{ ref: ['a'] }] }, { val: 'Te%st' }], - }, - ], - }, - } - const { values } = cqn2sql(cqn) - expect({ values }).toMatchSnapshot() - - //expect(custom1._outputObj.values).toEqual(['Te^%st']) - //expect(cqn.SELECT.where[0].args[1].val).toEqual('Te%st') - }) - - test('fn with .xpr as argument', () => { - const cqn = { - SELECT: { - from: { ref: ['Foo'] }, - where: [ - { - func: 'round', - args: [{ xpr: [{ ref: ['x'] }, '-', { val: 100 }] }, { val: 3 }], - }, - ], - }, - } - const { sql, values } = cqn2sql(cqn) - expect({ sql, values }).toMatchSnapshot() - }) - - test('without args', () => { - const cqn = { - SELECT: { - from: { ref: ['Foo'] }, - where: [{ func: 'current_date' }], - }, - } - const { sql } = cqn2sql(cqn) - expect(sql).toEqual('SELECT Foo.ID,Foo.a,Foo.b,Foo.c,Foo.x FROM Foo as Foo WHERE current_date') - }) - - test('fn with named arguments', () => { - const func = { - func: 'convert_currency', - args: { - amount: { ref: ['a'] }, - source_unit: { ref: ['b'] }, - target_unit: { val: 'USD' }, - } - } - const cqn = { - SELECT: { - columns: [func], - from: { ref: ['Foo'] }, - where: [func], - }, - } - - const { sql, values } = cqn2sql(cqn) - expect({ sql, values }).toEqual({ - sql: 'SELECT convert_currency(amount => Foo.a,source_unit => Foo.b,target_unit => ?) as convert_currency FROM Foo as Foo WHERE convert_currency(amount => Foo.a,source_unit => Foo.b,target_unit => ?)', - values: ['USD', 'USD'], - }) - }) - - test('fn with xpr extension', () => { - const cqn = { - SELECT: { - from: { ref: ['Foo'] }, - columns: [{ - func: 'row_number', - args: [], - xpr: ['over', { xpr: ['partition', 'by', { ref: ['a'] }] }] - }] - }, - } - - const { sql } = cqn2sql(cqn) - expect({ sql }).toEqual({ - sql: 'SELECT row_number() over (partition by Foo.a) as row_number FROM Foo as Foo', - }) - }) -}) diff --git a/db-service/test/cqn2sql/insert.test.js b/db-service/test/cqn2sql/insert.test.js deleted file mode 100644 index 7e4b25c6a..000000000 --- a/db-service/test/cqn2sql/insert.test.js +++ /dev/null @@ -1,122 +0,0 @@ -'use strict' -const { text } = require('stream/consumers') - -const cds = require('@sap/cds') -const _cqn2sql = require('../../lib/cqn2sql') - -describe('insert', () => { - let model - function cqn2sql(q) { - return _cqn2sql(q, model) - } - - beforeAll(async () => { - model = await cds.load(__dirname + '/testModel').then(cds.linked) - model = cds.compile.for.nodejs(JSON.parse(JSON.stringify(model))) - }) - - describe('insert only', () => { - // Values are missing - test('test with insert values into columns', async () => { - const cqnInsert = { - INSERT: { - into: { ref: ['Foo'] }, - columns: ['ID', 'b', 'x'], - values: [1, "'asd'", 2], - }, - } - - const { sql, entries } = cqn2sql(cqnInsert) - expect({ sql, entries: [[await text(entries[0][0])]] }).toMatchSnapshot() - }) - - test('test with insert rows into columns', async () => { - const cqnInsert = { - INSERT: { - into: { ref: ['Foo'] }, - columns: ['ID', 'b', 'a'], - rows: [ - [1, "'asd'", 2], - [9, "mmm'", 77], - ], - }, - } - const { sql, entries } = cqn2sql(cqnInsert) - expect({ sql, entries: [[await text(entries[0][0])]] }).toMatchSnapshot() - }) - - // no filtering in INSERT - xtest('test filter in insert rows into columns with not existing column', async () => { - const cqnInsert = { - INSERT: { - into: { ref: ['Foo2'] }, - columns: ['ID', 'not_existing', 'something'], - rows: [ - [1, "'asd'", 2], - [9, "mmm'", 77], - ], - }, - } - const { sql, entries } = cqn2sql(cqnInsert) - expect({ sql, entries: [[await text(entries[0][0])]] }).toMatchSnapshot() - }) - - test('test with insert entries', async () => { - const cqnInsert = { - INSERT: { - into: 'Foo2', - entries: [ - { ID: 1, name: null, a: 2 }, - { ID: null, name: "'asd'", a: 6 }, - ], - }, - } - - const { sql, entries } = cqn2sql(cqnInsert) - expect({ sql, entries: [[await text(entries[0][0])]] }).toMatchSnapshot() - }) - - test('test with insert with alias', async () => { - const cqnInsert = { - INSERT: { - into: { ref: ['Foo2'], as: 'Fooooo2' }, - entries: [ - { ID: 1, name: null, a: 2 }, - { ID: null, name: "'asd'", a: 6 }, - ], - }, - } - - const { sql, entries } = cqn2sql(cqnInsert) - expect({ sql, entries: [[await text(entries[0][0])]] }).toMatchSnapshot() - }) - }) - - describe('insert into ... select from ...', () => { - // sql is generated correctly, but not valid since number of columns is different in both tables - test('no columns', () => { - const cqnInsert = { - INSERT: { - into: 'Foo', - as: { SELECT: { from: { ref: ['Foo2'] } } }, - }, - } - - const { sql } = cqn2sql(cqnInsert) - expect(sql).toEqual('INSERT INTO Foo (ID,a,b,c,x) SELECT Foo2.ID,Foo2.name,Foo2.a FROM Foo2 as Foo2') - }) - - test('with columns', () => { - const cqnInsert = { - INSERT: { - into: 'Foo', - columns: ['ID'], - as: { SELECT: { from: { ref: ['Foo2'] }, columns: [{ ref: ['ID'] }] } }, - }, - } - - const { sql } = cqn2sql(cqnInsert, cds.model) - expect(sql).toEqual('INSERT INTO Foo (ID) SELECT Foo2.ID FROM Foo2 as Foo2') - }) - }) -}) diff --git a/db-service/test/cqn2sql/select.test.js b/db-service/test/cqn2sql/select.test.js deleted file mode 100644 index ca9656529..000000000 --- a/db-service/test/cqn2sql/select.test.js +++ /dev/null @@ -1,514 +0,0 @@ -'use strict' -const cds = require('@sap/cds') -const _cqn2sql = require('../../lib/cqn2sql') -function cqn2sql(q, m = cds.model) { - - return _cqn2sql(q, m) -} -const cqn = require('./cqn.js') - -// const getExpected = (sql, values) => { -// return { -// sql: sql, -// values: values || [] -// } -// } - -describe('cqn2sql', () => { - beforeAll(async () => { - cds.model = await cds.load(__dirname + '/testModel').then(cds.linked) - }) - describe('selection of columns of one table', () => { - test('with select with from ref and elements = undefined', () => { - const { sql } = cqn2sql(cqn.select) - expect(sql).toMatchSnapshot() - }) - - // empty columns will be ignored - test('with select with from ref and elements as empty array', () => { - const cqnSelect = { - SELECT: Object.assign({}, cqn.select.SELECT, { columns: [] }), - } - const { sql } = cqn2sql(cqnSelect) - expect(sql).toMatchSnapshot() - }) - - test('with select specific elements with from ref', () => { - const { sql } = cqn2sql(cqn.selectWithColumns) - expect(sql).toMatchSnapshot() - }) - - // empty orderBy will be ignored - test('with select with empty orderBy and specific elements with from type string', () => { - const { sql } = cqn2sql(cqn.selectWithColumnsEmptyOrderBy) - expect(sql).toMatchSnapshot() - }) - - test('with select with asterisk in columns', () => { - const { sql } = cqn2sql(cqn.selectWithColumnsWithAsterisk) - expect(sql).toMatchSnapshot() - }) - - test('select distinct', () => { - const { sql } = cqn2sql(cqn.selectDistinct) - expect(sql).toMatchSnapshot() - }) - - test('with select that has (faked) reflection model', () => { - const { sql } = cqn2sql(cqn.selectWithCSN) - expect(sql).toMatchSnapshot() - }) - - test('with select from non existent entity with star wildcard', () => { - expect(() => { - let q = cqn.selectNonExistent - // Skip cqn4sql as infer requires the entity to exist - const render = q => new _cqn2sql.class().render(q) - const { sql } = render(q) - expect(sql).toMatchSnapshot() - q = cds.ql.clone(q) - q.SELECT.expand = 'root' - render(q) // throws - }).toThrowError('Query was not inferred and includes expand. For which the metadata is missing.') - }) - - test('with select from non existent entity with star wildcard (extended)', () => { - expect(() => { - const customCqn2sql = class extends _cqn2sql.class { - SELECT_columns({ SELECT }) { - return SELECT.columns.map(x => `${this.quote(this.column_name(x))}`) - } - } - const q = cqn.selectNonExistent - // Skip cqn4sql as infer requires the entity to exist - const render = q => new customCqn2sql().render(q) - render(q) // throws - }).toThrowError( - `Query was not inferred and includes '*' in the columns. For which there is no column name available.`, - ) - }) - - test('select with static values', () => { - let query = CQL(`SELECT from Foo { - 5 as ID, - 'simple string' as a, - 3.14 as pi, - 3.1415 as pid : cds.Decimal(5,4), - 'large string' as stringl : cds.LargeString, - true as boolt : Boolean, - '1970-01-01' as date : cds.Date, - '00:00:00' as time : cds.Time, - '1970-01-01 00:00:00' as datetime : cds.DateTime, - '1970-01-01 00:00:00.000' as timestamp : cds.Timestamp - }`) - const { sql, values } = cqn2sql(query) - expect({ sql, values }).toMatchSnapshot() - }) - }) - - describe('WHERE', () => { - test('entries where one column holds entries smaller than 9', () => { - const { sql } = cqn2sql(cqn.selectWhereSmaller) - expect(sql).toMatchSnapshot() - }) - - test('entries where with place holder', () => { - const { sql, values } = cqn2sql(cqn.selectWhereWithOnePlaceholderCqn) - expect({ sql, values }).toMatchSnapshot() - }) - - test('entries where with int reference and param true', () => { - const { sql, values } = cqn2sql(cqn.selectWhereWithRefIntParamTrueCqn) - expect({ sql, values }).toMatchSnapshot() - }) - - test('where with partial cqn', () => { - const { sql } = cqn2sql(cqn.selectWhereCqn) - expect(sql).toMatchSnapshot() - }) - - test('where with two partial cqn', () => { - const { sql } = cqn2sql(cqn.selectWhereTwoCqn) - expect(sql).toMatchSnapshot() - }) - - test('entries where one column holds entries which are in list', () => { - const { sql } = cqn2sql(cqn.selectWhereList) - expect(sql).toMatchSnapshot() - }) - - test('select with a nested select in where', () => { - const { sql } = cqn2sql(cqn.selectWhereSelect) - expect(sql).toMatchSnapshot() - }) - - test('select with a nested select in a complex where', () => { - const { sql } = cqn2sql(cqn.selectComplexWhere) - expect(sql).toMatchSnapshot() - }) - - test('with select with exist in where condition', () => { - const { sql } = cqn2sql(cqn.selectWhereExists) - expect(sql).toMatchSnapshot() - }) - - test('with list of values', () => { - const cqn = { - SELECT: { - from: { ref: ['Foo'] }, - where: [ - { list: [{ ref: ['a'] }, { ref: ['b'] }, { val: 1 }] }, - '=', - { list: [{ ref: ['c'] }, { val: 'd' }, { ref: ['x'] }] }, - ], - }, - } - const { sql, values } = cqn2sql(cqn) - expect({ sql, values }).toMatchSnapshot() - }) - - test('with contains with one column in where clause', () => { - // const expected = getExpected("SELECT Foo.ID,Foo.a,Foo.b,Foo.c,Foo.x FROM Foo AS Foo FROM T WHERE ( b LIKE ( '%' || ? || '%' ) ESCAPE '^' )", ['5']) - const { sql, values } = cqn2sql(cqn.selectContainsOneColumn) - expect({ sql, values }).toMatchSnapshot() - }) - - test('EXISTS with nested EXISTS', () => { - const { sql } = cqn2sql(cqn.selectWhereNestedExists) - expect(sql).toMatchSnapshot() - }) - - test('with function without alias', () => { - const toThrow = () => { - return cqn2sql(cqn.selectFuncitonWithoutAlias) - } - expect(toThrow).toThrowError('Expecting expression to have an alias name') - }) - - test('with contains with multiple values', () => { - const cqn = { - SELECT: { - from: { ref: ['Foo'] }, - where: [ - { ref: ['a'] }, - '=', - { val: 0 }, - 'and', - { - func: 'contains', - args: [{ list: [{ ref: ['a'] }] }, { val: 'z' }, 'or', { val: 'zz' }], - }, - ], - }, - } - const toThrow = () => { - return cqn2sql(cqn) - } - expect(toThrow).toThrowError('Unsupported expr: or') - }) - - test('with contains with multiple arguments', () => { - const cqn = { - SELECT: { - from: { ref: ['Foo'] }, - where: [ - { ref: ['a'] }, - '=', - { val: 0 }, - 'and', - { - func: 'contains', - args: [{ list: [{ ref: ['a'] }, { ref: ['b'] }, { ref: ['c'] }, { ref: ['x'] }] }, { val: 'z' }], - }, - ], - }, - } - const { sql } = cqn2sql(cqn) - expect(sql).toMatchSnapshot() - }) - }) - describe('HAVING clauses', () => { - test('with select specific elements with from type string with having clause', () => { - const { sql } = cqn2sql(cqn.selectHaving) - expect(sql).toMatchSnapshot() - }) - }) - - describe('complex combinations', () => { - test('WHERE, GROUP BY, HAVING, ORDER BY, LIMIT, OFFSET', () => { - const { sql } = cqn2sql(cqn.selectAggregationLimitOrder) - expect(sql).toMatchSnapshot() - }) - - test('AS, sub query', () => { - const { sql } = cqn2sql(cqn.selectSubSelect) - expect(sql).toMatchSnapshot() - }) - - test('Exists in object mode in complex where', () => { - const { sql, values } = cqn2sql(cqn.selectComplexWhereWithExists) - expect({ sql, values }).toMatchSnapshot() - }) - }) - - describe('GROUP BY', () => { - test('GROUP BY two columns', () => { - const { sql } = cqn2sql(cqn.groupBy) - expect(sql).toMatchSnapshot() - }) - }) - - describe('ORDER BY', () => { - test('ORDER BY alias', () => { - const { sql } = cqn2sql(cqn.orderByWithAlias) - expect(sql).toMatchSnapshot() - }) - - test('ORDER BY with @cds.collate false', () => { - const { sql } = cqn2sql(cqn.orderByCollations) - expect(sql).toMatchSnapshot() - }) - }) - - describe('ONE', () => { - test('one results in limit 1', () => { - const { sql } = cqn2sql(cqn.one) - expect(sql).toMatchSnapshot() - }) - - test('one with additional limit with offset', () => { - // Original DB layer expectation is to mix limit and one - // One has priority over limit.rows, but limit.offset is still applied - const { sql, values } = cqn2sql(cqn.oneWithLimit) - expect(sql).toEqual('SELECT Foo.a,Foo.b,Foo.c FROM Foo as Foo LIMIT ? OFFSET ?') - expect(values).toEqual([1, 5]) - }) - }) - - describe('LIMIT', () => { - test('with limit without offset', () => { - const { sql } = cqn2sql(cqn.limit) - expect(sql).toMatchSnapshot() - }) - - test('with limit and offset', () => { - const { sql } = cqn2sql(cqn.limitOffset) - expect(sql).toMatchSnapshot() - }) - - test('limit without rows throws error', () => { - const toThrow = () => { - return cqn2sql({ SELECT: { from: { ref: ['Foo'] }, limit: { offset: { val: 5 } } } }) - } - expect(toThrow).toThrowError('Rows parameter is missing in SELECT.limit(rows, offset)') - }) - }) - - describe('aggregation functions', () => { - test('with select with same functions without alias in elements', () => { - const toThrow = () => { - return cqn2sql(cqn.selectWithSameFunctionsWithoutAlias) - } - expect(toThrow).toThrowError('Duplicate definition of element “count”') - }) - - test('with select with different functions without alias in elements', () => { - const { sql } = cqn2sql(cqn.selectWithFunctionsWithoutAlias) - expect(sql).toMatchSnapshot() - }) - - test('with select with functions in elements new notation', () => { - const { sql } = cqn2sql(cqn.selectWithAggregationNew) - expect(sql).toMatchSnapshot() - }) - - test('with select with count(1)', () => { - const { sql } = cqn2sql(cqn.selectWithCountOne) - expect(sql).toMatchSnapshot() - }) - - test('with select with functions in where clause new notation', () => { - const { sql } = cqn2sql(cqn.selectWhereAggregationNew) - expect(sql).toMatchSnapshot() - }) - }) - - describe('functions new notation', () => { - test('function with xpr', () => { - const { sql, values } = cqn2sql({ - SELECT: { - from: { ref: ['Foo'] }, - columns: [ - { - func: 'replace_regexpr', - args: [ - { - xpr: [{ val: 'A' }, 'flag', { val: 'i' }, 'in', { val: 'ABC-abc-AAA-aaa' }, 'with', { val: 'B' }], - }, - ], - as: 'replaced', - }, - ], - }, - }) - expect(sql).toMatchSnapshot() - expect(values).toMatchSnapshot() - }) - - test('function with multiple xpr', () => { - const { sql, values } = cqn2sql({ - SELECT: { - from: { ref: ['Foo'] }, - columns: [ - { - func: 'replace_regexpr', - args: [ - { ref: ['a'] }, - { val: 5 }, - { - xpr: [{ val: 'A' }, 'flag', { val: 'i' }, 'in', { val: 'ABC-abc-AAA-aaa' }, 'with', { val: 'B' }], - }, - ], - as: 'replaced', - }, - ], - }, - }) - expect(sql).toMatchSnapshot() - expect(values).toMatchSnapshot() - }) - - test('in orderby with 1 arg new notation', () => { - const { sql } = cqn2sql({ - SELECT: { - from: { ref: ['Foo'] }, - orderBy: [{ func: 'lower', args: [{ ref: ['c'] }], sort: 'desc' }], - }, - }) - expect(sql).toMatchSnapshot() - }) - - test('in filter with 1 arg new notation', () => { - const { sql, values } = cqn2sql({ - SELECT: { - from: { ref: ['Foo'] }, - where: [{ func: 'lower', args: [{ ref: ['c'] }] }, '=', { val: 'name' }], - }, - }) - expect({ sql, values }).toMatchSnapshot() - }) - - test('in filter with asterisk as arg new notation', () => { - const { sql } = cqn2sql({ - SELECT: { - from: { ref: ['Foo'] }, - having: [{ func: 'count', args: ['*'] }, '>', { val: 1 }], - }, - }) - expect(sql).toMatchSnapshot() - }) - - test('in filter with 2 arg new notation', () => { - const { sql } = cqn2sql({ - SELECT: { - from: { ref: ['Foo'] }, - where: [{ ref: ['c'] }, '=', { func: 'concat', args: [{ ref: ['a'] }, { ref: ['b'] }] }], - }, - }) - expect(sql).toMatchSnapshot() - }) - - test('in filter with 3 arg new notation', () => { - const { sql, values } = cqn2sql({ - SELECT: { - from: { ref: ['Foo'] }, - where: [{ ref: ['c'] }, '=', { func: 'concat', args: [{ val: 'Existing' }, { ref: ['a'] }, { val: '!' }] }], - }, - }) - expect({ sql, values }).toMatchSnapshot() - }) - - test('in filter with nested functions new notation', () => { - const { sql, values } = cqn2sql({ - SELECT: { - from: { ref: ['Foo'] }, - where: [ - { func: 'lower', args: [{ ref: ['a'] }] }, - '=', - { - func: 'lower', - args: [{ func: 'upper', args: [{ func: 'trim', args: [{ val: ' existing name ' }] }] }], - }, - 'and', - { func: 'length', args: [{ func: 'trim', args: [{ val: ' name' }] }] }, - '=', - { ref: ['b'] }, - ], - }, - }) - expect({ sql, values }).toMatchSnapshot() - }) - - test('in filter with subselect as function param', () => { - const subselect = { - SELECT: { - columns: [{ ref: ['ID'] }], - from: { ref: ['Foo2'] }, - where: [{ ref: ['a'] }, '=', { val: 1 }], - }, - } - const { sql } = cqn2sql({ - SELECT: { - from: { ref: ['Foo'] }, - where: [{ ref: ['ID'] }, '=', { func: 'any', args: [subselect] }], - }, - }) - expect(sql).toMatchSnapshot() - }) - }) - - describe('quoted column aliases', () => { - // aliases should be quoted only for HANA - test('simple select with column aliases', () => { - const cqn = { - SELECT: { - from: { ref: ['Foo'], as: 'T' }, - columns: [ - { ref: ['a'], as: 'A' }, - { val: true, as: 'True' }, - { val: false, as: 'False' }, - { val: null, as: 'Null' }, - { func: 'count', args: ['*'], as: 'CountFunc' }, - ], - }, - } - const { sql, values } = cqn2sql(cqn) - expect({ sql, values }).toMatchSnapshot() - }) - - // aliases should be quoted only for HANA - test('select with subselect with in and column aliases', () => { - const { sql, values } = cqn2sql(cqn.aliasWithInSubSelect) - expect({ sql, values }).toMatchSnapshot() - }) - - // aliases should be quoted only for HANA - test('select with subselect in exists and column aliases', () => { - const { sql } = cqn2sql(cqn.aliasWithNestedExists) - expect(sql).toMatchSnapshot() - }) - - // aliases should be quoted only for HANA - test('select with simple subselect and column aliases', () => { - const { sql } = cqn2sql(cqn.aliasWithSubSelect) - expect(sql).toMatchSnapshot() - }) - }) - - describe('joins', () => { - test.skip('with table join table', () => { - const { sql } = cqn2sql(cqn.join) - expect(sql).toMatchSnapshot() - }) - }) -}) diff --git a/db-service/test/cqn2sql/testModel.cds b/db-service/test/cqn2sql/testModel.cds deleted file mode 100644 index f5d80fc93..000000000 --- a/db-service/test/cqn2sql/testModel.cds +++ /dev/null @@ -1,100 +0,0 @@ -entity Foo { - key ID: Integer; - a: String; - b: String; - c: String; - x: Integer; -} - -entity Foo2 { - key ID: Integer; - name: String; - a: Integer; - virtual something : String(11); -} - -entity !["Foo2Quoted"] { - key !["ID"]: Integer; - !["name"]: String; - !["a"]: Integer; - virtual !["something"] : String(11); -} - -entity FooCollate { - key ID: UUID; - collateString: String; - nonCollateString: String @cds.collate: false; -} - - -entity Books { - key ID : Integer; - author : Composition of Author ; - descr : String; - code : String; -} - -entity Author { - key id : Integer; - key version : String; - parent : Association to Books; - } - -entity Travel { - key TravelUUID : UUID; - TravelID : Integer @readonly default 0; - BeginDate : Date; - EndDate : Date; - BookingFee : Decimal(16, 3); - TotalPrice : Decimal(16, 3) @readonly; - Description : String(1024); - to_Booking : Composition of many Booking on to_Booking.to_Travel = $self; -}; - -entity Booking { - key BookingUUID : UUID; - FlightPrice : Decimal(16, 3); - to_BookSupplement : Composition of many BookingSupplement on to_BookSupplement.to_Booking = $self; - to_Travel : Association to Travel; -}; - -entity BookingSupplement { - key BookSupplUUID : UUID; - Price : Decimal(16, 3); - to_Booking : Association to Booking; - to_Travel : Association to Travel; -}; - -entity DBDeepEntityChild { - key ID : Integer; - parent : Integer; - otherName : String; - otherName2 : String; -} - - entity EProjChild as projection on DBDeepEntityChild { - ID as IDRename, - parent as parentRename, - otherName as otherNameRename, - otherName2 as otherName2Rename - } - -entity DBDeepEntity { - key ID : Integer; - parent : Integer; - otherName : String; - otherName2 : String; - children : Composition of many EProjChild - on children.parentRename = ID; -} - -entity FProjDeep as projection on DBDeepEntity { - ID as IDRename, - parent as parentRename, - otherName as otherNameRename, - otherName2 as otherName2Rename, - children as childrenRename -} -service RenameService @(path:'/rename') { - entity SProjDeep as projection on FProjDeep; -} diff --git a/db-service/test/cqn2sql/update.test.js b/db-service/test/cqn2sql/update.test.js deleted file mode 100644 index 8cdca1e1f..000000000 --- a/db-service/test/cqn2sql/update.test.js +++ /dev/null @@ -1,144 +0,0 @@ -'use strict' -const cds = require('@sap/cds') -const _cqn2sql = require('../../lib/cqn2sql') -function cqn2sql(q, m = cds.model) { - return _cqn2sql(q, m) -} - -beforeAll(async () => { - cds.model = await cds.load(__dirname + '/testModel').then(cds.linked) -}) -describe('.update', () => { - test('test with entity of type string', () => { - const cqnUpdate = { - UPDATE: { - entity: { ref: ['Foo2'] }, - with: { ID: { val: 1 }, name: { val: "'asd'" }, a: { val: 2 } }, - }, - } - - const { sql, values } = cqn2sql(cqnUpdate) - expect({ sql, values }).toMatchSnapshot() - }) - - test('test with entity of type string and where clause', () => { - const cqnUpdate = { - UPDATE: { - entity: { ref: ['Foo2'] }, - with: { ID: { val: 1 }, name: { val: "'asd'" }, a: { val: 2 } }, - where: [{ ref: ['a'] }, '<', { val: 9 }], - }, - } - const { sql, values } = cqn2sql(cqnUpdate) - expect({ sql, values }).toMatchSnapshot() - }) - - test('test with setting a value to null', () => { - const cqnUpdate = { - UPDATE: { - entity: { ref: ['Foo2'] }, - with: { ID: { val: 1 }, name: { val: null }, a: { val: 2 } }, - where: [{ ref: ['a'] }, '<', { val: 9 }], - }, - } - - const { sql, values } = cqn2sql(cqnUpdate) - expect({ sql, values }).toMatchSnapshot() - }) - - test('test with entity and values with operators', () => { - const cqnUpdate = { - UPDATE: { - entity: 'Foo2', - with: { - ID: { val: 42 }, - name: { val: "'asd'" }, - a: { xpr: [{ ref: ['a'] }, '-', { val: 1 }] }, - count: { func: 'count', args: ['*'] }, - }, - }, - } - const { sql, values } = cqn2sql(cqnUpdate) - expect({ sql, values }).toMatchSnapshot() - }) - - //REVISIT aliasing with columns doesn't work - test('data alone still works', () => { - const cqnUpdate = { - UPDATE: { - entity: { ref: ['Foo2'] }, - data: { - ID: 1, - name: undefined, - a: null, - }, - }, - } - const { sql, values } = cqn2sql(cqnUpdate) - expect({ sql, values }).toMatchSnapshot() - }) - - test('virtual and non-existing filtered out from data', () => { - const cqnUpdate = { - UPDATE: { - entity: { ref: ['Foo2'] }, - data: { - ID: 1, - something: 'bla', - foo: null, - }, - }, - } - const { sql, values } = cqn2sql(cqnUpdate) - expect({ sql, values }).toMatchSnapshot() - }) - - test('set enhances data', () => { - const cqnUpdate = { - UPDATE: { - entity: { ref: ['Foo2'] }, - with: { ID: { val: 1 }, name: { val: "'asd'" } }, - data: { a: 2 }, - }, - } - - const { sql, values } = cqn2sql(cqnUpdate) - expect({ sql, values }).toMatchSnapshot() - }) - - test('virtual and non-existing fields filtered out from with', () => { - const cqnUpdate = { - UPDATE: { - entity: { ref: ['Foo2'] }, - with: { ID: { val: 1 }, name: { val: "'asd'" }, something: { val: 'bla' } /* foo: {ref: 'Foo'} */ }, - }, - } - - const { sql, values } = cqn2sql(cqnUpdate) - expect({ sql, values }).toMatchSnapshot() - }) - - test('set overwrites data', () => { - const cqnUpdate = { - UPDATE: { - entity: 'Foo2', - with: { ID: { val: 1 }, name: { val: "'asd'" }, a: { val: 6 } }, - data: { a: 2 }, - }, - } - - const { sql, values } = cqn2sql(cqnUpdate) - expect({ sql, values }).toMatchSnapshot() - }) - - // TODO change to native sql -> not really useful to test here - test.skip('test with subselect - sflight example', () => { - const qlUpdate = UPDATE(`Travel`).with({ - TotalPrice: CXL`coalesce (BookingFee, 0) + ${SELECT`coalesce (sum (FlightPrice + ${SELECT`coalesce (sum (Price),0)`.from( - `BookingSupplement`, - ).where`to_Booking_BookingUUID = BookingUUID`}),0)`.from(`Booking`).where`to_Travel_TravelUUID = TravelUUID`}`, - }) - const { sql, values } = cqn2sql(qlUpdate) - expect({ sql, values }).toMatchSnapshot() - }) -}) diff --git a/db-service/test/cqn2sql/upsert.test.js b/db-service/test/cqn2sql/upsert.test.js deleted file mode 100644 index 4e9c563d5..000000000 --- a/db-service/test/cqn2sql/upsert.test.js +++ /dev/null @@ -1,55 +0,0 @@ -'use strict' -const { text } = require('stream/consumers') - -const cds = require('@sap/cds') -const _cqn2sql = require('../../lib/cqn2sql') -function cqn2sql(q, m = cds.model) { - return _cqn2sql(q, m) -} - -beforeAll(async () => { - cds.model = await cds.load(__dirname + '/testModel').then(cds.linked) -}) - -describe('upsert', () => { - test('test with keys only', async () => { - const cqnUpsert = { - UPSERT: { - into: 'Foo2', - columns: ['ID'], - rows: [[1], [9]], - }, - } - - const { sql, entries } = cqn2sql(cqnUpsert) - expect({ sql, entries: [[await text(entries[0][0])]] }).toMatchSnapshot() - }) - - test('test with entries', async () => { - const cqnUpsert = { - UPSERT: { - into: 'Foo2', - entries: [ - { ID: 1, name: null, a: 2 }, - { ID: null, name: "'asd'", a: 6 }, - ], - }, - } - - const { sql, entries } = cqn2sql(cqnUpsert) - expect({ sql, entries: [[await text(entries[0][0])]] }).toMatchSnapshot() - }) - - test('test with rows (quoted)', async () => { - const cqnUpsert = { - UPSERT: { - into: '"Foo2Quoted"', - columns: ['"ID"', '"name"', '"a"'], - rows: [[1, null, 2]], - }, - } - - const { sql, entries } = cqn2sql(cqnUpsert) - expect({ sql, entries: [[await text(entries[0][0])]] }).toMatchSnapshot() - }) -}) diff --git a/db-service/test/cqn4sql/assocs2joins.test.js b/db-service/test/cqn4sql/assocs2joins.test.js index 4f29d6dfd..6037f0420 100644 --- a/db-service/test/cqn4sql/assocs2joins.test.js +++ b/db-service/test/cqn4sql/assocs2joins.test.js @@ -860,7 +860,7 @@ describe('Variations on ON', () => { } ` // inferred element name equals original ref navigation - expect(transformed.elements).to.include.key('toSelf_struct_one') + expect(transformed.elements).to.have.property('toSelf_struct_one') expect(transformed).to.deep.equal(expected) }) }) diff --git a/db-service/test/cqn4sql/not-persisted.test.js b/db-service/test/cqn4sql/not-persisted.test.js index 19769cb20..870709eb8 100644 --- a/db-service/test/cqn4sql/not-persisted.test.js +++ b/db-service/test/cqn4sql/not-persisted.test.js @@ -16,234 +16,237 @@ const cqn4sql = require('../../lib/cqn4sql') const cds = require('@sap/cds') const { expect } = cds.test let model -beforeAll(async () => { - model = await cds.load(__dirname + '/../bookshop/db/schema').then(cds.linked) -}) -describe('virtual fields', () => { - it('remove from columns', () => { - let query = cqn4sql(CQL`SELECT from bookshop.Foo { ID, virtualField }`, model) - expect(query).to.deep.equal(CQL`SELECT from bookshop.Foo as Foo { Foo.ID }`) - }) - - // If select list is empty already in input, we produce corresponding SQL. - // But if empty select list results from removing virtual fields, we throw an error. - it('error out if removal of virtual element leads to empty columns', () => { - let query = CQL`SELECT from bookshop.Foo { virtualField as x, stru.v }` - expect(() => cqn4sql(query, model)).to.throw('Queries must have at least one non-virtual column') - }) - - it('remove from columns in struc', () => { - let query = cqn4sql(CQL`SELECT from bookshop.Foo { ID, stru }`, model) - expect(query).to.deep.equal(CQL`SELECT from bookshop.Foo as Foo { +describe('not persisted', () => { + beforeAll(async () => { + model = await cds.load(__dirname + '/../bookshop/db/schema').then(cds.linked) + }) + + describe('virtual fields', () => { + it('remove from columns', () => { + let query = cqn4sql(CQL`SELECT from bookshop.Foo { ID, virtualField }`, model) + expect(query).to.deep.equal(CQL`SELECT from bookshop.Foo as Foo { Foo.ID }`) + }) + + // If select list is empty already in input, we produce corresponding SQL. + // But if empty select list results from removing virtual fields, we throw an error. + it('error out if removal of virtual element leads to empty columns', () => { + let query = CQL`SELECT from bookshop.Foo { virtualField as x, stru.v }` + expect(() => cqn4sql(query, model)).to.throw('Queries must have at least one non-virtual column') + }) + + it('remove from columns in struc', () => { + let query = cqn4sql(CQL`SELECT from bookshop.Foo { ID, stru }`, model) + expect(query).to.deep.equal(CQL`SELECT from bookshop.Foo as Foo { Foo.ID, Foo.stru_u, Foo.stru_nested_nu }`) - }) + }) - it('remove from columns with path into struc', () => { - let query = cqn4sql( - CQL`SELECT from bookshop.Foo { + it('remove from columns with path into struc', () => { + let query = cqn4sql( + CQL`SELECT from bookshop.Foo { ID, stru.u, stru.v, stru.nested.nu, stru.nested.nv }`, - model, - ) - expect(query).to.deep.equal(CQL`SELECT from bookshop.Foo as Foo { + model, + ) + expect(query).to.deep.equal(CQL`SELECT from bookshop.Foo as Foo { Foo.ID, Foo.stru_u, Foo.stru_nested_nu }`) - }) + }) - it('remove from columns via wildcard', () => { - let query = cqn4sql(CQL`SELECT from bookshop.Foo`, model) - expect(query).to.deep.equal(CQL`SELECT from bookshop.Foo as Foo { + it('remove from columns via wildcard', () => { + let query = cqn4sql(CQL`SELECT from bookshop.Foo`, model) + expect(query).to.deep.equal(CQL`SELECT from bookshop.Foo as Foo { Foo.ID, Foo.toFoo_ID, Foo.stru_u, Foo.stru_nested_nu }`) - }) + }) - it('remove from GROUP BY', () => { - let query = cqn4sql(CQL`SELECT from bookshop.Foo { ID } group by ID, virtualField`, model) - expect(query).to.deep.equal(CQL`SELECT from bookshop.Foo as Foo { Foo.ID } group by Foo.ID`) - }) + it('remove from GROUP BY', () => { + let query = cqn4sql(CQL`SELECT from bookshop.Foo { ID } group by ID, virtualField`, model) + expect(query).to.deep.equal(CQL`SELECT from bookshop.Foo as Foo { Foo.ID } group by Foo.ID`) + }) - it('remove from ORDER BY', () => { - let query = cqn4sql( - CQL`SELECT from bookshop.Foo { ID, virtualField as x } + it('remove from ORDER BY', () => { + let query = cqn4sql( + CQL`SELECT from bookshop.Foo { ID, virtualField as x } order by ID, x, Foo.virtualField`, - model, - ) - expect(query).to.deep.equal(CQL`SELECT from bookshop.Foo as Foo { Foo.ID } + model, + ) + expect(query).to.deep.equal(CQL`SELECT from bookshop.Foo as Foo { Foo.ID } order by ID`) - }) + }) - it('dont remove in xpr in ORDER BY', () => { - let query = cqn4sql( - CQL`SELECT from bookshop.Foo { ID, virtualField as x } + it('dont remove in xpr in ORDER BY', () => { + let query = cqn4sql( + CQL`SELECT from bookshop.Foo { ID, virtualField as x } order by ID, x, (Foo.toFoo.virtualField * 42)`, - model, - ) - expect(query).to.deep.equal( - CQL`SELECT from bookshop.Foo as Foo left join bookshop.Foo as toFoo on toFoo.ID = Foo.toFoo_ID + model, + ) + expect(query).to.deep.equal( + CQL`SELECT from bookshop.Foo as Foo left join bookshop.Foo as toFoo on toFoo.ID = Foo.toFoo_ID { Foo.ID } order by ID, (toFoo.virtualField * 42)`, - ) - }) + ) + }) - it('Navigation to virtual field does not cause join', () => { - let query = cqn4sql( - CQL`SELECT from bookshop.Foo { + it('Navigation to virtual field does not cause join', () => { + let query = cqn4sql( + CQL`SELECT from bookshop.Foo { ID, toFoo.virtualField, }`, - model, - ) - expect(query).to.deep.equal(CQL`SELECT from bookshop.Foo as Foo { + model, + ) + expect(query).to.deep.equal(CQL`SELECT from bookshop.Foo as Foo { Foo.ID, }`) - }) - - // Virtual fields in expressions are left untouched and will cause an error in the DB. - // The idea to replace conditions involving virtual fields by "1=1" doesn't work, as we - // are not able to detect where the conditions start/end (-> we don't understand xpr) - it('leave untouched in expressions', () => { - let query = cqn4sql( - CQL`SELECT from bookshop.Foo { + }) + + // Virtual fields in expressions are left untouched and will cause an error in the DB. + // The idea to replace conditions involving virtual fields by "1=1" doesn't work, as we + // are not able to detect where the conditions start/end (-> we don't understand xpr) + it('leave untouched in expressions', () => { + let query = cqn4sql( + CQL`SELECT from bookshop.Foo { ID, virtualField - 2 * stru.v + stru.nested.nv as c } where virtualField = 2 * stru.v + stru.nested.nv and virtualField`, - model, - ) - expect(query).to.deep.equal(CQL`SELECT from bookshop.Foo as Foo { + model, + ) + expect(query).to.deep.equal(CQL`SELECT from bookshop.Foo as Foo { Foo.ID, Foo.virtualField - 2 * Foo.stru_v + Foo.stru_nested_nv as c } where Foo.virtualField = 2 * Foo.stru_v + Foo.stru_nested_nv and Foo.virtualField`) - }) + }) - it('Navigation to virtual field does cause join in expression', () => { - let query = cqn4sql( - CQL`SELECT from bookshop.Foo { + it('Navigation to virtual field does cause join in expression', () => { + let query = cqn4sql( + CQL`SELECT from bookshop.Foo { ID, toFoo.virtualField + 42 / 20 as virtualField, }`, - model, - ) - expect(query).to.deep.equal(CQL`SELECT from bookshop.Foo as Foo + model, + ) + expect(query).to.deep.equal(CQL`SELECT from bookshop.Foo as Foo left join bookshop.Foo as toFoo on toFoo.ID = Foo.toFoo_ID { Foo.ID, toFoo.virtualField + 42 / 20 as virtualField }`) - }) + }) - it('leave untouched also in simple conditions', () => { - let query = cqn4sql(CQL`SELECT from bookshop.Foo { ID } where ID = 5 and virtualField = 6`, model) - expect(query).to.deep.equal( - CQL`SELECT from bookshop.Foo as Foo { Foo.ID } where Foo.ID = 5 and Foo.virtualField = 6`, - ) + it('leave untouched also in simple conditions', () => { + let query = cqn4sql(CQL`SELECT from bookshop.Foo { ID } where ID = 5 and virtualField = 6`, model) + expect(query).to.deep.equal( + CQL`SELECT from bookshop.Foo as Foo { Foo.ID } where Foo.ID = 5 and Foo.virtualField = 6`, + ) + }) }) -}) -describe('paths with @cds.persistence.skip', () => { - it('ignores column if assoc in path expression has target ”@cds.persistence.skip”', () => { - const q = CQL`SELECT from bookshop.NotSkipped { + describe('paths with @cds.persistence.skip', () => { + it('ignores column if assoc in path expression has target ”@cds.persistence.skip”', () => { + const q = CQL`SELECT from bookshop.NotSkipped { ID, skipped.notSkipped.text }` - const qx = CQL`SELECT from bookshop.NotSkipped as NotSkipped + const qx = CQL`SELECT from bookshop.NotSkipped as NotSkipped { NotSkipped.ID, }` - const res = cqn4sql(q, model) - expect(JSON.parse(JSON.stringify(res))).to.deep.equal(qx) - }) - it('ignores column if assoc in path expression has target ”@cds.persistence.skip” in order by / group by', () => { - const q = CQL`SELECT from bookshop.NotSkipped { + const res = cqn4sql(q, model) + expect(JSON.parse(JSON.stringify(res))).to.deep.equal(qx) + }) + it('ignores column if assoc in path expression has target ”@cds.persistence.skip” in order by / group by', () => { + const q = CQL`SELECT from bookshop.NotSkipped { ID } group by skipped.notSkipped.text order by skipped.notSkipped.text` - const qx = CQL`SELECT from bookshop.NotSkipped as NotSkipped + const qx = CQL`SELECT from bookshop.NotSkipped as NotSkipped { NotSkipped.ID, }` - const res = cqn4sql(q, model) - expect(JSON.parse(JSON.stringify(res))).to.deep.equal(qx) - }) - - // same as for virtual - it('error out if removal of element leads to empty columns', () => { - let query = CQL`SELECT from bookshop.NotSkipped { skipped.notSkipped.text }` - expect(() => cqn4sql(query, model)).to.throw('Queries must have at least one non-virtual column') - }) - - // same as for virtual - it('does not touch expression but renders the potentially wrong SQL', () => { - const q = CQL`SELECT from bookshop.NotSkipped { + const res = cqn4sql(q, model) + expect(JSON.parse(JSON.stringify(res))).to.deep.equal(qx) + }) + + // same as for virtual + it('error out if removal of element leads to empty columns', () => { + let query = CQL`SELECT from bookshop.NotSkipped { skipped.notSkipped.text }` + expect(() => cqn4sql(query, model)).to.throw('Queries must have at least one non-virtual column') + }) + + // same as for virtual + it('does not touch expression but renders the potentially wrong SQL', () => { + const q = CQL`SELECT from bookshop.NotSkipped { ID, skipped.notSkipped.text * 2 + 5 as bar } where (skipped.notSkipped.text / 2 + 5) = 42` - const qx = CQL`SELECT from bookshop.NotSkipped as NotSkipped + const qx = CQL`SELECT from bookshop.NotSkipped as NotSkipped left outer join bookshop.Skip as skipped on skipped.ID = NotSkipped.skipped_ID left outer join bookshop.NotSkipped as notSkipped2 on notSkipped2.ID = skipped.notSkipped_ID { NotSkipped.ID, notSkipped2.text * 2 + 5 as bar } where (notSkipped2.text / 2 + 5) = 42` - const res = cqn4sql(q, model) - expect(JSON.parse(JSON.stringify(res))).to.deep.equal(qx) - }) - it('No join for a skip path within filter if outer path is not persisted', () => { - const q = CQL`SELECT from bookshop.NotSkipped { + const res = cqn4sql(q, model) + expect(JSON.parse(JSON.stringify(res))).to.deep.equal(qx) + }) + it('No join for a skip path within filter if outer path is not persisted', () => { + const q = CQL`SELECT from bookshop.NotSkipped { ID, skipped[notSkipped.ID = 42].notSkipped.text }` - const qx = CQL`SELECT from bookshop.NotSkipped as NotSkipped + const qx = CQL`SELECT from bookshop.NotSkipped as NotSkipped { NotSkipped.ID, }` - const res = cqn4sql(q, model) - expect(JSON.parse(JSON.stringify(res))).to.deep.equal(qx) - }) + const res = cqn4sql(q, model) + expect(JSON.parse(JSON.stringify(res))).to.deep.equal(qx) + }) - it('Join for a skip path within filter if outer path is persisted', () => { - const q = CQL`SELECT from bookshop.SkippedAndNotSkipped { + it('Join for a skip path within filter if outer path is persisted', () => { + const q = CQL`SELECT from bookshop.SkippedAndNotSkipped { ID, self[skipped.ID = 42].ID }` - const qx = CQL`SELECT from bookshop.SkippedAndNotSkipped as SkippedAndNotSkipped + const qx = CQL`SELECT from bookshop.SkippedAndNotSkipped as SkippedAndNotSkipped left join bookshop.SkippedAndNotSkipped as self on self.ID = SkippedAndNotSkipped.self_ID and self.skipped_ID = 42 { SkippedAndNotSkipped.ID, self.ID as self_ID, }` - const res = cqn4sql(q, model) - expect(JSON.parse(JSON.stringify(res))).to.deep.equal(qx) - }) - it('Join for a skip path within filter if outer path is persisted in order by', () => { - const q = CQL`SELECT from bookshop.SkippedAndNotSkipped { + const res = cqn4sql(q, model) + expect(JSON.parse(JSON.stringify(res))).to.deep.equal(qx) + }) + it('Join for a skip path within filter if outer path is persisted in order by', () => { + const q = CQL`SELECT from bookshop.SkippedAndNotSkipped { ID } order by self[skipped.ID = 42].ID` - const qx = CQL`SELECT from bookshop.SkippedAndNotSkipped as SkippedAndNotSkipped + const qx = CQL`SELECT from bookshop.SkippedAndNotSkipped as SkippedAndNotSkipped left join bookshop.SkippedAndNotSkipped as self on self.ID = SkippedAndNotSkipped.self_ID and self.skipped_ID = 42 { SkippedAndNotSkipped.ID, } order by self.ID` - const res = cqn4sql(q, model) - expect(JSON.parse(JSON.stringify(res))).to.deep.equal(qx) - }) - - it('do not remove from simple conditions', () => { - let query = cqn4sql(CQL`SELECT from bookshop.NotSkipped { ID } where skipped.notSkipped.text`, model) - expect(query).to.deep.equal( - CQL`SELECT from bookshop.NotSkipped as NotSkipped + const res = cqn4sql(q, model) + expect(JSON.parse(JSON.stringify(res))).to.deep.equal(qx) + }) + + it('do not remove from simple conditions', () => { + let query = cqn4sql(CQL`SELECT from bookshop.NotSkipped { ID } where skipped.notSkipped.text`, model) + expect(query).to.deep.equal( + CQL`SELECT from bookshop.NotSkipped as NotSkipped left outer join bookshop.Skip as skipped on skipped.ID = NotSkipped.skipped_ID left outer join bookshop.NotSkipped as notSkipped2 on notSkipped2.ID = skipped.notSkipped_ID { NotSkipped.ID } where notSkipped2.text`, - ) + ) + }) }) }) diff --git a/db-service/test/deep/deep.test.js b/db-service/test/deep/deep.test.js index c571af185..cb8af89ca 100644 --- a/db-service/test/deep/deep.test.js +++ b/db-service/test/deep/deep.test.js @@ -1,5 +1,5 @@ const cds = require('../../../test/cds') -cds.test.in(__dirname) // IMPORTANT: that has to go before loading cds.env below +const {expect} = cds.test.in(__dirname) // IMPORTANT: that has to go before loading cds.env below cds.env.features.recursion_depth = 2 const { getDeepQueries, getExpandForDeep } = require('../../lib/deep-queries') @@ -13,7 +13,7 @@ describe('test deep query generation', () => { // SKIPPED because that test is testing obsolete internal implementation of deep delete test.skip('Deep DELETE with to-one all data provided', () => { const query = getExpandForDeep(DELETE.from(model.definitions.Root).where({ ID: 1 }), model.definitions.Root) - expect(query).toEqual({ + expect(query).to.eql({ SELECT: { from: { ref: ['Root'] }, where: [ @@ -279,7 +279,7 @@ describe('test deep query generation', () => { }), model.definitions.Root, ) - expect(query).toEqual({ + expect(query).to.eql({ SELECT: { from: { ref: ['Root'] }, columns: [ @@ -301,7 +301,7 @@ describe('test deep query generation', () => { }), model.definitions.Root, ) - expect(query).toEqual({ + expect(query).to.eql({ SELECT: { from: { ref: ['Root'] }, columns: [ @@ -327,7 +327,7 @@ describe('test deep query generation', () => { }), model.definitions.Root, ) - expect(query).toEqual({ + expect(query).to.eql({ SELECT: { from: { ref: ['Root'] }, columns: [ @@ -476,7 +476,7 @@ describe('test deep query generation', () => { model.definitions.Root, ) // TODO toManySubChild: null -> max recursion - expect(query).toEqual({ + expect(query).to.eql({ SELECT: { from: { ref: ['Root'] }, columns: [ @@ -538,7 +538,7 @@ describe('test deep query generation', () => { ) // expectation also needs to be adapted - expect(query).toMatchObject({ + expect(query).to.containSubset({ SELECT: { from: { ref: ['Root'] }, columns: [ @@ -674,7 +674,7 @@ describe('test deep query generation', () => { DELETE.from(model.definitions.Recursive).where({ ID: 5 }), model.definitions.Recursive, ) - expect(query).toEqual({ + expect(query).to.eql({ SELECT: { from: { ref: ['Recursive'] }, where: [ @@ -800,7 +800,7 @@ describe('test deep query generation', () => { const deletesArray = Array.from(deletes.values()) expectedInserts.forEach(insert => { - expect(insertsArray).toContainEqual(insert) + expect(insertsArray).to.deep.contain(insert) }) expect(updatesArray.length).toBe(0) @@ -851,25 +851,25 @@ describe('test deep query generation', () => { const l1s = dbState.l1s const l2s = l1s[0].l2s - expect(dbState).toMatchObject(root) + expect(dbState).to.containSubset(root) - expect(l1s).toMatchObject([ + expect(l1s).to.containSubset([ { - ID: expect.any(String), + // ID: expect.any(String), header_realm: entry.realm, header_uniqueName: entry.uniqueName, }, ]) - expect(l2s).toMatchObject([ + expect(l2s).to.containSubset([ { - ID: expect.any(String), + // ID: expect.any(String), l1_ID: l1s[0].ID, l1_header_realm: entry.realm, l1_header_uniqueName: entry.uniqueName, }, { - ID: expect.any(String), + // ID: expect.any(String), l1_ID: l1s[0].ID, l1_header_realm: entry.realm, l1_header_uniqueName: entry.uniqueName, diff --git a/hana/jest.config.js b/hana/jest.config.js deleted file mode 100644 index 95d58230d..000000000 --- a/hana/jest.config.js +++ /dev/null @@ -1 +0,0 @@ -exports.testTimeout = 120 * 1000 diff --git a/hana/lib/HANAService.js b/hana/lib/HANAService.js index 164e95934..ffbe61a2c 100644 --- a/hana/lib/HANAService.js +++ b/hana/lib/HANAService.js @@ -334,17 +334,17 @@ class HANAService extends SQLService { let { limit, one, orderBy, expand, columns = ['*'], localized, count, parent } = q.SELECT - const walkAlias = q => { - if (q.args) return q.as || walkAlias(q.args[0]) - if (q.SELECT?.from) return walkAlias(q.SELECT?.from) - return q.as - } - q.as = walkAlias(q) - const alias = q.alias = `${parent ? parent.alias + '.' : ''}${q.as}` - const src = q - // When one of these is defined wrap the query in a sub query if (expand || (parent && (limit || one || orderBy))) { + const walkAlias = q => { + if (q.args) return q.as || walkAlias(q.args[0]) + if (q.SELECT?.from) return walkAlias(q.SELECT?.from) + return q.as + } + q.as = walkAlias(q) + q.alias = `${parent ? parent.alias + '.' : ''}${q.as}` + const src = q + const { element, elements } = q q = cds.ql.clone(q) @@ -454,7 +454,7 @@ class HANAService extends SQLService { if (expand === 'root' && this._outputColumns) { this.cqn = q - const fromSQL = this.from({ ref: [alias] }) + const fromSQL = this.from({ ref: [q.src.alias] }) this.withclause.unshift(`${fromSQL} as (${this.sql})`) this.temporary.unshift({ blobs: this._blobs, select: `SELECT ${this._outputColumns} FROM ${fromSQL}` }) if (this.values) { @@ -480,7 +480,7 @@ class HANAService extends SQLService { ? x => { if (x === '*') return '*' // means x is a sub select expand - if (x.elements) { + if (x.elements && x.element?.isAssociation) { expands[this.column_name(x)] = x.SELECT.one ? null : [] const parent = src @@ -547,20 +547,19 @@ class HANAService extends SQLService { structures.push(x) return false } - let xpr = this.expr(x) const columnName = this.column_name(x) if (columnName === '_path_') { - path = xpr + path = this.expr(x) return false } if (x.element?.type === 'cds.Boolean') hasBooleans = true const converter = x.element?.[this.class._convertOutput] || (e => e) - return `${converter(this.quote(columnName))} as "${columnName.replace(/"/g, '""')}"` + return `${converter(this.quote(columnName), x.element)} as "${columnName.replace(/"/g, '""')}"` } : x => { if (x === '*') return '*' // means x is a sub select expand - if (x.elements) return false + if (x.elements && x.element?.isAssociation) return false return this.column_expr(x) }, ) @@ -767,9 +766,7 @@ class HANAService extends SQLService { } DROP(q) { - const { target } = q - const isView = target.query || target.projection - return (this.sql = `DROP ${isView ? 'VIEW' : 'TABLE'} ${this.quote(this.name(target.name))}`) + return (this.sql = super.DROP(q).replace('IF EXISTS', '')) } from_args(args) { @@ -959,7 +956,11 @@ class HANAService extends SQLService { const up = cur.toUpperCase() // When a logic operator is found the expression is not a comparison // When it is a local check it cannot be compared outside of the xpr - if (up in logicOperators) return !local + if (up in logicOperators) { + // ensure AND is not part of BETWEEN + if (up === 'AND' && xpr[i - 2]?.toUpperCase() in { 'BETWEEN': 1, 'NOT BETWEEN': 1 }) return true + return !local + } // When a compare operator is found the expression is a comparison if (up in compareOperators) return true // When a case operator is found it is the start of the expression @@ -1150,7 +1151,9 @@ class HANAService extends SQLService { // Reading int64 as string to not loose precision Int64: expr => `TO_NVARCHAR(${expr})`, // Reading decimal as string to not loose precision - Decimal: expr => `TO_NVARCHAR(${expr})`, + Decimal: (expr, elem) => elem?.scale + ? `TO_NVARCHAR(${expr}, '9.${''.padEnd(elem.scale, '0')}')` + : `TO_NVARCHAR(${expr})`, // HANA types 'cds.hana.ST_POINT': e => `(SELECT NEW ST_POINT(TO_NVARCHAR(${e})).ST_X() as "x", NEW ST_POINT(TO_NVARCHAR(${e})).ST_Y() as "y" FROM DUMMY WHERE (${e}) IS NOT NULL FOR JSON ('format'='no', 'omitnull'='no', 'arraywrap'='no') RETURNS NVARCHAR(2147483647))`, @@ -1300,9 +1303,21 @@ class HANAService extends SQLService { const con = await this.factory.create(this.options.credentials) this.dbc = con - const stmt = await this.dbc.prepare(createContainerTenant.replaceAll('{{{GROUP}}}', creds.containerGroup)) - const res = this.ensureDBC() && await stmt.run([creds.user, creds.password, creds.schema, !clean]) - res && DEBUG?.(res.changes.map?.(r => r.MESSAGE).join('\n')) + let i = 0 + let err + for (; i < 100; i++) { + try { + const stmt = await this.dbc.prepare(createContainerTenant.replaceAll('{{{GROUP}}}', creds.containerGroup)) + const res = this.ensureDBC() && await stmt.run([creds.user, creds.password, creds.schema, !clean]) + res && DEBUG?.(res.changes.map?.(r => r.MESSAGE).join('\n')) + break + } catch (e) { + err = e + } + } + if (i === 100) { + throw new Error(`Failed to create tenant: ${err.message || err.stack || err}`) + } } finally { await this.dbc.disconnect() delete this.dbc @@ -1375,16 +1390,19 @@ const compareOperators = { '<>': 1, '>=': 1, '<=': 1, - '!<': 1, - '!>': 1, 'IS': 1, 'IN': 1, + 'NOT IN': 1, 'LIKE': 1, + 'NOT LIKE': 1, 'IS NOT': 1, 'EXISTS': 1, + 'NOT EXISTS': 1, 'BETWEEN': 1, + 'NOT BETWEEN': 1, 'CONTAINS': 1, 'MEMBER OF': 1, + 'NOT MEMBER OF': 1, 'LIKE_REGEXPR': 1, } const lobTypes = { diff --git a/hana/lib/cql-functions.js b/hana/lib/cql-functions.js index 671c1f1a1..b78516553 100644 --- a/hana/lib/cql-functions.js +++ b/hana/lib/cql-functions.js @@ -42,7 +42,10 @@ const StandardFunctions = { maxdatetime: () => "'9999-12-31T23:59:59.999Z'", mindatetime: () => "'0001-01-01T00:00:00.000Z'", now: () => `session_context('$now')`, - fractionalseconds: x => `(TO_DECIMAL(SECOND(${x}),5,3) - TO_INTEGER(SECOND(${x})))` + current_date: () => 'current_utcdate', + current_time: () => 'current_utctime', + current_timestamp: () => 'current_utctimestamp', + fractionalseconds: x => `(TO_DECIMAL(SECOND(${x}),5,3) - TO_INTEGER(SECOND(${x})))`, } module.exports = StandardFunctions diff --git a/hana/lib/scripts/container-database.sql b/hana/lib/scripts/container-database.sql index f9b234c90..e3a5b237b 100644 --- a/hana/lib/scripts/container-database.sql +++ b/hana/lib/scripts/container-database.sql @@ -20,6 +20,7 @@ BEGIN SEQUENTIAL EXECUTION NO_PARAMS = SELECT * FROM _SYS_DI.T_NO_PARAMETERS; + SELECT 1 FROM _SYS_DI.T_DEFAULT_CONTAINER_USER_PRIVILEGES FOR UPDATE; -- lock to prevent race conditions SELECT COUNT(*) INTO USER_EXISTS FROM SYS.USERS WHERE USER_NAME = :USERNAME; SELECT COUNT(*) INTO USER_GROUP_EXISTS FROM SYS.USERGROUPS WHERE USERGROUP_NAME = :SCHEMANAME || '_USERS'; SELECT NAME INTO OPERATOR_ROLE FROM SYS.PRIVILEGES WHERE NAME = 'OPERATOR' OR NAME = 'USERGROUP OPERATOR'; @@ -53,4 +54,5 @@ BEGIN SEQUENTIAL EXECUTION COMMIT; SELECT * FROM :ALL_MESSAGES; END IF; + COMMIT; END; diff --git a/hana/package.json b/hana/package.json index c4449fc1f..3455fb368 100644 --- a/hana/package.json +++ b/hana/package.json @@ -16,15 +16,15 @@ "CHANGELOG.md" ], "scripts": { - "test": "npm start && jest --silent", - "test:remote": "jest --silent", + "test": "npm start && cds-test $(../test/find)", + "test:remote": "cds-test", "start": "npm run start:hce || npm run start:hxe", "start:hce": "cd ./tools/docker/hce/ && ./start.sh", "start:hxe": "cd ./tools/docker/hxe/ && ./start.sh" }, "dependencies": { - "hdb": "^0.19.5", - "@cap-js/db-service": "^1.9.0" + "@cap-js/db-service": "^1.9.0", + "hdb": "^0.19.5" }, "peerDependencies": { "@sap/hana-client": ">=2", diff --git a/hana/test/bookshop.test.js b/hana/test/bookshop.test.js deleted file mode 100644 index 38ca8da86..000000000 --- a/hana/test/bookshop.test.js +++ /dev/null @@ -1,3 +0,0 @@ -describe('hana', () => { - require('../../test/scenarios/bookshop') -}) diff --git a/hana/test/compliance b/hana/test/compliance new file mode 120000 index 000000000..9a3b79e79 --- /dev/null +++ b/hana/test/compliance @@ -0,0 +1 @@ +../../test \ No newline at end of file diff --git a/hana/test/compliance.test.js b/hana/test/compliance.test.js deleted file mode 100644 index 92c6e88bd..000000000 --- a/hana/test/compliance.test.js +++ /dev/null @@ -1,3 +0,0 @@ -describe('hana', () => { - require('../../test/compliance') -}) diff --git a/hana/test/lean-draft.test.js b/hana/test/lean-draft.test.js deleted file mode 100644 index 18899f227..000000000 --- a/hana/test/lean-draft.test.js +++ /dev/null @@ -1,3 +0,0 @@ -describe('lean-draft', () => { - require('../../sqlite/test/lean-draft.test') -}) diff --git a/hana/test/run.test.js b/hana/test/run.test.js index f23351d33..2131fa9c0 100644 --- a/hana/test/run.test.js +++ b/hana/test/run.test.js @@ -1,5 +1,4 @@ const cds = require('../../test/cds') -const { expect } = require('@jest/globals') let schema @@ -89,7 +88,7 @@ async function addData() { } describe('stored procedures', () => { - cds.test(__dirname, 'proc.cds') + const { expect } = cds.test(__dirname, 'proc.cds') beforeAll(async () => { await addData() @@ -112,30 +111,30 @@ describe('stored procedures', () => { } let res res = await cds.run(`CALL PROC_TEST_4(VAL_1 => ?,TEST_1 => ?,TEST_2 => ?,TEST_3 => ?)`, [2, 0]) - expect(res).toEqual(exp) + expect(res).to.containSubset(exp) res = await cds.run(`CALL PROC_TEST_4(TEST_1 => ?,TEST_2 => ?,TEST_3 => ?,VAL_1 => ?)`, [0, 2]) - expect(res).toEqual(exp) + expect(res).to.containSubset(exp) res = await cds.run(`CALL PROC_TEST_4(?,?,?,?)`, [0, 2]) - expect(res).toEqual(exp) + expect(res).to.containSubset(exp) }) test('multiple output parameters 0', async () => { const exp = { TEST_1: [{ TEST_1_COL_1: '1' }], TEST_2: '2' } const res = await cds.run('CALL "procTest0"(?,?)') - expect(res).toEqual(exp) + expect(res).to.containSubset(exp) }) test('multiple output parameters 1', async () => { const exp = { TEST_1: [{ TEST_1_COL_1: '1' }], TEST_2: [{ TEST_2_COL_1: '2' }], VAL_1: '1' } let res res = await cds.run('CALL PROC_TEST_1(TEST_1 => ?,TEST_2 => ?,VAL_2 => ?,VAL_1 => ?)', ['2', '1']) - expect(res).toEqual(exp) + expect(res).to.containSubset(exp) res = await cds.run('CALL PROC_TEST_1(TEST_1 => ?,TEST_2 => ?,VAL_1 => ?,VAL_2 => ?)', ['1', '2']) - expect(res).toEqual(exp) + expect(res).to.containSubset(exp) res = await cds.run('CALL PROC_TEST_1(TEST_1 => ?,VAL_1 => ?,TEST_2 => ?,VAL_2 => ?)', ['1', '2']) - expect(res).toEqual(exp) + expect(res).to.containSubset(exp) res = await cds.run('CALL PROC_TEST_1(?,?,?,?)', ['2', '1']) - expect(res).toEqual(exp) + expect(res).to.containSubset(exp) }) test('multiple output parameters 2', async () => { @@ -143,13 +142,13 @@ describe('stored procedures', () => { let res // also testing leading whitespaces res = await cds.run(' CALL PROC_TEST_2(TEST_1 => ?,TEST_2 => ?,VAL_2 => ?,VAL_1 => ?)', ['2', '1']) - expect(res).toEqual(exp) + expect(res).to.containSubset(exp) res = await cds.run(' CALL PROC_TEST_2(TEST_1 => ?,TEST_2 => ?,VAL_1 => ?,VAL_2 => ?)', ['1', '2']) - expect(res).toEqual(exp) + expect(res).to.containSubset(exp) res = await cds.run(' CALL PROC_TEST_2(TEST_1 => ?,VAL_1 => ?,TEST_2 => ?,VAL_2 => ?)', ['1', '2']) - expect(res).toEqual(exp) + expect(res).to.containSubset(exp) res = await cds.run(' CALL PROC_TEST_2(?,?,?,?)', ['2', '1']) - expect(res).toEqual(exp) + expect(res).to.containSubset(exp) }) test('multiple output parameters 3', async () => { @@ -157,13 +156,13 @@ describe('stored procedures', () => { let res // also testing multiple whitespaces res = await cds.run('CALL PROC_TEST_3(TEST_1 => ?,TEST_2 => ?,VAL_2 => ?,VAL_1 => ?)', ['2', '1']) - expect(res).toEqual(exp) + expect(res).to.containSubset(exp) res = await cds.run('CALL PROC_TEST_3(TEST_1 => ?,TEST_2 => ?,VAL_1 => ?,VAL_2 => ?)', ['1', '2']) - expect(res).toEqual(exp) + expect(res).to.containSubset(exp) res = await cds.run('CALL PROC_TEST_3(TEST_1 => ?,VAL_1 => ?,TEST_2 => ?,VAL_2 => ?)', ['1', '2']) - expect(res).toEqual(exp) + expect(res).to.containSubset(exp) res = await cds.run('CALL PROC_TEST_3(?,?,?,?)', ['2', '1']) - expect(res).toEqual(exp) + expect(res).to.containSubset(exp) }) test('arbitrary procedure name', async () => { @@ -174,14 +173,14 @@ describe('stored procedures', () => { '1', '2' ]) - expect(res).toEqual(exp) + expect(res).to.containSubset(exp) }) }) describe('with schema name', () => { test('schema name — undelimited', async () => { const result = await cds.run(`CALL ${schema}.MY_PROC(PARAM_0 => ?, PARAM_1 => ?, PARAM_2 => ?);`, [0]) - expect(result).toMatchObject({ + expect(result).to.containSubset({ PARAM_1: [{ NUM0: 0 }], PARAM_2: [{ NUM1: 1 }] }) @@ -189,7 +188,7 @@ describe('stored procedures', () => { test('schema name — delimited', async () => { const result = await cds.run(`CALL "${schema}"."MY_PROC"(PARAM_0 => ?, PARAM_1 => ?, PARAM_2 => ?);`, [0]) - expect(result).toMatchObject({ + expect(result).to.containSubset({ PARAM_1: [{ NUM0: 0 }], PARAM_2: [{ NUM1: 1 }] }) diff --git a/hana/test/sflight.test.js b/hana/test/sflight.test.js deleted file mode 100644 index 9906e7943..000000000 --- a/hana/test/sflight.test.js +++ /dev/null @@ -1,3 +0,0 @@ -describe('hana', () => { - require('../../test/scenarios/sflight') -}) diff --git a/hana/test/stream.test.js b/hana/test/stream.test.js index ff9727029..955c51b8b 100644 --- a/hana/test/stream.test.js +++ b/hana/test/stream.test.js @@ -1,3 +1,4 @@ +require('../../test/cds.js') describe('hana', () => { // REVISIT: fix streaming SQL syntax errors require('../../sqlite/test/general/stream.test') diff --git a/jest.config.js b/jest.config.js deleted file mode 100644 index ce3eed354..000000000 --- a/jest.config.js +++ /dev/null @@ -1,2 +0,0 @@ -// Fix debugging -exports.transform = {} diff --git a/package-lock.json b/package-lock.json index b9386a91e..1ecb182f1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,16 +14,11 @@ "postgres", "hana" ], + "dependencies": { + "@sap/cds": "^8.0.3" + }, "devDependencies": { - "@capire/sflight": "github:sap-samples/cap-sflight", - "@sap/hana-client": "^2.16.26", - "axios": "^1", - "chai": "^4.3.7", - "chai-as-promised": "^7.1.1", - "chai-subset": "^1.6.0", - "express": "^4", - "hdb": "^0.19.5", - "jest": "^29" + "@capire/sflight": "github:sap-samples/cap-sflight" } }, "db-service": { @@ -42,8 +37,7 @@ "version": "1.3.0", "license": "SEE LICENSE", "dependencies": { - "@cap-js/db-service": "^1.9.0", - "hdb": "^0.19.5" + "@cap-js/db-service": "^1.9.0" }, "peerDependencies": { "@sap/cds": ">=8.2", @@ -52,2927 +46,322 @@ "peerDependenciesMeta": { "@sap/hana-client": { "optional": true + }, + "hdb": { + "optional": true } } }, - "node_modules/@ampproject/remapping": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", - "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } + "node_modules/@cap-js/db-service": { + "resolved": "db-service", + "link": true + }, + "node_modules/@cap-js/hana": { + "resolved": "hana", + "link": true + }, + "node_modules/@cap-js/postgres": { + "resolved": "postgres", + "link": true + }, + "node_modules/@cap-js/sqlite": { + "resolved": "sqlite", + "link": true }, - "node_modules/@babel/code-frame": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz", - "integrity": "sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==", + "node_modules/@capire/sflight": { + "version": "1.0.0", + "resolved": "git+ssh://git@github.com/sap-samples/cap-sflight.git#bebfc86571f108e328d736b698f3ab0ef94d2782", "dev": true, - "license": "MIT", + "license": "SAP SAMPLE CODE LICENSE", "dependencies": { - "@babel/highlight": "^7.24.7", - "picocolors": "^1.0.0" + "@sap/cds": ">=7.0.0", + "@sap/xssec": "^3", + "express": "^4", + "hdb": "^0.19.1" }, "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.24.9", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.24.9.tgz", - "integrity": "sha512-e701mcfApCJqMMueQI0Fb68Amflj83+dvAvHawoBpAz+GDjCIyGHzNwnefjsWJ3xiYAqqiQFoWbspGYBdb2/ng==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" + "node": ">=18" } }, - "node_modules/@babel/core": { - "version": "7.24.9", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.9.tgz", - "integrity": "sha512-5e3FI4Q3M3Pbr21+5xJwCv6ZT6KmGkI0vw3Tozy5ODAQFTIWe37iT8Cr7Ice2Ntb+M3iSKCEWMB1MBgKrW3whg==", - "dev": true, - "license": "MIT", + "node_modules/@sap/cds": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@sap/cds/-/cds-8.2.0.tgz", + "integrity": "sha512-sc/xzoIVXhp4Kgeeu1yBCJfJCHXqDkb0qbQPfwKMoJCnSDNA5CiBIXyE9y0i8uG+6H2RZXkotq05zoWVL9HWmw==", "dependencies": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.24.9", - "@babel/helper-compilation-targets": "^7.24.8", - "@babel/helper-module-transforms": "^7.24.9", - "@babel/helpers": "^7.24.8", - "@babel/parser": "^7.24.8", - "@babel/template": "^7.24.7", - "@babel/traverse": "^7.24.8", - "@babel/types": "^7.24.9", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" + "@sap/cds-compiler": ">=5.1", + "@sap/cds-fiori": "^1", + "@sap/cds-foss": "^5.0.0" + }, + "bin": { + "cds-deploy": "lib/dbs/cds-deploy.js", + "cds-serve": "bin/serve.js", + "cds-test": "bin/test.js", + "chest": "bin/test.js" }, "engines": { - "node": ">=6.9.0" + "node": ">=18" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" + "peerDependencies": { + "express": ">=4" + }, + "peerDependenciesMeta": { + "express": { + "optional": true + } } }, - "node_modules/@babel/generator": { - "version": "7.24.9", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.24.9.tgz", - "integrity": "sha512-G8v3jRg+z8IwY1jHFxvCNhOPYPterE4XljNgdGTYfSTtzzwjIswIzIaSPSLs3R7yFuqnqNeay5rjICfqVr+/6A==", - "dev": true, - "license": "MIT", + "node_modules/@sap/cds-compiler": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@sap/cds-compiler/-/cds-compiler-5.2.0.tgz", + "integrity": "sha512-ymp1ChXMbU5D6P6O7QOaM8J5DUVNjtm6hr8EXoQsywtwwKuMp5UfM5n6+ube1MmInU3L9nI57l/3G3MacK+6Qg==", "dependencies": { - "@babel/types": "^7.24.9", - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25", - "jsesc": "^2.5.1" + "antlr4": "4.9.3" + }, + "bin": { + "cdsc": "bin/cdsc.js", + "cdshi": "bin/cdshi.js", + "cdsse": "bin/cdsse.js" }, "engines": { - "node": ">=6.9.0" + "node": ">=18" } }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.24.8.tgz", - "integrity": "sha512-oU+UoqCHdp+nWVDkpldqIQL/i/bvAv53tRqLG/s+cOXxe66zOYLU7ar/Xs3LdmBihrUMEUhwu6dMZwbNOYDwvw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.24.8", - "@babel/helper-validator-option": "^7.24.8", - "browserslist": "^4.23.1", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" + "node_modules/@sap/cds-fiori": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@sap/cds-fiori/-/cds-fiori-1.2.7.tgz", + "integrity": "sha512-F6Uf9wvkv0fXW+Fh7PiV2BbB/k+p1cFJLkQCCKDRJH8HvlxWEcXcn/YIvBrQGuX+GToi125MxB3wd712d8OLTA==", + "license": "SEE LICENSE IN LICENSE", + "peerDependencies": { + "@sap/cds": ">=7.6", + "express": ">=4" } }, - "node_modules/@babel/helper-environment-visitor": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.24.7.tgz", - "integrity": "sha512-DoiN84+4Gnd0ncbBOM9AZENV4a5ZiL39HYMyZJGZ/AZEykHYdJw0wW3kdcsh9/Kn+BRXHLkkklZ51ecPKmI1CQ==", - "dev": true, - "license": "MIT", + "node_modules/@sap/cds-foss": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@sap/cds-foss/-/cds-foss-5.0.1.tgz", + "integrity": "sha512-q6h7LkEx6w9LswCIQzJJ2mnoyeGS8jrmBXN4I4+aECRL60mkLskoqGetot+2tX2xXGxCYJuo5v1dtSafwBqTRQ==", + "license": "See LICENSE in LICENSE", "dependencies": { - "@babel/types": "^7.24.7" + "big.js": "^6.1.1", + "generic-pool": "^3.8.2", + "xmlbuilder": "^15.1.1", + "yaml": "^2.2.2" }, "engines": { - "node": ">=6.9.0" + "node": ">=14" } }, - "node_modules/@babel/helper-function-name": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.24.7.tgz", - "integrity": "sha512-FyoJTsj/PEUWu1/TYRiXTIHc8lbw+TDYkZuoE43opPS5TrI7MyONBE1oNvfguEXAD9yhQRrVBnXdXzSLQl9XnA==", - "dev": true, - "license": "MIT", + "node_modules/@sap/hana-client": { + "version": "2.22.27", + "resolved": "https://registry.npmjs.org/@sap/hana-client/-/hana-client-2.22.27.tgz", + "integrity": "sha512-rUqeinc+sgkzT7Mp69nuurcwPC7F74lZy8Io8gkfs3R5OzTEJFEzDComWnNC5bHzY8IhN8mUaTzfoAgG0yriyw==", + "devOptional": true, + "hasInstallScript": true, + "hasShrinkwrap": true, + "license": "SEE LICENSE IN developer-license-3_1.txt", + "optional": true, + "peer": true, "dependencies": { - "@babel/template": "^7.24.7", - "@babel/types": "^7.24.7" + "debug": "3.1.0" }, "engines": { - "node": ">=6.9.0" + "node": ">=4.0.0" } }, - "node_modules/@babel/helper-hoist-variables": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.24.7.tgz", - "integrity": "sha512-MJJwhkoGy5c4ehfoRyrJ/owKeMl19U54h27YYftT0o2teQ3FJ3nQUf/I3LlJsX4l3qlw7WRXUmiyajvHXoTubQ==", - "dev": true, - "license": "MIT", + "node_modules/@sap/hana-client/node_modules/debug": { + "version": "3.1.0", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "optional": true, + "peer": true, "dependencies": { - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" + "ms": "2.0.0" } }, - "node_modules/@babel/helper-module-imports": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.7.tgz", - "integrity": "sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==", + "node_modules/@sap/hana-client/node_modules/ms": { + "version": "2.0.0", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "optional": true, + "peer": true + }, + "node_modules/@sap/xssec": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/@sap/xssec/-/xssec-3.6.1.tgz", + "integrity": "sha512-OJouwIWClefpsJ8rVCziEydeDHDNOMA4hjsjw9OqolbbObaiYMMDRU0YJbPe7XL5JkLgrtt+CLCBCsNERxcCZg==", "dev": true, - "license": "MIT", + "license": "SAP DEVELOPER LICENSE AGREEMENT", "dependencies": { - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" + "axios": "^1.6", + "debug": "^4.3.4", + "jsonwebtoken": "^9.0.2", + "node-rsa": "^1.1.1" }, "engines": { - "node": ">=6.9.0" + "node": ">=14" } }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.24.9", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.24.9.tgz", - "integrity": "sha512-oYbh+rtFKj/HwBQkFlUzvcybzklmVdVV3UU+mN7n2t/q3yGHbuVdNxyFvSBO1tfvjyArpHNcWMAzsSPdyI46hw==", - "dev": true, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", "license": "MIT", "dependencies": { - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-module-imports": "^7.24.7", - "@babel/helper-simple-access": "^7.24.7", - "@babel/helper-split-export-declaration": "^7.24.7", - "@babel/helper-validator-identifier": "^7.24.7" + "mime-types": "~2.1.34", + "negotiator": "0.6.3" }, "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" + "node": ">= 0.6" } }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.8.tgz", - "integrity": "sha512-FFWx5142D8h2Mgr/iPVGH5G7w6jDn4jUSpZTyDnQO0Yn7Ks2Kuz6Pci8H6MPCoUJegd/UZQ3tAvfLCxQSnWWwg==", - "dev": true, - "license": "MIT", + "node_modules/antlr4": { + "version": "4.9.3", + "resolved": "https://registry.npmjs.org/antlr4/-/antlr4-4.9.3.tgz", + "integrity": "sha512-qNy2odgsa0skmNMCuxzXhM4M8J1YDaPv3TI+vCdnOAanu0N982wBrSqziDKRDctEZLZy9VffqIZXc0UGjjSP/g==", "engines": { - "node": ">=6.9.0" + "node": ">=14" } }, - "node_modules/@babel/helper-simple-access": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.24.7.tgz", - "integrity": "sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.24.7", - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "license": "MIT" }, - "node_modules/@babel/helper-split-export-declaration": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.24.7.tgz", - "integrity": "sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==", + "node_modules/asn1": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", + "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" + "safer-buffer": "~2.1.0" } }, - "node_modules/@babel/helper-string-parser": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.24.8.tgz", - "integrity": "sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==", + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } + "license": "MIT" }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz", - "integrity": "sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==", + "node_modules/axios": { + "version": "1.7.7", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz", + "integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==", "dev": true, "license": "MIT", - "engines": { - "node": ">=6.9.0" + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" } }, - "node_modules/@babel/helper-validator-option": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.24.8.tgz", - "integrity": "sha512-xb8t9tD1MHLungh/AIoWYN+gVHaB9kwlu8gffXGSt3FFEIT7RjS+xWbc2vUD1UTZdIpKj/ab3rdqJ7ufngyi2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" }, - "node_modules/@babel/helpers": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.8.tgz", - "integrity": "sha512-gV2265Nkcz7weJJfvDoAEVzC1e2OTDpkGbEsebse8koXUJUXPsCMi7sRo/+SPMuMZ9MtUPnGwITTnQnU5YjyaQ==", - "dev": true, + "node_modules/better-sqlite3": { + "version": "11.3.0", + "resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-11.3.0.tgz", + "integrity": "sha512-iHt9j8NPYF3oKCNOO5ZI4JwThjt3Z6J6XrcwG85VNMVzv1ByqrHWv5VILEbCMFWDsoHhXvQ7oC8vgRXFAKgl9w==", + "hasInstallScript": true, "license": "MIT", "dependencies": { - "@babel/template": "^7.24.7", - "@babel/types": "^7.24.8" - }, - "engines": { - "node": ">=6.9.0" + "bindings": "^1.5.0", + "prebuild-install": "^7.1.1" } }, - "node_modules/@babel/highlight": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.7.tgz", - "integrity": "sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==", - "dev": true, + "node_modules/big.js": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-6.2.1.tgz", + "integrity": "sha512-bCtHMwL9LeDIozFn+oNhhFoq+yQ3BNdnsLSASUxLciOb1vgvpHsIO1dsENiGMgbb4SkP5TrzWzRiLddn8ahVOQ==", "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.24.7", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0", - "picocolors": "^1.0.0" - }, "engines": { - "node": ">=6.9.0" + "node": "*" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/bigjs" } }, - "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, + "node_modules/bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", "license": "MIT", "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" + "file-uri-to-path": "1.0.0" } }, - "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, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", "license": "MIT", "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" } }, - "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, - "license": "MIT", - "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, - "license": "MIT" - }, - "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, - "license": "MIT", - "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, - "license": "MIT", - "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, - "license": "MIT", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/parser": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.8.tgz", - "integrity": "sha512-WzfbgXOkGzZiXXCqk43kKwZjzwx4oulxZi3nq2TYL9mOjQv6kYwul9mz6ID36njuL7Xkp6nJEfok848Zj10j/w==", - "dev": true, - "license": "MIT", - "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, - "license": "MIT", - "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, - "license": "MIT", - "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, - "license": "MIT", - "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, - "license": "MIT", - "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, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.24.7.tgz", - "integrity": "sha512-6ddciUPe/mpMnOKv/U+RSd2vvVy+Yw/JfBB0ZHYjEZt9NLHmCUylNYlsbqCCS1Bffjlb0fCwC9Vqz+sBz6PsiQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" - }, - "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, - "license": "MIT", - "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, - "license": "MIT", - "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, - "license": "MIT", - "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, - "license": "MIT", - "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, - "license": "MIT", - "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, - "license": "MIT", - "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, - "license": "MIT", - "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.24.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.24.7.tgz", - "integrity": "sha512-c/+fVeJBB0FeKsFvwytYiUD+LBvhHjGSI0g446PRGdSVGZLRNArBUno2PETbAly3tpiNAQR5XaZ+JslxkotsbA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/template": { - "version": "7.24.7", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.7.tgz", - "integrity": "sha512-jYqfPrU9JTF0PmPy1tLYHW4Mp4KlgxJD9l2nP9fD6yT/ICi554DmrWBAEYpIelzjHf1msDP3PxJIRt/nFNfBig==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.24.7", - "@babel/parser": "^7.24.7", - "@babel/types": "^7.24.7" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.24.8", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.8.tgz", - "integrity": "sha512-t0P1xxAPzEDcEPmjprAQq19NWum4K0EQPjMwZQZbHt+GiZqvjCHjj755Weq1YRPVzBI+3zSfvScfpnuIecVFJQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.24.7", - "@babel/generator": "^7.24.8", - "@babel/helper-environment-visitor": "^7.24.7", - "@babel/helper-function-name": "^7.24.7", - "@babel/helper-hoist-variables": "^7.24.7", - "@babel/helper-split-export-declaration": "^7.24.7", - "@babel/parser": "^7.24.8", - "@babel/types": "^7.24.8", - "debug": "^4.3.1", - "globals": "^11.1.0" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/types": { - "version": "7.24.9", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.9.tgz", - "integrity": "sha512-xm8XrMKz0IlUdocVbYJe0Z9xEgidU7msskG8BbhnTPK/HZ2z/7FP7ykqPgrUH+C+r414mNfNWam1f2vqOjqjYQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.24.8", - "@babel/helper-validator-identifier": "^7.24.7", - "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, - "license": "MIT" - }, - "node_modules/@cap-js/db-service": { - "resolved": "db-service", - "link": true - }, - "node_modules/@cap-js/hana": { - "resolved": "hana", - "link": true - }, - "node_modules/@cap-js/postgres": { - "resolved": "postgres", - "link": true - }, - "node_modules/@cap-js/sqlite": { - "resolved": "sqlite", - "link": true - }, - "node_modules/@capire/sflight": { - "version": "1.0.0", - "resolved": "git+ssh://git@github.com/sap-samples/cap-sflight.git#bebfc86571f108e328d736b698f3ab0ef94d2782", - "dev": true, - "license": "SAP SAMPLE CODE LICENSE", - "dependencies": { - "@sap/cds": ">=7.0.0", - "@sap/xssec": "^3", - "express": "^4", - "hdb": "^0.19.1" - }, - "engines": { - "node": ">=18" - } - }, - "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, - "license": "ISC", - "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/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, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/console": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", - "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/core": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", - "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/console": "^29.7.0", - "@jest/reporters": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@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.7.0", - "jest-config": "^29.7.0", - "jest-haste-map": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-resolve-dependencies": "^29.7.0", - "jest-runner": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "jest-watcher": "^29.7.0", - "micromatch": "^4.0.4", - "pretty-format": "^29.7.0", - "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.7.0", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", - "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-mock": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/expect": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", - "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "expect": "^29.7.0", - "jest-snapshot": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/expect-utils": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", - "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", - "dev": true, - "license": "MIT", - "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.7.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", - "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "@sinonjs/fake-timers": "^10.0.2", - "@types/node": "*", - "jest-message-util": "^29.7.0", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/globals": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", - "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/expect": "^29.7.0", - "@jest/types": "^29.6.3", - "jest-mock": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/reporters": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", - "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@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.7.0", - "jest-util": "^29.7.0", - "jest-worker": "^29.7.0", - "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, - "license": "MIT", - "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, - "license": "MIT", - "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.7.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", - "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/console": "^29.7.0", - "@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.7.0", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", - "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/test-result": "^29.7.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/transform": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", - "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", - "dev": true, - "license": "MIT", - "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.7.0", - "jest-regex-util": "^29.6.3", - "jest-util": "^29.7.0", - "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, - "license": "MIT", - "dependencies": { - "@jest/schemas": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", - "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/set-array": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", - "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@sap/cds": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/@sap/cds/-/cds-8.2.0.tgz", - "integrity": "sha512-sc/xzoIVXhp4Kgeeu1yBCJfJCHXqDkb0qbQPfwKMoJCnSDNA5CiBIXyE9y0i8uG+6H2RZXkotq05zoWVL9HWmw==", - "dependencies": { - "@sap/cds-compiler": ">=5.1", - "@sap/cds-fiori": "^1", - "@sap/cds-foss": "^5.0.0" - }, - "bin": { - "cds-deploy": "lib/dbs/cds-deploy.js", - "cds-serve": "bin/serve.js", - "cds-test": "bin/test.js", - "chest": "bin/test.js" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "express": ">=4" - }, - "peerDependenciesMeta": { - "express": { - "optional": true - } - } - }, - "node_modules/@sap/cds-compiler": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@sap/cds-compiler/-/cds-compiler-5.2.0.tgz", - "integrity": "sha512-ymp1ChXMbU5D6P6O7QOaM8J5DUVNjtm6hr8EXoQsywtwwKuMp5UfM5n6+ube1MmInU3L9nI57l/3G3MacK+6Qg==", - "dependencies": { - "antlr4": "4.9.3" - }, - "bin": { - "cdsc": "bin/cdsc.js", - "cdshi": "bin/cdshi.js", - "cdsse": "bin/cdsse.js" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@sap/cds-fiori": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@sap/cds-fiori/-/cds-fiori-1.2.7.tgz", - "integrity": "sha512-F6Uf9wvkv0fXW+Fh7PiV2BbB/k+p1cFJLkQCCKDRJH8HvlxWEcXcn/YIvBrQGuX+GToi125MxB3wd712d8OLTA==", - "license": "SEE LICENSE IN LICENSE", - "peerDependencies": { - "@sap/cds": ">=7.6", - "express": ">=4" - } - }, - "node_modules/@sap/cds-foss": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@sap/cds-foss/-/cds-foss-5.0.1.tgz", - "integrity": "sha512-q6h7LkEx6w9LswCIQzJJ2mnoyeGS8jrmBXN4I4+aECRL60mkLskoqGetot+2tX2xXGxCYJuo5v1dtSafwBqTRQ==", - "license": "See LICENSE in LICENSE", - "dependencies": { - "big.js": "^6.1.1", - "generic-pool": "^3.8.2", - "xmlbuilder": "^15.1.1", - "yaml": "^2.2.2" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/@sap/hana-client": { - "version": "2.22.29", - "resolved": "https://registry.npmjs.org/@sap/hana-client/-/hana-client-2.22.29.tgz", - "integrity": "sha512-2FuruUG4azz5+7Vihiv0nNNUjoIYWBhg/gYZUr2tQrSvvc4hT4P4BLOPgpZ31kvN/TPaedIqHXcItvh3ikY5QQ==", - "devOptional": true, - "hasInstallScript": true, - "hasShrinkwrap": true, - "license": "SEE LICENSE IN developer-license-3_2.txt", - "dependencies": { - "debug": "3.1.0" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/@sap/hana-client/node_modules/debug": { - "version": "3.1.0", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "devOptional": true, - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/@sap/hana-client/node_modules/ms": { - "version": "2.0.0", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "devOptional": true - }, - "node_modules/@sap/xssec": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/@sap/xssec/-/xssec-3.6.1.tgz", - "integrity": "sha512-OJouwIWClefpsJ8rVCziEydeDHDNOMA4hjsjw9OqolbbObaiYMMDRU0YJbPe7XL5JkLgrtt+CLCBCsNERxcCZg==", - "dev": true, - "license": "SAP DEVELOPER LICENSE AGREEMENT", - "dependencies": { - "axios": "^1.6", - "debug": "^4.3.4", - "jsonwebtoken": "^9.0.2", - "node-rsa": "^1.1.1" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/@sinclair/typebox": { - "version": "0.27.8", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", - "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@sinonjs/commons": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", - "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "type-detect": "4.0.8" - } - }, - "node_modules/@sinonjs/fake-timers": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", - "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@sinonjs/commons": "^3.0.0" - } - }, - "node_modules/@types/babel__core": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", - "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", - "dev": true, - "license": "MIT", - "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.8", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", - "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__template": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", - "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__traverse": { - "version": "7.20.6", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.6.tgz", - "integrity": "sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.20.7" - } - }, - "node_modules/@types/graceful-fs": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", - "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/istanbul-lib-coverage": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", - "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/istanbul-lib-report": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", - "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/istanbul-lib-coverage": "*" - } - }, - "node_modules/@types/istanbul-reports": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", - "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/istanbul-lib-report": "*" - } - }, - "node_modules/@types/node": { - "version": "20.14.10", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.10.tgz", - "integrity": "sha512-MdiXf+nDuMvY0gJKxyfZ7/6UFsETO7mGKF54MVD/ekJS6HdFtpZFBgrh6Pseu64XTb2MLyFPlbW6hj8HYRQNOQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "undici-types": "~5.26.4" - } - }, - "node_modules/@types/stack-utils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", - "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/yargs": { - "version": "17.0.32", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz", - "integrity": "sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@types/yargs-parser": { - "version": "21.0.3", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", - "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "license": "MIT", - "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - }, - "engines": { - "node": ">= 0.6" - } - }, - "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, - "license": "MIT", - "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, - "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, - "license": "MIT", - "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, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/antlr4": { - "version": "4.9.3", - "resolved": "https://registry.npmjs.org/antlr4/-/antlr4-4.9.3.tgz", - "integrity": "sha512-qNy2odgsa0skmNMCuxzXhM4M8J1YDaPv3TI+vCdnOAanu0N982wBrSqziDKRDctEZLZy9VffqIZXc0UGjjSP/g==", - "engines": { - "node": ">=14" - } - }, - "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, - "license": "ISC", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "license": "MIT", - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", - "license": "MIT" - }, - "node_modules/asn1": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", - "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "safer-buffer": "~2.1.0" - } - }, - "node_modules/assertion-error": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", - "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", - "dev": true, - "license": "MIT", - "engines": { - "node": "*" - } - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/axios": { - "version": "1.7.7", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz", - "integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "follow-redirects": "^1.15.6", - "form-data": "^4.0.0", - "proxy-from-env": "^1.1.0" - } - }, - "node_modules/babel-jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", - "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/transform": "^29.7.0", - "@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, - "license": "BSD-3-Clause", - "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, - "license": "BSD-3-Clause", - "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-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, - "license": "MIT", - "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, - "license": "MIT", - "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, - "license": "MIT", - "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, - "license": "MIT" - }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/better-sqlite3": { - "version": "11.3.0", - "resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-11.3.0.tgz", - "integrity": "sha512-iHt9j8NPYF3oKCNOO5ZI4JwThjt3Z6J6XrcwG85VNMVzv1ByqrHWv5VILEbCMFWDsoHhXvQ7oC8vgRXFAKgl9w==", - "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "bindings": "^1.5.0", - "prebuild-install": "^7.1.1" - } - }, - "node_modules/big.js": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/big.js/-/big.js-6.2.1.tgz", - "integrity": "sha512-bCtHMwL9LeDIozFn+oNhhFoq+yQ3BNdnsLSASUxLciOb1vgvpHsIO1dsENiGMgbb4SkP5TrzWzRiLddn8ahVOQ==", - "license": "MIT", - "engines": { - "node": "*" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/bigjs" - } - }, - "node_modules/bindings": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", - "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", - "license": "MIT", - "dependencies": { - "file-uri-to-path": "1.0.0" - } - }, - "node_modules/bl": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", - "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "license": "MIT", - "dependencies": { - "buffer": "^5.5.0", - "inherits": "^2.0.4", - "readable-stream": "^3.4.0" - } - }, - "node_modules/body-parser": { - "version": "1.20.3", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", - "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", - "license": "MIT", - "dependencies": { - "bytes": "3.1.2", - "content-type": "~1.0.5", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.13.0", - "raw-body": "2.5.2", - "type-is": "~1.6.18", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/body-parser/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/body-parser/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, - "license": "MIT", - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browserslist": { - "version": "4.23.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.2.tgz", - "integrity": "sha512-qkqSyistMYdxAcw+CzbZwlBy8AGmS/eEWs+sEV5TnLRGDOL+C5M2EnH6tlZyg0YoAxGJAFKh61En9BR941GnHA==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "caniuse-lite": "^1.0.30001640", - "electron-to-chromium": "^1.4.820", - "node-releases": "^2.0.14", - "update-browserslist-db": "^1.1.0" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "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, - "license": "Apache-2.0", - "dependencies": { - "node-int64": "^0.4.0" - } - }, - "node_modules/buffer": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", - "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.1.13" - } - }, - "node_modules/buffer-equal-constant-time": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", - "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", - "dev": true, - "license": "BSD-3-Clause" - }, - "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, - "license": "MIT" - }, - "node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/call-bind": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", - "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", - "license": "MIT", - "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001642", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001642.tgz", - "integrity": "sha512-3XQ0DoRgLijXJErLSl+bLnJ+Et4KqV1PY6JJBGAFlsNsz31zeAIncyeZfLCabHK/jtSh+671RM9YMldxjUPZtA==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "CC-BY-4.0" - }, - "node_modules/chai": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.5.0.tgz", - "integrity": "sha512-RITGBfijLkBddZvnn8jdqoTypxvqbOLYQkGGxXzeFjVHvudaPw0HNFD9x928/eUwYWd2dPCugVqspGALTZZQKw==", - "dev": true, - "license": "MIT", - "dependencies": { - "assertion-error": "^1.1.0", - "check-error": "^1.0.3", - "deep-eql": "^4.1.3", - "get-func-name": "^2.0.2", - "loupe": "^2.3.6", - "pathval": "^1.1.1", - "type-detect": "^4.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/chai-as-promised": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/chai-as-promised/-/chai-as-promised-7.1.2.tgz", - "integrity": "sha512-aBDHZxRzYnUYuIAIPBH2s511DjlKPzXNlXSGFC8CwmroWQLfrW0LtE1nK3MAwwNhJPa9raEjNCmRoFpG0Hurdw==", - "dev": true, - "license": "WTFPL", - "dependencies": { - "check-error": "^1.0.2" - }, - "peerDependencies": { - "chai": ">= 2.1.2 < 6" - } - }, - "node_modules/chai-subset": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/chai-subset/-/chai-subset-1.6.0.tgz", - "integrity": "sha512-K3d+KmqdS5XKW5DWPd5sgNffL3uxdDe+6GdnJh3AYPhwnBGRY5urfvfcbRtWIvvpz+KxkL9FeBB6MZewLUNwug==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/chai/node_modules/type-detect": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.1.0.tgz", - "integrity": "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/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, - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/check-error": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", - "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==", - "dev": true, - "license": "MIT", - "dependencies": { - "get-func-name": "^2.0.2" - }, - "engines": { - "node": "*" - } - }, - "node_modules/chownr": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", - "license": "ISC" - }, - "node_modules/ci-info": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", - "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/cjs-module-lexer": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.3.1.tgz", - "integrity": "sha512-a3KdPAANPbNE4ZUv9h6LckSl9zLsYOP4MBmhIPkRaeyybt+r4UghLvq+xw/YwUcC1gqylCkL4rdVs3Lwupjm4Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", - "dev": true, - "license": "MIT", - "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, - "license": "MIT" - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, - "license": "MIT", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true, - "license": "MIT" - }, - "node_modules/content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "license": "MIT", - "dependencies": { - "safe-buffer": "5.2.1" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/content-type": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", - "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "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, - "license": "MIT" - }, - "node_modules/cookie": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", - "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookie-signature": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", - "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", - "license": "MIT" - }, - "node_modules/create-jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", - "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-config": "^29.7.0", - "jest-util": "^29.7.0", - "prompts": "^2.0.1" - }, - "bin": { - "create-jest": "bin/create-jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "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, - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/debug": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", - "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", - "dev": true, - "license": "MIT", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/decompress-response": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", - "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", - "license": "MIT", - "dependencies": { - "mimic-response": "^3.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/dedent": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.3.tgz", - "integrity": "sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "babel-plugin-macros": "^3.1.0" - }, - "peerDependenciesMeta": { - "babel-plugin-macros": { - "optional": true - } - } - }, - "node_modules/deep-eql": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.4.tgz", - "integrity": "sha512-SUwdGfqdKOwxCPeVYjwSyRpJ7Z+fhpwIAtmCUdZIWZ/YP5R9WAsyuSgpLVDi9bjWoN2LXHNss/dk3urXtdQxGg==", - "dev": true, - "license": "MIT", - "dependencies": { - "type-detect": "^4.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "license": "MIT", - "engines": { - "node": ">=4.0.0" - } - }, - "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, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/define-data-property": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", - "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", - "license": "MIT", - "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "gopd": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/destroy": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", - "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", - "license": "MIT", - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, - "node_modules/detect-libc": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", - "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", - "license": "Apache-2.0", - "engines": { - "node": ">=8" - } - }, - "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, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "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, - "license": "MIT", - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/ecdsa-sig-formatter": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", - "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "safe-buffer": "^5.0.1" - } - }, - "node_modules/ee-first": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", - "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", - "license": "MIT" - }, - "node_modules/electron-to-chromium": { - "version": "1.4.827", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.827.tgz", - "integrity": "sha512-VY+J0e4SFcNfQy19MEoMdaIcZLmDCprqvBtkii1WTCTQHpRvf5N8+3kTYCgL/PcntvwQvmMJWTuDPsq+IlhWKQ==", - "dev": true, - "license": "ISC" - }, - "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, - "license": "MIT", - "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, - "license": "MIT" - }, - "node_modules/encodeurl": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", - "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "license": "MIT", - "dependencies": { - "once": "^1.4.0" - } - }, - "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, - "license": "MIT", - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, - "node_modules/es-define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", - "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", - "license": "MIT", - "dependencies": { - "get-intrinsic": "^1.2.4" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/escalade": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", - "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", - "license": "MIT" - }, - "node_modules/escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, - "license": "BSD-2-Clause", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/etag": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", - "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dev": true, - "license": "MIT", - "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/expand-template": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", - "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", - "license": "(MIT OR WTFPL)", - "engines": { - "node": ">=6" - } - }, - "node_modules/expect": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", - "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/expect-utils": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/express": { - "version": "4.21.1", - "resolved": "https://registry.npmjs.org/express/-/express-4.21.1.tgz", - "integrity": "sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==", - "license": "MIT", - "dependencies": { - "accepts": "~1.3.8", - "array-flatten": "1.1.1", - "body-parser": "1.20.3", - "content-disposition": "0.5.4", - "content-type": "~1.0.4", - "cookie": "0.7.1", - "cookie-signature": "1.0.6", - "debug": "2.6.9", - "depd": "2.0.0", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "etag": "~1.8.1", - "finalhandler": "1.3.1", - "fresh": "0.5.2", - "http-errors": "2.0.0", - "merge-descriptors": "1.0.3", - "methods": "~1.1.2", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "path-to-regexp": "0.1.10", - "proxy-addr": "~2.0.7", - "qs": "6.13.0", - "range-parser": "~1.2.1", - "safe-buffer": "5.2.1", - "send": "0.19.0", - "serve-static": "1.16.2", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "type-is": "~1.6.18", - "utils-merge": "1.0.1", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.10.0" - } - }, - "node_modules/express/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/express/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true, - "license": "MIT" - }, - "node_modules/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, - "license": "Apache-2.0", - "dependencies": { - "bser": "2.1.1" - } - }, - "node_modules/file-uri-to-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", - "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", - "license": "MIT" - }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, - "license": "MIT", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/finalhandler": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", - "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", - "license": "MIT", - "dependencies": { - "debug": "2.6.9", - "encodeurl": "~2.0.0", - "escape-html": "~1.0.3", - "on-finished": "2.4.1", - "parseurl": "~1.3.3", - "statuses": "2.0.1", - "unpipe": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/finalhandler/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/finalhandler/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "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, - "license": "MIT", - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/follow-redirects": { - "version": "1.15.6", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", - "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "license": "MIT", - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, - "node_modules/form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "dev": true, - "license": "MIT", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fresh": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/fs-constants": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", - "license": "MIT" - }, - "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, - "license": "ISC" - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/generic-pool": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.9.0.tgz", - "integrity": "sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==", - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "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, - "license": "MIT", - "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, - "license": "ISC", - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-func-name": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", - "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "*" - } - }, - "node_modules/get-intrinsic": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", - "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "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, - "license": "MIT", - "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, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/github-from-package": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", - "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", - "license": "MIT" - }, - "node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "dev": true, - "license": "ISC", - "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/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, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "license": "MIT", - "dependencies": { - "get-intrinsic": "^1.1.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/has-property-descriptors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", - "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", - "license": "MIT", - "dependencies": { - "es-define-property": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-proto": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", - "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/hdb": { - "version": "0.19.10", - "resolved": "https://registry.npmjs.org/hdb/-/hdb-0.19.10.tgz", - "integrity": "sha512-er0oyute1aMjf6v41JU7z1a6Zo8lqj3muC7C4Uoi81Xf4WNdjPb424wUnXIhaf4HS8H9ARDyWrMGJTvPU2jjPw==", - "license": "Apache-2.0", - "dependencies": { - "iconv-lite": "^0.4.18" - }, - "engines": { - "node": ">= 0.12" - } - }, - "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, - "license": "MIT" - }, - "node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "node_modules/body-parser": { + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", + "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", "license": "MIT", "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.13.0", + "raw-body": "2.5.2", + "type-is": "~1.6.18", + "unpipe": "1.0.0" }, "engines": { - "node": ">= 0.8" - } - }, - "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, - "license": "Apache-2.0", - "engines": { - "node": ">=10.17.0" + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" } }, - "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "license": "MIT", "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, - "engines": { - "node": ">=0.10.0" + "ms": "2.0.0" } }, - "node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", "funding": [ { "type": "github", @@ -2987,86 +376,39 @@ "url": "https://feross.org/support" } ], - "license": "BSD-3-Clause" - }, - "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, "license": "MIT", "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, - "license": "MIT", - "engines": { - "node": ">=0.8.19" + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" } }, - "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==", - "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", "dev": true, - "license": "ISC", - "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==", - "license": "ISC" - }, - "node_modules/ini": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "license": "ISC" + "license": "BSD-3-Clause" }, - "node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", "license": "MIT", "engines": { - "node": ">= 0.10" + "node": ">= 0.8" } }, - "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, - "license": "MIT" - }, - "node_modules/is-core-module": { - "version": "2.14.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.14.0.tgz", - "integrity": "sha512-a5dFJih5ZLYlRtDc0dZWP7RiKr6xIKzmn/oAYCDvdLThadVgyJwlaoQPmRtMSpz+rk0OGAgIu+TcM9HUF0fk1A==", - "dev": true, + "node_modules/call-bind": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", + "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", "license": "MIT", "dependencies": { - "hasown": "^2.0.2" + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" }, "engines": { "node": ">= 0.4" @@ -3075,788 +417,573 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "license": "ISC" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "dev": true, "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, "engines": { - "node": ">=8" + "node": ">= 0.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, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, "engines": { - "node": ">=6" + "node": ">= 0.6" } }, - "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, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", "license": "MIT", "engines": { - "node": ">=0.12.0" + "node": ">= 0.6" } }, - "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, + "node_modules/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", "license": "MIT", "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">= 0.6" } }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, - "license": "ISC" - }, - "node_modules/istanbul-lib-coverage": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", - "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=8" - } + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", + "license": "MIT" }, - "node_modules/istanbul-lib-instrument": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", - "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", + "node_modules/debug": { + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", + "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", "dev": true, - "license": "BSD-3-Clause", + "license": "MIT", "dependencies": { - "@babel/core": "^7.23.9", - "@babel/parser": "^7.23.9", - "@istanbuljs/schema": "^0.1.3", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^7.5.4" + "ms": "2.1.2" }, "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-instrument/node_modules/semver": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", - "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" + "node": ">=6.0" }, - "engines": { - "node": ">=10" + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "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, - "license": "BSD-3-Clause", + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "license": "MIT", "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^4.0.0", - "supports-color": "^7.1.0" + "mimic-response": "^3.1.0" }, "engines": { "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "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, - "license": "BSD-3-Clause", - "dependencies": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" - }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "license": "MIT", "engines": { - "node": ">=10" + "node": ">=4.0.0" } }, - "node_modules/istanbul-reports": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", - "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", - "dev": true, - "license": "BSD-3-Clause", + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "license": "MIT", "dependencies": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" }, "engines": { - "node": ">=8" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", - "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", "dev": true, "license": "MIT", - "dependencies": { - "@jest/core": "^29.7.0", - "@jest/types": "^29.6.3", - "import-local": "^3.0.2", - "jest-cli": "^29.7.0" - }, - "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": ">=0.4.0" } }, - "node_modules/jest-changed-files": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", - "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", - "dev": true, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", "license": "MIT", - "dependencies": { - "execa": "^5.0.0", - "jest-util": "^29.7.0", - "p-limit": "^3.1.0" - }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">= 0.8" } }, - "node_modules/jest-circus": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", - "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", - "dev": true, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", "license": "MIT", - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/expect": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@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.7.0", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "p-limit": "^3.1.0", - "pretty-format": "^29.7.0", - "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": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" } }, - "node_modules/jest-cli": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", - "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/core": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "create-jest": "^29.7.0", - "exit": "^0.1.2", - "import-local": "^3.0.2", - "jest-config": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "yargs": "^17.3.1" - }, - "bin": { - "jest": "bin/jest.js" - }, + "node_modules/detect-libc": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", + "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", + "license": "Apache-2.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": ">=8" } }, - "node_modules/jest-config": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", - "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", "dev": true, - "license": "MIT", + "license": "Apache-2.0", "dependencies": { - "@babel/core": "^7.11.6", - "@jest/test-sequencer": "^29.7.0", - "@jest/types": "^29.6.3", - "babel-jest": "^29.7.0", - "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.7.0", - "jest-environment-node": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-runner": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "micromatch": "^4.0.4", - "parse-json": "^5.2.0", - "pretty-format": "^29.7.0", - "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 - } + "safe-buffer": "^5.0.1" } }, - "node_modules/jest-diff": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", - "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", - "dev": true, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", "license": "MIT", - "dependencies": { - "chalk": "^4.0.0", - "diff-sequences": "^29.6.3", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">= 0.8" } }, - "node_modules/jest-docblock": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", - "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", - "dev": true, + "node_modules/end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", "license": "MIT", "dependencies": { - "detect-newline": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "once": "^1.4.0" } }, - "node_modules/jest-each": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", - "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", - "dev": true, + "node_modules/es-define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", + "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", "license": "MIT", "dependencies": { - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "jest-get-type": "^29.6.3", - "jest-util": "^29.7.0", - "pretty-format": "^29.7.0" + "get-intrinsic": "^1.2.4" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">= 0.4" } }, - "node_modules/jest-environment-node": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", - "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", - "dev": true, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", "license": "MIT", - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0" - }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">= 0.4" } }, - "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, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", "license": "MIT", "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">= 0.6" } }, - "node_modules/jest-haste-map": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", - "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", - "dev": true, - "license": "MIT", - "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.7.0", - "jest-worker": "^29.7.0", - "micromatch": "^4.0.4", - "walker": "^1.0.8" - }, + "node_modules/expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "license": "(MIT OR WTFPL)", "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "optionalDependencies": { - "fsevents": "^2.3.2" + "node": ">=6" } }, - "node_modules/jest-leak-detector": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", - "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", - "dev": true, + "node_modules/express": { + "version": "4.21.0", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.0.tgz", + "integrity": "sha512-VqcNGcj/Id5ZT1LZ/cfihi3ttTn+NJmkli2eZADigjq29qTlWi/hAQ43t/VLPq8+UX06FCEx3ByOYet6ZFblng==", "license": "MIT", "dependencies": { - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.3", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.6.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.3.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.10", + "proxy-addr": "~2.0.7", + "qs": "6.13.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.19.0", + "serve-static": "1.16.2", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">= 0.10.0" } }, - "node_modules/jest-matcher-utils": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", - "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", - "dev": true, + "node_modules/express/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "license": "MIT", "dependencies": { - "chalk": "^4.0.0", - "jest-diff": "^29.7.0", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "ms": "2.0.0" } }, - "node_modules/jest-message-util": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", - "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", - "dev": true, + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "license": "MIT" + }, + "node_modules/finalhandler": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", + "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^29.6.3", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">= 0.8" } }, - "node_modules/jest-mock": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", - "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", - "dev": true, + "node_modules/finalhandler/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "license": "MIT", "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "ms": "2.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==", + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/follow-redirects": { + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], "license": "MIT", "engines": { - "node": ">=6" - }, - "peerDependencies": { - "jest-resolve": "*" + "node": ">=4.0" }, "peerDependenciesMeta": { - "jest-resolve": { + "debug": { "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==", + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", "dev": true, "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">= 6" } }, - "node_modules/jest-resolve": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", - "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-pnp-resolver": "^1.2.2", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "resolve": "^1.20.0", - "resolve.exports": "^2.0.0", - "slash": "^3.0.0" - }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">= 0.6" } }, - "node_modules/jest-resolve-dependencies": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", - "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", - "dev": true, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", "license": "MIT", - "dependencies": { - "jest-regex-util": "^29.6.3", - "jest-snapshot": "^29.7.0" - }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">= 0.6" } }, - "node_modules/jest-runner": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", - "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", - "dev": true, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "license": "MIT" + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", "license": "MIT", - "dependencies": { - "@jest/console": "^29.7.0", - "@jest/environment": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "graceful-fs": "^4.2.9", - "jest-docblock": "^29.7.0", - "jest-environment-node": "^29.7.0", - "jest-haste-map": "^29.7.0", - "jest-leak-detector": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-resolve": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-util": "^29.7.0", - "jest-watcher": "^29.7.0", - "jest-worker": "^29.7.0", - "p-limit": "^3.1.0", - "source-map-support": "0.5.13" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-runtime": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", - "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", - "dev": true, + "node_modules/generic-pool": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.9.0.tgz", + "integrity": "sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==", "license": "MIT", - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/fake-timers": "^29.7.0", - "@jest/globals": "^29.7.0", - "@jest/source-map": "^29.6.3", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@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.7.0", - "jest-message-util": "^29.7.0", - "jest-mock": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "slash": "^3.0.0", - "strip-bom": "^4.0.0" - }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">= 4" } }, - "node_modules/jest-snapshot": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", - "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", - "dev": true, + "node_modules/get-intrinsic": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", + "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", "license": "MIT", "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.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "babel-preset-current-node-syntax": "^1.0.0", - "chalk": "^4.0.0", - "expect": "^29.7.0", - "graceful-fs": "^4.2.9", - "jest-diff": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "natural-compare": "^1.4.0", - "pretty-format": "^29.7.0", - "semver": "^7.5.3" + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-snapshot/node_modules/semver": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", - "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" + "node": ">= 0.4" }, - "engines": { - "node": ">=10" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-util": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", - "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", - "dev": true, + "node_modules/github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", + "license": "MIT" + }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", "license": "MIT", "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" + "get-intrinsic": "^1.1.3" }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-validate": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", - "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", - "dev": true, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", "license": "MIT", "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.7.0" + "es-define-property": "^1.0.0" }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "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, + "node_modules/has-proto": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", + "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", "license": "MIT", "engines": { - "node": ">=10" + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-watcher": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", - "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", - "dev": true, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", "license": "MIT", - "dependencies": { - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "jest-util": "^29.7.0", - "string-length": "^4.0.1" - }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/jest-worker": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", - "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", - "dev": true, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", "license": "MIT", "dependencies": { - "@types/node": "*", - "jest-util": "^29.7.0", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" + "function-bind": "^1.1.2" }, "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "node": ">= 0.4" } }, - "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, - "license": "MIT", + "node_modules/hdb": { + "version": "0.19.10", + "resolved": "https://registry.npmjs.org/hdb/-/hdb-0.19.10.tgz", + "integrity": "sha512-er0oyute1aMjf6v41JU7z1a6Zo8lqj3muC7C4Uoi81Xf4WNdjPb424wUnXIhaf4HS8H9ARDyWrMGJTvPU2jjPw==", + "license": "Apache-2.0", "dependencies": { - "has-flag": "^4.0.0" + "iconv-lite": "^0.4.18" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" + "node": ">= 0.12" } }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", "license": "MIT", "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" }, - "bin": { - "js-yaml": "bin/js-yaml.js" + "engines": { + "node": ">= 0.8" } }, - "node_modules/jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" }, "engines": { - "node": ">=4" + "node": ">=0.10.0" } }, - "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, - "license": "MIT" + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "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/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "license": "ISC" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", "license": "MIT", - "bin": { - "json5": "lib/cli.js" - }, "engines": { - "node": ">=6" + "node": ">= 0.10" } }, "node_modules/jsonwebtoken": { @@ -3918,46 +1045,6 @@ "safe-buffer": "^5.0.1" } }, - "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, - "license": "MIT", - "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, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "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, - "license": "MIT" - }, - "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, - "license": "MIT", - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/lodash.includes": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", @@ -3997,8 +1084,7 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", - "dev": true, - "license": "MIT" + "dev": true }, "node_modules/lodash.once": { "version": "4.1.1", @@ -4007,65 +1093,6 @@ "dev": true, "license": "MIT" }, - "node_modules/loupe": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", - "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==", - "dev": true, - "license": "MIT", - "dependencies": { - "get-func-name": "^2.0.1" - } - }, - "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, - "license": "ISC", - "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, - "license": "MIT", - "dependencies": { - "semver": "^7.5.3" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/make-dir/node_modules/semver": { - "version": "7.6.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz", - "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/makeerror": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", - "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "tmpl": "1.0.5" - } - }, "node_modules/media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -4084,13 +1111,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true, - "license": "MIT" - }, "node_modules/methods": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", @@ -4100,20 +1120,6 @@ "node": ">= 0.6" } }, - "node_modules/micromatch": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.7.tgz", - "integrity": "sha512-LPP/3KorzCwBxfeUuZmaR6bG2kdeHSbe0P2tY3FLRU4vYrjYz5hI4QZwV0njUx3jeuKe67YukQ1LSPZBKDqO/Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, "node_modules/mime": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", @@ -4147,16 +1153,6 @@ "node": ">= 0.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, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/mimic-response": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", @@ -4169,19 +1165,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, "node_modules/minimist": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", @@ -4210,13 +1193,6 @@ "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==", "license": "MIT" }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true, - "license": "MIT" - }, "node_modules/negotiator": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", @@ -4250,19 +1226,10 @@ "node": ">=10" } }, - "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, - "license": "MIT" - }, - "node_modules/node-releases": { - "version": "2.0.14", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", - "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", - "dev": true, - "license": "MIT" + "node_modules/node-abi/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "node_modules/node-rsa": { "version": "1.1.1", @@ -4274,29 +1241,6 @@ "asn1": "^0.2.4" } }, - "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, - "license": "MIT", - "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, - "license": "MIT", - "dependencies": { - "path-key": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/object-inspect": { "version": "1.13.2", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", @@ -4330,96 +1274,6 @@ "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, - "license": "MIT", - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "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, - "license": "MIT", - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/p-locate/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, - "license": "MIT", - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "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, - "license": "MIT", - "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, - "license": "MIT", - "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/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", @@ -4429,59 +1283,12 @@ "node": ">= 0.8" } }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-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, - "license": "MIT", - "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, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true, - "license": "MIT" - }, "node_modules/path-to-regexp": { "version": "0.1.10", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.10.tgz", "integrity": "sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==", "license": "MIT" }, - "node_modules/pathval": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", - "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "*" - } - }, "node_modules/pg": { "version": "8.13.0", "resolved": "https://registry.npmjs.org/pg/-/pg-8.13.0.tgz", @@ -4571,49 +1378,6 @@ "split2": "^4.1.0" } }, - "node_modules/picocolors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", - "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", - "dev": true, - "license": "ISC" - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/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, - "license": "MIT", - "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, - "license": "MIT", - "dependencies": { - "find-up": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/postgres-array": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", @@ -4679,48 +1443,6 @@ "node": ">=10" } }, - "node_modules/pretty-format": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", - "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/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, - "license": "MIT", - "dependencies": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -4751,23 +1473,6 @@ "once": "^1.3.1" } }, - "node_modules/pure-rand": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", - "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/dubzzz" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fast-check" - } - ], - "license": "MIT" - }, "node_modules/qs": { "version": "6.13.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", @@ -4831,13 +1536,6 @@ "node": ">=0.10.0" } }, - "node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "dev": true, - "license": "MIT" - }, "node_modules/readable-stream": { "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", @@ -4852,67 +1550,6 @@ "node": ">= 6" } }, - "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, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/resolve": { - "version": "1.22.8", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", - "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", - "dev": true, - "license": "MIT", - "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, - "license": "MIT", - "dependencies": { - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "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, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "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, - "license": "MIT", - "engines": { - "node": ">=10" - } - }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", @@ -4937,17 +1574,7 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "license": "MIT" - }, - "node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } + "license": "MIT" }, "node_modules/send": { "version": "0.19.0", @@ -5041,29 +1668,6 @@ "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", "license": "ISC" }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/side-channel": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", @@ -5082,13 +1686,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "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, - "license": "ISC" - }, "node_modules/simple-concat": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", @@ -5134,44 +1731,6 @@ "simple-concat": "^1.0.0" } }, - "node_modules/sisteransi": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", - "dev": true, - "license": "MIT" - }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "license": "MIT", - "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, - "license": "BSD-3-Clause", - "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, - "license": "MIT", - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, "node_modules/split2": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", @@ -5181,26 +1740,6 @@ "node": ">= 10.x" } }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/stack-utils": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", - "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "escape-string-regexp": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", @@ -5219,107 +1758,6 @@ "safe-buffer": "~5.2.0" } }, - "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, - "license": "MIT", - "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, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/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, - "license": "MIT", - "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, - "license": "MIT", - "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, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/tar-fs": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", @@ -5348,51 +1786,6 @@ "node": ">=6" } }, - "node_modules/test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", - "dev": true, - "license": "ISC", - "dependencies": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - }, - "engines": { - "node": ">=8" - } - }, - "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, - "license": "BSD-3-Clause" - }, - "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, - "license": "MIT", - "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, - "license": "MIT", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, "node_modules/toidentifier": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", @@ -5414,29 +1807,6 @@ "node": "*" } }, - "node_modules/type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/type-fest": { - "version": "0.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, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", @@ -5450,13 +1820,6 @@ "node": ">= 0.6" } }, - "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true, - "license": "MIT" - }, "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", @@ -5466,37 +1829,6 @@ "node": ">= 0.8" } }, - "node_modules/update-browserslist-db": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz", - "integrity": "sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "escalade": "^3.1.2", - "picocolors": "^1.0.1" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -5512,21 +1844,6 @@ "node": ">= 0.4.0" } }, - "node_modules/v8-to-istanbul": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", - "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", - "dev": true, - "license": "ISC", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.12", - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^2.0.0" - }, - "engines": { - "node": ">=10.12.0" - } - }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", @@ -5536,70 +1853,12 @@ "node": ">= 0.8" } }, - "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, - "license": "Apache-2.0", - "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, - "license": "ISC", - "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, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "license": "ISC" }, - "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, - "license": "ISC", - "dependencies": { - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.7" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, "node_modules/xmlbuilder": { "version": "15.1.1", "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz", @@ -5618,23 +1877,6 @@ "node": ">=0.4" } }, - "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, - "license": "ISC", - "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, - "license": "ISC" - }, "node_modules/yaml": { "version": "2.4.5", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.4.5.tgz", @@ -5647,48 +1889,6 @@ "node": ">= 14" } }, - "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, - "license": "MIT", - "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, - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "postgres": { "name": "@cap-js/postgres", "version": "1.10.0", diff --git a/package.json b/package.json index 534e34987..d92e512b8 100644 --- a/package.json +++ b/package.json @@ -10,18 +10,10 @@ "hana" ], "devDependencies": { - "@capire/sflight": "github:sap-samples/cap-sflight", - "@sap/hana-client": "^2.16.26", - "axios": "^1", - "chai": "^4.3.7", - "chai-as-promised": "^7.1.1", - "chai-subset": "^1.6.0", - "express": "^4", - "hdb": "^0.19.5", - "jest": "^29" + "@capire/sflight": "github:sap-samples/cap-sflight" }, "scripts": { - "test": "CDS_JEST_MEM_FIX=1 npm t -w db-service -w sqlite", + "test": "npm t -w db-service -w sqlite", "lint": "npx eslint ." }, "license": "SEE LICENSE" diff --git a/postgres/jest.config.js b/postgres/jest.config.js deleted file mode 100644 index 2ae866131..000000000 --- a/postgres/jest.config.js +++ /dev/null @@ -1,10 +0,0 @@ -exports.testTimeout = 30 * 1000 - -// Ignore inherited tests that encounter tuple errors -if (!process.env.CI) exports.testPathIgnorePatterns = [ - '/test/timezone.test.js', - '/test/service.test.js', - '/test/service-types.test.js', - '/test/service-admin.test.js', - '/test/odata-string-functions.test.js', -] diff --git a/postgres/lib/PostgresService.js b/postgres/lib/PostgresService.js index 4b910f48b..4d40ac3c9 100644 --- a/postgres/lib/PostgresService.js +++ b/postgres/lib/PostgresService.js @@ -253,7 +253,7 @@ GROUP BY k return this.dbc.query(sql) } - onPlainSQL(req, next) { + async onPlainSQL(req, next) { const query = req.query if (this.options.independentDeploy) { // REVISIT: Should not be needed when deployment supports all types or sends CQNs @@ -289,8 +289,19 @@ GROUP BY k // eslint-disable-next-line no-unused-vars req.query = query.replace(/('|")(\1|[^\1]*?\1)|(\?)/g, (a, _b, _c, d, _e, _f, _g) => (d ? '$' + i++ : a)) } - - return super.onPlainSQL(req, next) + try { + return await super.onPlainSQL(req, next) + } + catch (err) { + if (err.code === '3F000') { + if (this.options?.credentials?.schema) { + cds.error`Failed to configure schema ("${this.options?.credentials?.schema}") before plainSQL call: ${req.query}` + } else { + cds.error`No schema was configure / detected before plainSQL call: ${req.query}` + } + } + throw err + } } async onSELECT({ query, data }) { @@ -532,7 +543,10 @@ GROUP BY k Int64: cds.env.features.ieee754compatible ? expr => `cast(${expr} as varchar)` : undefined, // REVISIT: always cast to string in next major // Reading decimal as string to not loose precision - Decimal: cds.env.features.ieee754compatible ? expr => `cast(${expr} as varchar)` : undefined, + Decimal: cds.env.features.ieee754compatible ? (expr, elem) => elem?.scale + ? `to_char(${expr},'FM${'0'.padStart(elem.precision, '9')}${'D'.padEnd(elem.scale + 1, '0')}')` + : `cast(${expr} as varchar)` + : undefined, // Convert point back to json format 'cds.hana.ST_POINT': expr => `CASE WHEN (${expr}) IS NOT NULL THEN json_object('x':(${expr})[0],'y':(${expr})[1])::varchar END`, @@ -567,14 +581,21 @@ GROUP BY k `) await this.exec(`CREATE DATABASE "${creds.database}" OWNER="${creds.user}" TEMPLATE=template0`) } catch { + // Failed to connect to database + if (!this.dbc) { + return this.database({ database }) + } // Failed to reset database } finally { - await this.dbc.end() - delete this.dbc - - // Update credentials to new Database owner - await this.disconnect() - this.options.credentials = Object.assign({}, system, creds) + // Only clean when successfully connected + if (this.dbc) { + await this.dbc.end() + delete this.dbc + + // Update credentials to new Database owner + await this.disconnect() + this.options.credentials = Object.assign({}, system, creds) + } } } @@ -589,18 +610,20 @@ GROUP BY k try { if (!clean) { - await this.tx(async tx => { - // await tx.run(`DROP USER IF EXISTS "${creds.user}"`) - await tx - .run(`CREATE USER "${creds.user}" IN GROUP "${creds.usergroup}" PASSWORD '${creds.password}'`) - .catch(e => { - if (e.code === '42710') return - throw e - }) - }) - await this.tx(async tx => { - await tx.run(`GRANT CREATE, CONNECT ON DATABASE "${creds.database}" TO "${creds.user}";`) - }) + await cds + .run(`CREATE USER "${creds.user}" IN GROUP "${creds.usergroup}" PASSWORD '${creds.password}'`) + .catch(e => { + if (e.code === '42710') return + throw e + }) + // Retry granting priviledges as this is being done by multiple instances + // Postgres just rejects when other connections are granting the same user + const grant = (i = 0) => cds.run(`GRANT CREATE, CONNECT ON DATABASE "${creds.database}" TO "${creds.user}";`) + .catch((err) => { + if (i > 100) throw err + return grant(i + 1) + }) + await grant() } // Update credentials to new Schema owner @@ -610,7 +633,7 @@ GROUP BY k // Create new schema using schema owner await this.tx(async tx => { await tx.run(`DROP SCHEMA IF EXISTS "${creds.schema}" CASCADE`) - if (!clean) await tx.run(`CREATE SCHEMA "${creds.schema}" AUTHORIZATION "${creds.user}"`).catch(() => { }) + if (!clean) await tx.run(`CREATE SCHEMA "${creds.schema}" AUTHORIZATION "${creds.user}"`) }) } finally { await this.disconnect() diff --git a/postgres/lib/cql-functions.js b/postgres/lib/cql-functions.js index c38ef7f8a..6c91c152b 100644 --- a/postgres/lib/cql-functions.js +++ b/postgres/lib/cql-functions.js @@ -24,6 +24,26 @@ const StandardFunctions = { minute: x => `date_part('minute', ${castVal(x)})`, second: x => `floor(date_part('second', ${castVal(x)}))`, fractionalseconds: x => `CAST(date_part('second', ${castVal(x)}) - floor(date_part('second', ${castVal(x)})) AS DECIMAL)`, + totalseconds: x => `( + ( + ( + CAST(substring(${x},2,strpos(${x},'DT') - 2) AS INTEGER) + ) + ( + EXTRACT (EPOCH FROM + CAST( + replace( + replace( + replace( + substring(${x},strpos(${x},'DT') + 2), + 'H',':' + ),'M',':' + ),'S','Z' + ) + as TIME) + ) - 0.5 + ) + ) * 86400 + )`, now: function() { return this.session_context({val: '$now'}) } diff --git a/postgres/package.json b/postgres/package.json index 04b949357..dfdff40c7 100644 --- a/postgres/package.json +++ b/postgres/package.json @@ -23,7 +23,7 @@ "CHANGELOG.md" ], "scripts": { - "test": "npm start && jest --silent", + "test": "npm start && cds-test $(../test/find)", "start": "docker compose -f pg-stack.yml up -d" }, "dependencies": { diff --git a/postgres/test/__snapshots__/csn2postgres.test.js.snap b/postgres/test/__snapshots__/csn2postgres.test.js.snap deleted file mode 100644 index 7da315d2f..000000000 --- a/postgres/test/__snapshots__/csn2postgres.test.js.snap +++ /dev/null @@ -1,218 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`CSN to PostgreSQL Create Statements should return PostgreSQL compatible statements for beershop-admin-service 1`] = ` -" -CREATE TABLE BeershopAdminService_UserScopes ( - username VARCHAR(255) NOT NULL, - is_admin BOOLEAN, - PRIMARY KEY(username) -); - -CREATE TABLE csw_Beers ( - ID VARCHAR(36) NOT NULL, - createdAt TIMESTAMP, - createdBy VARCHAR(255), - modifiedAt TIMESTAMP, - modifiedBy VARCHAR(255), - name VARCHAR(100), - abv DECIMAL(3, 1), - ibu INTEGER, - brewery_ID VARCHAR(36), - PRIMARY KEY(ID) -); - -CREATE TABLE csw_Brewery ( - ID VARCHAR(36) NOT NULL, - createdAt TIMESTAMP, - createdBy VARCHAR(255), - modifiedAt TIMESTAMP, - modifiedBy VARCHAR(255), - name VARCHAR(150), - PRIMARY KEY(ID) -); - -CREATE TABLE csw_TypeChecks ( - ID VARCHAR(36) NOT NULL, - type_Boolean BOOLEAN, - type_Int32 INTEGER, - type_Int64 BIGINT, - type_Decimal DECIMAL(2, 1), - type_Double FLOAT8, - type_Date DATE, - type_Time TIME, - type_DateTime TIMESTAMP, - type_Timestamp TIMESTAMP, - type_String VARCHAR(255), - type_Binary BYTEA, - type_LargeBinary BYTEA, - type_LargeString TEXT, - PRIMARY KEY(ID) -); - -CREATE VIEW BeershopAdminService_Beers AS SELECT - Beers_0.ID, - Beers_0.createdAt, - Beers_0.createdBy, - Beers_0.modifiedAt, - Beers_0.modifiedBy, - Beers_0.name, - Beers_0.abv, - Beers_0.ibu, - Beers_0.brewery_ID -FROM csw_Beers AS Beers_0; - -CREATE VIEW BeershopAdminService_Breweries AS SELECT - Brewery_0.ID, - Brewery_0.createdAt, - Brewery_0.createdBy, - Brewery_0.modifiedAt, - Brewery_0.modifiedBy, - Brewery_0.name -FROM csw_Brewery AS Brewery_0; -" -`; - -exports[`CSN to PostgreSQL Create Statements should return PostgreSQL compatible statements for beershop-service 1`] = ` -" -CREATE TABLE csw_Beers ( - ID VARCHAR(36) NOT NULL, - createdAt TIMESTAMP, - createdBy VARCHAR(255), - modifiedAt TIMESTAMP, - modifiedBy VARCHAR(255), - name VARCHAR(100), - abv DECIMAL(3, 1), - ibu INTEGER, - brewery_ID VARCHAR(36), - PRIMARY KEY(ID) -); - -CREATE TABLE csw_Brewery ( - ID VARCHAR(36) NOT NULL, - createdAt TIMESTAMP, - createdBy VARCHAR(255), - modifiedAt TIMESTAMP, - modifiedBy VARCHAR(255), - name VARCHAR(150), - PRIMARY KEY(ID) -); - -CREATE TABLE csw_TypeChecks ( - ID VARCHAR(36) NOT NULL, - type_Boolean BOOLEAN, - type_Int32 INTEGER, - type_Int64 BIGINT, - type_Decimal DECIMAL(2, 1), - type_Double FLOAT8, - type_Date DATE, - type_Time TIME, - type_DateTime TIMESTAMP, - type_Timestamp TIMESTAMP, - type_String VARCHAR(255), - type_Binary BYTEA, - type_LargeBinary BYTEA, - type_LargeString TEXT, - PRIMARY KEY(ID) -); - -CREATE TABLE DRAFT_DraftAdministrativeData ( - DraftUUID VARCHAR(36) NOT NULL, - CreationDateTime TIMESTAMP, - CreatedByUser VARCHAR(256), - DraftIsCreatedByMe BOOLEAN, - LastChangeDateTime TIMESTAMP, - LastChangedByUser VARCHAR(256), - InProcessByUser VARCHAR(256), - DraftIsProcessedByMe BOOLEAN, - PRIMARY KEY(DraftUUID) -); - -CREATE TABLE BeershopService_TypeChecksWithDraft_drafts ( - ID VARCHAR(36) NOT NULL, - type_Boolean BOOLEAN NULL, - type_Int32 INTEGER NULL, - type_Int64 BIGINT NULL, - type_Decimal DECIMAL(2, 1) NULL, - type_Double FLOAT8 NULL, - type_Date DATE NULL, - type_Time TIME NULL, - type_DateTime TIMESTAMP NULL, - type_Timestamp TIMESTAMP NULL, - type_String VARCHAR(255) NULL, - type_Binary BYTEA NULL, - type_LargeBinary BYTEA NULL, - type_LargeString TEXT NULL, - IsActiveEntity BOOLEAN, - HasActiveEntity BOOLEAN, - HasDraftEntity BOOLEAN, - DraftAdministrativeData_DraftUUID VARCHAR(36) NOT NULL, - PRIMARY KEY(ID) -); - -CREATE VIEW BeershopService_Beers AS SELECT - Beers_0.ID, - Beers_0.createdAt, - Beers_0.createdBy, - Beers_0.modifiedAt, - Beers_0.modifiedBy, - Beers_0.name, - Beers_0.abv, - Beers_0.ibu, - Beers_0.brewery_ID -FROM csw_Beers AS Beers_0; - -CREATE VIEW BeershopService_Breweries AS SELECT - Brewery_0.ID, - Brewery_0.createdAt, - Brewery_0.createdBy, - Brewery_0.modifiedAt, - Brewery_0.modifiedBy, - Brewery_0.name -FROM csw_Brewery AS Brewery_0; - -CREATE VIEW BeershopService_TypeChecks AS SELECT - TypeChecks_0.ID, - TypeChecks_0.type_Boolean, - TypeChecks_0.type_Int32, - TypeChecks_0.type_Int64, - TypeChecks_0.type_Decimal, - TypeChecks_0.type_Double, - TypeChecks_0.type_Date, - TypeChecks_0.type_Time, - TypeChecks_0.type_DateTime, - TypeChecks_0.type_Timestamp, - TypeChecks_0.type_String, - TypeChecks_0.type_Binary, - TypeChecks_0.type_LargeBinary, - TypeChecks_0.type_LargeString -FROM csw_TypeChecks AS TypeChecks_0; - -CREATE VIEW BeershopService_TypeChecksWithDraft AS SELECT - TypeChecks_0.ID, - TypeChecks_0.type_Boolean, - TypeChecks_0.type_Int32, - TypeChecks_0.type_Int64, - TypeChecks_0.type_Decimal, - TypeChecks_0.type_Double, - TypeChecks_0.type_Date, - TypeChecks_0.type_Time, - TypeChecks_0.type_DateTime, - TypeChecks_0.type_Timestamp, - TypeChecks_0.type_String, - TypeChecks_0.type_Binary, - TypeChecks_0.type_LargeBinary, - TypeChecks_0.type_LargeString -FROM csw_TypeChecks AS TypeChecks_0; - -CREATE VIEW BeershopService_DraftAdministrativeData AS SELECT - DraftAdministrativeData.DraftUUID, - DraftAdministrativeData.CreationDateTime, - DraftAdministrativeData.CreatedByUser, - DraftAdministrativeData.DraftIsCreatedByMe, - DraftAdministrativeData.LastChangeDateTime, - DraftAdministrativeData.LastChangedByUser, - DraftAdministrativeData.InProcessByUser, - DraftAdministrativeData.DraftIsProcessedByMe -FROM DRAFT_DraftAdministrativeData AS DraftAdministrativeData; -" -`; diff --git a/postgres/test/bookshop.test.js b/postgres/test/bookshop.test.js deleted file mode 100644 index 566318915..000000000 --- a/postgres/test/bookshop.test.js +++ /dev/null @@ -1,3 +0,0 @@ -describe('postgres', () => { - require('../../test/scenarios/bookshop') -}) diff --git a/postgres/test/compliance b/postgres/test/compliance new file mode 120000 index 000000000..9a3b79e79 --- /dev/null +++ b/postgres/test/compliance @@ -0,0 +1 @@ +../../test \ No newline at end of file diff --git a/postgres/test/compliance.test.js b/postgres/test/compliance.test.js deleted file mode 100644 index 8ff384bcf..000000000 --- a/postgres/test/compliance.test.js +++ /dev/null @@ -1,3 +0,0 @@ -describe('postgres', () => { - require('../../test/compliance') -}) diff --git a/postgres/test/connect.test.js b/postgres/test/connect.test.js index dc937283d..0a5e1af30 100644 --- a/postgres/test/connect.test.js +++ b/postgres/test/connect.test.js @@ -1,15 +1,16 @@ const { Client } = require('pg') const PgService = require('../lib/PostgresService') -const { expect } = require('chai') const cds = require('../../test/cds.js') process.env.DEBUG && jest.setTimeout(100000) // fake the manifestation of the db connection -Client.prototype.connect = jest.fn(() => Promise.resolve({})) +Client.prototype.connect = () => { } describe('connect to pg db', () => { + const { expect } = cds.test + test('in docker', async () => { cds.env.requires.db = require('@cap-js/postgres/test/service.json') const pgService = new PgService() diff --git a/postgres/test/csn2postgres.test.js b/postgres/test/csn2postgres.test.js deleted file mode 100644 index 971149d53..000000000 --- a/postgres/test/csn2postgres.test.js +++ /dev/null @@ -1,20 +0,0 @@ -const cds = require('../../test/cds.js') - -describe('CSN to PostgreSQL', () => { - describe('Create Statements', () => { - test('should return PostgreSQL compatible statements for beershop-service', async () => { - const servicePath = `${__dirname}/beershop/srv/beershop-service` - const csn = await cds.load(servicePath) - const sql = cds.compile(csn).to.sql({ dialect: 'postgres', as: 'str' }) - // REVISIT: snapshot contains data types without TIMEZONE - update after cds compiler change - see #152 - expect(sql).toMatchSnapshot() - }) - test('should return PostgreSQL compatible statements for beershop-admin-service', async () => { - const servicePath = `${__dirname}/beershop/srv/beershop-admin-service` - const csn = await cds.load(servicePath) - const sql = cds.compile(csn).to.sql({ dialect: 'postgres', as: 'str' }) - // REVISIT: snapshot contains data types without TIMEZONE - update after cds compiler change - see #152 - expect(sql).toMatchSnapshot() - }) - }) -}) diff --git a/postgres/test/service-types.test.js b/postgres/test/service-types.test.js index 523220710..901d41761 100644 --- a/postgres/test/service-types.test.js +++ b/postgres/test/service-types.test.js @@ -6,11 +6,12 @@ else cds.env.features.lean_draft = true cds.env.features.stream_compat = true const project = resolve(__dirname, 'beershop') -const { GET, POST, expect, data } = cds.test('serve', '--project', project).verbose() process.env.DEBUG && jest.setTimeout(100000) describe('OData to Postgres dialect', () => { + const { GET, POST, expect, data } = cds.test('serve', '--project', project) + data.autoIsolation(true) data.autoReset(true) diff --git a/postgres/test/service.test.js b/postgres/test/service.test.js index 6580912f5..6a2964b3b 100644 --- a/postgres/test/service.test.js +++ b/postgres/test/service.test.js @@ -11,7 +11,7 @@ const guidRegEx = /\b[0-9a-f]{8}\b-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-\b[0-9a-f // run test suite with different sets of data describe('OData to Postgres dialect', () => { - const { GET, POST, PUT, PATCH, DELETE, expect, data } = cds.test('serve', '--project', project).verbose() + const { GET, POST, PUT, PATCH, DELETE, expect, data } = cds.test('serve', '--project', project) data.autoIsolation(true) data.autoReset(true) diff --git a/postgres/test/sflight.test.js b/postgres/test/sflight.test.js deleted file mode 100644 index 017f47720..000000000 --- a/postgres/test/sflight.test.js +++ /dev/null @@ -1,3 +0,0 @@ -describe('postgres', () => { - require('../../test/scenarios/sflight') -}) diff --git a/postgres/test/streaming.compat.test.js b/postgres/test/streaming.compat.test.js index 8f173fbfb..64d5e67a2 100644 --- a/postgres/test/streaming.compat.test.js +++ b/postgres/test/streaming.compat.test.js @@ -1,3 +1,4 @@ +require('../../test/cds.js') describe('postgres', () => { require('../../sqlite/test/general/stream.compat.test.js') }) diff --git a/postgres/test/streaming.test.js b/postgres/test/streaming.test.js index 94dda5565..5d4950637 100644 --- a/postgres/test/streaming.test.js +++ b/postgres/test/streaming.test.js @@ -1,3 +1,4 @@ +require('../../test/cds.js') describe('postgres', () => { require('../../sqlite/test/general/stream.test.js') }) diff --git a/sqlite/lib/SQLiteService.js b/sqlite/lib/SQLiteService.js index 899019e3f..0f6f6ed65 100644 --- a/sqlite/lib/SQLiteService.js +++ b/sqlite/lib/SQLiteService.js @@ -220,7 +220,10 @@ class SQLiteService extends SQLService { Int64: cds.env.features.ieee754compatible ? expr => `CAST(${expr} as TEXT)` : undefined, // REVISIT: always cast to string in next major // Reading decimal as string to not loose precision - Decimal: cds.env.features.ieee754compatible ? expr => `CAST(${expr} as TEXT)` : undefined, + Decimal: cds.env.features.ieee754compatible ? (expr, elem) => elem?.scale + ? `CASE WHEN ${expr} IS NULL THEN NULL ELSE format('%.${elem.scale}f', ${expr}) END` + : `CAST(${expr} as TEXT)` + : undefined, // Binary is not allowed in json objects Binary: expr => `${expr} || ''`, } diff --git a/sqlite/package.json b/sqlite/package.json index 1b0c44461..261a51352 100644 --- a/sqlite/package.json +++ b/sqlite/package.json @@ -23,7 +23,7 @@ "CHANGELOG.md" ], "scripts": { - "test": "jest --silent" + "test": "cds-test $(../test/find)" }, "dependencies": { "@cap-js/db-service": "^1.9.0", diff --git a/sqlite/test/bookshop.test.js b/sqlite/test/bookshop.test.js deleted file mode 100644 index a62f6e549..000000000 --- a/sqlite/test/bookshop.test.js +++ /dev/null @@ -1,3 +0,0 @@ -describe('sqlite', () => { - require('../../test/scenarios/bookshop') -}) diff --git a/sqlite/test/compliance b/sqlite/test/compliance new file mode 120000 index 000000000..9a3b79e79 --- /dev/null +++ b/sqlite/test/compliance @@ -0,0 +1 @@ +../../test \ No newline at end of file diff --git a/sqlite/test/compliance.test.js b/sqlite/test/compliance.test.js deleted file mode 100644 index f71232842..000000000 --- a/sqlite/test/compliance.test.js +++ /dev/null @@ -1,3 +0,0 @@ -describe('sqlite', () => { - require('../../test/compliance') -}) diff --git a/sqlite/test/deep/deepInputProcessing.test.js b/sqlite/test/deep/deepInputProcessing.test.js index 982088008..853b05986 100644 --- a/sqlite/test/deep/deepInputProcessing.test.js +++ b/sqlite/test/deep/deepInputProcessing.test.js @@ -1,8 +1,8 @@ const cds = require('../../../test/cds') -const { POST, PUT, GET } = cds.test(__dirname, 'deep.cds') - describe('UUID Generation', () => { + const { POST, PUT, GET, expect } = cds.test(__dirname, 'deep.cds') + test('generate UUID on insert', async () => { const uuid = cds.utils.uuid() const res = await POST('/bla/RootUUID', { @@ -12,9 +12,9 @@ describe('UUID Generation', () => { toManySubChild: [{ text: 'a' }, { text: 'b' }], }, }) - expect(res.status).toBe(201) + expect(res.status).to.equal(201) - expect(res.data).toMatchObject({ + expect(res.data).to.containSubset({ ID: uuid, name: null, toOneChild: { @@ -24,15 +24,15 @@ describe('UUID Generation', () => { }) // uuid is properly generated - expect(res.data.toOneChild.ID).toBeDefined() + expect(res.data.toOneChild.ID).to.exist // and propagated - expect(res.data.toOneChild.ID).toEqual(res.data.toOneChild_ID) + expect(res.data.toOneChild.ID).to.equal(res.data.toOneChild_ID) // uuid is properly generated - expect(res.data.toOneChild.toManySubChild[0].ID).toBeDefined() - expect(res.data.toOneChild.toManySubChild[1].ID).toBeDefined() + expect(res.data.toOneChild.toManySubChild[0].ID).to.exist + expect(res.data.toOneChild.toManySubChild[1].ID).to.exist // and propagated - expect(res.data.toOneChild.ID).toEqual(res.data.toOneChild.toManySubChild[0].backlink_ID) - expect(res.data.toOneChild.ID).toEqual(res.data.toOneChild.toManySubChild[1].backlink_ID) + expect(res.data.toOneChild.ID).to.equal(res.data.toOneChild.toManySubChild[0].backlink_ID) + expect(res.data.toOneChild.ID).to.equal(res.data.toOneChild.toManySubChild[1].backlink_ID) }) test('generate UUID on update', async () => { const uuid = cds.utils.uuid() @@ -43,7 +43,7 @@ describe('UUID Generation', () => { toManySubChild: [{ text: 'a' }, { text: 'b' }], }, }) - expect(resPost.status).toBe(201) + expect(resPost.status).to.equal(201) // new children are created const resUpdate = await PUT(`/bla/RootUUID(${uuid})`, { @@ -52,14 +52,14 @@ describe('UUID Generation', () => { toManySubChild: [{ text: 'a' }, { text: 'b' }], }, }) - expect(resUpdate.status).toBe(200) + expect(resUpdate.status).to.equal(200) const resRead = await GET(`/bla/RootUUID(${uuid})?$expand=toOneChild($expand=toManySubChild)`) // foreign keys are set correctly (deep) - expect(resRead.data.toOneChild.ID).toEqual(resRead.data.toOneChild_ID) - expect(resRead.data.toOneChild.ID).toEqual(resRead.data.toOneChild.toManySubChild[0].backlink_ID) - expect(resRead.data.toOneChild.ID).toEqual(resRead.data.toOneChild.toManySubChild[1].backlink_ID) + expect(resRead.data.toOneChild.ID).to.equal(resRead.data.toOneChild_ID) + expect(resRead.data.toOneChild.ID).to.equal(resRead.data.toOneChild.toManySubChild[0].backlink_ID) + expect(resRead.data.toOneChild.ID).to.equal(resRead.data.toOneChild.toManySubChild[1].backlink_ID) }) test('generate UUID on update programmatically', async () => { @@ -98,16 +98,16 @@ describe('UUID Generation', () => { // in the query `select … { *, toOneChild { … } }` the expand actually replaces the // the association `toOneChild` from the wildcard, hence `updated.toOneChild_ID === undefined` - // expect(updated.toOneChild.ID).toEqual(updated.toOneChild_ID) + // expect(updated.toOneChild.ID).to.equal(updated.toOneChild_ID) // foreign keys are set correctly (deep) - expect(updated.toOneChild.ID).toEqual(updated.toOneChild.toManySubChild[0].backlink_ID) - expect(updated.toOneChild.ID).toEqual(updated.toOneChild.toManySubChild[1].backlink_ID) + expect(updated.toOneChild.ID).to.equal(updated.toOneChild.toManySubChild[0].backlink_ID) + expect(updated.toOneChild.ID).to.equal(updated.toOneChild.toManySubChild[1].backlink_ID) // new IDs are generated (deep) - expect(inserted.toOneChild.ID).not.toEqual(updated.toOneChild.ID) - expect(inserted.toOneChild.toManySubChild[0].ID).not.toEqual(updated.toOneChild.toManySubChild[0].ID) - expect(inserted.toOneChild.toManySubChild[1].ID).not.toEqual(updated.toOneChild.toManySubChild[1].ID) + expect(inserted.toOneChild.ID).not.to.equal(updated.toOneChild.ID) + expect(inserted.toOneChild.toManySubChild[0].ID).not.to.equal(updated.toOneChild.toManySubChild[0].ID) + expect(inserted.toOneChild.toManySubChild[1].ID).not.to.equal(updated.toOneChild.toManySubChild[1].ID) }) test('update root and delete child', async () => { @@ -118,21 +118,21 @@ describe('UUID Generation', () => { text: 'abc', }, }) - expect(resPost.status).toBe(201) + expect(resPost.status).to.equal(201) // child should be deleted const resUpdate = await PUT(`/bla/RootUUID(${uuid})`, { toOneChild: null, }) - expect(resUpdate.status).toBe(200) - expect(resUpdate.data).toMatchObject({ + expect(resUpdate.status).to.equal(200) + expect(resUpdate.data).to.containSubset({ '@odata.context': '$metadata#RootUUID/$entity', ID: uuid, name: null, }) const resRead = await GET(`/bla/RootUUID(${uuid})?$expand=toOneChild`) - expect(resRead.data).toMatchObject({ + expect(resRead.data).to.containSubset({ ID: uuid, name: null, toOneChild: null, @@ -147,6 +147,6 @@ describe('UUID Generation', () => { rText: 'abc', }, }) - expect(resPost.status).toBe(201) + expect(resPost.status).to.equal(201) }) }) diff --git a/sqlite/test/deep/deepPersistenceSkip.test.js b/sqlite/test/deep/deepPersistenceSkip.test.js index 102bfe357..15cfc1652 100644 --- a/sqlite/test/deep/deepPersistenceSkip.test.js +++ b/sqlite/test/deep/deepPersistenceSkip.test.js @@ -1,7 +1,8 @@ const cds = require('../../../test/cds') -const { POST, PUT, DELETE } = cds.test(__dirname, 'deep.cds') describe('deep operations with @cds.persistence.skip', () => { + const { POST, PUT, DELETE, expect } = cds.test(__dirname, 'deep.cds') + test('skip child to one with @cds.persistence.skip on deep insert', async () => { const uuid = cds.utils.uuid() const res = await POST('/bla/RootUUID', { @@ -10,15 +11,15 @@ describe('deep operations with @cds.persistence.skip', () => { text: 'abc', }, }) - expect(res.status).toBe(201) + expect(res.status).to.equal(201) - expect(res.data).toMatchObject({ + expect(res.data).to.containSubset({ ID: uuid, name: null, toOneChild_ID: null, - toOneSkip_ID: expect.any(String), + // toOneSkip_ID: expect.any(String), }) - expect(res.data.toOneSkip_ID).toBeDefined() + expect(res.data.toOneSkip_ID).to.exist }) test('skip child to many with @cds.persistence.skip on deep insert', async () => { @@ -27,9 +28,9 @@ describe('deep operations with @cds.persistence.skip', () => { ID: uuid, toManySkip: [{ text: 'a' }, { text: 'b' }], }) - expect(res.status).toBe(201) + expect(res.status).to.equal(201) - expect(res.data).toMatchObject({ + expect(res.data).to.containSubset({ ID: uuid, name: null, toOneChild_ID: null, @@ -49,26 +50,26 @@ describe('deep operations with @cds.persistence.skip', () => { ], }, }) - expect(res.status).toBe(201) + expect(res.status).to.equal(201) - expect(res.data).toMatchObject({ + expect(res.data).to.containSubset({ ID: uuid, name: null, toOneChild: { - ID: expect.any(String), + // ID: expect.any(String), text: 'abc', toManySubChild: [ { - ID: expect.any(String), + // ID: expect.any(String), backlink_ID: res.data.toOneChild.ID, text: 'a', - toOneSkipChild_ID: expect.any(String), + // toOneSkipChild_ID: expect.any(String), }, { - ID: expect.any(String), + // ID: expect.any(String), backlink_ID: res.data.toOneChild.ID, text: 'b', - toOneSkipChild_ID: expect.any(String), + // toOneSkipChild_ID: expect.any(String), }, ], }, @@ -85,7 +86,7 @@ describe('deep operations with @cds.persistence.skip', () => { text: 'abc', }, }) - expect(resPost.status).toBe(201) + expect(resPost.status).to.equal(201) const resUpdate = await PUT(`/bla/RootUUID(${uuid})`, { name: 'abc', @@ -93,14 +94,14 @@ describe('deep operations with @cds.persistence.skip', () => { text: 'cd', }, }) - expect(resUpdate.status).toBe(200) + expect(resUpdate.status).to.equal(200) - expect(resUpdate.data).toEqual({ + expect(resUpdate.data).to.containSubset({ '@odata.context': '$metadata#RootUUID/$entity', ID: uuid, name: 'abc', toOneChild_ID: null, - toOneSkip_ID: expect.any(String), + // toOneSkip_ID: expect.any(String), }) }) @@ -112,9 +113,9 @@ describe('deep operations with @cds.persistence.skip', () => { text: 'abc', }, }) - expect(resPost.status).toBe(201) + expect(resPost.status).to.equal(201) const resUpdate = await DELETE(`/bla/RootUUID(${uuid})`) - expect(resUpdate.status).toBe(204) + expect(resUpdate.status).to.equal(204) }) }) diff --git a/sqlite/test/general/delete-rename.test.js b/sqlite/test/general/delete-rename.test.js index 2805809c9..529769265 100644 --- a/sqlite/test/general/delete-rename.test.js +++ b/sqlite/test/general/delete-rename.test.js @@ -1,8 +1,8 @@ const cds = require('../../../test/cds.js') -const { POST, DELETE } = cds.test(__dirname, 'testModel.cds') - describe('delete on rename', () => { + const { POST, DELETE, expect } = cds.test(__dirname, 'testModel.cds') + test('delete on projection with renamed elements', async () => { let res res = await POST('/rename/SProjDeep', { @@ -15,11 +15,11 @@ describe('delete on rename', () => { { IDRename: 2, otherNameRename: 'children name', otherName2Rename: 'children name 2' }, ], }) - expect(res).toMatchObject({ status: 201 }) + expect(res).to.containSubset({ status: 201 }) // make sure the resulting query is resolved all the way to the database table res = await DELETE('/rename/SProjDeep(1)/childrenRename(1)') - expect(res).toMatchObject({ status: 204 }) + expect(res).to.containSubset({ status: 204 }) }) }) diff --git a/sqlite/test/general/insert-as-select.test.js b/sqlite/test/general/insert-as-select.test.js index 0908afb98..257b799bf 100644 --- a/sqlite/test/general/insert-as-select.test.js +++ b/sqlite/test/general/insert-as-select.test.js @@ -1,9 +1,9 @@ const cds = require('../../../test/cds.js') -cds.test(__dirname, 'testModel.cds') - const assert = require('assert') describe('insert as select', () => { + cds.test(__dirname, 'testModel.cds') + test('make sure that the placeholder values of the prepared statement are passed to the database', async () => { // fill other table first await cds.run(INSERT({ ID: 42, name: 'Foo2' }).into('Foo2')) diff --git a/sqlite/test/general/localized.test.js b/sqlite/test/general/localized.test.js index 42e15b150..7d15c75d6 100644 --- a/sqlite/test/general/localized.test.js +++ b/sqlite/test/general/localized.test.js @@ -1,7 +1,8 @@ const cds = require('../../../test/cds.js') -const { GET, POST } = cds.test(__dirname, 'model.cds') describe('localized', () => { + const { GET, POST, expect } = cds.test(__dirname, 'model.cds') + beforeAll(async () => { return await POST('/test/fooLocalized', { ID: 5, @@ -12,9 +13,9 @@ describe('localized', () => { test('generic request without language header falls back to default', async () => { const res = await GET('/test/fooLocalized') - expect(res.status).toBe(200) + expect(res.status).to.equal(200) - expect(res.data).toEqual({ + expect(res.data).to.deep.equal({ '@odata.context': '$metadata#fooLocalized', value: [ { @@ -27,9 +28,9 @@ describe('localized', () => { test('generic request with language header is localized', async () => { const res = await GET('/test/fooLocalized', { headers: { 'Accept-Language': 'de' } }) - expect(res.status).toBe(200) + expect(res.status).to.equal(200) - expect(res.data).toEqual({ + expect(res.data).to.deep.equal({ '@odata.context': '$metadata#fooLocalized', value: [ { @@ -46,10 +47,10 @@ describe('localized', () => { cds.context = { locale: 'de' } return db.tx(async () => { const result = await SELECT.from('test.fooLocalized') - expect(result).toEqual([{ ID: 5, text: 'english' }]) + expect(result).to.deep.equal([{ ID: 5, text: 'english' }]) const resultLocalized = await SELECT.localized('test.fooLocalized') - expect(resultLocalized).toEqual([{ ID: 5, text: 'deutsch' }]) + expect(resultLocalized).to.deep.equal([{ ID: 5, text: 'deutsch' }]) }) }) }) diff --git a/sqlite/test/general/managed.test.js b/sqlite/test/general/managed.test.js index 839f771b0..1cfab31fe 100644 --- a/sqlite/test/general/managed.test.js +++ b/sqlite/test/general/managed.test.js @@ -1,8 +1,8 @@ const cds = require('../../../test/cds.js') -const { POST, PUT, sleep } = cds.test(__dirname, 'model.cds') - describe('Managed thingies', () => { + const { POST, PUT, sleep, expect } = cds.test(__dirname, 'model.cds') + test('INSERT execute on db only', async () => { const db = await cds.connect.to('db') return db.tx(async () => { @@ -10,12 +10,12 @@ describe('Managed thingies', () => { await INSERT.into('test.foo').entries({ ID: 2, modifiedBy: 'samuel' }) const result = await SELECT.from('test.foo').where({ ID: 2 }) - expect(result).toEqual([ + expect(result).to.containSubset([ { ID: 2, - createdAt: expect.any(String), + // createdAt: expect.any(String), createdBy: 'anonymous', - modifiedAt: expect.any(String), + // modifiedAt: expect.any(String), modifiedBy: 'samuel', }, ]) @@ -31,74 +31,74 @@ describe('Managed thingies', () => { await UPSERT.into('test.foo').entries({ ID: 3, modifiedBy: 'samuel' }) const result = await SELECT.from('test.foo').where({ ID: 3 }) - expect(result).toEqual([ + expect(result).to.containSubset([ { ID: 3, createdAt: null, createdBy: null, - modifiedAt: expect.any(String), + // modifiedAt: expect.any(String), modifiedBy: 'samuel', }, ]) const { modifiedAt } = result[0] - expect(modifiedAt).toEqual(cds.context.timestamp.toISOString()) + expect(modifiedAt).to.equal(cds.context.timestamp.toISOString()) await sleep(11) // ensure some ms are passed const modified = new Date(modifiedAt).getTime() const now = Date.now() - expect(now - modified).toBeGreaterThan(0) - expect(now - modified).toBeLessThan(10 * 1000) // 10s + expect(now - modified).to.be.greaterThan(0) + expect(now - modified).to.be.lessThan(10 * 1000) // 10s }) }) test('on insert is filled', async () => { const resPost = await POST('/test/foo', { ID: 4 }) - expect(resPost.status).toBe(201) + expect(resPost.status).to.equal(201) - expect(resPost.data).toEqual({ + expect(resPost.data).to.containSubset({ '@odata.context': '$metadata#foo/$entity', ID: 4, - createdAt: expect.any(String), + // createdAt: expect.any(String), createdBy: 'anonymous', - modifiedAt: expect.any(String), + // modifiedAt: expect.any(String), modifiedBy: 'anonymous', }) const { createdAt, modifiedAt } = resPost.data - expect(createdAt).toEqual(modifiedAt) + expect(createdAt).to.equal(modifiedAt) await sleep(11) // ensure some ms are passed const now = Date.now() const created = new Date(createdAt).getTime() - expect(now - created).toBeGreaterThan(0) - expect(now - created).toBeLessThan(10 * 1000) // 10s + expect(now - created).to.be.greaterThan(0) + expect(now - created).to.be.lessThan(10 * 1000) // 10s }) test('on update is filled', async () => { const resPost = await POST('/test/foo', { ID: 5 }) const resUpdate = await PUT('/test/foo(5)', {}) - expect(resUpdate.status).toBe(200) + expect(resUpdate.status).to.equal(200) - expect(resUpdate.data).toEqual({ + expect(resUpdate.data).to.containSubset({ '@odata.context': '$metadata#foo/$entity', ID: 5, createdAt: resPost.data.createdAt, createdBy: resPost.data.createdBy, - modifiedAt: expect.any(String), + // modifiedAt: expect.any(String), modifiedBy: 'anonymous', }) const { createdAt, modifiedAt } = resUpdate.data - expect(createdAt).not.toEqual(modifiedAt) + expect(createdAt).not.to.equal(modifiedAt) const insertTime = new Date(createdAt).getTime() const updateTime = new Date(modifiedAt).getTime() - expect(updateTime).toBeGreaterThan(insertTime) + expect(updateTime).to.be.greaterThan(insertTime) }) test('managed attributes are shared within a transaction', async () => { @@ -112,10 +112,10 @@ describe('Managed thingies', () => { result = await tx.run(SELECT.from('test.foo').where('ID in', [4711, 4712])) } finally { await tx.rollback() - expect(result[0].createdAt).toEqual(tx.context.timestamp.toISOString()) - expect(result[0].createdAt).toEqual(result[1].createdAt) - expect(result[0].createdBy).toEqual('tom') - expect(result[0].createdBy).toEqual(result[1].createdBy) + expect(result[0].createdAt).to.equal(tx.context.timestamp.toISOString()) + expect(result[0].createdAt).to.equal(result[1].createdAt) + expect(result[0].createdBy).to.equal('tom') + expect(result[0].createdBy).to.equal(result[1].createdBy) } }) }) diff --git a/sqlite/test/general/stream.compat.test.js b/sqlite/test/general/stream.compat.test.js index 8e2b0e2f4..c179da19d 100644 --- a/sqlite/test/general/stream.compat.test.js +++ b/sqlite/test/general/stream.compat.test.js @@ -1,19 +1,18 @@ -const cds = require('../../../test/cds.js') process.env.CDS_CONFIG = JSON.stringify({ features : { stream_compat: true } }) - -const { fs, path } = cds.utils +const cds = require('../../../test/cds.js') const { Readable } = require('stream') -const checkSize = async stream => { - let size = 0 - for await (const chunk of stream) { - size += chunk.length - } - expect(size).toEqual(7891) -} - describe('streaming', () => { - cds.test(__dirname, 'model.cds') + const { expect } = cds.test(__dirname, 'model.cds') + const { fs, path } = cds.utils + + const checkSize = async stream => { + let size = 0 + for await (const chunk of stream) { + size += chunk.length + } + expect(size).to.equal(7891) + } describe('Streaming API', () => { beforeAll(async () => { @@ -44,7 +43,7 @@ describe('streaming', () => { const { Images } = cds.entities('test') const { data: str } = await SELECT.one.from(Images).columns('data').where({ ID: 1 }) const buffer = Buffer.from(str, 'base64') - expect(buffer.length).toBe(7891) + expect(buffer.length).to.equal(7891) }) test('READ multiple stream properties with _streaming = true', async () => { @@ -60,11 +59,11 @@ describe('streaming', () => { const [{ data: str1, ID, data2: str2 }] = await SELECT.from(Images) .columns(['data', 'ID', 'data2']) .where({ ID: 1 }) - expect(ID).toBe(1) + expect(ID).to.equal(1) const buffer1 = Buffer.from(str1, 'base64') - expect(buffer1.length).toBe(7891) + expect(buffer1.length).to.equal(7891) const buffer2 = Buffer.from(str2, 'base64') - expect(buffer2.length).toBe(7891) + expect(buffer2.length).to.equal(7891) }) test('READ null stream property', async () => { @@ -72,13 +71,13 @@ describe('streaming', () => { const cqn = SELECT.from(Images).columns('data').where({ ID: 2 }) cqn._streaming = true const { value: stream } = await cqn - expect(stream).toBeNull() + expect(stream).to.be.null }) test('READ null stream property', async () => { const { Images } = cds.entities('test') const [{ data: stream }] = await SELECT.from(Images).columns('data').where({ ID: 2 }) - expect(stream).toBeNull() + expect(stream).to.be.null }) }) @@ -94,9 +93,9 @@ describe('streaming', () => { }) try { await UPDATE(Images).with({ data: stream }).where({ ID: 1 }) - expect(1).toBe(2) + expect(1).to.equal(2) } catch (err) { - expect(err.code).toEqual('ERR_INVALID_ARG_TYPE') + expect(err.code).to.equal('ERR_INVALID_ARG_TYPE') } }) @@ -105,7 +104,7 @@ describe('streaming', () => { const stream = fs.createReadStream(path.join(__dirname, 'samples/test.jpg')) const changes = await UPDATE(Images).with({ data2: stream }).where({ ID: 3 }) - expect(changes).toEqual(1) + expect(changes).to.equal(1) const cqn = SELECT.from(Images).columns('data2').where({ ID: 3 }) cqn._streaming = true @@ -119,14 +118,14 @@ describe('streaming', () => { const stream2 = fs.createReadStream(path.join(__dirname, 'samples/test.jpg')) const changes = await UPDATE(Images).with({ data: stream1, data2: stream2 }).where({ ID: 4 }) - expect(changes).toEqual(1) + expect(changes).to.equal(1) const cqn = SELECT.from(Images).columns(['data', 'data2']).where({ ID: 4 }) const [{ data: str1, data2: str2 }] = await cqn const buffer1 = Buffer.from(str1, 'base64') - expect(buffer1.length).toBe(7891) + expect(buffer1.length).to.equal(7891) const buffer2 = Buffer.from(str2, 'base64') - expect(buffer2.length).toBe(7891) + expect(buffer2.length).to.equal(7891) }) test('WRITE multiple blob properties', async () => { @@ -135,14 +134,14 @@ describe('streaming', () => { const blob2 = fs.readFileSync(path.join(__dirname, 'samples/test.jpg')) const changes = await UPDATE(Images).with({ data: blob1, data2: blob2 }).where({ ID: 4 }) - expect(changes).toEqual(1) + expect(changes).to.equal(1) const cqn = SELECT.from(Images).columns(['data', 'data2']).where({ ID: 4 }) const [{ data: str1, data2: str2 }] = await cqn const buffer1 = Buffer.from(str1, 'base64') - expect(buffer1.length).toBe(7891) + expect(buffer1.length).to.equal(7891) const buffer2 = Buffer.from(str2, 'base64') - expect(buffer2.length).toBe(7891) + expect(buffer2.length).to.equal(7891) }) test('WRITE stream property on view', async () => { @@ -150,7 +149,7 @@ describe('streaming', () => { const stream = fs.createReadStream(path.join(__dirname, 'samples/test.jpg')) const changes = await UPDATE(ImagesView).with({ renamedData: stream }).where({ ID: 1 }) - expect(changes).toEqual(1) + expect(changes).to.equal(1) const cqn = SELECT.from(ImagesView).columns('renamedData').where({ ID: 1 }) cqn._streaming = true @@ -197,7 +196,7 @@ describe('streaming', () => { }) // TODO: breaks on Postgres, because INSERT tries to decode it as base64 string (InputConverters) - xtest('WRITE dataset from json generator stream', async () => { + test.skip('WRITE dataset from json generator stream', async () => { const { Images } = cds.entities('test') const start = 2000 diff --git a/sqlite/test/general/stream.test.js b/sqlite/test/general/stream.test.js index 6fc6a7680..f7c002dbc 100644 --- a/sqlite/test/general/stream.test.js +++ b/sqlite/test/general/stream.test.js @@ -1,17 +1,17 @@ const cds = require('../../../test/cds.js') -const { fs, path } = cds.utils const { Readable } = require('stream') -const checkSize = async stream => { - let size = 0 - for await (const chunk of stream) { - size += chunk.length - } - expect(size).toEqual(7891) -} - describe('streaming', () => { - cds.test(__dirname, 'model.cds') + const { expect } = cds.test(__dirname, 'model.cds') + const { fs, path } = cds.utils + + const checkSize = async stream => { + let size = 0 + for await (const chunk of stream) { + size += chunk.length + } + expect(size).to.equal(7891) + } describe('Streaming API', () => { beforeAll(async () => { @@ -50,13 +50,13 @@ describe('streaming', () => { .columns('data', { val: 'image/jpeg', as: '$mediaContentType' }) .where({ ID: 1 }) await checkSize(stream) - expect(val).toEqual('image/jpeg') + expect(val).to.equal('image/jpeg') })) test('READ null stream property with .from, .column and .where', async () => cds.tx(async () => { const { Images } = cds.entities('test') const [{ data: stream }] = await SELECT.from(Images).columns('data').where({ ID: 2 }) - expect(stream).toBeNull() + expect(stream).to.be.null })) // re-enable after /cap/cdsnode/issues/2130 has been fixed @@ -64,7 +64,7 @@ describe('streaming', () => { const { Images } = cds.entities('test') const [{ ID, data: stream }] = await SELECT.from(Images).columns(['ID', 'data']).where({ ID: 1 }) await checkSize(stream) - expect(ID).toEqual(1) + expect(ID).to.equal(1) })) test('READ multiple stream properties with .from, .column and .where', async () => cds.tx(async () => { @@ -76,7 +76,7 @@ describe('streaming', () => { .where({ ID: 1 }) await checkSize(stream1) await checkSize(stream2) - expect(ID).toEqual(1) + expect(ID).to.equal(1) })) test('READ all entries with stream property with .from, .column ', async () => cds.tx(async () => { @@ -88,52 +88,52 @@ describe('streaming', () => { ] = await SELECT.from(Images).columns(['ID', 'data', 'data2']) await checkSize(stream1) await checkSize(stream2) - expect(stream3).toBeNull() + expect(stream3).to.be.null await checkSize(stream4) await checkSize(stream5) - expect(stream6).toBeNull() - expect(ID1).toEqual(1) - expect(ID2).toEqual(2) - expect(ID3).toEqual(3) + expect(stream6).to.be.null + expect(ID1).to.equal(1) + expect(ID2).to.equal(2) + expect(ID3).to.equal(3) })) test('READ one ignore stream properties if columns = all', async () => cds.tx(async () => { const { Images } = cds.entities('test') const result = await SELECT.from(Images).where({ ID: 1 }) - expect(result[0].ID).toBe(1) - expect(result[0].data).toBeUndefined() - expect(result[0].data2).toBeUndefined() + expect(result[0].ID).equals(1) + expect(result[0].data).to.be.undefined + expect(result[0].data2).to.be.undefined })) test('READ multiple entries ignore stream properties if columns = all', async () => cds.tx(async () => { const { Images } = cds.entities('test') const result = await SELECT.from(Images) - expect(result[0].ID).toBe(1) - expect(result[0].data).toBeUndefined() - expect(result[0].data2).toBeUndefined() - expect(result[1].ID).toBe(2) - expect(result[1].data).toBeUndefined() - expect(result[1].data2).toBeUndefined() + expect(result[0].ID).equals(1) + expect(result[0].data).to.be.undefined + expect(result[0].data2).to.be.undefined + expect(result[1].ID).equals(2) + expect(result[1].data).to.be.undefined + expect(result[1].data2).to.be.undefined })) test('READ ignore stream properties if columns = *', async () => cds.tx(async () => { const { Images } = cds.entities('test') const result = await SELECT.from(Images).columns('*').where({ ID: 1 }) - expect(result[0].ID).toBe(1) - expect(result[0].data).toBeUndefined() - expect(result[0].data2).toBeUndefined() + expect(result[0].ID).equals(1) + expect(result[0].data).to.be.undefined + expect(result[0].data2).to.be.undefined })) test('READ all properties from not existing entry', async () => cds.tx(async () => { const { Images } = cds.entities('test') const res = await SELECT.from(Images).columns('*').where({ ID: 15 }) - expect(res.length).toBe(0) + expect(res.length).equals(0) })) test('READ stream property from not existing entry', async () => cds.tx(async () => { const { Images } = cds.entities('test') const res = await SELECT.from(Images).columns('data').where({ ID: 15 }) - expect(res.length).toBe(0) + expect(res.length).equals(0) })) }) @@ -149,9 +149,9 @@ describe('streaming', () => { }) try { await UPDATE(Images).with({ data: stream }).where({ ID: 1 }) - expect(1).toBe(2) + expect(1).equals(2) } catch (err) { - expect(err.code).toEqual('ERR_INVALID_ARG_TYPE') + expect(err.code).to.equal('ERR_INVALID_ARG_TYPE') } })) @@ -160,7 +160,7 @@ describe('streaming', () => { const stream = fs.createReadStream(path.join(__dirname, 'samples/test.jpg')) const changes = await UPDATE(Images).with({ data2: stream }).where({ ID: 3 }) - expect(changes).toEqual(1) + expect(changes).to.equal(1) const [{ data2: stream_ }] = await SELECT.from(Images).columns('data2').where({ ID: 3 }) await checkSize(stream_) @@ -172,7 +172,7 @@ describe('streaming', () => { const stream2 = fs.createReadStream(path.join(__dirname, 'samples/test.jpg')) const changes = await UPDATE(Images).with({ data: stream1, data2: stream2 }).where({ ID: 4 }) - expect(changes).toEqual(1) + expect(changes).to.equal(1) const [{ data: stream1_, data2: stream2_ @@ -188,7 +188,7 @@ describe('streaming', () => { const { data: stream } = await SELECT.one.from(Images).columns('data').where({ ID: 1 }) const changes = await UPDATE(Images).with({ data2: stream }).where({ ID: 3 }) - expect(changes).toEqual(1) + expect(changes).to.equal(1) const [{ data2: stream_ }] = await SELECT.from(Images).columns('data2').where({ ID: 3 }) await checkSize(stream_) @@ -200,7 +200,7 @@ describe('streaming', () => { const blob2 = fs.readFileSync(path.join(__dirname, 'samples/test.jpg')) const changes = await UPDATE(Images).with({ data: blob1, data2: blob2 }).where({ ID: 4 }) - expect(changes).toEqual(1) + expect(changes).to.equal(1) const [{ data: stream1_, @@ -217,7 +217,7 @@ describe('streaming', () => { const stream = fs.createReadStream(path.join(__dirname, 'samples/test.jpg')) const changes = await UPDATE(ImagesView).with({ renamedData: stream }).where({ ID: 1 }) - expect(changes).toEqual(1) + expect(changes).to.equal(1) const [{ renamedData: stream_ }] = await SELECT.from(ImagesView).columns('renamedData').where({ ID: 1 }) await checkSize(stream_) @@ -257,7 +257,7 @@ describe('streaming', () => { await Promise.all([wrap(out1000), wrap(out1001)]) })) - xtest('WRITE dataset from json generator stream', async () => cds.tx(async () => { + test.skip('WRITE dataset from json generator stream', async () => cds.tx(async () => { const { Images } = cds.entities('test') const start = 2000 diff --git a/sqlite/test/general/temporal.test.js b/sqlite/test/general/temporal.test.js index 9892badc5..3ee8ea2f2 100644 --- a/sqlite/test/general/temporal.test.js +++ b/sqlite/test/general/temporal.test.js @@ -1,7 +1,7 @@ const cds = require('../../../test/cds.js') describe('temporal', () => { - const { GET, POST } = cds.test(__dirname, 'model.cds') + const { GET, POST, expect } = cds.test(__dirname, 'model.cds') beforeAll(async () => { const db = await cds.connect.to('db') @@ -17,19 +17,19 @@ describe('temporal', () => { validAt = '1970-01-01T00:00:00.000Z' res = await GET(`/test/fooTemporal?sap-valid-at=${validAt}`) - expect(res.data.value.length).toBe(0) + expect(res.data.value.length).equals(0) validAt = '1995-01-01T00:00:00.000Z' res = await GET(`/test/fooTemporal?sap-valid-at=${validAt}`) - expect(res.data.value.length).toBe(1) + expect(res.data.value.length).equals(1) const it = res.data.value[0] - expect(it).toMatchObject({ ID: 1 }) + expect(it).to.containSubset({ ID: 1 }) // managed and temporal shall not clash - expect(it.createdAt).not.toEqual(it.validFrom) + expect(it.createdAt).not.equals(it.validFrom) validAt = '2010-01-01T00:00:00.000Z' res = await GET(`/test/fooTemporal?sap-valid-at=${validAt}`) - expect(res.data.value.length).toBe(2) + expect(res.data.value.length).equals(2) }) test('UPSERT', async () => { @@ -37,6 +37,6 @@ describe('temporal', () => { const url = `/test/fooTemporal?sap-valid-from=${validFrom}` const data = { ID: 42, validFrom } const res = await POST(url, data) - expect(res.data).toMatchObject({ validFrom }) + expect(res.data).to.containSubset({ validFrom }) }) }) diff --git a/sqlite/test/general/uuid.test.js b/sqlite/test/general/uuid.test.js index 2c449cbba..9c81e7fef 100644 --- a/sqlite/test/general/uuid.test.js +++ b/sqlite/test/general/uuid.test.js @@ -1,14 +1,15 @@ const cds = require('../../../test/cds.js') -cds.test(__dirname, 'model.cds') describe('UUID Generation', () => { + const {expect} = cds.test(__dirname, 'model.cds') + test('INSERT with one entry', async () => { const db = await cds.connect.to('db') return db.tx(async () => { await INSERT.into('test.bar').entries({}) const result = await SELECT.from('test.bar') - expect(result).toEqual([{ ID: expect.any(String) }]) + expect(result).to.have.nested.property('0.ID').to.be.a('string') await DELETE('test.bar') }) @@ -19,17 +20,18 @@ describe('UUID Generation', () => { await INSERT.into('test.bar').entries([{}, {}]) const result = await SELECT.from('test.bar') - expect(result).toEqual([{ ID: expect.any(String) }, { ID: expect.any(String) }]) - expect(result[0].ID).not.toEqual(result[1].ID) + expect(result).to.have.length(2) + expect(result).to.have.nested.property('0.ID').to.be.a('string') + expect(result).to.have.nested.property('1.ID').to.be.a('string') + expect(result[0].ID).not.to.equal(result[1].ID) await DELETE('test.bar') }) }) test('INSERT entity with missing key as association throws error', async () => { - expect.assertions(1) - return expect( - cds.run(INSERT.into('test.BooksWithAssocAsKey').entries([{}])) - ).rejects.toMatchObject({ code: 'SQLITE_CONSTRAINT_NOTNULL' }) + await expect( + INSERT.into('test.BooksWithAssocAsKey').entries([{}]) + ).rejectedWith({code:'SQLITE_CONSTRAINT_NOTNULL'}) }) }) diff --git a/sqlite/test/sflight.test.js b/sqlite/test/sflight.test.js deleted file mode 100644 index 8a3d534ee..000000000 --- a/sqlite/test/sflight.test.js +++ /dev/null @@ -1,3 +0,0 @@ -describe('sqlite', () => { - require('../../test/scenarios/sflight') -}) diff --git a/test/cds.js b/test/cds.js index a95af55d0..f003d482c 100644 --- a/test/cds.js +++ b/test/cds.js @@ -36,10 +36,12 @@ cds.test = Object.setPrototypeOf(function () { global.beforeAll(() => { try { - const testSource = /(.*[\\/])test[\\/]/.exec(require.main.filename)?.[1] - const serviceDefinitionPath = testSource + 'test/service' + const path = cds.utils.path + const sep = path.sep + const testSource = process.argv[1].split(`${sep}test${sep}`)[0] + const serviceDefinitionPath = `${testSource}/test/service` cds.env.requires.db = require(serviceDefinitionPath) - require(testSource + 'cds-plugin') + require(testSource + '/cds-plugin') } catch { // Default to sqlite for packages without their own service cds.env.requires.db = require('@cap-js/sqlite/test/service') @@ -116,9 +118,19 @@ cds.test = Object.setPrototypeOf(function () { global.cds.resolve.cache = {} }) + ret.expect = cdsTest.expect return ret }, cdsTest.constructor.prototype) +cds.test.expect = cdsTest.expect + +// REVISIT: remove once sflight or cds-test is adjusted to the correct behavior +const expect = cdsTest.expect().__proto__.constructor.prototype +const _includes = expect.includes +expect.includes = function (x) { + return typeof x === 'object' ? this.subset(...arguments) : _includes.apply(this, arguments) +} + // Release cds._context for garbage collection global.afterEach(() => { cds._context.disable() diff --git a/test/compliance/CREATE.test.js b/test/compliance/CREATE.test.js index e76952a6f..d3f824983 100644 --- a/test/compliance/CREATE.test.js +++ b/test/compliance/CREATE.test.js @@ -27,11 +27,63 @@ describe('CREATE', () => { const literals = Object.keys(model.definitions).filter(n => model.definitions[n].kind === 'entity') + describe('custom entites', () => { + const entityName = 'custom.entity' + + afterEach(async () => { + const db = await cds.connect() + + const { globals } = cds.entities('basic.literals') + + await db.run({ DROP: { entity: globals } }).catch(() => { }) + await db.run({ DROP: { entity: entityName } }).catch(() => { }) + await db.run({ DROP: { table: { ref: [entityName] } } }).catch(() => { }) + await db.run({ DROP: { view: { ref: [entityName] } } }).catch(() => { }) + }) + + test('definiton provided', async () => { + const db = await cds.connect() + + const { globals } = cds.entities('basic.literals') + + const entity = new cds.entity({ + kind: 'entity', + name: entityName, + elements: globals.elements + }) + await db.run({ CREATE: { entity } }) + // REVISIT: reading from entities not in the model requires additional hanlding in infer + // await SELECT.from(entity) + }) + + test('definiton provided', async () => { + const db = await cds.connect() + + const { globals } = cds.entities('basic.literals') + + const query = SELECT.from(globals) + // REVISIT: reading from entities not in the model requires additional hanlding in infer + /* + const entity = new cds.entity({ + kind: 'entity', + name: entityName, + query, + elements: query.elements + }) + */ + + await db.run({ CREATE: { entity: globals } }) + await db.run({ CREATE: { entity: entityName, as: query } }) + // await SELECT.from(entity) + }) + }) + literals.forEach(table => { const path = table.split('.') const type = path[path.length - 1] const entity = model.definitions[table] const desc = !only.length || only.includes(type) ? describe : describe.skip + if (entity.query) return // Skip complex view as cqn4sql does not allow union views desc(`${entity.projection ? 'View' : 'Type'}: ${type}`, () => { let db @@ -134,7 +186,7 @@ describe('CREATE', () => { if (typeof b === 'function') return `${b}` return b }, - Object.keys(obj).length === 1 ? undefined : '\t ', + Object.keys(obj).length === 1 ? undefined : '\t ', // TODO: adjust for new reporter rendering ) // Super hacky way to make the jest report look nice .replace(/\n}/g, '\n\t }'), diff --git a/test/compliance/DELETE.test.js b/test/compliance/DELETE.test.js index 8c659a6f5..ce23f6424 100644 --- a/test/compliance/DELETE.test.js +++ b/test/compliance/DELETE.test.js @@ -1,7 +1,15 @@ +const cds = require('../cds.js') + describe('DELETE', () => { - describe('from', () => { - test.skip('missing', () => { - throw new Error('not supported') + const { data, expect } = cds.test(__dirname + '/resources') + data.autoIsolation(true) + data.autoReset() + + describe.skip('from', () => { + test('ref', async () => { + const { globals } = cds.entities('basic.projection') + const changes = await cds.run(CQL`DELETE FROM ${globals}`) + expect(changes | 0).to.eq(3, 'Ensure that all rows are affected') }) }) diff --git a/test/compliance/DROP.test.js b/test/compliance/DROP.test.js index 2f21ca528..9f9b7e1ae 100644 --- a/test/compliance/DROP.test.js +++ b/test/compliance/DROP.test.js @@ -1,3 +1,4 @@ +require('../cds') describe('DROP', () => { // TODO: reference to ./definitions.test.js describe('entity', () => { diff --git a/test/compliance/INSERT.test.js b/test/compliance/INSERT.test.js index 1e66482e7..5895bc524 100644 --- a/test/compliance/INSERT.test.js +++ b/test/compliance/INSERT.test.js @@ -1,3 +1,5 @@ +require('../cds') + describe('INSERT', () => { describe('into', () => { test.skip('missing', () => { diff --git a/test/compliance/SELECT.test.js b/test/compliance/SELECT.test.js index 0c450c840..e9279ceae 100644 --- a/test/compliance/SELECT.test.js +++ b/test/compliance/SELECT.test.js @@ -1,34 +1,35 @@ const assert = require('assert') +const os = require('os') const cds = require('../cds.js') -// Set cds.root before requiring cds.Service as it resolves and caches package.json -// Call default cds.test API - describe('SELECT', () => { - // chai.use(chaiAsPromised) const { data, expect } = cds.test(__dirname + '/resources') data.autoIsolation(true) describe('from', () => { test('table', async () => { - const res = await cds.run(CQL`SELECT bool FROM basic.literals.globals`) + const { globals } = cds.entities('basic.projection') + const res = await cds.run(CQL`SELECT bool FROM ${globals}`) assert.strictEqual(res.length, 3, 'Ensure that all rows are coming back') }) test('table *', async () => { - const res = await cds.run(CQL`SELECT * FROM basic.literals.globals`) + const { globals } = cds.entities('basic.projection') + const res = await cds.run(CQL`SELECT * FROM ${globals}`) assert.strictEqual(res.length, 3, 'Ensure that all rows are coming back') }) test('projection', async () => { - const res = await cds.run(CQL`SELECT bool FROM basic.projection.globals`) + const { globals } = cds.entities('basic.projection') + const res = await cds.run(CQL`SELECT bool FROM ${globals}`) assert.strictEqual(res.length, 3, 'Ensure that all rows are coming back') }) test('join', async () => { + const { globals } = cds.entities('basic.projection') const res = await cds.run(CQL` SELECT A.bool - FROM basic.projection.globals as A + FROM ${globals} as A LEFT JOIN basic.projection.globals AS B ON A.bool=B.bool`) assert.strictEqual(res.length, 3, 'Ensure that all rows are coming back') @@ -42,14 +43,59 @@ describe('SELECT', () => { } }) + test('from select', async () => { + const { globals } = cds.entities('basic.projection') + const res = await cds.run(CQL`SELECT bool FROM (SELECT bool FROM ${globals}) AS nested`) + assert.strictEqual(res.length, 3, 'Ensure that all rows are coming back') + }) + + test('from ref', async () => { + const { string } = cds.entities('basic.literals') + const cqn = CQL`SELECT * FROM ${string}[string = ${'yes'}]` + const res = await cds.run(cqn) + assert.strictEqual(res.length, 1, `Ensure that only 'yes' matches`) + }) + + test('from non existant entity', async () => { + const cqn = CQL`SELECT * FROM ![¿HoWdIdYoUmAnAgeToCaLaNeNtItyThIsNaMe?]` + await expect(cds.run(cqn)).rejected + }) + }) + + describe('columns', () => { + test('missing', async () => { + const { globals } = cds.entities('basic.literals') + const cqn = CQL`SELECT FROM ${globals}` + const res = await cds.run(cqn) + assert.strictEqual(res.length, 3, 'Ensure that all rows are coming back') + assert.strictEqual('bool' in res[0], true, 'Ensure that all columns are coming back') + }) + + test('star', async () => { + const { globals } = cds.entities('basic.literals') + const cqn = CQL`SELECT * FROM ${globals}` + const res = await cds.run(cqn) + assert.strictEqual(res.length, 3, 'Ensure that all rows are coming back') + assert.strictEqual('bool' in res[0], true, 'Ensure that all columns are coming back') + }) + + test('specific', async () => { + const { globals } = cds.entities('basic.literals') + const cqn = CQL`SELECT bool FROM ${globals}` + const res = await cds.run(cqn) + assert.strictEqual(res.length, 3, 'Ensure that all rows are coming back') + assert.strictEqual('bool' in res[0], true, 'Ensure that all columns are coming back') + }) + test('statics', async () => { + const { globals } = cds.entities('basic.projection') const res = await cds.run(CQL` SELECT null as ![nullt] : String, 'String' as ![string], 0 as ![integer], 0.1 as ![decimal] - FROM basic.projection.globals + FROM ${globals} `) // Should return a row for each row inside the target table @@ -64,80 +110,72 @@ describe('SELECT', () => { }) }) - test('like wildcard', async () => { - const res = await cds.run(CQL` - SELECT string FROM basic.projection.string WHERE string LIKE 'ye_' - `) - - assert.strictEqual(res.length, 1, `Ensure that only 'true' matches`) + test('select func', async () => { + const { string } = cds.entities('basic.projection') + const cqn = CQL`SELECT count() FROM ${string}` + const res = await cds.run(cqn) + assert.strictEqual(res.length, 1, 'Ensure that all rows are coming back') + assert.strictEqual(res[0].count, 3, 'Ensure that the function is applied') }) - test('like regex uses native regex support', async () => { - let ret = await SELECT.from('basic.projection.string').where('string like', /ye./) - expect(ret.length).to.eq(1) + test('select funcs', async () => { + const { string } = cds.entities('basic.projection') + const cqn = CQL`SELECT min(string),max(string),count() FROM ${string}` + const res = await cds.run(cqn) + assert.strictEqual(res.length, 1, 'Ensure that all rows are coming back') + assert.strictEqual(res[0].min, 'no', 'Ensure that the function is applied') + assert.strictEqual(res[0].max, 'yes', 'Ensure that the function is applied') + assert.strictEqual(res[0].count, 3, 'Ensure that the function is applied') }) - test('= regex behaves like string', async () => { - expect(await SELECT.from('basic.projection.string').where('string =', /ye./)).to.have.property('length', 0) - expect(await SELECT.from('basic.projection.string').where('string =', /yes/)).to.have.property('length', 1) + test('select funcs (duplicates)', async () => { + const { string } = cds.entities('basic.projection') + const cqn = CQL`SELECT count(*),count(1),count(string),count(char) FROM ${string}` + await expect(cds.run(cqn)).rejected }) - test('from select', async () => { - const res = await cds.run(CQL`SELECT bool FROM (SELECT bool FROM basic.projection.globals) AS nested`) - assert.strictEqual(res.length, 3, 'Ensure that all rows are coming back') + test('select func alias', async () => { + const { string } = cds.entities('basic.projection') + const cqn = CQL`SELECT count() as count_renamed FROM ${string}` + const res = await cds.run(cqn) + assert.strictEqual(res.length, 1, 'Ensure that all rows are coming back') + assert.strictEqual(res[0].count_renamed, 3, 'Ensure that the function is applied and aliased') }) - test('from ref', async () => { - const cqn = { - SELECT: { - from: { - ref: [ - { - id: 'basic.projection.string', - where: [ - { - ref: ['string'], - }, - '=', - { - val: 'yes', - }, - ], - }, - ], - }, - }, - } - + test('select funcs alias', async () => { + const { string } = cds.entities('basic.projection') + const cqn = CQL` + SELECT + count(*) as count_star, + count(1) as count_one, + count(string) as count_string, + count(char) as count_char + FROM ${string}` const res = await cds.run(cqn) - assert.strictEqual(res.length, 1, `Ensure that only 'yes' matches`) + assert.strictEqual(res.length, 1, 'Ensure that all rows are coming back') + assert.strictEqual(res[0].count_star, 3, 'Ensure that the function is applied and aliased') + assert.strictEqual(res[0].count_one, 3, 'Ensure that the function is applied and aliased') + assert.strictEqual(res[0].count_string, 3, 'Ensure that the function is applied and aliased') + assert.strictEqual(res[0].count_char, 0, 'Ensure that the function is applied and aliased') + }) + + test('select funcs alias (duplicates)', async () => { + const { string } = cds.entities('basic.projection') + const cqn = CQL`SELECT min(string) as count,max(string) as count,count() FROM ${string}` + await expect(cds.run(cqn)).rejected }) test('select function (wrong)', async () => { - const cqn = CQL` - SELECT - 'func' as function : cds.String - FROM basic.projection.globals - ` + const { globals } = cds.entities('basic.projection') + const cqn = CQL`SELECT 'func' as function : cds.String FROM ${globals}` cqn.SELECT.columns[0].val = function () { } - await expect(cds.run(cqn)).rejected }) test.skip('select xpr', async () => { - const cqn = { - SELECT: { - from: { ref: ['basic.projection.string'] }, - columns: [ - { - xpr: [{ val: 'yes' }, '=', { ref: ['string'] }], - as: 'xpr', - cast: { type: 'cds.Boolean' }, - }, - ], - }, - } - + // REVISIT: Make HANAService ANSI SQL compliant by wrapping compare expressions into case statements for columns + const { string } = cds.entities('basic.projection') + const cqn = CQL`SELECT (${'yes'} = string) as xpr : cds.Boolean FROM ${string}` const res = await cds.run(cqn) assert.strictEqual(res.length, 3, 'Ensure that all rows are coming back') assert.equal(res[0].xpr, true) @@ -145,27 +183,32 @@ describe('SELECT', () => { assert.equal(res[2].xpr, false) }) - test('select 200 columns', async () => { - const cqn = { - SELECT: { - from: { ref: ['basic.projection.string'] }, - columns: new Array(200).fill().map((_, i) => ({ as: `${i}`, val: i })), - }, - } + test('select calculation', async () => { + const { string } = cds.entities('basic.projection') + const cqn = CQL`SELECT (string || string) as string FROM ${string}` + const res = await cds.run(cqn) + assert.strictEqual(res.length, 3, 'Ensure that all rows are coming back') + }) + + test('select sub select', async () => { + const { string } = cds.entities('basic.projection') + const cqn = CQL`SELECT (SELECT string FROM ${string} as sub WHERE sub.string = root.string) as string FROM ${string} as root` + const res = await cds.run(cqn) + assert.strictEqual(res.length, 3, 'Ensure that all rows are coming back') + }) + test('select 200 columns', async () => { + const { string } = cds.entities('basic.projection') + const cqn = SELECT(new Array(200).fill().map((_, i) => ({ as: `${i}`, val: i }))).from(string) const res = await cds.run(cqn) assert.strictEqual(res.length, 3, 'Ensure that all rows are coming back') assert.equal(Object.keys(res[0]).length, cqn.SELECT.columns.length) }) + const nulls = length => new Array(length).fill().map((_, i) => ({ as: `null${i}`, val: null })) test('select 200 null columns', async () => { - const cqn = { - SELECT: { - from: { ref: ['basic.projection.string'] }, - columns: new Array(200).fill().map((_, i) => ({ as: `null${i}`, val: null })), - }, - } - + const { string } = cds.entities('basic.projection') + const cqn = SELECT(nulls(200)).from(string) const res = await cds.run(cqn) assert.strictEqual(res.length, 3, 'Ensure that all rows are coming back') // ensure that all null values are returned @@ -177,82 +220,83 @@ describe('SELECT', () => { }) test('expand to many with 200 columns', async () => { - const nulls = length => new Array(length).fill().map((_, i) => ({ as: `null${i}`, val: null })) - const cqn = { - SELECT: { - from: { ref: ['complex.associations.Authors'] }, - columns: [{ ref: ['ID'] }, { ref: ['name'] }, { ref: ['books'], expand: ['*', ...nulls(197)] }] - }, - } - + const { Authors } = cds.entities('complex.associations') + const cqn = SELECT([{ ref: ['ID'] }, { ref: ['name'] }, { ref: ['books'], expand: ['*', ...nulls(197)] }]).from(Authors) const res = await cds.run(cqn) // ensure that all values are returned in json format assert.strictEqual(Object.keys(res[0].books[0]).length, 200) }) test('expand to one with 200 columns', async () => { - const nulls = length => new Array(length).fill().map((_, i) => ({ as: `null${i}`, val: null })) - const cqn = { - SELECT: { - from: { ref: ['complex.associations.Books'] }, - columns: [{ ref: ['ID'] }, { ref: ['title'] }, { ref: ['author'], expand: ['*', ...nulls(198)] }] - }, - } - + const { Books } = cds.entities('complex.associations') + const cqn = SELECT([{ ref: ['ID'] }, { ref: ['title'] }, { ref: ['author'], expand: ['*', ...nulls(198)] }]).from(Books) const res = await cds.run(cqn) // ensure that all values are returned in json format assert.strictEqual(Object.keys(res[0].author).length, 200) }) test('expand association with static values', async () => { - const cqn = { - SELECT: { - from: { ref: ['complex.associations.unmanaged.Authors'] }, - columns: [{ ref: ['static'], expand: ['*'] }] - }, - } - + const { Authors } = cds.entities('complex.associations.unmanaged') + const cqn = CQL`SELECT static{*} FROM ${Authors}` const res = await cds.run(cqn) // ensure that all values are returned in json format assert.strictEqual(res[0].static.length, 1) }) test.skip('invalid cast (wrong)', async () => { - await expect( - cds.run(CQL` - SELECT - 'String' as ![string] : cds.DoEsNoTeXiSt - FROM basic.projection.globals - `), - { - message: 'Not supported type: cds.DoEsNoTeXiSt', - }, - ).rejected + const { globals } = cds.entities('basic.projection') + const cqn = CQL`SELECT 'String' as ![string] : cds.DoEsNoTeXiSt FROM ${globals}` + await expect(cds.run(cqn), { message: 'Not supported type: cds.DoEsNoTeXiSt' }) + .rejected }) }) - describe('columns', () => { - test.skip('missing', () => { - throw new Error('not supported') + describe('excluding', () => { + test('without columns', async () => { + const { string } = cds.entities('basic.literals') + const cqn = CQL`SELECT FROM ${string} excluding { string }` + const res = await cds.run(cqn) + assert.strictEqual(res.length, 3, 'Ensure that all rows are coming back') + assert.strictEqual('string' in res[0], false, 'Ensure that excluded columns are missing') }) - }) - describe('excluding', () => { - test.skip('missing', () => { - throw new Error('not supported') + test('with start', async () => { + const { string } = cds.entities('basic.literals') + const cqn = CQL`SELECT FROM ${string} { * } excluding { string }` + const res = await cds.run(cqn) + assert.strictEqual(res.length, 3, 'Ensure that all rows are coming back') + assert.strictEqual('string' in res[0], false, 'Ensure that excluded columns are missing') + }) + + test('with extra columns', async () => { + const { string } = cds.entities('basic.literals') + const cqn = CQL`SELECT FROM ${string} { *, ${'extra'} } excluding { string }` + const res = await cds.run(cqn) + assert.strictEqual(res.length, 3, 'Ensure that all rows are coming back') + assert.strictEqual('string' in res[0], false, 'Ensure that excluded columns are missing') + assert.strictEqual('extra' in res[0], true, 'Ensure that specific columns are included') + }) + + test('with specific columns', async () => { + const { string } = cds.entities('basic.literals') + const cqn = CQL`SELECT FROM ${string} { string, char } excluding { string }` + const res = await cds.run(cqn) + assert.strictEqual(res.length, 3, 'Ensure that all rows are coming back') + assert.strictEqual('string' in res[0], true, 'Ensure that specific columns are included') }) }) describe('where', () => { test('empty where clause', async () => { - const cqn = CQL`SELECT bool FROM basic.literals.globals` + const { globals } = cds.entities('basic.literals') + const cqn = CQL`SELECT bool FROM ${globals}` cqn.SELECT.where = [] const res = await cds.run(cqn) assert.strictEqual(res.length, 3, 'Ensure that all rows are coming back') }) test('compare with DateTime column', async () => { - const entity = `basic.literals.dateTime` + const { dateTime: entity } = cds.entities('basic.literals') const dateTime = '1970-02-02T10:09:34Z' const timestamp = dateTime.slice(0, -1) + '.000Z' await DELETE.from(entity) @@ -264,54 +308,134 @@ describe('SELECT', () => { }) test('combine expr and other compare', async () => { - const cqn = CQL`SELECT bool FROM basic.literals.globals` - cqn.SELECT.where = [ - { - xpr: [ - { - xpr: [{ ref: ['bool'] }, '!=', { val: true }] - } - ] - }, - 'and', - { ref: ['bool'] }, '=', { val: false } - ] - const res = await cds.run(cqn) + const { globals } = cds.entities('basic.literals') + const res = await cds.run(CQL`SELECT bool FROM ${globals} WHERE (bool != ${true}) and bool = ${false}`) assert.strictEqual(res.length, 1, 'Ensure that all rows are coming back') }) - + test('exists path expression', async () => { - const cqn = { - SELECT: { - from: { ref: ["complex.associations.Books"] }, - where: [ - "exists", - { - ref: [ - "author", - { id: "books", where: [{ ref: ["author", "name"] }, "=", { val: "Emily" }] }] - } - ] - } - } - expect(cds.run(cqn)).to.eventually.be.rejectedWith('Only foreign keys of “author” can be accessed in infix filter, but found “name”'); + const { Books } = cds.entities('complex.associations') + const cqn = CQL`SELECT * FROM ${Books} WHERE exists author.books[author.name = ${'Emily'}]` + await expect(cds.run(cqn)) + .to.be.rejectedWith('Only foreign keys of “author” can be accessed in infix filter, but found “name”'); }) test('exists path expression (unmanaged)', async () => { - const cqn = { - SELECT: { - from: { ref: ["complex.associations.unmanaged.Books"] }, - where: [ - "exists", - { - ref: [ - "author", - { id: "books", where: [{ ref: ["author", "name"] }, "=", { val: "Emily" }] }] - } - ] - } - } - expect(cds.run(cqn)).to.eventually.be.rejectedWith('Unexpected unmanaged association “author” in filter expression of “books”'); + const { Books } = cds.entities('complex.associations.unmanaged') + const cqn = CQL`SELECT * FROM ${Books} WHERE exists author.books[author.name = ${'Emily'}]` + await expect(cds.run(cqn)) + .to.be.rejectedWith('Unexpected unmanaged association “author” in filter expression of “books”'); + }) + + test('like wildcard', async () => { + const { string } = cds.entities('basic.projection') + const res = await cds.run(CQL`SELECT string FROM ${string} WHERE string LIKE 'ye_'`) + assert.strictEqual(res.length, 1, `Ensure that only 'true' matches`) + }) + + test('like regex uses native regex support', async () => { + let ret = await SELECT.from('basic.projection.string').where('string like', /ye./) + expect(ret.length).to.eq(1) + }) + + test('= regex behaves like string', async () => { + expect(await SELECT.from('basic.projection.string').where('string =', /ye./)).to.have.property('length', 0) + expect(await SELECT.from('basic.projection.string').where('string =', /yes/)).to.have.property('length', 1) + }) + + test('ref in list', async () => { + const { string } = cds.entities('basic.projection') + const cqn = CQL`SELECT string FROM ${string} WHERE string in (${'yes'},${'no'})` + const res = await cds.run(cqn) + assert.strictEqual(res.length, 2, 'Ensure that all rows are coming back') + }) + + test('list in list of list', async () => { + const { string } = cds.entities('basic.projection') + const cqn = CQL`SELECT string FROM ${string} WHERE (string) in ((${'yes'}),(${'no'}))` + const res = await cds.run(cqn) + assert.strictEqual(res.length, 2, 'Ensure that all rows are coming back') + }) + + test('list in list of list (static)', async () => { + const { string } = cds.entities('basic.projection') + const cqn = CQL`SELECT string FROM ${string} WHERE (string,${'static'}) in ((${'yes'},${'static'}),(${'no'},${'static'}))` + const res = await cds.run(cqn) + assert.strictEqual(res.length, 2, 'Ensure that all rows are coming back') + }) + + test('ref in SELECT', async () => { + const { string } = cds.entities('basic.projection') + const cqn = CQL`SELECT string FROM ${string} WHERE string in (SELECT string from ${string})` + const res = await cds.run(cqn) + assert.strictEqual(res.length, 3, 'Ensure that all rows are coming back') + }) + + test('ref in SELECT alias', async () => { + const { string } = cds.entities('basic.projection') + const cqn = CQL`SELECT string FROM ${string} WHERE string in (SELECT string as string_renamed from ${string})` + const res = await cds.run(cqn) + assert.strictEqual(res.length, 3, 'Ensure that all rows are coming back') + }) + + test('param ?', async () => { + const { string } = cds.entities('basic.projection') + const cqn = CQL`SELECT string FROM ${string} WHERE string = ?` + const res = await cds.run(cqn, ['yes']) + assert.strictEqual(res.length, 1, 'Ensure that all rows are coming back') + }) + + // REVISIT: it is not yet fully supported to have named parameters on all databases + test.skip('param named', async () => { + const { string } = cds.entities('basic.projection') + const cqn = CQL`SELECT string FROM ${string} WHERE string = :param` + const res = await cds.run(cqn, { param: 'yes' }) + assert.strictEqual(res.length, 1, 'Ensure that all rows are coming back') + }) + + test.skip('param number', async () => { + const { string } = cds.entities('basic.projection') + const cqn = CQL`SELECT string FROM ${string} WHERE string = :7` + const res = await cds.run(cqn, { 7: 'yes' }) + assert.strictEqual(res.length, 1, 'Ensure that all rows are coming back') + }) + + test('param multiple uses', async () => { + const { string } = cds.entities('basic.projection') + const cqn = CQL`SELECT string FROM ${string} WHERE string = ?` + let res = await cds.run(cqn, ['yes']) + assert.strictEqual(res.length, 1, 'Ensure that all rows are coming back') + res = await cds.run(cqn, ['']) + assert.strictEqual(res.length, 0, 'Ensure that all rows are coming back') + res = await cds.run(cqn, ['no']) + assert.strictEqual(res.length, 1, 'Ensure that all rows are coming back') + }) + + test('func', async () => { + const { string } = cds.entities('basic.projection') + const cqn = CQL`SELECT string FROM ${string} WHERE concat(string, string) = 'yesyes'` + const res = await cds.run(cqn) + assert.strictEqual(res.length, 1, 'Ensure that all rows are coming back') + }) + + test('random combination 1', async () => { + const { string } = cds.entities('basic.projection') + const cqn = CQL`SELECT string FROM ${string} WHERE (string || string) = ${'yesyes'} and string in (SELECT string from ${string} WHERE string = ${'yes'})` + const res = await cds.run(cqn) + assert.strictEqual(res.length, 1, 'Ensure that all rows are coming back') + }) + + // search tests don't check results as the search behavior is undefined + test('search one column', async () => { + const { string } = cds.entities('basic.literals') + const cqn = CQL`SELECT * FROM ${string} WHERE search((string),${'yes'})` + await cds.run(cqn) + }) + + test('search multiple column', async () => { + const { string } = cds.entities('basic.literals') + const cqn = CQL`SELECT * FROM ${string} WHERE search((string,char,short,medium,large),${'yes'})` + await cds.run(cqn) }) test.skip('ref select', async () => { @@ -341,35 +465,128 @@ describe('SELECT', () => { }) }) - describe('having', () => { - test.skip('missing', () => { - throw new Error('not supported') + describe('groupby', () => { + test('single ref', async () => { + const { string } = cds.entities('basic.literals') + const cqn = CQL`SELECT string FROM ${string} GROUP BY string` + const res = await cds.run(cqn) + assert.strictEqual(res.length, 3, 'Ensure that all rows are coming back') + }) + + test('multiple refs', async () => { + const { string } = cds.entities('basic.literals') + const cqn = CQL`SELECT string FROM ${string} GROUP BY string, char` + const res = await cds.run(cqn) + assert.strictEqual(res.length, 3, 'Ensure that all rows are coming back') + }) + + test('static val', async () => { + const { string } = cds.entities('basic.literals') + const cqn = CQL`SELECT string FROM ${string} GROUP BY string,${1}` + const res = await cds.run(cqn) + assert.strictEqual(res.length, 3, 'Ensure that all rows are coming back') + }) + + test('func', async () => { + const { string } = cds.entities('basic.literals') + const cqn = CQL`SELECT string FROM ${string} GROUP BY string,now()` + const res = await cds.run(cqn) + assert.strictEqual(res.length, 3, 'Ensure that all rows are coming back') + }) + + test('func', async () => { + const { string } = cds.entities('basic.literals') + const cqn = CQL`SELECT string FROM ${string} GROUP BY string,now()` + const res = await cds.run(cqn) + assert.strictEqual(res.length, 3, 'Ensure that all rows are coming back') }) }) - describe('groupby', () => { - test.skip('missing', () => { - throw new Error('not supported') + describe('having', () => { + test('ignore empty array', async () => { + const { string } = cds.entities('basic.literals') + const cqn = CQL`SELECT string FROM ${string}` + cqn.SELECT.having = [] + const res = await cds.run(cqn) + assert.strictEqual(res.length, 3, 'Ensure that all rows are coming back') + }) + + test('without groupby (not allowed)', async () => { + const { string } = cds.entities('basic.literals') + const cqn = CQL`SELECT string FROM ${string} HAVING string = ${'yes'}` + await expect(cds.run(cqn)).rejected + }) + + test('with groupby', async () => { + const { string } = cds.entities('basic.literals') + const cqn = CQL`SELECT string FROM ${string} GROUP BY string HAVING string = ${'yes'}` + const res = await cds.run(cqn) + assert.strictEqual(res.length, 1, 'Ensure that all rows are coming back') }) }) describe('orderby', () => { - test.skip('missing', () => { - throw new Error('not supported') + test('ignore empty array', async () => { + const { string } = cds.entities('basic.literals') + const cqn = CQL`SELECT string FROM ${string}` + cqn.SELECT.orderBy = [] + const res = await cds.run(cqn) + assert.strictEqual(res.length, 3, 'Ensure that all rows are coming back') + }) + + test('single ref', async () => { + const { string } = cds.entities('basic.literals') + const cqn = CQL`SELECT string FROM ${string} ORDER BY string` + const res = await cds.run(cqn) + assert.strictEqual(res.length, 3, 'Ensure that all rows are coming back') + const sorted = res.toSorted((a, b) => String.prototype.localeCompare.call(a.string, b.string)) + assert.deepEqual(res, sorted, 'Ensure that all rows are in the correct order') + }) + + test('single ref asc (explicit)', async () => { + const { string } = cds.entities('basic.literals') + const cqn = CQL`SELECT string FROM ${string} ORDER BY string asc` + const res = await cds.run(cqn) + assert.strictEqual(res.length, 3, 'Ensure that all rows are coming back') + const sorted = res.toSorted((a, b) => String.prototype.localeCompare.call(a.string, b.string)) + assert.deepEqual(res, sorted, 'Ensure that all rows are in the correct order') + }) + + test('single ref desc', async () => { + const { string } = cds.entities('basic.literals') + const cqn = CQL`SELECT string FROM ${string} ORDER BY string desc` + const res = await cds.run(cqn) + assert.strictEqual(res.length, 3, 'Ensure that all rows are coming back') + const sorted = res.toSorted((a, b) => String.prototype.localeCompare.call(b.string, a.string)) + assert.deepEqual(res, sorted, 'Ensure that all rows are in the correct order') + }) + + test('localized', async () => { + const { string } = cds.entities('basic.literals') + const cqn = CQL`SELECT string FROM ${string} ORDER BY string` + cqn.SELECT.localized = true + const res = await cds.run(cqn) + assert.strictEqual(res.length, 3, 'Ensure that all rows are coming back') + const sorted = res.toSorted((a, b) => String.prototype.localeCompare.call(a.string, b.string)) + assert.deepEqual(res, sorted, 'Ensure that all rows are in the correct order') }) }) describe('limit', () => { - describe('rows', () => { - test.skip('missing', () => { - throw new Error('not supported') - }) + test('rows', async () => { + const { string } = cds.entities('basic.literals') + const cqn = CQL`SELECT string FROM ${string} ORDER BY string LIMIT ${1}` + const res = await cds.run(cqn) + assert.strictEqual(res.length, 1, 'Ensure that all rows are coming back') + assert.strictEqual(res[0].string, 'no', 'Ensure that the first row is coming back') }) - describe('offset', () => { - test.skip('missing', () => { - throw new Error('not supported') - }) + test('offset', async () => { + const { string } = cds.entities('basic.literals') + const cqn = CQL`SELECT string FROM ${string} ORDER BY string LIMIT ${1} OFFSET ${1}` + const res = await cds.run(cqn) + assert.strictEqual(res.length, 1, 'Ensure that all rows are coming back') + assert.strictEqual(res[0].string, 'null', 'Ensure that the first row is coming back') }) }) @@ -511,8 +728,34 @@ describe('SELECT', () => { }) describe('search', () => { - test.skip('missing', () => { - throw new Error('not supported') + // Make sure that the queries work, but never check their behavior as it is undefined + + test('single word', async () => { + const { string } = cds.entities('basic.literals') + const cqn = CQL`SELECT * FROM ${string}` + cqn.SELECT.search = [{ val: 'yes' }] + await cds.run(cqn) + }) + + test('single quoted word', async () => { + const { string } = cds.entities('basic.literals') + const cqn = CQL`SELECT * FROM ${string}` + cqn.SELECT.search = [{ val: '"yes"' }] + await cds.run(cqn) + }) + + test('multiple words', async () => { + const { string } = cds.entities('basic.literals') + const cqn = CQL`SELECT * FROM ${string}` + cqn.SELECT.search = [{ val: 'yes no' }] + await cds.run(cqn) + }) + + test('multiple quoted words', async () => { + const { string } = cds.entities('basic.literals') + const cqn = CQL`SELECT * FROM ${string}` + cqn.SELECT.search = [{ val: '"yes" "no"' }] + await cds.run(cqn) }) }) @@ -528,14 +771,521 @@ describe('SELECT', () => { }) describe('one', () => { - test.skip('missing', () => { - throw new Error('not supported') + test('simple', async () => { + const { string } = cds.entities('basic.literals') + const cqn = CQL`SELECT string FROM ${string} ORDER BY string` + cqn.SELECT.one = true + const res = await cds.run(cqn) + assert.strictEqual(!Array.isArray(res) && typeof res, 'object', 'Ensure that the result is an object') + assert.strictEqual(res.string, 'no', 'Ensure that the first row is coming back') + }) + + test('conflicting with limit clause', async () => { + const { string } = cds.entities('basic.literals') + const cqn = CQL`SELECT string FROM ${string} ORDER BY string LIMIT 2 OFFSET 1` + cqn.SELECT.one = true + const res = await cds.run(cqn) + assert.strictEqual(!Array.isArray(res) && typeof res, 'object', 'Ensure that the result is an object') + assert.strictEqual(res.string, 'null', 'Ensure that the second row is coming back') }) }) describe('distinct', () => { - test.skip('missing', () => { - throw new Error('not supported') + test('simple', async () => { + const { string } = cds.entities('basic.literals') + const cqn = CQL`SELECT string FROM ${string}` + cqn.SELECT.distinct = true + const res = await cds.run(cqn) + assert.strictEqual(res.length, 3, 'Ensure that all rows are coming back') + }) + + test('static val', async () => { + const { string } = cds.entities('basic.literals') + const cqn = CQL`SELECT ${'static'} FROM ${string}` + cqn.SELECT.distinct = true + const res = await cds.run(cqn) + assert.strictEqual(res.length, 1, 'Ensure that all rows are coming back') }) }) + + describe('expr', () => { + const minimal = true + + const model = cds.load(__dirname + '/resources/db', { sync: true }) + const targetName = 'basic.projection.all' + const { [targetName]: target } = model.definitions //cds.entities('basic.projection') + + const unified = {} + + // === Start defining ref === + unified.ref = Object.keys(target.elements) + .map(e => { + const ref = { ref: [e] } + Object.defineProperty(ref, 'element', { configurable: true, value: target.elements[e] }) + if (ref.element.virtual) return false + return ref + }) + .filter(a => a) + + const noUUIDRefs = ref => cds.builtin.types[ref.element?.type] !== cds.builtin.types.UUID + const noBooleanRefs = ref => !(cds.builtin.types[ref.element?.type] instanceof cds.builtin.types.boolean.constructor) + const noBinaryRefs = ref => !(cds.builtin.types[ref.element?.type] === cds.builtin.types.Binary || cds.builtin.types[ref.element?.type] === cds.builtin.types.LargeBinary) + const noBlobRefs = ref => noBinaryRefs(ref) && cds.builtin.types[ref.element?.type] !== cds.builtin.types.LargeString + const timeRefs = ref => cds.builtin.types[ref.element?.type] === cds.builtin.types.Time + const dateRefs = ref => cds.builtin.types[ref.element?.type] === cds.builtin.types.Date + const datetimeRefs = ref => cds.builtin.types[ref.element?.type] === cds.builtin.types.DateTime + const timestampRefs = ref => cds.builtin.types[ref.element?.type] === cds.builtin.types.Timestamp + const numberRefs = ref => cds.builtin.types[ref.element?.type] instanceof cds.builtin.types.number.constructor + const stringRefs = ref => cds.builtin.types[ref.element?.type] instanceof cds.builtin.types.string.constructor && noBinaryRefs(ref) + + // === Start defining val === + unified.null = { val: null } + + unified.boolean = [ + { val: true }, + { val: false }, + ] + + unified.UUID = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'] + .map(fill => ({ val: '00000000-0000-0000-0000-000000000000'.replace(/0/g, fill) })) + + unified.uinteger_positive = [ + { val: 1 }, + ] + + unified.uinteger = [ + { val: 0 }, + ...unified.uinteger_positive + ] + + unified.integer = [ + ...unified.uinteger, + ...unified.uinteger_positive.map(n => ({ val: -1 * n.val })) + ] + + unified.float = [ + { val: 0.1 }, + { val: 0.9 }, + { val: 1.1 }, + { val: -0.1 }, + { val: -0.9 }, + { val: -1.1 }, + ] + + unified.numeric = [ + ...unified.integer, + ...unified.float, + ] + + unified.string = [ + { val: '' }, + { val: 'c' }, + { val: 'short' }, + { val: 'medium length string' }, + { val: ''.padEnd(5000, 'large length string') }, + { val: ''.padEnd(5001, 'overflowing length string') }, + ] + + unified.date = [ + { val: new Date() }, + { val: (new Date()).toISOString() }, + ] + + unified.time = [ + { val: '00:00:00' }, + { val: '23:59:59' }, + ] + + const cdsTypeVals = { + 'cds.Boolean': unified.boolean, + 'cds.UUID': unified.UUID, + 'cds.UInt8': unified.uinteger, + 'cds.Int16': unified.integer, + 'cds.Int32': unified.integer, + 'cds.Int64': unified.integer, + 'cds.Double': unified.numeric, + 'cds.Decimal': unified.numeric, + 'cds.String': unified.string, + 'cds.Date': unified.date, + 'cds.Time': unified.time, + 'cds.DateTime': unified.date, + 'cds.Timestamp': unified.date, + } + + unified.val = [ + unified.null, + ...unified.boolean, + ...unified.UUID, + ...unified.numeric, + ...unified.string, + ...unified.date, + ...unified.time, + ] + .map(v => ({ ...v, as: 'val' })) + + // === Start defining func === + unified.window = [ + { func: 'row_number' }, + ...unified.ref.filter(numberRefs).map(ref => ({ func: 'avg', args: [ref] })), + ...unified.ref.filter(noBlobRefs).map(ref => ({ func: 'count', args: [ref] })), + { func: 'count', args: ['*'] }, + { func: 'count' }, + ...unified.ref.filter(noBlobRefs).filter(noBooleanRefs).map(ref => ({ func: 'max', args: [ref] })), + ...unified.ref.filter(noBlobRefs).filter(noBooleanRefs).map(ref => ({ func: 'min', args: [ref] })), + ...unified.ref.filter(numberRefs).map(ref => ({ func: 'sum', args: [ref] })), + ] + + const windowOrdered = [ + { func: 'rank' }, + { func: 'dense_rank' }, + { func: 'percent_rank' }, + { func: 'cume_dist' }, + ...unified.uinteger_positive.map(n => ({ func: 'ntile', args: [n] })), + ...['lag', 'lead'].map(func => { + return unified.ref.filter(noBlobRefs).map(ref => { + return [ + { func, args: [ref] }, + ...unified.uinteger_positive.map(offset => [ + { func, args: [ref, offset] }, + ...(unified[ref.element.type]?.map(def => ({ func, args: [ref, offset, def] })) || []), + ]).flat(), + ] + }).flat() + }).flat(), + ...unified.ref.filter(noBlobRefs).map(ref => ({ func: 'first_value', args: [ref] })), + ...unified.ref.filter(noBlobRefs).map(ref => ({ func: 'last_value', args: [ref] })), + ...unified.ref.filter(noBlobRefs).map(ref => + unified.uinteger_positive.map(n => ({ func: 'nth_value', args: [ref, n] })) + ).flat(), + ] + + const overPartition = { xpr: ['PARTITION BY', unified.ref[0]] } + const overOrderBy = { xpr: ['ORDER BY', unified.ref[0]] } + const overPartitionOrderBy = { xpr: ['ORDER BY', unified.ref[0]] } + unified.window = [ + ...unified.window.map(func => ({ as: func.func, xpr: [func, 'OVER', { xpr: [] }] })), + ...(minimal ? [] : unified.window.map(func => ({ as: func.func, xpr: [func, 'OVER', overPartition] }))), + ...(minimal ? [] : unified.window.map(func => ({ as: func.func, xpr: [func, 'OVER', overOrderBy] }))), + ...(minimal ? [] : windowOrdered.map(func => ({ as: func.func, xpr: [func, 'OVER', overOrderBy] }))), + ...unified.window.map(func => ({ as: func.func, xpr: [func, 'OVER', overPartitionOrderBy] })), + ...windowOrdered.map(func => ({ as: func.func, xpr: [func, 'OVER', overPartitionOrderBy] })), + ] + + unified.aggregate = [ + ...unified.ref.filter(numberRefs).map(ref => ({ func: 'average', args: [ref] })), + ...unified.ref.filter(noBlobRefs).map(ref => ({ func: 'count', args: [ref] })), + { func: 'count', args: ['*'] }, + { func: 'count' }, + ...unified.ref.filter(noBlobRefs).map(ref => ({ func: 'countdistinct', args: [ref] })), + ...unified.ref.filter(noBlobRefs).filter(noBooleanRefs).map(ref => ({ func: 'max', args: [ref] })), + ...unified.ref.filter(noBlobRefs).filter(noBooleanRefs).map(ref => ({ func: 'min', args: [ref] })), + ...unified.ref.filter(numberRefs).map(ref => ({ func: 'sum', args: [ref] })), + ...unified.ref.filter(stringRefs).filter(noBlobRefs).map(ref => ({ func: 'string_agg', args: [ref, { val: ',' }] })), + ] + + unified.scalar = [ + // TODO: investigate search issue for nvarchar columns + ...unified.ref.filter(ref => cds.builtin.types[ref.element?.type] === cds.builtin.types.LargeString).map(ref => { + return unified.string.map(val => ({ func: 'search', args: [ref, val] })) + }).flat(), + // ...unified.string.map(val => ({ func: 'search', args: [{ list: unified.ref.filter(stringRefs) }, val] })), + ...unified.ref.filter(stringRefs).filter(noBooleanRefs).map(X => { + return unified.ref.filter(stringRefs).filter(noBooleanRefs).slice(0, minimal ? 1 : undefined).map(Y => ({ func: 'concat', args: [X, Y] })) + }).flat(), + // argument less functions + ...['current_date', 'current_time', 'current_timestamp', 'now', 'maxdatetime', 'mindatetime'].map(func => { + return [ + { func }, + { func, args: [] }, + ] + }).flat(), + // X string function + ...[ + 'length', 'tolower', 'toupper', 'trim', // OData spec + // 'soundex', + 'ltrim', 'rtrim', + ].map(func => { + return [ + ...unified.string.map(val => ({ func, args: [val] })), + ...unified.ref.filter(stringRefs).map(ref => ({ func, args: [ref] })), + ] + }).flat(), + // X,Y string functions + ...['contains', 'indexof', 'startswith', 'endswith'].map(func => { + return unified.ref.filter(stringRefs).map(ref => { + return [ + ...unified.string.slice(0, minimal ? 1 : undefined).map(val => ({ func, args: [ref, val] })), + ...unified.ref.filter(stringRefs).filter(noBlobRefs).slice(0, minimal ? 1 : undefined).map(ref2 => ({ func, args: [ref, ref2] })) + ] + }).flat() + }).flat(), + ...unified.ref.filter(stringRefs).map(ref => { + return [ + ...unified.uinteger.slice(0, minimal ? 1 : undefined).map(offset => ({ func: 'substring', args: [ref, offset] })), + ...unified.uinteger.slice(0, minimal ? 1 : undefined).map(offset => { + return unified.uinteger.slice(0, minimal ? 1 : undefined).map(end => ({ func: 'substring', args: [ref, offset, end] })) + }).flat(), + ] + }).flat(), + ...unified.ref.filter(stringRefs).map(ref => ({ func: 'matchespattern', args: [ref, { val: '.*' }] })), + // X numeric function + ...[ + 'ceiling', 'floor', 'round', // OData spec + 'abs', 'sign', 'sin', 'tan', + ].map(func => { + return [ + ...unified.numeric.map(val => ({ func, args: [val] })), + ...unified.ref.filter(numberRefs).map(ref => ({ func, args: [ref] })), + ] + }).flat(), + // X,Y numeric function + ...['atan2', 'power'].map(func => { + return unified.ref.filter(numberRefs).map(ref => { + return unified.numeric.slice(0, minimal ? 1 : undefined).map(val => ({ func, args: [ref, val] })) + }) + .flat() + }).flat(), + // numeric functions with picky inputs + { func: 'acos', args: [{ val: 0 }] }, + { func: 'asin', args: [{ val: 0 }] }, + { func: 'atan', args: [{ val: 0 }] }, + { func: 'cos', args: [{ val: 0 }] }, + { func: 'exp', args: [{ val: 2 }] }, + { func: 'ln', args: [{ val: 2 }] }, + { func: 'sqrt', args: [{ val: 2 }] }, + { func: 'log', args: [{ val: 2 }, { val: 2 }] }, + { func: 'mod', args: [{ val: 2, cast: { type: 'cds.Integer' } }, { val: 2, cast: { type: 'cds.Integer' } }] }, + // X timestamp function + ...['year', 'month', 'day', 'hour', 'minute', 'second', 'fractionalseconds'].map(func => { + return [ + ...unified.date.map(val => ({ func, args: [val] })), + ...unified.ref.filter(timestampRefs).map(ref => ({ func, args: [ref] })), + ] + }).flat(), + // X datetime function + ...['year', 'month', 'day', 'hour', 'minute', 'second'].map(func => { + return [ + ...unified.date.map(val => ({ func, args: [val] })), + ...unified.ref.filter(datetimeRefs).map(ref => ({ func, args: [ref] })), + ] + }).flat(), + // X date function + ...['year', 'month', 'day'].map(func => { + return [ + ...unified.date.map(val => ({ func, args: [val] })), + ...unified.ref.filter(dateRefs).map(ref => ({ func, args: [ref] })), + ] + }).flat(), + // X time function + ...['hour', 'minute', 'second'].map(func => { + return [ + ...unified.date.map(val => ({ func, args: [val] })), + ...unified.ref.filter(timeRefs).map(ref => ({ func, args: [ref] })), + ] + }).flat(), + ...['$user.id', '$user.locale', '$valid.from', '$valid.to', '$now'].map(val => ({ func: 'session_context', args: [{ val }] })), + ...unified.ref.map(ref => ({ func: 'coalesce', args: [ref, ref] })), + ] + + unified.func = [ + ...unified.window, + ...unified.aggregate, + ...unified.scalar, + ] + + // === Start defining xpr === + unified.comparators = [ + ...['=', '<>', '==', '!=', '>', '<', '>=', '<='].map(op => { + return unified.ref.filter(noBlobRefs).map(ref => { + const typeVals = cdsTypeVals[ref.element.type] || [] + return [ + ...[ + { xpr: [ref, op, ref] }, + { xpr: [ref, op, unified.null] }, + { xpr: [unified.null, op, ref] }, + ].slice(0, minimal ? 1 : undefined), + ...typeVals.slice(0, minimal ? 1 : undefined).map(val => { + val = { ...val, cast: ref.element } + return [ + { xpr: [ref, op, val] }, + { xpr: [val, op, ref] }, + { xpr: [val, op, unified.null] }, + { xpr: [unified.null, op, val] }, + ].slice(0, minimal ? 1 : undefined) + }).flat(), + ] + }).flat() + }).flat(), + ...['IN', 'NOT IN'].map(op => { + return unified.ref.filter(noBlobRefs).map(ref => { + return [ + { xpr: [ref, op, { list: [ref] }] }, + { xpr: [ref, op, { list: [ref, ref] }] }, + { xpr: [{ list: [ref] }, op, { list: [{ list: [ref] }] }] }, + { xpr: [{ list: [ref, ref] }, op, { list: [{ list: [ref, ref] }] }] }, + { xpr: [ref, op, SELECT(ref).from(targetName)] }, + { xpr: [{ list: [ref] }, op, SELECT(ref).from(targetName)] }, + { xpr: [{ list: [ref, ref] }, op, SELECT([{ ...ref, as: 'a' }, { ...ref, as: 'b' }]).from(targetName)] }, + // Repreating the previous statements replaceing ref with null + { xpr: [unified.null, op, { list: [ref] }] }, + { xpr: [unified.null, op, { list: [ref, ref] }] }, + { xpr: [{ list: [unified.null] }, op, { list: [{ list: [ref] }] }] }, + { xpr: [{ list: [unified.null, unified.null] }, op, { list: [{ list: [ref, ref] }] }] }, + { xpr: [unified.null, op, SELECT(ref).from(targetName)] }, + { xpr: [{ list: [unified.null] }, op, SELECT(ref).from(targetName)] }, + { xpr: [{ list: [unified.null, unified.null] }, op, SELECT([{ ...ref, as: 'a' }, { ...ref, as: 'b' }]).from(targetName)] }, + ] + }).flat() + }).flat(), + ...['LIKE', 'NOT LIKE'].map(op => { + return unified.ref.filter(stringRefs).filter(noBlobRefs).map(ref => { + return [ + { xpr: [ref, op, { val: '%' }] }, + { xpr: [ref, op, { val: '_' }] }, + { xpr: [ref, op, { val: '"' }] }, + { xpr: [ref, op, { val: "'" }] }, + ...unified.string.slice(0, minimal ? 1 : undefined).map(val => ({ xpr: [ref, op, val] })), + ...unified.ref.filter(stringRefs).filter(noBlobRefs).slice(0, minimal ? 1 : undefined).map(ref2 => ({ xpr: [ref, op, ref2] })), + ] + }).flat() + }).flat(), + ...['BETWEEN', 'NOT BETWEEN'].map(op => { + return unified.ref.filter(noBlobRefs).map(ref => { + const typeVals = cdsTypeVals[ref.element.type] || [] + const nul = { ...unified.null, cast: ref.element } + return [ + ...[ + { xpr: [ref, op, ref, 'AND', ref] }, + { xpr: [nul, op, ref, 'AND', ref] }, + { xpr: [ref, op, nul, 'AND', ref] }, + { xpr: [nul, op, nul, 'AND', ref] }, + { xpr: [ref, op, ref, 'AND', nul] }, + { xpr: [nul, op, ref, 'AND', nul] }, + { xpr: [ref, op, nul, 'AND', nul] }, + ].slice(0, minimal ? 1 : undefined), + ...typeVals.slice(0, minimal ? 1 : undefined).map(val => { + val = { ...val, cast: ref.element } + return [ + { xpr: [val, op, ref, 'AND', ref] }, + { xpr: [ref, op, val, 'AND', ref] }, + { xpr: [val, op, val, 'AND', ref] }, + { xpr: [ref, op, ref, 'AND', val] }, + { xpr: [val, op, ref, 'AND', val] }, + { xpr: [ref, op, val, 'AND', val] }, + ].slice(0, minimal ? 1 : undefined) + }).flat(), + ] + }).flat() + }).flat(), + ...['EXISTS', 'NOT EXISTS'].map(op => { + return unified.ref.filter(noBlobRefs).map(ref => { + return [ + { xpr: [op, SELECT.from(targetName)] }, + { xpr: [op, SELECT([ref]).from(targetName)] }, + { xpr: [op, SELECT([{ val: 1 }]).from(targetName)] }, + ] + }).flat() + }).flat(), + ] + + unified.operators = [ + ...['*', '/', '+', '-'].map(op => { + return unified.ref.filter(numberRefs).map(ref => { + const typeVals = cdsTypeVals[ref.element.type] || [] + return [ + ...[ + { xpr: [ref, op, ref] }, + { xpr: [ref, op, unified.null] }, + { xpr: [unified.null, op, ref] }, + ].slice(0, minimal ? 1 : undefined), + ...typeVals.map(val => { + val = { ...val, cast: ref.element } + return [ + { xpr: [ref, op, val] }, + { xpr: [val, op, ref] }, + { xpr: [val, op, unified.null] }, + { xpr: [unified.null, op, val] }, + ].slice(0, minimal ? 1 : undefined) + }).flat(), + ] + }).flat() + }).flat(), + ] + + unified.xpr = (function* () { + for (const comp of unified.comparators) { + yield { xpr: ['CASE', 'WHEN', comp, 'THEN', { val: true }, 'ELSE', { val: false }, 'END'], as: 'xpr' } + if (!minimal || unified.comparators[0] === comp) { + yield { xpr: ['CASE', 'WHEN', { xpr: ['NOT', ...comp.xpr] }, 'THEN', { val: true }, 'ELSE', { val: false }, 'END'], as: 'xpr' } + // for (const comp2 of unified.comparators) { + yield { xpr: ['CASE', 'WHEN', comp, 'AND', comp, 'THEN', { val: true }, 'ELSE', { val: false }, 'END'], as: 'xpr' } + yield { xpr: ['CASE', 'WHEN', comp, 'OR', comp, 'THEN', { val: true }, 'ELSE', { val: false }, 'END'], as: 'xpr' } + // } + } + } + for (const xpr of unified.operators) { + xpr.as = 'xpr' + yield xpr + } + }) + + Object.defineProperty(unified.xpr, 'length', { + get: () => (unified.comparators.length) + 3 + unified.operators.length + }) + + // === Start defining list === + unified.list = [] + + // === Start defining SELECT === + unified.SELECT = (function* () { + const wrap = col => { + const ret = SELECT([col]).from(targetName).limit(1) + ret.as = 'select' + return ret + } + for (const col of unified.ref) { + yield wrap(col) + } + for (const col of unified.val) { + yield wrap(col) + } + for (const col of unified.func) { + yield wrap(col) + } + for (const col of unified.xpr()) { + yield wrap(col) + } + for (const col of unified.list) { + yield wrap(col) + } + }) + + Object.defineProperty(unified.SELECT, 'length', { + get: () => ( + unified.ref.length + + unified.val.length + + unified.func.length + + unified.xpr.length + + unified.list.length + ) + }) + + for (let type of ['ref', 'val', 'func', 'xpr', 'list', 'SELECT']) { + describe(`${type}: ${unified[type].length}`, () => { + test('execute', async () => { + // const batchCount = Math.min(os.availableParallelism() - 1, cds.db.factory.options.max || 1) + const batches = new Array(1).fill('') + const iterator = typeof unified[type] === 'function' ? unified[type]() : unified[type][Symbol.iterator]() + + const { [targetName]: target } = cds.entities + await Promise.all(batches.map(() => cds.tx(async (tx) => { + for (const t of iterator) { + // limit(0) still validates that the query is valid, but improves test execution time + await tx.run(SELECT([t]).from(target).limit(0)) + } + }))) + }) + }) + } + }) }) diff --git a/test/compliance/UPDATE.test.js b/test/compliance/UPDATE.test.js index f49c34999..ec7e20039 100644 --- a/test/compliance/UPDATE.test.js +++ b/test/compliance/UPDATE.test.js @@ -3,116 +3,131 @@ const Books = 'complex.associations.Books' const BooksUnique = 'complex.uniques.Books' describe('UPDATE', () => { + const { data, expect } = cds.test(__dirname + '/resources') + data.autoIsolation(true) + data.autoReset() + describe('entity', () => { - test.skip('missing', () => { - throw new Error('not supported') + test('string', async () => { + const { string } = cds.entities('basic.literals') + const changes = await UPDATE.entity(string) + expect(changes).to.equal(0) }) }) describe('data', () => { - test.skip('missing', () => { - throw new Error('not supported') + test('string', async () => { + const { string } = cds.entities('basic.literals') + await UPDATE(string).data({ string: 'updated' }) + const result = await SELECT.one.from(string) + expect(result.string).to.equal('updated') + }) + + test('number', async () => { + const { number } = cds.entities('basic.literals') + await INSERT({ integer32: 0 }).into(number) + await UPDATE(number).data({ integer32: 3 }) + const result = await SELECT.one.from(number) + expect(result.integer32).to.equal(3) }) }) - describe('with database', () => { - cds.test(__dirname + '/resources') - describe('where', () => { - test('flat with or on key', async () => { - const insert = await cds.run( - INSERT.into(Books).entries([ - { - ID: 5, - title: 'foo', - }, - { - ID: 6, - title: 'bar', - }, - ]), - ) - expect(insert.affectedRows).toEqual(2) - - const update = await cds.run( - UPDATE.entity(Books) - .set({ - title: 'foo', - }) - .where({ - ID: 5, - or: { - ID: 6, - }, - }), - ) - expect(update).toEqual(2) - }) - test.skip('missing', () => { - throw new Error('not supported') - }) + describe('with', () => { + test('val', async () => { + const { string } = cds.entities('basic.literals') + await UPDATE(string).with({ string: { val: 'updated' } }) + const result = await SELECT.one.from(string) + expect(result.string).to.equal('updated') }) - describe('uniques in deep updates', () => { - test('2nd level unique constraints ', async () => { - // number must be unique for each book - - await cds.tx(async tx => { - await tx.run(DELETE.from(BooksUnique).where({ ID: 1 })) - await expect( - tx.run( - INSERT.into(BooksUnique).entries([ - { - ID: 1, - title: 'foo', - pages: [ - { - ID: 1, - number: 1, - }, - { - ID: 2, - number: 1, // unique constraint violation - }, - ], - }, - { - ID: 2, - title: 'bar', - }, - ]), - ), - ).rejects.toBeTruthy() - }) - - await cds.tx(async tx => { - await tx.run(DELETE.from(BooksUnique).where({ ID: 1 })) - const data = { - ID: 1, - title: 'foo', - pages: [ - { - ID: 1, - number: 1, - }, - { - ID: 2, - number: 2, - }, - ], - } - await tx.run(INSERT.into(BooksUnique).entries([data])) - - // Create new entries with conflicting numbers - data.pages[0].ID = 3 - data.pages[1].ID = 4 - await tx.run(UPDATE(BooksUnique).data(data)) // first, old entries are deleted, so no violation - - data.pages[0].ID = 5 - data.pages[0].number = 1 // would fail without the update below first - data.pages[1].number = 999 - await tx.run(UPDATE(BooksUnique).data(data)) - }) - }) + test('xpr', async () => { + const { number } = cds.entities('basic.literals') + await INSERT({ integer32: 1 }).into(number) + await UPDATE(number).with({ integer32: { xpr: [{ ref: ['integer32'] }, '+', { val: 2 }] } }) + const result = await SELECT.one.from(number) + expect(result.integer32).to.equal(3) + }) + + test('func', async () => { + const { string } = cds.entities('basic.literals') + await UPDATE(string).with({ string: { func: 'concat', args: [{ val: 'a' }, { val: 'b' }] } }) + const result = await SELECT.one.from(string) + expect(result.string).to.equal('ab') + }) + }) + + describe('data + with', () => { + test('string', async () => { + const { string } = cds.entities('basic.literals') + await UPDATE(string) + .data({ string: 'updated' }) + .with({ medium: { func: 'concat', args: [{ val: 'a' }, { val: 'b' }] } }) + const result = await SELECT.one.from(string) + expect(result.string).to.equal('updated') + expect(result.medium).to.equal('ab') + }) + + test('number', async () => { + const { number } = cds.entities('basic.literals') + await INSERT({ integer32: 0 }).into(number) + await UPDATE(number) + .data({ integer32: 1 }) + .with({ integer64: { xpr: [{ ref: ['integer32'] }, '+', { val: 2 }] } }) + const result = await SELECT.one.from(number) + expect(result.integer32).to.equal(1) + expect(result.integer64).to.equal('2') + }) + }) + + describe('where', () => { + test('flat with or on key', async () => { + const insert = await cds.run( + INSERT.into(Books).entries([ + { ID: 5, title: 'foo' }, + { ID: 6, title: 'bar' }, + ]), + ) + expect(insert.affectedRows).to.equal(2) + + const update = await cds.run( + UPDATE.entity(Books) + .set({ title: 'foo' }) + .where({ ID: 5, or: { ID: 6 } }), + ) + expect(update).to.equal(2) + }) + }) + + describe('uniques in deep updates', () => { + test('2nd level unique constraints', async () => { + // number must be unique for each book + const data = { + ID: 1, + title: 'foo', + pages: [ + // Set both numbers to the same value to be conflicting + { ID: 1, number: 0 }, + { ID: 2, number: 0 }, + ], + } + + await DELETE.from(BooksUnique).where(`ID=${1}`) + await expect(INSERT(data).into(BooksUnique)).rejects.to.be.truthy + + // Update the numbers to be non conflicting + data.pages[0].number = 1 + data.pages[1].number = 2 + await INSERT(data).into(BooksUnique) + + // Create new entries with conflicting numbers + data.pages[0].ID = 3 + data.pages[1].ID = 4 + await UPDATE(BooksUnique).data(data) // first, old entries are deleted, so no violation + + data.pages[0].ID = 5 + data.pages[0].number = 1 // would fail without the update below first + data.pages[1].number = 999 + await UPDATE(BooksUnique).data(data) }) }) }) diff --git a/test/compliance/api.test.js b/test/compliance/api.test.js index a66f912fc..e304a664a 100644 --- a/test/compliance/api.test.js +++ b/test/compliance/api.test.js @@ -4,7 +4,7 @@ const cds = require('../cds.js') * Tests explicitely, that all DBs behave exactly the same for affected rows */ describe('affected rows', () => { -const { expect } = cds.test(__dirname + '/resources') + const { expect } = cds.test(__dirname + '/resources') test('Delete returns affected rows', async () => { const affectedRows = await DELETE.from('complex.associations.Books').where('ID = 4712') @@ -17,18 +17,18 @@ const { expect } = cds.test(__dirname + '/resources') // affectedRows is an InsertResult, so we need to do lose comparison here, as strict will not work due to InsertResult expect(affectedRows == 1).to.be.eq(true) // InsertResult - expect(affectedRows).to.include({ affectedRows: 1 }) // lastInsertRowid not available on postgres + expect(affectedRows).not.to.include({ _affectedRows: 1 }) // lastInsertRowid not available on postgres }) test('Update returns affected rows', async () => { const { count } = await SELECT.one`count(*)`.from('complex.associations.Books') - - const affectedRows = await UPDATE.entity('complex.associations.Books').data({title: 'Book'}) + + const affectedRows = await UPDATE.entity('complex.associations.Books').data({ title: 'Book' }) expect(affectedRows).to.be.eq(count) }) - test('Upsert returns affected rows', async () => { - const affectedRows = await UPSERT.into('complex.associations.Books').entries({ID: 9999999, title: 'Book'}) + test('Upsert returns affected rows', async () => { + const affectedRows = await UPSERT.into('complex.associations.Books').entries({ ID: 9999999, title: 'Book' }) expect(affectedRows).to.be.eq(1) }) }) diff --git a/test/compliance/definitions.test.js b/test/compliance/definitions.test.js index 0cfa0eaf9..1a520b039 100644 --- a/test/compliance/definitions.test.js +++ b/test/compliance/definitions.test.js @@ -1,3 +1,5 @@ +require('../cds') + describe('definitions', () => { describe('types', () => { test.skip('missing', () => { diff --git a/test/compliance/keywords.test.js b/test/compliance/keywords.test.js index 56c81a89c..cdb433ade 100644 --- a/test/compliance/keywords.test.js +++ b/test/compliance/keywords.test.js @@ -1,8 +1,9 @@ 'use strict' const cds = require('../../test/cds.js') -const { expect } = cds.test(__dirname + '/resources') describe('keywords', () => { + const { expect } = cds.test(__dirname + '/resources') + test('insert, update, select', async () => { const { Order } = cds.entities const data = { @@ -50,13 +51,13 @@ describe('keywords', () => { const { ASC } = cds.entities await UPSERT.into(ASC) .columns(['ID', 'select']) - .rows([[42,4711]]) + .rows([[42, 4711]]) let select = await SELECT.one.from(ASC, ['ID', 'select']).where('ID = 42') expect(select).to.eql({ ID: 42, select: 4711 }) - await UPSERT.into(ASC).entries({ID: 42, alias: 9}) + await UPSERT.into(ASC).entries({ ID: 42, alias: 9 }) .columns(['ID', 'select']) - .rows([[42,4711]]) + .rows([[42, 4711]]) select = await SELECT.one.from(ASC).where('ID = 42') expect(select).to.eql({ ID: 42, select: 4711, alias: 9 }) }) diff --git a/test/compliance/literals.test.js b/test/compliance/literals.test.js index 49c85cf16..68026f9e0 100644 --- a/test/compliance/literals.test.js +++ b/test/compliance/literals.test.js @@ -1,3 +1,5 @@ +require('../cds') + describe('literals', () => { describe('globals', () => { describe('boolean', () => { diff --git a/test/compliance/resources/db/basic/literals/basic.literals.number.js b/test/compliance/resources/db/basic/literals/basic.literals.number.js index 998242e00..cd25e4f67 100644 --- a/test/compliance/resources/db/basic/literals/basic.literals.number.js +++ b/test/compliance/resources/db/basic/literals/basic.literals.number.js @@ -57,11 +57,11 @@ module.exports = [ }, { decimal: '3.14153', - '=decimal': /^3\.1415/ + '=decimal': '3.1415' }, { decimal: 3.14, - '=decimal': /^3\.14/ + '=decimal': '3.1400' }, { double: 3.14159265358979 diff --git a/test/compliance/resources/db/basic/projection.cds b/test/compliance/resources/db/basic/projection.cds index a7647c2b7..eaa4df5ec 100644 --- a/test/compliance/resources/db/basic/projection.cds +++ b/test/compliance/resources/db/basic/projection.cds @@ -8,3 +8,275 @@ entity string as projection on literals.string; entity date as projection on literals.date; entity time as projection on literals.time; entity dateTime as projection on literals.dateTime; + +entity ![all] as + select from literals.globals { + bool, + cast(null as UUID) as uuid, + cast(null as UInt8) as integer8, + cast(null as Int16) as integer16, + cast(null as Int32) as integer32, + cast(null as Int64) as integer64, + cast(null as cds.Double) as double, + cast(null as cds.Decimal) as float, + cast(null as cds.Decimal(5, 4)) as decimal, + cast(null as String) as string, + cast(null as String(1)) as char, + cast(null as String(10)) as short, + cast(null as String(100)) as medium, + cast(null as String(5000)) as large, + cast(null as LargeString) as blob, + cast(null as Date) as date, + cast(null as Time) as time, + cast(null as DateTime) as dateTime, + cast(null as Timestamp) as timestamp, + cast(null as LargeString) as stringArray, + cast(null as LargeString) as integerArray, + cast(null as Binary) as binary, + cast(null as LargeBinary) as largebinary, + // NULL as vector : Vector, + } + union all + select from literals.uuid { + cast(null as Boolean) as bool, + uuid, + cast(null as UInt8) as integer8, + cast(null as Int16) as integer16, + cast(null as Int32) as integer32, + cast(null as Int64) as integer64, + cast(null as cds.Double) as double, + cast(null as cds.Decimal) as float, + cast(null as cds.Decimal(5, 4)) as decimal, + cast(null as String) as string, + cast(null as String(1)) as char, + cast(null as String(10)) as short, + cast(null as String(100)) as medium, + cast(null as String(5000)) as large, + cast(null as LargeString) as blob, + cast(null as Date) as date, + cast(null as Time) as time, + cast(null as DateTime) as dateTime, + cast(null as Timestamp) as timestamp, + cast(null as LargeString) as stringArray, + cast(null as LargeString) as integerArray, + cast(null as Binary) as binary, + cast(null as LargeBinary) as largebinary, + // NULL as vector : Vector, + } + union all + select from literals.number { + cast(null as Boolean) as bool, + cast(null as UUID) as uuid, + integer8, + integer16, + integer32, + integer64, + double, + float, + decimal, + cast(null as String) as string, + cast(null as String(1)) as char, + cast(null as String(10)) as short, + cast(null as String(100)) as medium, + cast(null as String(5000)) as large, + cast(null as LargeString) as blob, + cast(null as Date) as date, + cast(null as Time) as time, + cast(null as DateTime) as dateTime, + cast(null as Timestamp) as timestamp, + cast(null as LargeString) as stringArray, + cast(null as LargeString) as integerArray, + cast(null as Binary) as binary, + cast(null as LargeBinary) as largebinary, + // NULL as vector : Vector, + } + union all + select from literals.string { + cast(null as Boolean) as bool, + cast(null as UUID) as uuid, + cast(null as UInt8) as integer8, + cast(null as Int16) as integer16, + cast(null as Int32) as integer32, + cast(null as Int64) as integer64, + cast(null as cds.Double) as double, + cast(null as cds.Decimal) as float, + cast(null as cds.Decimal(5, 4)) as decimal, + string, + char, + short, + medium, + large, + blob, + cast(null as Date) as date, + cast(null as Time) as time, + cast(null as DateTime) as dateTime, + cast(null as Timestamp) as timestamp, + cast(null as LargeString) as stringArray, + cast(null as LargeString) as integerArray, + cast(null as Binary) as binary, + cast(null as LargeBinary) as largebinary, + // NULL as vector : Vector, + } + union all + select from literals.date { + cast(null as Boolean) as bool, + cast(null as UUID) as uuid, + cast(null as UInt8) as integer8, + cast(null as Int16) as integer16, + cast(null as Int32) as integer32, + cast(null as Int64) as integer64, + cast(null as cds.Double) as double, + cast(null as cds.Decimal) as float, + cast(null as cds.Decimal(5, 4)) as decimal, + cast(null as String) as string, + cast(null as String(1)) as char, + cast(null as String(10)) as short, + cast(null as String(100)) as medium, + cast(null as String(5000)) as large, + cast(null as LargeString) as blob, + date, + cast(null as Time) as time, + cast(null as DateTime) as dateTime, + cast(null as Timestamp) as timestamp, + cast(null as LargeString) as stringArray, + cast(null as LargeString) as integerArray, + cast(null as Binary) as binary, + cast(null as LargeBinary) as largebinary, + // NULL as vector : Vector, + } + + union all + select from literals.time { + cast(null as Boolean) as bool, + cast(null as UUID) as uuid, + cast(null as UInt8) as integer8, + cast(null as Int16) as integer16, + cast(null as Int32) as integer32, + cast(null as Int64) as integer64, + cast(null as cds.Double) as double, + cast(null as cds.Decimal) as float, + cast(null as cds.Decimal(5, 4)) as decimal, + cast(null as String) as string, + cast(null as String(1)) as char, + cast(null as String(10)) as short, + cast(null as String(100)) as medium, + cast(null as String(5000)) as large, + cast(null as LargeString) as blob, + cast(null as Date) as date, + time, + cast(null as DateTime) as dateTime, + cast(null as Timestamp) as timestamp, + cast(null as LargeString) as stringArray, + cast(null as LargeString) as integerArray, + cast(null as Binary) as binary, + cast(null as LargeBinary) as largebinary, + // NULL as vector : Vector, + } + union all + select from literals.dateTime { + cast(null as Boolean) as bool, + cast(null as UUID) as uuid, + cast(null as UInt8) as integer8, + cast(null as Int16) as integer16, + cast(null as Int32) as integer32, + cast(null as Int64) as integer64, + cast(null as cds.Double) as double, + cast(null as cds.Decimal) as float, + cast(null as cds.Decimal(5, 4)) as decimal, + cast(null as String) as string, + cast(null as String(1)) as char, + cast(null as String(10)) as short, + cast(null as String(100)) as medium, + cast(null as String(5000)) as large, + cast(null as LargeString) as blob, + cast(null as Date) as date, + cast(null as Time) as time, + dateTime, + cast(null as Timestamp) as timestamp, + cast(null as LargeString) as stringArray, + cast(null as LargeString) as integerArray, + cast(null as Binary) as binary, + cast(null as LargeBinary) as largebinary, + // NULL as vector : Vector, + } + union all + select from literals.timestamp { + cast(null as Boolean) as bool, + cast(null as UUID) as uuid, + cast(null as UInt8) as integer8, + cast(null as Int16) as integer16, + cast(null as Int32) as integer32, + cast(null as Int64) as integer64, + cast(null as cds.Double) as double, + cast(null as cds.Decimal) as float, + cast(null as cds.Decimal(5, 4)) as decimal, + cast(null as String) as string, + cast(null as String(1)) as char, + cast(null as String(10)) as short, + cast(null as String(100)) as medium, + cast(null as String(5000)) as large, + cast(null as LargeString) as blob, + cast(null as Date) as date, + cast(null as Time) as time, + cast(null as DateTime) as dateTime, + timestamp, + cast(null as LargeString) as stringArray, + cast(null as LargeString) as integerArray, + cast(null as Binary) as binary, + cast(null as LargeBinary) as largebinary, + // NULL as vector : Vector, + } + union all + select from literals.array { + cast(null as Boolean) as bool, + cast(null as UUID) as uuid, + cast(null as UInt8) as integer8, + cast(null as Int16) as integer16, + cast(null as Int32) as integer32, + cast(null as Int64) as integer64, + cast(null as cds.Double) as double, + cast(null as cds.Decimal) as float, + cast(null as cds.Decimal(5, 4)) as decimal, + cast(null as String) as string, + cast(null as String(1)) as char, + cast(null as String(10)) as short, + cast(null as String(100)) as medium, + cast(null as String(5000)) as large, + cast(null as LargeString) as blob, + cast(null as Date) as date, + cast(null as Time) as time, + cast(null as DateTime) as dateTime, + cast(null as Timestamp) as timestamp, + string as stringArray, + integer as integerArray, + cast(null as Binary) as binary, + cast(null as LargeBinary) as largebinary, + // NULL as vector : Vector, + } + union all + select from literals.binaries { + cast(null as Boolean) as bool, + cast(null as UUID) as uuid, + cast(null as UInt8) as integer8, + cast(null as Int16) as integer16, + cast(null as Int32) as integer32, + cast(null as Int64) as integer64, + cast(null as cds.Double) as double, + cast(null as cds.Decimal) as float, + cast(null as cds.Decimal(5, 4)) as decimal, + cast(null as String) as string, + cast(null as String(1)) as char, + cast(null as String(10)) as short, + cast(null as String(100)) as medium, + cast(null as String(5000)) as large, + cast(null as LargeString) as blob, + cast(null as Date) as date, + cast(null as Time) as time, + cast(null as DateTime) as dateTime, + cast(null as Timestamp) as timestamp, + cast(null as LargeString) as stringArray, + cast(null as LargeString) as integerArray, + binary, + largebinary, + // NULL as vector : Vector, + }; diff --git a/test/compliance/strictMode.test.js b/test/compliance/strictMode.test.js index 4c077ecc5..982da32ea 100644 --- a/test/compliance/strictMode.test.js +++ b/test/compliance/strictMode.test.js @@ -6,7 +6,7 @@ describe('strict mode', () => { process.env.cds_features_db__strict = true }) - cds.test(__dirname + '/resources') + const {expect} = cds.test(__dirname + '/resources') afterAll(() => { process.env.cds_features_db__strict = undefined @@ -19,8 +19,8 @@ describe('strict mode', () => { } catch (e) { error = e } - if (expectedMessage === 'notExisting') expect(error.message.toLowerCase()).toContain('notexisting') - else expect(error.message).toEqual(expectedMessage) + if (expectedMessage === 'notExisting') expect(error.message.toLowerCase()).to.contain('notexisting') + else expect(error.message).to.equal(expectedMessage) } describe('UPDATE Scenarios', () => { test('Update with multiple errors', async () => { diff --git a/test/compliance/timestamps.test.js b/test/compliance/timestamps.test.js index 49857c49c..47e733844 100644 --- a/test/compliance/timestamps.test.js +++ b/test/compliance/timestamps.test.js @@ -1,25 +1,25 @@ const cds = require('../cds.js') describe('datetime handling', () => { - cds.test(__dirname + '/resources') + const { expect } = cds.test(__dirname + '/resources') test('datetime elements as key', async () => { let res - const payload = { dt: '2020-12-31T01:02:03Z', int: 4711} - res = await cds.db.run(INSERT.into('DateTimeEntity').entries(payload)) - res = await cds.db.run(SELECT.one.from('DateTimeEntity').where({dt: new Date(payload .dt).toISOString() })) - expect(res).toMatchObject(payload) - res = await cds.db.run(SELECT.one.from('DateTimeEntity').where({dt: new Date(payload .dt).toISOString() })) - expect(res).toMatchObject(payload) - res = await cds.db.run(SELECT.one.from('DateTimeEntity').where({dt: payload.dt})) - expect(res).toMatchObject(payload) + const payload = { dt: '2020-12-31T01:02:03Z', int: 4711 } + res = await INSERT.into('DateTimeEntity').entries(payload) + res = await SELECT.one.from('DateTimeEntity').where({ dt: new Date(payload.dt).toISOString() }) + expect(res).to.containSubset(payload) + res = await SELECT.one.from('DateTimeEntity').where({ dt: new Date(payload.dt) }) + expect(res).to.containSubset(payload) + res = await SELECT.one.from('DateTimeEntity').where({ dt: payload.dt }) + expect(res).to.containSubset(payload) }) - test('$now in view', async () => { - const req_timestamp = new Date().toISOString() - await cds.db.run(INSERT.into('TimestampEntity').entries({ ID: 1, ts: req_timestamp })) - const { now } = await cds.db.run(SELECT.one.from('TimestampView')) - expect(now.match(/\.(\d\d\d)Z/)[1].match(/000/)).toBeNull() //> check that we get ms precision - const diff = Math.abs(new Date(req_timestamp).getTime() - new Date(now).getTime()) - expect(diff).toBeLessThan(1000) //> check that we get the same timezone offset - }) + // REVISIT: The test was not actually testing anything valid and the compiler doesn't do what we would expect + test.skip('$now in view', async () => cds.tx(async tx => { + const req_timestamp = new Date() + await INSERT.into('TimestampEntity').entries({ ID: 1, ts: req_timestamp }) + const { now } = await SELECT.one.from('TimestampView') + expect(now.match(/\.(\d\d\d)Z/)[1].match(/000/)).to.be.null //> check that we get ms precision + expect(new Date(now) | 0).to.be.eq(tx.context.timestamp | 0) + })) }) diff --git a/test/find b/test/find new file mode 100755 index 000000000..150882fb1 --- /dev/null +++ b/test/find @@ -0,0 +1,2 @@ +# /bin/bash +find -L test -type f -regex .*$npm_config_file$npm_config_test\\.test\\.js diff --git a/test/scenarios/sflight/integration.test.js b/test/scenarios/sflight/integration.test.js index a54eec5ea..c2b67335d 100644 --- a/test/scenarios/sflight/integration.test.js +++ b/test/scenarios/sflight/integration.test.js @@ -2,6 +2,11 @@ const childProcess = require('child_process') const path = require('path') const cds = require('../../cds') +// REVISIT: @capire/sflight tests don't expect IEEE754 compliant responses, but do send the IEEE754 header according to the test +// As it is not possible to configure the IEEE754 flag for @cap-js/hana it is required to inject this configurable:true +// As the cqn2sql output converter injection doesn't allow for it to be overwritten at a later stage, but configurable will stay true +Object.defineProperty(cds.builtin.types.Decimal.constructor.prototype, "CQN2HANA:convertOutput", { get: () => a => a, configurable: true }) + const sflightPath = require.resolve('@capire/sflight/package.json').slice(0, -13) // IMPORTANT: Wrapping that in beforeAll to avoid loading cds.env before cds.test() @@ -17,9 +22,13 @@ describe('Integration', () => { describe('Jest', () => { require(path.resolve(sflightPath, 'test/odata.test.js')) + + beforeAll(() => { + Object.defineProperty(cds.builtin.types.Decimal.constructor.prototype, "CQN2HANA:convertOutput", { value: a => a }) + }) }) - xdescribe.each(dirs)('%s', dir => { + describe.skip.each(dirs)('%s', dir => { // Install all dev dependencies for the UI5 apps beforeAll(() => npm(`app/${dir}/`, ['ci']), 60 * 1000) diff --git a/sqlite/test/lean-draft.test.js b/test/scenarios/sflight/lean-draft.test.js similarity index 99% rename from sqlite/test/lean-draft.test.js rename to test/scenarios/sflight/lean-draft.test.js index 3ed01a5c5..2dfbbceee 100644 --- a/sqlite/test/lean-draft.test.js +++ b/test/scenarios/sflight/lean-draft.test.js @@ -1,9 +1,14 @@ const NEW_DRAFT_TRAVELUUID = '11111111111111111111111111111111' const EDIT_DRAFT_TRAVELUUID = '71657221A8E4645C17002DF03754AB66' -const cds = require('../../test/cds.js') +const cds = require('../../cds.js') describe('draft tests', () => { + // Had to be moved before for cds-test might break jest + beforeAll(() => { + cds.env.features.ieee754compatible = true + }) + const { GET, POST, PATCH, DELETE, expect } = cds.test('@capire/sflight') // NOTE: all access to cds.env has to go after the call to cds.test() or cds.test.in() // (see https://cap.cloud.sap/docs/node.js/cds-test#cds-test-env-check) @@ -238,7 +243,7 @@ describe('draft tests', () => { expect(res.data.value.length).to.be.eq(1) expect(res.data.value[0]).to.containSubset({ BeginDate: '2023-08-04', - BookingFee: v => /^90/.test(v), + BookingFee: '90.000', CurrencyCode_code: 'USD', Description: 'Vacation to USA', EndDate: '2024-05-31', @@ -350,11 +355,11 @@ describe('draft tests', () => { expect(res.data.value.length).to.be.eq(1) expect(res.data.value[0]).to.containSubset({ BeginDate: '2023-08-04', - BookingFee: v => /^90/.test(v), + BookingFee: '90.000', CurrencyCode_code: 'USD', Description: 'Vacation to USA', EndDate: '2024-05-31', - TotalPrice: v => /^5624/.test(v), + TotalPrice: '5624.000', TravelID: 32, TravelStatus_code: 'O', TravelUUID: EDIT_DRAFT_TRAVELUUID, @@ -435,11 +440,11 @@ describe('draft tests', () => { expect(res.status).to.be.eq(200) expect(res.data.value[0]).to.containSubset({ BeginDate: '2024-05-30', - BookingFee: v => /^20/.test(v), + BookingFee: '20.000', CurrencyCode_code: 'USD', Description: 'Sightseeing in New York City, New York', EndDate: '2024-05-30', - TotalPrice: v => /^7375/.test(v), + TotalPrice: '7375.000', TravelID: 4133, TravelStatus_code: 'A', TravelUUID: '76757221A8E4645C17002DF03754AB66', @@ -498,11 +503,11 @@ describe('draft tests', () => { expect(res.status).to.be.eq(200) expect(res.data).to.containSubset({ BeginDate: '2023-08-04', - BookingFee: v => /^20/.test(v), + BookingFee: '20.000', CurrencyCode_code: 'USD', Description: 'Business Trip for Christine, Pierre', EndDate: '2023-08-04', - TotalPrice: v => /900/.test(v), + TotalPrice: '900.000', TravelID: 1, TravelStatus_code: 'O', TravelUUID: '52657221A8E4645C17002DF03754AB66', @@ -557,7 +562,7 @@ describe('draft tests', () => { BookSupplUUID: '85D87221A8E4645C17002DF03754AB66', BookingSupplementID: 1, CurrencyCode_code: 'EUR', - Price: v => /20/.test(v), + Price: '20.000', to_Supplement_SupplementID: 'ML-0023', to_Supplement: { Description: 'Trout Meuniere', SupplementID: 'ML-0023' }, to_Travel: { TravelStatus: { code: 'A', fieldControl: 1 }, TravelUUID: '76757221A8E4645C17002DF03754AB66' }, @@ -579,7 +584,7 @@ describe('draft tests', () => { ConnectionID: '0018', CurrencyCode_code: 'USD', FlightDate: '2024-05-30', - FlightPrice: v => /^3657/.test(v), + FlightPrice: '3657.000', to_Carrier_AirlineID: 'GA', to_Customer_CustomerID: '000115', BookingStatus: { code: 'N', name: 'New' }, @@ -791,7 +796,7 @@ describe('draft tests', () => { { auth: { username: 'user1', password: 'user1' } }, ) expect(res.data).to.containSubset({ - BookingFee: v => /^12/.test(v), + BookingFee: '12.000', TravelUUID, IsActiveEntity: false, }) @@ -817,7 +822,7 @@ describe('draft tests', () => { TravelID: 0, BeginDate: '2032-10-22', EndDate: '2032-12-22', - BookingFee: v => /^12/.test(v), + BookingFee: '12.000', TotalPrice: null, CurrencyCode_code: null, Description: null, @@ -840,7 +845,7 @@ describe('draft tests', () => { ) expect(res.data).to.containSubset({ BeginDate: '2032-10-22', - BookingFee: v => /^12/.test(v), + BookingFee: '12.000', CurrencyCode_code: null, Description: null, EndDate: '2032-12-22', diff --git a/test/scenarios/sflight/read.test.js b/test/scenarios/sflight/read.test.js index 893e62740..6c9bdccf2 100644 --- a/test/scenarios/sflight/read.test.js +++ b/test/scenarios/sflight/read.test.js @@ -1,11 +1,6 @@ process.env.cds_requires_db_kind = 'better-sqlite' const cds = require('../../cds.js') -// REVISIT: @capire/sflight tests don't expect IEEE754 compliance responses, but do send the IEE754 header according to the test -// As it is not possible to configure the IEEE754 flag for @cap-js/hana it is required to inject this configurable:true -// As the cqn2sql output converter injection doesn't allow for it to be overwritten at a later stage, but configurable will stay true -Object.defineProperty(cds.builtin.types.Decimal.constructor.prototype, "CQN2HANA:convertOutput", { value: a => a, configurable: true }) - // IMPORTANT: Wrapping that in beforeAll to avoid loading cds.env before cds.test() beforeAll(() => { if (cds.env.fiori) cds.env.fiori.lean_draft = true @@ -17,10 +12,6 @@ describe('SFlight - Read', () => { const { expect, GET, axios } = cds.test('@capire/sflight') axios.defaults.auth = { username: 'alice', password: 'admin' } - beforeAll(async () => { - Object.defineProperty(cds.builtin.types.Decimal.constructor.prototype, "CQN2HANA:convertOutput", { value: a => a }) - }) - const processorPaths = [ // 'Travel?$count=true&$orderby=TravelID desc&$filter=(IsActiveEntity eq false or SiblingEntity/IsActiveEntity eq null)&$expand=DraftAdministrativeData,TravelStatus,to_Agency,to_Customer&$skip=0&$top=30', 'Travel', From fa50efc7bc2deeae21cf694790a9335acc92e8a2 Mon Sep 17 00:00:00 2001 From: Bob den Os Date: Thu, 10 Oct 2024 06:41:42 +0200 Subject: [PATCH 2/4] Fix test errors for newer tests and older node version --- db-service/test/deep/deep.test.js | 6 +++--- test/compliance/SELECT.test.js | 8 ++++---- test/compliance/UPDATE.test.js | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/db-service/test/deep/deep.test.js b/db-service/test/deep/deep.test.js index cb8af89ca..0d89b26f9 100644 --- a/db-service/test/deep/deep.test.js +++ b/db-service/test/deep/deep.test.js @@ -803,8 +803,8 @@ describe('test deep query generation', () => { expect(insertsArray).to.deep.contain(insert) }) - expect(updatesArray.length).toBe(0) - expect(deletesArray.length).toBe(0) + expect(updatesArray.length).to.eq(0) + expect(deletesArray.length).to.eq(0) }) @@ -832,7 +832,7 @@ describe('test deep query generation', () => { const insert = INSERT.into(entity).entries(entry) const result = await cds.db.run(insert) - expect(result > 0).toBe(true) + expect(result > 0).to.eq(true) const root = { uniqueName: entry.uniqueName, realm: entry.realm } diff --git a/test/compliance/SELECT.test.js b/test/compliance/SELECT.test.js index e9279ceae..56f27007f 100644 --- a/test/compliance/SELECT.test.js +++ b/test/compliance/SELECT.test.js @@ -539,7 +539,7 @@ describe('SELECT', () => { const cqn = CQL`SELECT string FROM ${string} ORDER BY string` const res = await cds.run(cqn) assert.strictEqual(res.length, 3, 'Ensure that all rows are coming back') - const sorted = res.toSorted((a, b) => String.prototype.localeCompare.call(a.string, b.string)) + const sorted = [...res].sort((a, b) => String.prototype.localeCompare.call(a.string, b.string)) assert.deepEqual(res, sorted, 'Ensure that all rows are in the correct order') }) @@ -548,7 +548,7 @@ describe('SELECT', () => { const cqn = CQL`SELECT string FROM ${string} ORDER BY string asc` const res = await cds.run(cqn) assert.strictEqual(res.length, 3, 'Ensure that all rows are coming back') - const sorted = res.toSorted((a, b) => String.prototype.localeCompare.call(a.string, b.string)) + const sorted = [...res].sort((a, b) => String.prototype.localeCompare.call(a.string, b.string)) assert.deepEqual(res, sorted, 'Ensure that all rows are in the correct order') }) @@ -557,7 +557,7 @@ describe('SELECT', () => { const cqn = CQL`SELECT string FROM ${string} ORDER BY string desc` const res = await cds.run(cqn) assert.strictEqual(res.length, 3, 'Ensure that all rows are coming back') - const sorted = res.toSorted((a, b) => String.prototype.localeCompare.call(b.string, a.string)) + const sorted = [...res].sort((a, b) => String.prototype.localeCompare.call(b.string, a.string)) assert.deepEqual(res, sorted, 'Ensure that all rows are in the correct order') }) @@ -567,7 +567,7 @@ describe('SELECT', () => { cqn.SELECT.localized = true const res = await cds.run(cqn) assert.strictEqual(res.length, 3, 'Ensure that all rows are coming back') - const sorted = res.toSorted((a, b) => String.prototype.localeCompare.call(a.string, b.string)) + const sorted = [...res].sort((a, b) => String.prototype.localeCompare.call(a.string, b.string)) assert.deepEqual(res, sorted, 'Ensure that all rows are in the correct order') }) }) diff --git a/test/compliance/UPDATE.test.js b/test/compliance/UPDATE.test.js index ec7e20039..a5a9c4df4 100644 --- a/test/compliance/UPDATE.test.js +++ b/test/compliance/UPDATE.test.js @@ -112,7 +112,7 @@ describe('UPDATE', () => { } await DELETE.from(BooksUnique).where(`ID=${1}`) - await expect(INSERT(data).into(BooksUnique)).rejects.to.be.truthy + await expect(INSERT(data).into(BooksUnique)).rejected // Update the numbers to be non conflicting data.pages[0].number = 1 From d1b9cf3c5fb2a776c76dd72e209bbd7c93cb2541 Mon Sep 17 00:00:00 2001 From: Bob den Os Date: Thu, 10 Oct 2024 08:53:14 +0200 Subject: [PATCH 3/4] Add conflicted env in release-please.yml --- .github/workflows/release-please.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml index 8eeec2bd4..fbbb2b3e9 100644 --- a/.github/workflows/release-please.yml +++ b/.github/workflows/release-please.yml @@ -36,6 +36,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: npm test -w hana if: ${{ steps.release.outputs.releases_created }} + env: TAG: ${{ steps.hxe.outputs.TAG }} IMAGE_ID: ${{ steps.hxe.outputs.IMAGE_ID }} From dff6ee04f9e23879f93f9980da91e321b6dbdb96 Mon Sep 17 00:00:00 2001 From: Bob den Os Date: Thu, 10 Oct 2024 12:23:50 +0200 Subject: [PATCH 4/4] Remove target check before cqn4sql --- db-service/lib/SQLService.js | 1 - 1 file changed, 1 deletion(-) diff --git a/db-service/lib/SQLService.js b/db-service/lib/SQLService.js index c96d21749..ecf4f7d02 100644 --- a/db-service/lib/SQLService.js +++ b/db-service/lib/SQLService.js @@ -367,7 +367,6 @@ class SQLService extends DatabaseService { */ cqn4sql(q) { if ( - (!q.target || q.target._unresolved) && !cds.env.features.db_strict && !q.SELECT?.from?.join && !q.SELECT?.from?.SELECT &&