diff --git a/.github/dependabot.yml b/.github/dependabot.yml index ecfa1fc00..7e31559e5 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -57,19 +57,10 @@ updates: applies-to: version-updates patterns: - "*apollo-server*" - babel: - applies-to: version-updates - patterns: - - "@babel*" metascraper: applies-to: version-updates patterns: - "metascraper*" - typescript: - applies-to: version-updates - patterns: - - "ts*" - - "*types?" # webapp - package-ecosystem: docker diff --git a/.github/workflows/test-e2e.yml b/.github/workflows/test-e2e.yml index 8e3570d95..d232e2909 100644 --- a/.github/workflows/test-e2e.yml +++ b/.github/workflows/test-e2e.yml @@ -3,8 +3,62 @@ name: ocelot.social end-to-end test CI on: push jobs: - docker_preparation: - name: Fullstack test preparation + prepare_neo4j_image: + name: Fullstack | prepare neo4j image + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.1.7 + + - name: Build docker image + run: | + docker build --target community -t "ocelotsocialnetwork/neo4j-community:test" neo4j/ + docker save "ocelotsocialnetwork/neo4j-community:test" > /tmp/neo4j.tar + + - name: Cache docker image + uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.0.2 + with: + path: /tmp/neo4j.tar + key: ${{ github.run_id }}-e2e-neo4j-cache + + prepare_backend_image: + name: Fullstack | prepare backend image + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.1.7 + + - name: Build docker image + run: | + docker build --target test -t "ocelotsocialnetwork/backend:test" backend/ + docker save "ocelotsocialnetwork/backend:test" > /tmp/backend.tar + + - name: Cache docker image + uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.0.2 + with: + path: /tmp/backend.tar + key: ${{ github.run_id }}-e2e-backend-cache + + prepare_webapp_image: + name: Fullstack | prepare webapp image + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.1.7 + + - name: Build docker image + run: | + docker build --target test -t "ocelotsocialnetwork/webapp:test" webapp/ + docker save "ocelotsocialnetwork/webapp:test" > /tmp/webapp.tar + + - name: Cache docker image + uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.0.2 + with: + path: /tmp/webapp.tar + key: ${{ github.run_id }}-e2e-webapp-cache + + prepare_cypress: + name: Fullstack | prepare cypress runs-on: ubuntu-latest steps: - name: Checkout code @@ -13,18 +67,7 @@ jobs: - name: Copy env files run: | cp webapp/.env.template webapp/.env - cp frontend/.env.dist frontend/.env - cp backend/.env.template backend/.env - - - name: Build docker images - run: | - mkdir /tmp/images - docker build --target community -t "ocelotsocialnetwork/neo4j-community:test" neo4j/ - docker save "ocelotsocialnetwork/neo4j-community:test" > /tmp/images/neo4j.tar - docker build --target test -t "ocelotsocialnetwork/backend:test" backend/ - docker save "ocelotsocialnetwork/backend:test" > /tmp/images/backend.tar - docker build --target test -t "ocelotsocialnetwork/webapp:test" webapp/ - docker save "ocelotsocialnetwork/webapp:test" > /tmp/images/webapp.tar + cp backend/.env.test_e2e backend/.env - name: Install cypress requirements run: | @@ -35,21 +78,20 @@ jobs: cd .. yarn install - - name: Cache docker images - id: cache + - name: Cache docker image + uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.0.2 with: path: | /opt/cucumber-json-formatter /home/runner/.cache/Cypress /home/runner/work/Ocelot-Social/Ocelot-Social - /tmp/images/ - key: ${{ github.run_id }}-e2e-preparation-cache + key: ${{ github.run_id }}-e2e-cypress fullstack_tests: - name: Fullstack tests + name: Fullstack | tests if: success() - needs: docker_preparation + needs: [prepare_neo4j_image, prepare_backend_image, prepare_webapp_image, prepare_cypress] runs-on: ubuntu-latest env: jobs: 8 @@ -58,26 +100,42 @@ jobs: # run copies of the current job in parallel job: [1, 2, 3, 4, 5, 6, 7, 8] steps: - - name: Restore cache + - name: Restore cypress cache uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.0.2 - id: cache with: path: | /opt/cucumber-json-formatter /home/runner/.cache/Cypress /home/runner/work/Ocelot-Social/Ocelot-Social - /tmp/images/ - key: ${{ github.run_id }}-e2e-preparation-cache - fail-on-cache-miss: true + key: ${{ github.run_id }}-e2e-cypress + restore-keys: ${{ github.run_id }}-e2e-cypress + - name: Restore neo4j cache + uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.0.2 + with: + path: /tmp/neo4j.tar + key: ${{ github.run_id }}-e2e-neo4j-cache + + - name: Restore backend cache + uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.0.2 + with: + path: /tmp/backend.tar + key: ${{ github.run_id }}-e2e-backend-cache + + - name: Restore webapp cache + uses: actions/cache@5a3ec84eff668545956fd18022155c47e93e2684 # v4.0.2 + with: + path: /tmp/webapp.tar + key: ${{ github.run_id }}-e2e-webapp-cache + - name: Boot up test system | docker compose run: | chmod +x /opt/cucumber-json-formatter sudo ln -fs /opt/cucumber-json-formatter /usr/bin/cucumber-json-formatter - docker load < /tmp/images/neo4j.tar - docker load < /tmp/images/backend.tar - docker load < /tmp/images/webapp.tar - docker compose -f docker-compose.yml -f docker-compose.test.yml up --detach --no-deps webapp neo4j backend --build + docker load < /tmp/neo4j.tar + docker load < /tmp/backend.tar + docker load < /tmp/webapp.tar + docker compose -f docker-compose.yml -f docker-compose.test.yml up --build --detach --no-deps webapp neo4j backend mailserver sleep 90s - name: Full stack tests | run tests @@ -98,17 +156,24 @@ jobs: name: ocelot-e2e-test-report-pr${{ needs.docker_preparation.outputs.pr-number }} path: /home/runner/work/Ocelot-Social/Ocelot-Social/cypress/reports/cucumber_html_report - cleanup: - name: Cleanup - needs: [docker_preparation, fullstack_tests] + cleanup_cache: + name: Cleanup Cache + needs: fullstack_tests runs-on: ubuntu-latest - permissions: write-all continue-on-error: true steps: - - name: Delete cache - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Checkout code + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.1.7 + + - name: Full stack tests | cleanup cache run: | - gh extension install actions/gh-actions-cache - KEY="${{ github.run_id }}-e2e-preparation-cache" - gh actions-cache delete $KEY -R Ocelot-Social-Community/Ocelot-Social --confirm \ No newline at end of file + cacheKeys=$(gh cache list --json key --jq '.[] | select(.key | startswith("${{ github.run_id }}-e2e-")) | .key') + set +e + echo "Deleting caches..." + for cacheKey in $cacheKeys + do + gh cache delete "$cacheKey" + done + echo "Done" + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/.github/workflows/test.lint_pr.yml b/.github/workflows/test.lint_pr.yml index ab9d4b970..dd25d5cbc 100644 --- a/.github/workflows/test.lint_pr.yml +++ b/.github/workflows/test.lint_pr.yml @@ -17,7 +17,7 @@ jobs: runs-on: ubuntu-latest if: ${{ github.actor != 'dependabot[bot]' }} steps: - - uses: amannn/action-semantic-pull-request@04501d43b574e4c1d23c629ffe4dcec27acfdeff # v5.5.3 + - uses: amannn/action-semantic-pull-request@335288255954904a41ddda8947c8f2c844b8bfeb # v5.5.3 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: diff --git a/CHANGELOG.md b/CHANGELOG.md index e3b1a955a..332b24ceb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,8 +4,63 @@ All notable changes to this project will be documented in this file. Dates are d Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog). +#### [3.5.3](https://github.com/Ocelot-Social-Community/Ocelot-Social/compare/3.5.2...3.5.3) + +- fix(backend): correct email from [`#8501`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8501) +- refactor(backend): types for global config [`#8485`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8485) +- fix warning in workflow for lower case as [`#8494`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8494) + +#### [3.5.2](https://github.com/Ocelot-Social-Community/Ocelot-Social/compare/3.5.1...3.5.2) + +> 6 May 2025 + +- v3.5.2 [`#8498`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8498) +- fix emails2 [`#8497`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8497) + +#### [3.5.1](https://github.com/Ocelot-Social-Community/Ocelot-Social/compare/3.5.0...3.5.1) + +> 6 May 2025 + +- v3.5.1 [`#8496`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8496) +- fix emails in production [`#8493`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8493) + +#### [3.5.0](https://github.com/Ocelot-Social-Community/Ocelot-Social/compare/3.4.0...3.5.0) + +> 6 May 2025 + +- v3.5.0 [`#8492`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8492) +- feat(webapp): user teaser popover [`#8450`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8450) +- feat(backend): signup email localized [`#8459`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8459) +- lint json [`#8472`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8472) +- refactor(other): cypress: simplify cucumber preprocessor imports and some linting [`#8489`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8489) +- fix backend node23 [`#8488`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8488) +- refactor(workflow): parallelize e2e preparation [`#8481`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8481) +- refactor(backend): types for context + `slug` [`#8486`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8486) +- feat(backend): emails for notifications [`#8435`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8435) +- remove some dependabot groups & no alpine version to allow update [`#8475`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8475) +- build(deps-dev): bump the babel group with 3 updates [`#8478`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8478) +- build(deps-dev): bump @types/node from 22.15.2 to 22.15.3 in /backend [`#8479`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8479) +- refactor(backend): refactor context [`#8434`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8434) +- build(deps): bump amannn/action-semantic-pull-request [`#8480`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8480) +- build(deps-dev): bump eslint-plugin-prettier in /webapp [`#8332`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8332) +- remove all helpers on src/helpers [`#8469`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8469) +- move models into database folder [`#8471`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8471) +- also lint cjs files [`#8467`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8467) +- refactor(backend): refactor badges [`#8465`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8465) +- refactor(backend): move resolvers into graphql folder [`#8470`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8470) +- refactor(webapp): remove unused packages [`#8468`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8468) +- refactor(backend): remove unused packages [`#8466`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8466) +- fix(backend): fix backend dev and dev:debug command [`#8439`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8439) +- move distanceToMe onto Location [`#8464`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8464) +- feat(backend): distanceToMe [`#8462`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8462) +- fix(webapp): fixed padding for mobile in basic layout [`#8455`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8455) +- Fix ocelot.social link for imprint and donation [`#8461`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8461) + #### [3.4.0](https://github.com/Ocelot-Social-Community/Ocelot-Social/compare/3.3.0...3.4.0) +> 28 April 2025 + +- v3.4.0 [`#8454`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8454) - fix(webapp): fix badge focus [`#8452`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8452) - feat(backend): branding middlewares [`#8429`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8429) - refactor(webapp): make login, registration, password-reset layout brandable [`#8440`](https://github.com/Ocelot-Social-Community/Ocelot-Social/pull/8440) @@ -1383,15 +1438,31 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog). - updated CHANGELOG.md [`9d9075f`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/9d9075f2117b2eb4b607e7d59ab18c7e655c6ea7) -#### [0.6.4](https://github.com/Ocelot-Social-Community/Ocelot-Social/compare/0.6.3...0.6.4) +#### [0.6.4](https://github.com/Ocelot-Social-Community/Ocelot-Social/compare/v0.6.4...0.6.4) > 8 February 2021 -- regenerated `CHANGELOG.md` [`ee688ec`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/ee688ece24cf592b3989e83340701ca8772e876e) -- fetch full history [`5ecee4d`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/5ecee4d73a92d2e5c5ae971d79848ed27f65a72c) -- don't fail if tag exists (release) [`39c82fc`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/39c82fcb37d5c8e7e78a79288e1ef6280f8d0892) +- - adjusted changelog to ocelot-social repo [`9603882`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/9603882edebf8967e05abfa94e4e1ebf452d4e24) +- - first steps towards docker image deployment & github autotagging [`5503216`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/5503216ad4a0230ac533042e4a69806590fc2a5a) +- - deploy structure image [`a60400b`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/a60400b4fe6f59bbb80e1073db4def3ba205e1a7) -#### [0.6.3](https://github.com/Ocelot-Social-Community/Ocelot-Social/compare/0.6.0...0.6.3) +#### [v0.6.4](https://github.com/Ocelot-Social-Community/Ocelot-Social/compare/0.6.3...v0.6.4) + +> 9 February 2021 + +- chore(release): 0.6.4 [`8b7570d`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/8b7570dc35d0ea431f673a711ac051f1e1320acb) +- change user roles is working, test fails [`8c3310a`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/8c3310abaf87c0e5597fec4f93fb37d27122c9e7) +- change user role: tests are working [`f10da4b`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/f10da4b09388fe1e2b85abd53f6ffc67c785d4c1) + +#### [0.6.3](https://github.com/Ocelot-Social-Community/Ocelot-Social/compare/v0.6.3...0.6.3) + +> 8 February 2021 + +- - adjusted changelog to ocelot-social repo [`9603882`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/9603882edebf8967e05abfa94e4e1ebf452d4e24) +- - fixed changelog [`cf70b12`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/cf70b12ed74011924ea788ab932fc9d7ac0e6bd9) +- - yarn install to allow yarn auto-changelog [`fc496aa`](https://github.com/Ocelot-Social-Community/Ocelot-Social/commit/fc496aa04cb7e804da4335da0cb5cda26f874ea2) + +#### [v0.6.3](https://github.com/Ocelot-Social-Community/Ocelot-Social/compare/0.6.0...v0.6.3) > 8 February 2021 diff --git a/backend/.codecov.yml b/backend/.codecov.yml deleted file mode 100644 index 97bec0084..000000000 --- a/backend/.codecov.yml +++ /dev/null @@ -1,2 +0,0 @@ -coverage: - range: "60...100" diff --git a/backend/.env.test_e2e b/backend/.env.test_e2e new file mode 100644 index 000000000..e21ce3057 --- /dev/null +++ b/backend/.env.test_e2e @@ -0,0 +1,41 @@ +DEBUG=true + +NEO4J_URI=bolt://localhost:7687 +NEO4J_USERNAME=neo4j +NEO4J_PASSWORD=letmein +GRAPHQL_URI=http://localhost:4000 +CLIENT_URI=http://localhost:3000 + +# E-Mail default settings +EMAIL_SUPPORT="devops@ocelot.social" +EMAIL_DEFAULT_SENDER="devops@ocelot.social" +SMTP_HOST=mailserver +SMTP_PORT=1025 +SMTP_IGNORE_TLS=true +SMTP_MAX_CONNECTIONS=5 +SMTP_MAX_MESSAGES=Infinity +SMTP_USERNAME= +SMTP_PASSWORD= +SMTP_SECURE="false" # true for 465, false for other ports +SMTP_DKIM_DOMAINNAME= +SMTP_DKIM_KEYSELECTOR= +SMTP_DKIM_PRIVATKEY= + +JWT_SECRET="b/&&7b78BF&fv/Vd" +JWT_EXPIRES="2y" +MAPBOX_TOKEN="pk.eyJ1IjoiYnVzZmFrdG9yIiwiYSI6ImNraDNiM3JxcDBhaWQydG1uczhpZWtpOW4ifQ.7TNRTO-o9aK1Y6MyW_Nd4g" + +PRIVATE_KEY_PASSPHRASE="a7dsf78sadg87ad87sfagsadg78" + +SENTRY_DSN_BACKEND= +COMMIT= +PUBLIC_REGISTRATION=false +INVITE_REGISTRATION=true + +AWS_ACCESS_KEY_ID= +AWS_SECRET_ACCESS_KEY= +AWS_ENDPOINT= +AWS_REGION= +AWS_BUCKET= + +CATEGORIES_ACTIVE=false diff --git a/backend/.eslintrc.cjs b/backend/.eslintrc.cjs index 1fe6b8779..9883fae83 100644 --- a/backend/.eslintrc.cjs +++ b/backend/.eslintrc.cjs @@ -133,7 +133,7 @@ module.exports = { 'error', { allowModules: ['apollo-server-testing', 'rosie', '@faker-js/faker', 'ts-jest'] }, ], // part of n/recommended - // 'n/no-unpublished-require': 'error', // part of n/recommended + 'n/no-unpublished-require': ['error', { allowModules: ['ts-jest', 'require-json5'] }], // part of n/recommended // 'n/no-unsupported-features/es-builtins': 'error', // part of n/recommended // 'n/no-unsupported-features/es-syntax': 'error', // part of n/recommended // 'n/no-unsupported-features/node-builtins': 'error', // part of n/recommended @@ -204,7 +204,6 @@ module.exports = { tsconfigRootDir: __dirname, project: ['./tsconfig.json'], // this is to properly reference the referenced project database without requirement of compiling it - // eslint-disable-next-line camelcase EXPERIMENTAL_useSourceOfProjectReferenceRedirect: true, }, }, @@ -224,5 +223,10 @@ module.exports = { 'jest/unbound-method': 'error', }, }, + { + extends: ['plugin:jsonc/recommended-with-jsonc'], + files: ['*.json', '*.json5', '*.jsonc'], + parser: 'jsonc-eslint-parser', + }, ], } diff --git a/backend/.graphqlconfig b/backend/.graphqlconfig deleted file mode 100644 index ca328bc83..000000000 --- a/backend/.graphqlconfig +++ /dev/null @@ -1,3 +0,0 @@ -{ - "schemaPath": "./src/schema.graphql" -} diff --git a/backend/.prettierrc.js b/backend/.prettierrc.cjs similarity index 100% rename from backend/.prettierrc.js rename to backend/.prettierrc.cjs diff --git a/backend/Dockerfile b/backend/Dockerfile index f89fa2d3d..2897fe2f6 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -1,4 +1,4 @@ -FROM node:20.12.1-alpine3.19 AS base +FROM node:23.11.0-alpine AS base LABEL org.label-schema.name="ocelot.social:backend" LABEL org.label-schema.description="Backend of the Social Network Software ocelot.social" LABEL org.label-schema.usage="https://github.com/Ocelot-Social-Community/Ocelot-Social/blob/master/README.md" @@ -10,7 +10,7 @@ LABEL maintainer="devops@ocelot.social" ENV NODE_ENV="production" ENV PORT="4000" EXPOSE ${PORT} -RUN apk --no-cache add git python3 make g++ bash +RUN apk --no-cache add git python3 make g++ bash linux-headers RUN mkdir -p /app WORKDIR /app CMD ["/bin/bash", "-c", "yarn run start"] diff --git a/backend/babel.config.json b/backend/babel.config.json deleted file mode 100644 index f36dbeadb..000000000 --- a/backend/babel.config.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "presets": [ - [ - "@babel/preset-env", - { - "targets": { - "node": "10" - } - } - ] - ], - "plugins": [ - "@babel/plugin-proposal-throw-expressions" - ] -} diff --git a/backend/jest.config.cjs b/backend/jest.config.cjs index 8d322ff08..3441db428 100644 --- a/backend/jest.config.cjs +++ b/backend/jest.config.cjs @@ -1,6 +1,7 @@ /* eslint-disable import/no-commonjs */ -const { pathsToModuleNameMapper } = require('ts-jest') const requireJSON5 = require('require-json5') +const { pathsToModuleNameMapper } = require('ts-jest') + const { compilerOptions } = requireJSON5('./tsconfig.json') module.exports = { diff --git a/backend/package.json b/backend/package.json index 6d12190ed..38bf966ac 100644 --- a/backend/package.json +++ b/backend/package.json @@ -1,6 +1,6 @@ { "name": "ocelot-social-backend", - "version": "3.4.0", + "version": "3.5.3", "description": "GraphQL Backend for ocelot.social", "repository": "https://github.com/Ocelot-Social-Community/Ocelot-Social", "author": "ocelot.social Community", @@ -10,9 +10,9 @@ "scripts": { "start": "node build/src/", "build": "tsc && tsc-alias && ./scripts/build.copy.files.sh", - "dev": "nodemon --exec ts-node --require tsconfig-paths/register src/ -e js,ts,gql", - "dev:debug": "nodemon --exec babel-node --inspect=0.0.0.0:9229 src/ -e js,ts,gql", - "lint": "eslint --max-warnings=0 --report-unused-disable-directives --ext .js,.ts .", + "dev": "nodemon --exec ts-node --require tsconfig-paths/register src/index.ts -e js,ts,gql", + "dev:debug": "nodemon --exec node --inspect=0.0.0.0:9229 build/src/index.js -e js,ts,gql", + "lint": "eslint --max-warnings=0 --report-unused-disable-directives --ext .js,.ts,.cjs,.json,.json5,.jsonc .", "test": "cross-env NODE_ENV=test NODE_OPTIONS=--max-old-space-size=8192 jest --runInBand --coverage --forceExit --detectOpenHandles", "db:reset": "ts-node --require tsconfig-paths/register src/db/reset.ts", "db:reset:withmigrations": "ts-node --require tsconfig-paths/register src/db/reset-with-migrations.ts", @@ -27,30 +27,16 @@ "prod:db:data:branding": "node build/src/db/data-branding.js" }, "dependencies": { - "@babel/cli": "~7.27.0", - "@babel/core": "^7.26.10", - "@babel/node": "~7.26.0", - "@babel/plugin-proposal-throw-expressions": "^7.25.9", - "@babel/preset-env": "~7.26.9", - "@babel/register": "^7.23.7", "@sentry/node": "^5.15.4", - "apollo-cache-inmemory": "~1.6.6", - "apollo-client": "~2.6.10", - "apollo-link-context": "~1.0.20", - "apollo-link-http": "~1.5.17", "apollo-server": "~2.14.2", "apollo-server-express": "^2.14.2", "aws-sdk": "^2.1692.0", - "babel-core": "~7.0.0-0", - "babel-eslint": "~10.1.0", - "babel-jest": "~29.7.0", - "babel-plugin-transform-runtime": "^6.23.0", "bcryptjs": "~3.0.2", "body-parser": "^1.20.3", "cheerio": "~1.0.0", - "cors": "~2.8.5", "cross-env": "~7.0.3", "dotenv": "~16.5.0", + "email-templates": "^12.0.2", "express": "^5.1.0", "graphql": "^14.6.0", "graphql-middleware": "~4.0.2", @@ -92,10 +78,11 @@ "node-fetch": "^2.7.0", "nodemailer": "^6.10.1", "nodemailer-html-to-text": "^3.2.0", + "preview-email": "^3.1.0", + "pug": "^3.0.3", "request": "~2.88.2", "sanitize-html": "~2.16.0", "slug": "~9.1.0", - "subscriptions-transport-ws": "^0.9.19", "trunc-html": "~1.1.2", "uuid": "~9.0.1", "validator": "^13.15.0", @@ -104,9 +91,11 @@ "devDependencies": { "@eslint-community/eslint-plugin-eslint-comments": "^4.5.0", "@faker-js/faker": "9.7.0", + "@types/email-templates": "^10.0.4", "@types/jest": "^29.5.14", "@types/lodash": "^4.17.16", - "@types/node": "^22.15.2", + "@types/node": "^22.15.3", + "@types/slug": "^5.0.9", "@types/uuid": "~9.0.1", "@typescript-eslint/eslint-plugin": "^5.62.0", "@typescript-eslint/parser": "^5.62.0", @@ -117,6 +106,7 @@ "eslint-import-resolver-typescript": "^4.3.4", "eslint-plugin-import": "^2.31.0", "eslint-plugin-jest": "^28.11.0", + "eslint-plugin-jsonc": "^2.20.0", "eslint-plugin-n": "^17.17.0", "eslint-plugin-no-catch-all": "^1.1.0", "eslint-plugin-prettier": "^5.2.6", @@ -135,7 +125,10 @@ }, "resolutions": { "**/**/fs-capacitor": "^6.2.0", - "**/graphql-upload": "^11.0.0" + "**/graphql-upload": "^11.0.0", + "**/strip-ansi": "6.0.1", + "**/string-width": "4.2.0", + "**/wrap-ansi": "7.0.0" }, "engines": { "node": ">=20.12.1" diff --git a/backend/public/img/badges/default_verification.svg b/backend/public/img/badges/default_verification.svg index 7bde29f35..c138d734c 100644 --- a/backend/public/img/badges/default_verification.svg +++ b/backend/public/img/badges/default_verification.svg @@ -15,7 +15,8 @@ + id="path1" + style="fill:#868383;fill-opacity:1" /> \ No newline at end of file + + + + + + + + + + + + diff --git a/backend/public/img/badges/verification_red_developer.svg b/backend/public/img/badges/verification_red_developer.svg index 55d363c9a..4cdb47793 100644 --- a/backend/public/img/badges/verification_red_developer.svg +++ b/backend/public/img/badges/verification_red_developer.svg @@ -1 +1,38 @@ -</> \ No newline at end of file + + + + + + + </> + + diff --git a/backend/public/img/badges/verification_red_moderator.svg b/backend/public/img/badges/verification_red_moderator.svg index bb2e5fde6..ee1b87605 100644 --- a/backend/public/img/badges/verification_red_moderator.svg +++ b/backend/public/img/badges/verification_red_moderator.svg @@ -1 +1,29 @@ - \ No newline at end of file + + + + + + + + + diff --git a/backend/public/providers.json b/backend/public/providers.json index ef9f04bff..28b10de5b 100644 --- a/backend/public/providers.json +++ b/backend/public/providers.json @@ -1,257 +1,234 @@ [ - { - "provider_name": "Codepen", - "provider_url": "https:\/\/codepen.io", - "endpoints": [ - { - "schemes": [ - "http:\/\/codepen.io\/*", - "https:\/\/codepen.io\/*" - ], - "url": "http:\/\/codepen.io\/api\/oembed" - } - ] - }, - { - "provider_name": "DTube", - "provider_url": "https:\/\/d.tube\/", - "endpoints": [ - { - "schemes": [ - "https:\/\/d.tube\/v\/*" - ], - "url": "https:\/\/api.d.tube\/oembed", - "discovery": true - } - ] - }, - { - "provider_name": "Facebook (Post)", - "provider_url": "https:\/\/www.facebook.com\/", - "endpoints": [ - { - "schemes": [ - "https:\/\/www.facebook.com\/*\/posts\/*", - "https:\/\/www.facebook.com\/photos\/*", - "https:\/\/www.facebook.com\/*\/photos\/*", - "https:\/\/www.facebook.com\/photo.php*", - "https:\/\/www.facebook.com\/photo.php", - "https:\/\/www.facebook.com\/*\/activity\/*", - "https:\/\/www.facebook.com\/permalink.php", - "https:\/\/www.facebook.com\/media\/set?set=*", - "https:\/\/www.facebook.com\/questions\/*", - "https:\/\/www.facebook.com\/notes\/*\/*\/*" - ], - "url": "https:\/\/www.facebook.com\/plugins\/post\/oembed.json", - "discovery": true - } - ] - }, - { - "provider_name": "Facebook (Video)", - "provider_url": "https:\/\/www.facebook.com\/", - "endpoints": [ - { - "schemes": [ - "https:\/\/www.facebook.com\/*\/videos\/*", - "https:\/\/www.facebook.com\/video.php" - ], - "url": "https:\/\/www.facebook.com\/plugins\/video\/oembed.json", - "discovery": true - } - ] - }, - { - "provider_name": "Flickr", - "provider_url": "https:\/\/www.flickr.com\/", - "endpoints": [ - { - "schemes": [ - "http:\/\/*.flickr.com\/photos\/*", - "http:\/\/flic.kr\/p\/*", - "https:\/\/*.flickr.com\/photos\/*", - "https:\/\/flic.kr\/p\/*" - ], - "url": "https:\/\/www.flickr.com\/services\/oembed\/", - "discovery": true - } - ] - }, - { - "provider_name": "GIPHY", - "provider_url": "https:\/\/giphy.com", - "endpoints": [ - { - "schemes": [ - "https:\/\/giphy.com\/gifs\/*", - "http:\/\/gph.is\/*", - "https:\/\/media.giphy.com\/media\/*\/giphy.gif" - ], - "url": "https:\/\/giphy.com\/services\/oembed", - "discovery": true - } - ] - }, - { - "provider_name": "Instagram", - "provider_url": "https:\/\/instagram.com", - "endpoints": [ - { - "schemes": [ - "http:\/\/instagram.com\/p\/*", - "http:\/\/instagr.am\/p\/*", - "http:\/\/www.instagram.com\/p\/*", - "http:\/\/www.instagr.am\/p\/*", - "https:\/\/instagram.com\/p\/*", - "https:\/\/instagr.am\/p\/*", - "https:\/\/www.instagram.com\/p\/*", - "https:\/\/www.instagr.am\/p\/*" - ], - "url": "https:\/\/api.instagram.com\/oembed", - "formats": [ - "json" - ] - } - ] - }, - { - "provider_name": "Meetup", - "provider_url": "http:\/\/www.meetup.com", - "endpoints": [ - { - "schemes": [ - "http:\/\/meetup.com\/*", - "https:\/\/www.meetup.com\/*", - "https:\/\/meetup.com\/*", - "http:\/\/meetu.ps\/*" - ], - "url": "https:\/\/api.meetup.com\/oembed", - "formats": [ - "json" - ] - } - ] - }, - { - "provider_name": "MixCloud", - "provider_url": "https:\/\/mixcloud.com\/", - "endpoints": [ - { - "schemes": [ - "http:\/\/www.mixcloud.com\/*\/*\/", - "https:\/\/www.mixcloud.com\/*\/*\/" - ], - "url": "https:\/\/www.mixcloud.com\/oembed\/" - } - ] - }, - { - "provider_name": "Reddit", - "provider_url": "https:\/\/reddit.com\/", - "endpoints": [ - { - "schemes": [ - "https:\/\/reddit.com\/r\/*\/comments\/*\/*", - "https:\/\/www.reddit.com\/r\/*\/comments\/*\/*" - ], - "url": "https:\/\/www.reddit.com\/oembed" - } - ] - }, - { - "provider_name": "SlideShare", - "provider_url": "http:\/\/www.slideshare.net\/", - "endpoints": [ - { - "schemes": [ - "http:\/\/www.slideshare.net\/*\/*", - "http:\/\/fr.slideshare.net\/*\/*", - "http:\/\/de.slideshare.net\/*\/*", - "http:\/\/es.slideshare.net\/*\/*", - "http:\/\/pt.slideshare.net\/*\/*" - ], - "url": "http:\/\/www.slideshare.net\/api\/oembed\/2", - "discovery": true - } - ] - }, - { - "provider_name": "SoundCloud", - "provider_url": "http:\/\/soundcloud.com\/", - "endpoints": [ - { - "schemes": [ - "http:\/\/soundcloud.com\/*", - "https:\/\/soundcloud.com\/*" - ], - "url": "https:\/\/soundcloud.com\/oembed" - } - ] - }, - { - "provider_name": "Twitch", - "provider_url": "https:\/\/www.twitch.tv", - "endpoints": [ - { - "schemes": [ - "http:\/\/clips.twitch.tv\/*", - "https:\/\/clips.twitch.tv\/*", - "http:\/\/www.twitch.tv\/*", - "https:\/\/www.twitch.tv\/*", - "http:\/\/twitch.tv\/*", - "https:\/\/twitch.tv\/*" - ], - "url": "https:\/\/api.twitch.tv\/v4\/oembed", - "formats": [ - "json" - ] - } - ] - }, - { - "provider_name": "Twitter", - "provider_url": "http:\/\/www.twitter.com\/", - "endpoints": [ - { - "schemes": [ - "https:\/\/twitter.com\/*\/status\/*", - "https:\/\/*.twitter.com\/*\/status\/*" - ], - "url": "https:\/\/publish.twitter.com\/oembed" - } - ] - }, - { - "provider_name": "Vimeo", - "provider_url": "https:\/\/vimeo.com\/", - "endpoints": [ - { - "schemes": [ - "https:\/\/vimeo.com\/*", - "https:\/\/vimeo.com\/album\/*\/video\/*", - "https:\/\/vimeo.com\/channels\/*\/*", - "https:\/\/vimeo.com\/groups\/*\/videos\/*", - "https:\/\/vimeo.com\/ondemand\/*\/*", - "https:\/\/player.vimeo.com\/video\/*" - ], - "url": "https:\/\/vimeo.com\/api\/oembed.{format}", - "discovery": true - } - ] - }, - { - "provider_name": "YouTube", - "provider_url": "https:\/\/www.youtube.com\/", - "endpoints": [ - { - "schemes": [ - "https:\/\/*.youtube.com\/watch*", - "https:\/\/*.youtube.com\/v\/*", - "https:\/\/youtu.be\/*" - ], - "url": "https:\/\/www.youtube.com\/oembed", - "discovery": true - } - ] - } -] \ No newline at end of file + { + "provider_name": "Codepen", + "provider_url": "https://codepen.io", + "endpoints": [ + { + "schemes": ["http://codepen.io/*", "https://codepen.io/*"], + "url": "http://codepen.io/api/oembed" + } + ] + }, + { + "provider_name": "DTube", + "provider_url": "https://d.tube/", + "endpoints": [ + { + "schemes": ["https://d.tube/v/*"], + "url": "https://api.d.tube/oembed", + "discovery": true + } + ] + }, + { + "provider_name": "Facebook (Post)", + "provider_url": "https://www.facebook.com/", + "endpoints": [ + { + "schemes": [ + "https://www.facebook.com/*/posts/*", + "https://www.facebook.com/photos/*", + "https://www.facebook.com/*/photos/*", + "https://www.facebook.com/photo.php*", + "https://www.facebook.com/photo.php", + "https://www.facebook.com/*/activity/*", + "https://www.facebook.com/permalink.php", + "https://www.facebook.com/media/set?set=*", + "https://www.facebook.com/questions/*", + "https://www.facebook.com/notes/*/*/*" + ], + "url": "https://www.facebook.com/plugins/post/oembed.json", + "discovery": true + } + ] + }, + { + "provider_name": "Facebook (Video)", + "provider_url": "https://www.facebook.com/", + "endpoints": [ + { + "schemes": ["https://www.facebook.com/*/videos/*", "https://www.facebook.com/video.php"], + "url": "https://www.facebook.com/plugins/video/oembed.json", + "discovery": true + } + ] + }, + { + "provider_name": "Flickr", + "provider_url": "https://www.flickr.com/", + "endpoints": [ + { + "schemes": [ + "http://*.flickr.com/photos/*", + "http://flic.kr/p/*", + "https://*.flickr.com/photos/*", + "https://flic.kr/p/*" + ], + "url": "https://www.flickr.com/services/oembed/", + "discovery": true + } + ] + }, + { + "provider_name": "GIPHY", + "provider_url": "https://giphy.com", + "endpoints": [ + { + "schemes": [ + "https://giphy.com/gifs/*", + "http://gph.is/*", + "https://media.giphy.com/media/*/giphy.gif" + ], + "url": "https://giphy.com/services/oembed", + "discovery": true + } + ] + }, + { + "provider_name": "Instagram", + "provider_url": "https://instagram.com", + "endpoints": [ + { + "schemes": [ + "http://instagram.com/p/*", + "http://instagr.am/p/*", + "http://www.instagram.com/p/*", + "http://www.instagr.am/p/*", + "https://instagram.com/p/*", + "https://instagr.am/p/*", + "https://www.instagram.com/p/*", + "https://www.instagr.am/p/*" + ], + "url": "https://api.instagram.com/oembed", + "formats": ["json"] + } + ] + }, + { + "provider_name": "Meetup", + "provider_url": "http://www.meetup.com", + "endpoints": [ + { + "schemes": [ + "http://meetup.com/*", + "https://www.meetup.com/*", + "https://meetup.com/*", + "http://meetu.ps/*" + ], + "url": "https://api.meetup.com/oembed", + "formats": ["json"] + } + ] + }, + { + "provider_name": "MixCloud", + "provider_url": "https://mixcloud.com/", + "endpoints": [ + { + "schemes": ["http://www.mixcloud.com/*/*/", "https://www.mixcloud.com/*/*/"], + "url": "https://www.mixcloud.com/oembed/" + } + ] + }, + { + "provider_name": "Reddit", + "provider_url": "https://reddit.com/", + "endpoints": [ + { + "schemes": [ + "https://reddit.com/r/*/comments/*/*", + "https://www.reddit.com/r/*/comments/*/*" + ], + "url": "https://www.reddit.com/oembed" + } + ] + }, + { + "provider_name": "SlideShare", + "provider_url": "http://www.slideshare.net/", + "endpoints": [ + { + "schemes": [ + "http://www.slideshare.net/*/*", + "http://fr.slideshare.net/*/*", + "http://de.slideshare.net/*/*", + "http://es.slideshare.net/*/*", + "http://pt.slideshare.net/*/*" + ], + "url": "http://www.slideshare.net/api/oembed/2", + "discovery": true + } + ] + }, + { + "provider_name": "SoundCloud", + "provider_url": "http://soundcloud.com/", + "endpoints": [ + { + "schemes": ["http://soundcloud.com/*", "https://soundcloud.com/*"], + "url": "https://soundcloud.com/oembed" + } + ] + }, + { + "provider_name": "Twitch", + "provider_url": "https://www.twitch.tv", + "endpoints": [ + { + "schemes": [ + "http://clips.twitch.tv/*", + "https://clips.twitch.tv/*", + "http://www.twitch.tv/*", + "https://www.twitch.tv/*", + "http://twitch.tv/*", + "https://twitch.tv/*" + ], + "url": "https://api.twitch.tv/v4/oembed", + "formats": ["json"] + } + ] + }, + { + "provider_name": "Twitter", + "provider_url": "http://www.twitter.com/", + "endpoints": [ + { + "schemes": ["https://twitter.com/*/status/*", "https://*.twitter.com/*/status/*"], + "url": "https://publish.twitter.com/oembed" + } + ] + }, + { + "provider_name": "Vimeo", + "provider_url": "https://vimeo.com/", + "endpoints": [ + { + "schemes": [ + "https://vimeo.com/*", + "https://vimeo.com/album/*/video/*", + "https://vimeo.com/channels/*/*", + "https://vimeo.com/groups/*/videos/*", + "https://vimeo.com/ondemand/*/*", + "https://player.vimeo.com/video/*" + ], + "url": "https://vimeo.com/api/oembed.{format}", + "discovery": true + } + ] + }, + { + "provider_name": "YouTube", + "provider_url": "https://www.youtube.com/", + "endpoints": [ + { + "schemes": [ + "https://*.youtube.com/watch*", + "https://*.youtube.com/v/*", + "https://youtu.be/*" + ], + "url": "https://www.youtube.com/oembed", + "discovery": true + } + ] + } +] diff --git a/backend/scripts/build.copy.files.sh b/backend/scripts/build.copy.files.sh index 7279291d6..1daf19b55 100755 --- a/backend/scripts/build.copy.files.sh +++ b/backend/scripts/build.copy.files.sh @@ -3,15 +3,12 @@ # public cp -r public/ build/public/ -# html files -mkdir -p build/src/middleware/helpers/email/templates/ -cp -r src/middleware/helpers/email/templates/*.html build/src/middleware/helpers/email/templates/ +# email files +mkdir -p build/src/emails/templates/ +cp -r src/emails/templates/ build/src/emails/ -mkdir -p build/src/middleware/helpers/email/templates/en/ -cp -r src/middleware/helpers/email/templates/en/*.html build/src/middleware/helpers/email/templates/en/ - -mkdir -p build/src/middleware/helpers/email/templates/de/ -cp -r src/middleware/helpers/email/templates/de/*.html build/src/middleware/helpers/email/templates/de/ +mkdir -p build/src/emails/locales/ +cp -r src/emails/locales/ build/src/emails/ # gql files mkdir -p build/src/graphql/types/ diff --git a/backend/src/config/index.ts b/backend/src/config/index.ts index 9b82299ae..658c7e97c 100644 --- a/backend/src/config/index.ts +++ b/backend/src/config/index.ts @@ -1,8 +1,10 @@ /* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ + /* eslint-disable n/no-process-env */ import { config } from 'dotenv' +// eslint-disable-next-line import/no-namespace +import * as SMTPTransport from 'nodemailer/lib/smtp-pool' import emails from './emails' import metadata from './metadata' @@ -13,18 +15,20 @@ config() // Use Cypress env or process.env // eslint-disable-next-line @typescript-eslint/no-explicit-any declare let Cypress: any | undefined -const env = typeof Cypress !== 'undefined' ? Cypress.env() : process.env +const env = (typeof Cypress !== 'undefined' ? Cypress.env() : process.env) as typeof process.env const environment = { - NODE_ENV: env.NODE_ENV || process.env.NODE_ENV, + NODE_ENV: env.NODE_ENV ?? process.env.NODE_ENV, DEBUG: env.NODE_ENV !== 'production' && env.DEBUG, TEST: env.NODE_ENV === 'test', PRODUCTION: env.NODE_ENV === 'production', // used for staging enviroments if 'PRODUCTION=true' and 'PRODUCTION_DB_CLEAN_ALLOW=true' PRODUCTION_DB_CLEAN_ALLOW: env.PRODUCTION_DB_CLEAN_ALLOW === 'true' || false, // default = false - DISABLED_MIDDLEWARES: ['test', 'development'].includes(env.NODE_ENV as string) + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + DISABLED_MIDDLEWARES: ['test', 'development'].includes(env.NODE_ENV!) ? (env.DISABLED_MIDDLEWARES?.split(',') ?? []) : [], + SEND_MAIL: env.NODE_ENV !== 'test', } const required = { @@ -34,32 +38,51 @@ const required = { } const server = { - CLIENT_URI: env.CLIENT_URI || 'http://localhost:3000', - GRAPHQL_URI: env.GRAPHQL_URI || 'http://localhost:4000', - JWT_EXPIRES: env.JWT_EXPIRES || '2y', + CLIENT_URI: env.CLIENT_URI ?? 'http://localhost:3000', + GRAPHQL_URI: env.GRAPHQL_URI ?? 'http://localhost:4000', + JWT_EXPIRES: env.JWT_EXPIRES ?? '2y', } -const hasDKIMData = env.SMTP_DKIM_DOMAINNAME && env.SMTP_DKIM_KEYSELECTOR && env.SMTP_DKIM_PRIVATKEY +const SMTP_HOST = env.SMTP_HOST +const SMTP_PORT = (env.SMTP_PORT && parseInt(env.SMTP_PORT)) || undefined +const SMTP_IGNORE_TLS = env.SMTP_IGNORE_TLS !== 'false' // default = true +const SMTP_SECURE = env.SMTP_SECURE === 'true' +const SMTP_USERNAME = env.SMTP_USERNAME +const SMTP_PASSWORD = env.SMTP_PASSWORD +const SMTP_DKIM_DOMAINNAME = env.SMTP_DKIM_DOMAINNAME +const SMTP_DKIM_KEYSELECTOR = env.SMTP_DKIM_KEYSELECTOR +// PEM format = https://docs.progress.com/bundle/datadirect-hybrid-data-pipeline-installation-46/page/PEM-file-format.html +const SMTP_DKIM_PRIVATKEY = env.SMTP_DKIM_PRIVATKEY?.replace(/\\n/g, '\n') // replace all "\n" in .env string by real line break +const SMTP_MAX_CONNECTIONS = (env.SMTP_MAX_CONNECTIONS && parseInt(env.SMTP_MAX_CONNECTIONS)) || 5 +const SMTP_MAX_MESSAGES = (env.SMTP_MAX_MESSAGES && parseInt(env.SMTP_MAX_MESSAGES)) || 100 -const smtp = { - SMTP_HOST: env.SMTP_HOST, - SMTP_PORT: env.SMTP_PORT, - SMTP_IGNORE_TLS: env.SMTP_IGNORE_TLS !== 'false', // default = true - SMTP_SECURE: env.SMTP_SECURE === 'true', - SMTP_USERNAME: env.SMTP_USERNAME, - SMTP_PASSWORD: env.SMTP_PASSWORD, - SMTP_DKIM_DOMAINNAME: hasDKIMData && env.SMTP_DKIM_DOMAINNAME, - SMTP_DKIM_KEYSELECTOR: hasDKIMData && env.SMTP_DKIM_KEYSELECTOR, - // PEM format: https://docs.progress.com/bundle/datadirect-hybrid-data-pipeline-installation-46/page/PEM-file-format.html - SMTP_DKIM_PRIVATKEY: hasDKIMData && env.SMTP_DKIM_PRIVATKEY.replace(/\\n/g, '\n'), // replace all "\n" in .env string by real line break - SMTP_MAX_CONNECTIONS: env.SMTP_MAX_CONNECTIONS || 5, - SMTP_MAX_MESSAGES: env.SMTP_MAX_MESSAGES || 100, +const nodemailerTransportOptions: SMTPTransport.Options = { + host: SMTP_HOST, + port: SMTP_PORT, + ignoreTLS: SMTP_IGNORE_TLS, + secure: SMTP_SECURE, // true for 465, false for other ports + pool: true, + maxConnections: SMTP_MAX_CONNECTIONS, + maxMessages: SMTP_MAX_MESSAGES, +} +if (SMTP_USERNAME && SMTP_PASSWORD) { + nodemailerTransportOptions.auth = { + user: SMTP_USERNAME, + pass: SMTP_PASSWORD, + } +} +if (SMTP_DKIM_DOMAINNAME && SMTP_DKIM_KEYSELECTOR && SMTP_DKIM_PRIVATKEY) { + nodemailerTransportOptions.dkim = { + domainName: SMTP_DKIM_DOMAINNAME, + keySelector: SMTP_DKIM_KEYSELECTOR, + privateKey: SMTP_DKIM_PRIVATKEY, + } } const neo4j = { - NEO4J_URI: env.NEO4J_URI || 'bolt://localhost:7687', - NEO4J_USERNAME: env.NEO4J_USERNAME || 'neo4j', - NEO4J_PASSWORD: env.NEO4J_PASSWORD || 'neo4j', + NEO4J_URI: env.NEO4J_URI ?? 'bolt://localhost:7687', + NEO4J_USERNAME: env.NEO4J_USERNAME ?? 'neo4j', + NEO4J_PASSWORD: env.NEO4J_PASSWORD ?? 'neo4j', } const sentry = { @@ -69,7 +92,7 @@ const sentry = { const redis = { REDIS_DOMAIN: env.REDIS_DOMAIN, - REDIS_PORT: env.REDIS_PORT, + REDIS_PORT: (env.REDIS_PORT && parseInt(env.REDIS_PORT)) || undefined, REDIS_PASSWORD: env.REDIS_PASSWORD, } @@ -109,10 +132,11 @@ export default { ...environment, ...server, ...required, - ...smtp, ...neo4j, ...sentry, ...redis, ...s3, ...options, } + +export { nodemailerTransportOptions } diff --git a/backend/src/constants/subscriptions.ts b/backend/src/constants/subscriptions.ts new file mode 100644 index 000000000..ec3e79e63 --- /dev/null +++ b/backend/src/constants/subscriptions.ts @@ -0,0 +1,3 @@ +export const NOTIFICATION_ADDED = 'NOTIFICATION_ADDED' +export const CHAT_MESSAGE_ADDED = 'CHAT_MESSAGE_ADDED' +export const ROOM_COUNT_UPDATED = 'ROOM_COUNT_UPDATED' diff --git a/backend/src/context/database.ts b/backend/src/context/database.ts new file mode 100644 index 000000000..f6ccdc9ca --- /dev/null +++ b/backend/src/context/database.ts @@ -0,0 +1,49 @@ +import { getDriver, getNeode } from '@db/neo4j' + +import type { Driver } from 'neo4j-driver' + +export const query = + (driver: Driver) => + async ({ query, variables = {} }: { driver; query: string; variables: object }) => { + const session = driver.session() + + const result = session.readTransaction(async (transaction) => { + const response = await transaction.run(query, variables) + return response + }) + + try { + return await result + } finally { + await session.close() + } + } + +export const mutate = + (driver: Driver) => + async ({ query, variables = {} }: { driver; query: string; variables: object }) => { + const session = driver.session() + + const result = session.writeTransaction(async (transaction) => { + const response = await transaction.run(query, variables) + return response + }) + + try { + return await result + } finally { + await session.close() + } + } + +export default () => { + const driver = getDriver() + const neode = getNeode() + + return { + driver, + neode, + query: query(driver), + mutate: mutate(driver), + } +} diff --git a/backend/src/context/pubsub.ts b/backend/src/context/pubsub.ts new file mode 100644 index 000000000..3d99bba6d --- /dev/null +++ b/backend/src/context/pubsub.ts @@ -0,0 +1,25 @@ +import { RedisPubSub } from 'graphql-redis-subscriptions' +import { PubSub } from 'graphql-subscriptions' +import Redis from 'ioredis' + +import CONFIG from '@config/index' + +export default () => { + const { REDIS_DOMAIN, REDIS_PORT, REDIS_PASSWORD } = CONFIG + if (!(REDIS_DOMAIN && REDIS_PORT && REDIS_PASSWORD)) { + return new PubSub() + } + + const options = { + host: REDIS_DOMAIN, + port: REDIS_PORT, + password: REDIS_PASSWORD, + retryStrategy: (times) => { + return Math.min(times * 50, 2000) + }, + } + return new RedisPubSub({ + publisher: new Redis(options), + subscriber: new Redis(options), + }) +} diff --git a/backend/src/db/factories.ts b/backend/src/db/factories.ts index 90a666205..95db5a859 100644 --- a/backend/src/db/factories.ts +++ b/backend/src/db/factories.ts @@ -10,7 +10,7 @@ import { Factory } from 'rosie' import slugify from 'slug' import { v4 as uuid } from 'uuid' -import generateInviteCode from '@schema/resolvers/helpers/generateInviteCode' +import generateInviteCode from '@graphql/resolvers/helpers/generateInviteCode' import { getDriver, getNeode } from './neo4j' diff --git a/backend/src/db/migrations-examples/20200123150105-merge_duplicate_user_accounts.ts b/backend/src/db/migrations-examples/20200123150105-merge_duplicate_user_accounts.ts index eda3206b4..ea64c58af 100644 --- a/backend/src/db/migrations-examples/20200123150105-merge_duplicate_user_accounts.ts +++ b/backend/src/db/migrations-examples/20200123150105-merge_duplicate_user_accounts.ts @@ -10,7 +10,7 @@ import { throwError, concat } from 'rxjs' import { flatMap, mergeMap, map, catchError, filter } from 'rxjs/operators' import { getDriver } from '@db/neo4j' -import normalizeEmail from '@schema/resolvers/helpers/normalizeEmail' +import normalizeEmail from '@graphql/resolvers/helpers/normalizeEmail' export const description = ` This migration merges duplicate :User and :EmailAddress nodes. It became diff --git a/backend/src/db/migrations-examples/20200312140328-bulk_upload_to_s3.ts b/backend/src/db/migrations-examples/20200312140328-bulk_upload_to_s3.ts deleted file mode 100644 index 0307a2e6e..000000000 --- a/backend/src/db/migrations-examples/20200312140328-bulk_upload_to_s3.ts +++ /dev/null @@ -1,117 +0,0 @@ -/* eslint-disable @typescript-eslint/no-unsafe-argument */ -/* eslint-disable @typescript-eslint/no-unsafe-return */ -/* eslint-disable @typescript-eslint/no-unsafe-call */ -/* eslint-disable @typescript-eslint/no-unsafe-member-access */ -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ -/* eslint-disable security/detect-non-literal-fs-filename */ -import https from 'https' -import { existsSync, createReadStream } from 'node:fs' -import path from 'node:path' - -import { S3 } from 'aws-sdk' -import mime from 'mime-types' - -import s3Configs from '@config/index' -import { getDriver } from '@db/neo4j' - -export const description = ` -Upload all image files to a S3 compatible object storage in order to reduce -load on our backend. -` - -export async function up(next) { - const driver = getDriver() - const session = driver.session() - const transaction = session.beginTransaction() - const agent = new https.Agent({ - maxSockets: 5, - }) - - const { - AWS_ENDPOINT: endpoint, - AWS_REGION: region, - AWS_BUCKET: Bucket, - S3_CONFIGURED, - } = s3Configs - - if (!S3_CONFIGURED) { - // eslint-disable-next-line no-console - console.log('No S3 given, cannot upload image files') - return - } - - const s3 = new S3({ region, endpoint, httpOptions: { agent } }) - try { - // Implement your migration here. - const { records } = await transaction.run('MATCH (image:Image) RETURN image.url as url') - let urls = records.map((r) => r.get('url')) - urls = urls.filter((url) => url.startsWith('/uploads')) - const locations = await Promise.all( - urls - .map((url) => { - return async () => { - const { pathname } = new URL(url, 'http://example.org') - const fileLocation = path.join(__dirname, `../../../public/${pathname}`) - const s3Location = `original${pathname}` - // eslint-disable-next-line n/no-sync - if (existsSync(fileLocation)) { - const mimeType = mime.lookup(fileLocation) - const params = { - Bucket, - Key: s3Location, - ACL: 'public-read', - ContentType: mimeType || 'image/jpeg', - Body: createReadStream(fileLocation), - } - - const data = await s3.upload(params).promise() - const { Location: spacesUrl } = data - - const updatedRecord = await transaction.run( - 'MATCH (image:Image {url: $url}) SET image.url = $spacesUrl RETURN image.url as url', - { url, spacesUrl }, - ) - const [updatedUrl] = updatedRecord.records.map((record) => record.get('url')) - return updatedUrl - } - } - }) - .map((p) => p()), - ) - // eslint-disable-next-line no-console - console.log('this is locations', locations) - await transaction.commit() - next() - } catch (error) { - // eslint-disable-next-line no-console - console.log(error) - await transaction.rollback() - // eslint-disable-next-line no-console - console.log('rolled back') - throw new Error(error) - } finally { - await session.close() - } -} - -export async function down(next) { - const driver = getDriver() - const session = driver.session() - const transaction = session.beginTransaction() - - try { - // Implement your migration here. - await transaction.run(``) - await transaction.commit() - next() - // eslint-disable-next-line no-catch-all/no-catch-all - } catch (error) { - // eslint-disable-next-line no-console - console.log(error) - await transaction.rollback() - // eslint-disable-next-line no-console - console.log('rolled back') - } finally { - await session.close() - } -} diff --git a/backend/src/models/Badge.ts b/backend/src/db/models/Badge.ts similarity index 100% rename from backend/src/models/Badge.ts rename to backend/src/db/models/Badge.ts diff --git a/backend/src/models/Category.ts b/backend/src/db/models/Category.ts similarity index 100% rename from backend/src/models/Category.ts rename to backend/src/db/models/Category.ts diff --git a/backend/src/models/Comment.ts b/backend/src/db/models/Comment.ts similarity index 100% rename from backend/src/models/Comment.ts rename to backend/src/db/models/Comment.ts diff --git a/backend/src/models/Donations.ts b/backend/src/db/models/Donations.ts similarity index 100% rename from backend/src/models/Donations.ts rename to backend/src/db/models/Donations.ts diff --git a/backend/src/models/EmailAddress.ts b/backend/src/db/models/EmailAddress.ts similarity index 100% rename from backend/src/models/EmailAddress.ts rename to backend/src/db/models/EmailAddress.ts diff --git a/backend/src/models/Group.ts b/backend/src/db/models/Group.ts similarity index 100% rename from backend/src/models/Group.ts rename to backend/src/db/models/Group.ts diff --git a/backend/src/models/Image.ts b/backend/src/db/models/Image.ts similarity index 100% rename from backend/src/models/Image.ts rename to backend/src/db/models/Image.ts diff --git a/backend/src/models/InviteCode.ts b/backend/src/db/models/InviteCode.ts similarity index 100% rename from backend/src/models/InviteCode.ts rename to backend/src/db/models/InviteCode.ts diff --git a/backend/src/models/Location.ts b/backend/src/db/models/Location.ts similarity index 100% rename from backend/src/models/Location.ts rename to backend/src/db/models/Location.ts diff --git a/backend/src/models/Migration.ts b/backend/src/db/models/Migration.ts similarity index 100% rename from backend/src/models/Migration.ts rename to backend/src/db/models/Migration.ts diff --git a/backend/src/models/Post.ts b/backend/src/db/models/Post.ts similarity index 100% rename from backend/src/models/Post.ts rename to backend/src/db/models/Post.ts diff --git a/backend/src/models/Report.ts b/backend/src/db/models/Report.ts similarity index 100% rename from backend/src/models/Report.ts rename to backend/src/db/models/Report.ts diff --git a/backend/src/models/SocialMedia.ts b/backend/src/db/models/SocialMedia.ts similarity index 100% rename from backend/src/models/SocialMedia.ts rename to backend/src/db/models/SocialMedia.ts diff --git a/backend/src/models/Tag.ts b/backend/src/db/models/Tag.ts similarity index 100% rename from backend/src/models/Tag.ts rename to backend/src/db/models/Tag.ts diff --git a/backend/src/models/UnverifiedEmailAddress.ts b/backend/src/db/models/UnverifiedEmailAddress.ts similarity index 100% rename from backend/src/models/UnverifiedEmailAddress.ts rename to backend/src/db/models/UnverifiedEmailAddress.ts diff --git a/backend/src/models/User.spec.ts b/backend/src/db/models/User.spec.ts similarity index 100% rename from backend/src/models/User.spec.ts rename to backend/src/db/models/User.spec.ts diff --git a/backend/src/models/User.ts b/backend/src/db/models/User.ts similarity index 100% rename from backend/src/models/User.ts rename to backend/src/db/models/User.ts diff --git a/backend/src/models/index.ts b/backend/src/db/models/index.ts similarity index 100% rename from backend/src/models/index.ts rename to backend/src/db/models/index.ts diff --git a/backend/src/db/neo4j.ts b/backend/src/db/neo4j.ts index dcd19a0ea..ecaef68b5 100644 --- a/backend/src/db/neo4j.ts +++ b/backend/src/db/neo4j.ts @@ -1,12 +1,9 @@ -/* eslint-disable @typescript-eslint/no-unsafe-argument */ - -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable import/no-named-as-default-member */ import neo4j, { Driver } from 'neo4j-driver' import Neode from 'neode' import CONFIG from '@config/index' -import models from '@models/index' +import models from '@db/models/index' let driver: Driver const defaultOptions = { diff --git a/backend/src/db/seed.ts b/backend/src/db/seed.ts index 0e2c2c61d..ed1fff1a4 100644 --- a/backend/src/db/seed.ts +++ b/backend/src/db/seed.ts @@ -26,6 +26,8 @@ if (CONFIG.PRODUCTION && !CONFIG.PRODUCTION_DB_CLEAN_ALLOW) { throw new Error(`You cannot seed the database in a non-staging and real production environment!`) } +CONFIG.SEND_MAIL = true + const languages = ['de', 'en', 'es', 'fr', 'it', 'pt', 'pl'] ;(async function () { diff --git a/backend/src/db/seed/badges.ts b/backend/src/db/seed/badges.ts index ce8bff9ba..dc044419c 100644 --- a/backend/src/db/seed/badges.ts +++ b/backend/src/db/seed/badges.ts @@ -9,155 +9,155 @@ export const trophies = async () => { trophyBear: await Factory.build('badge', { id: 'trophy_bear', type: 'trophy', - description: 'You earned a Bear', + description: 'Has earned a Bear', icon: '/img/badges/trophy_blue_bear.svg', }), trophyPanda: await Factory.build('badge', { id: 'trophy_panda', type: 'trophy', - description: 'You earned a Panda', + description: 'Has earned a Panda', icon: '/img/badges/trophy_blue_panda.svg', }), trophyRabbit: await Factory.build('badge', { id: 'trophy_rabbit', type: 'trophy', - description: 'You earned a Rabbit', + description: 'Has earned a Rabbit', icon: '/img/badges/trophy_blue_rabbit.svg', }), trophyRacoon: await Factory.build('badge', { id: 'trophy_racoon', type: 'trophy', - description: 'You earned a Racoon', + description: 'Has earned a Racoon', icon: '/img/badges/trophy_blue_racoon.svg', }), trophyRhino: await Factory.build('badge', { id: 'trophy_rhino', type: 'trophy', - description: 'You earned a Rhino', + description: 'Has earned a Rhino', icon: '/img/badges/trophy_blue_rhino.svg', }), trophyTiger: await Factory.build('badge', { id: 'trophy_tiger', type: 'trophy', - description: 'You earned a Tiger', + description: 'Has earned a Tiger', icon: '/img/badges/trophy_blue_tiger.svg', }), trophyTurtle: await Factory.build('badge', { id: 'trophy_turtle', type: 'trophy', - description: 'You earned a Turtle', + description: 'Has earned a Turtle', icon: '/img/badges/trophy_blue_turtle.svg', }), trophyWhale: await Factory.build('badge', { id: 'trophy_whale', type: 'trophy', - description: 'You earned a Whale', + description: 'Has earned a Whale', icon: '/img/badges/trophy_blue_whale.svg', }), trophyWolf: await Factory.build('badge', { id: 'trophy_wolf', type: 'trophy', - description: 'You earned a Wolf', + description: 'Has earned a Wolf', icon: '/img/badges/trophy_blue_wolf.svg', }), // Green Transports trophyAirship: await Factory.build('badge', { id: 'trophy_airship', type: 'trophy', - description: 'You earned an Airship', + description: 'Has earned an Airship', icon: '/img/badges/trophy_green_airship.svg', }), trophyAlienship: await Factory.build('badge', { id: 'trophy_alienship', type: 'trophy', - description: 'You earned an Alienship', + description: 'Has earned an Alienship', icon: '/img/badges/trophy_green_alienship.svg', }), trophyBalloon: await Factory.build('badge', { id: 'trophy_balloon', type: 'trophy', - description: 'You earned a Balloon', + description: 'Has earned a Balloon', icon: '/img/badges/trophy_green_balloon.svg', }), trophyBigballoon: await Factory.build('badge', { id: 'trophy_bigballoon', type: 'trophy', - description: 'You earned a Big Balloon', + description: 'Has earned a Big Balloon', icon: '/img/badges/trophy_green_bigballoon.svg', }), trophyCrane: await Factory.build('badge', { id: 'trophy_crane', type: 'trophy', - description: 'You earned a Crane', + description: 'Has earned a Crane', icon: '/img/badges/trophy_green_crane.svg', }), trophyGlider: await Factory.build('badge', { id: 'trophy_glider', type: 'trophy', - description: 'You earned a Glider', + description: 'Has earned a Glider', icon: '/img/badges/trophy_green_glider.svg', }), trophyHelicopter: await Factory.build('badge', { id: 'trophy_helicopter', type: 'trophy', - description: 'You earned a Helicopter', + description: 'Has earned a Helicopter', icon: '/img/badges/trophy_green_helicopter.svg', }), // Green Animals trophyBee: await Factory.build('badge', { id: 'trophy_bee', type: 'trophy', - description: 'You earned a Bee', + description: 'Has earned a Bee', icon: '/img/badges/trophy_green_bee.svg', }), trophyButterfly: await Factory.build('badge', { id: 'trophy_butterfly', type: 'trophy', - description: 'You earned a Butterfly', + description: 'Has earned a Butterfly', icon: '/img/badges/trophy_green_butterfly.svg', }), // Green Plants trophyFlower: await Factory.build('badge', { id: 'trophy_flower', type: 'trophy', - description: 'You earned a Flower', + description: 'Has earned a Flower', icon: '/img/badges/trophy_green_flower.svg', }), trophyLifetree: await Factory.build('badge', { id: 'trophy_lifetree', type: 'trophy', - description: 'You earned the tree of life', + description: 'Has earned the tree of life', icon: '/img/badges/trophy_green_lifetree.svg', }), // Green Misc trophyDoublerainbow: await Factory.build('badge', { id: 'trophy_doublerainbow', type: 'trophy', - description: 'You earned the Double Rainbow', + description: 'Has earned the Double Rainbow', icon: '/img/badges/trophy_green_doublerainbow.svg', }), trophyEndrainbow: await Factory.build('badge', { id: 'trophy_endrainbow', type: 'trophy', - description: 'You earned the End of the Rainbow', + description: 'Has earned the End of the Rainbow', icon: '/img/badges/trophy_green_endrainbow.svg', }), trophyMagicrainbow: await Factory.build('badge', { id: 'trophy_magicrainbow', type: 'trophy', - description: 'You earned the Magic Rainbow', + description: 'Has earned the Magic Rainbow', icon: '/img/badges/trophy_green_magicrainbow.svg', }), trophyStarter: await Factory.build('badge', { id: 'trophy_starter', type: 'trophy', - description: 'You earned the Starter Badge', + description: 'Has earned the Starter Badge', icon: '/img/badges/trophy_green_starter.svg', }), trophySuperfounder: await Factory.build('badge', { id: 'trophy_superfounder', type: 'trophy', - description: 'You earned the Super Founder Badge', + description: 'Has earned the Super Founder Badge', icon: '/img/badges/trophy_green_superfounder.svg', }), } @@ -169,19 +169,19 @@ export const verification = async () => { verificationModerator: await Factory.build('badge', { id: 'verification_moderator', type: 'verification', - description: 'You are a Moderator', + description: 'Is a Moderator', icon: '/img/badges/verification_red_moderator.svg', }), verificationAdmin: await Factory.build('badge', { id: 'verification_admin', type: 'verification', - description: 'You are an Administrator', + description: 'Is an Administrator', icon: '/img/badges/verification_red_admin.svg', }), verificationDeveloper: await Factory.build('badge', { id: 'verification_developer', type: 'verification', - description: 'You are a Developer', + description: 'Is a Developer', icon: '/img/badges/verification_red_developer.svg', }), } diff --git a/backend/src/db/types/User.ts b/backend/src/db/types/User.ts new file mode 100644 index 000000000..5f621868b --- /dev/null +++ b/backend/src/db/types/User.ts @@ -0,0 +1,32 @@ +import { Integer, Node } from 'neo4j-driver' + +export interface UserDbProperties { + allowEmbedIframes: boolean + awaySince?: string + createdAt: string + deleted: boolean + disabled: boolean + emailNotificationsChatMessage?: boolean + emailNotificationsCommentOnObservedPost?: boolean + emailNotificationsFollowingUsers?: boolean + emailNotificationsGroupMemberJoined?: boolean + emailNotificationsGroupMemberLeft?: boolean + emailNotificationsGroupMemberRemoved?: boolean + emailNotificationsGroupMemberRoleChanged?: boolean + emailNotificationsMention?: boolean + emailNotificationsPostInGroup?: boolean + encryptedPassword: string + id: string + lastActiveAt?: string + lastOnlineStatus?: string + locale: string + name: string + role: string + showShoutsPublicly: boolean + slug: string + termsAndConditionsAgreedAt: string + termsAndConditionsAgreedVersion: string + updatedAt: string +} + +export type User = Node diff --git a/backend/src/emails/__snapshots__/sendChatMessageMail.spec.ts.snap b/backend/src/emails/__snapshots__/sendChatMessageMail.spec.ts.snap new file mode 100644 index 000000000..786bad9c0 --- /dev/null +++ b/backend/src/emails/__snapshots__/sendChatMessageMail.spec.ts.snap @@ -0,0 +1,255 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`sendChatMessageMail English chat_message template 1`] = ` +{ + "attachments": [], + "from": "ocelot.social ", + "html": " + + + + + + + + +
+
+
+
+
+

Hello chatReceiver,

+
+
+

you have received a new chat message from chatSender. +

Show Chat +
+

See you soon on ocelot.social!

+

– The ocelot.social Team


+

PS: If you don't want to receive e-mails anymore, change your notification settings!

+
+
+ +
+ +", + "subject": "ocelot.social – Notification: New chat message", + "text": "HELLO CHATRECEIVER, + +you have received a new chat message from chatSender +[http://webapp:3000/user/chatSender/chatsender]. + +Show Chat [http://webapp:3000/chat] + +See you soon on ocelot.social [https://ocelot.social]! + +– The ocelot.social Team + + +PS: If you don't want to receive e-mails anymore, change your notification +settings [http://webapp:3000/settings/notifications]! + + +ocelot.social Community [https://ocelot.social]", + "to": "user@example.org", +} +`; + +exports[`sendChatMessageMail German chat_message template 1`] = ` +{ + "attachments": [], + "from": "ocelot.social ", + "html": " + + + + + + + + +
+
+
+
+
+

Hallo chatReceiver,

+
+
+

du hast eine neue Chat-Nachricht von chatSender erhalten. +

Chat anzeigen +
+

Bis bald bei ocelot.social!

+

– Dein ocelot.social Team


+

PS: Möchtest du keine E-Mails mehr erhalten, dann ändere deine Benachrichtigungseinstellungen!

+
+
+ +
+ +", + "subject": "ocelot.social – Benachrichtigung: Neue Chat Nachricht", + "text": "HALLO CHATRECEIVER, + +du hast eine neue Chat-Nachricht von chatSender +[http://webapp:3000/user/chatSender/chatsender] erhalten. + +Chat anzeigen [http://webapp:3000/chat] + +Bis bald bei ocelot.social [https://ocelot.social]! + +– Dein ocelot.social Team + + +PS: Möchtest du keine E-Mails mehr erhalten, dann ändere deine +Benachrichtigungseinstellungen [http://webapp:3000/settings/notifications]! + + +ocelot.social Community [https://ocelot.social]", + "to": "user@example.org", +} +`; diff --git a/backend/src/emails/__snapshots__/sendEmailVerification.spec.ts.snap b/backend/src/emails/__snapshots__/sendEmailVerification.spec.ts.snap new file mode 100644 index 000000000..87815f5e6 --- /dev/null +++ b/backend/src/emails/__snapshots__/sendEmailVerification.spec.ts.snap @@ -0,0 +1,261 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`sendEmailVerification English renders correctly 1`] = ` +{ + "attachments": [], + "from": "ocelot.social ", + "html": " + + + + + + + + +
+
+
+
+
+

Hello User,

+
+
+

So, you want to change your e-mail? No problem! Just click the button below to verify your new address:

Verify e-mail address +

If you don't want to change your e-mail address feel free to ignore this message.

+

If the above button doesn't work, you can also copy the following code into your browser window: 123456

+
+

See you soon on ocelot.social!

+

– The ocelot.social Team

+
+
+ +
+ +", + "subject": "New E-Mail Address ocelot.social", + "text": "HELLO USER, + +So, you want to change your e-mail? No problem! Just click the button below to +verify your new address: + +Verify e-mail address +[http://webapp:3000/settings/my-email-address/verify?email=user%40example.org&nonce=123456] + +If you don't want to change your e-mail address feel free to ignore this +message. + +If the above button doesn't work, you can also copy the following code into your +browser window: 123456 + +See you soon on ocelot.social [https://ocelot.social]! + +– The ocelot.social Team + + +ocelot.social Community [https://ocelot.social]", + "to": "user@example.org", +} +`; + +exports[`sendEmailVerification German renders correctly 1`] = ` +{ + "attachments": [], + "from": "ocelot.social ", + "html": " + + + + + + + + +
+
+
+
+
+

Hallo User,

+
+
+

Du möchtest also deine E-Mail ändern? Kein Problem! Mit Klick auf diesen Button kannst Du Deine neue E-Mail Adresse bestätigen:

E-Mail Adresse bestätigen +

Falls Du deine E-Mail Adresse doch nicht ändern möchtest, kannst du diese Nachricht einfach ignorieren.

+

Sollte der Button für Dich nicht funktionieren, kannst Du auch folgenden Code in Dein Browserfenster kopieren: 123456

+
+

Bis bald bei ocelot.social!

+

– Dein ocelot.social Team

+
+
+ +
+ +", + "subject": "Neue E-Mail Addresse ocelot.social", + "text": "HALLO USER, + +Du möchtest also deine E-Mail ändern? Kein Problem! Mit Klick auf diesen Button +kannst Du Deine neue E-Mail Adresse bestätigen: + +E-Mail Adresse bestätigen +[http://webapp:3000/settings/my-email-address/verify?email=user%40example.org&nonce=123456] + +Falls Du deine E-Mail Adresse doch nicht ändern möchtest, kannst du diese +Nachricht einfach ignorieren. + +Sollte der Button für Dich nicht funktionieren, kannst Du auch folgenden Code in +Dein Browserfenster kopieren: 123456 + +Bis bald bei ocelot.social [https://ocelot.social]! + +– Dein ocelot.social Team + + +ocelot.social Community [https://ocelot.social]", + "to": "user@example.org", +} +`; diff --git a/backend/src/emails/__snapshots__/sendNotificationMail.spec.ts.snap b/backend/src/emails/__snapshots__/sendNotificationMail.spec.ts.snap new file mode 100644 index 000000000..1c4f0dc8e --- /dev/null +++ b/backend/src/emails/__snapshots__/sendNotificationMail.spec.ts.snap @@ -0,0 +1,2281 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`sendNotificationMail English changed_group_member_role template 1`] = ` +{ + "attachments": [], + "from": "ocelot.social ", + "html": " + + + + + + + + +
+
+
+
+
+

Hello Jenny Rostock,

+
+
+

your role in the group “The Group” has been changed. Click on the button to view this group:

View group +
+

See you soon on ocelot.social!

+

– The ocelot.social Team


+

PS: If you don't want to receive e-mails anymore, change your notification settings!

+
+
+ +
+ +", + "subject": "ocelot.social – Notification: Role in group changed", + "text": "HELLO JENNY ROSTOCK, + +your role in the group “The Group” has been changed. Click on the button to view +this group: + +View group [http://webapp:3000/group/g1/the-group] + +See you soon on ocelot.social [https://ocelot.social]! + +– The ocelot.social Team + + +PS: If you don't want to receive e-mails anymore, change your notification +settings [http://webapp:3000/settings/notifications]! + + +ocelot.social Community [https://ocelot.social]", + "to": "user@example.org", +} +`; + +exports[`sendNotificationMail English commented_on_post template 1`] = ` +{ + "attachments": [], + "from": "ocelot.social ", + "html": " + + + + + + + + +
+
+
+
+
+

Hello Jenny Rostock,

+
+
+

Peter Lustig commented on a post that you are observing with the title “New Post”. Click on the button to view this comment: +

View comment +
+

See you soon on ocelot.social!

+

– The ocelot.social Team


+

PS: If you don't want to receive e-mails anymore, change your notification settings!

+
+
+ +
+ +", + "subject": "ocelot.social – Notification: New comment on post", + "text": "HELLO JENNY ROSTOCK, + +Peter Lustig [http://webapp:3000/user/u2/peter-lustig] commented on a post that +you are observing with the title “New Post”. Click on the button to view this +comment: + +View comment [http://webapp:3000/post/p1/new-post#commentId-c1] + +See you soon on ocelot.social [https://ocelot.social]! + +– The ocelot.social Team + + +PS: If you don't want to receive e-mails anymore, change your notification +settings [http://webapp:3000/settings/notifications]! + + +ocelot.social Community [https://ocelot.social]", + "to": "user@example.org", +} +`; + +exports[`sendNotificationMail English followed_user_posted template 1`] = ` +{ + "attachments": [], + "from": "ocelot.social ", + "html": " + + + + + + + + +
+
+
+
+
+

Hello Jenny Rostock,

+
+
+

Peter Lustig, a user you are following, wrote a new post with the title “New Post”. Click on the button to view this post: +

View post +
+

See you soon on ocelot.social!

+

– The ocelot.social Team


+

PS: If you don't want to receive e-mails anymore, change your notification settings!

+
+
+ +
+ +", + "subject": "ocelot.social – Notification: New post by followd user", + "text": "HELLO JENNY ROSTOCK, + +Peter Lustig [http://webapp:3000/user/u2/peter-lustig], a user you are +following, wrote a new post with the title “New Post”. Click on the button to +view this post: + +View post [http://webapp:3000/post/p1/new-post] + +See you soon on ocelot.social [https://ocelot.social]! + +– The ocelot.social Team + + +PS: If you don't want to receive e-mails anymore, change your notification +settings [http://webapp:3000/settings/notifications]! + + +ocelot.social Community [https://ocelot.social]", + "to": "user@example.org", +} +`; + +exports[`sendNotificationMail English mentioned_in_comment template 1`] = ` +{ + "attachments": [], + "from": "ocelot.social ", + "html": " + + + + + + + + +
+
+
+
+
+

Hello Jenny Rostock,

+
+
+

Peter Lustig mentioned you in a comment to the post with the title “New Post”. Click on the button to view this comment: +

View comment +
+

See you soon on ocelot.social!

+

– The ocelot.social Team


+

PS: If you don't want to receive e-mails anymore, change your notification settings!

+
+
+ +
+ +", + "subject": "ocelot.social – Notification: Mentioned in comment", + "text": "HELLO JENNY ROSTOCK, + +Peter Lustig [http://webapp:3000/user/u2/peter-lustig] mentioned you in a +comment to the post with the title “New Post”. Click on the button to view this +comment: + +View comment [http://webapp:3000/post/p1/new-post#commentId-c1] + +See you soon on ocelot.social [https://ocelot.social]! + +– The ocelot.social Team + + +PS: If you don't want to receive e-mails anymore, change your notification +settings [http://webapp:3000/settings/notifications]! + + +ocelot.social Community [https://ocelot.social]", + "to": "user@example.org", +} +`; + +exports[`sendNotificationMail English mentioned_in_post template 1`] = ` +{ + "attachments": [], + "from": "ocelot.social ", + "html": " + + + + + + + + +
+
+
+
+
+

Hello Jenny Rostock,

+
+
+

Peter Lustig mentioned you in a post with the title “New Post”. Click on the button to view this post: +

View post +
+

See you soon on ocelot.social!

+

– The ocelot.social Team


+

PS: If you don't want to receive e-mails anymore, change your notification settings!

+
+
+ +
+ +", + "subject": "ocelot.social – Notification: Mentioned in post", + "text": "HELLO JENNY ROSTOCK, + +Peter Lustig [http://webapp:3000/user/u2/peter-lustig] mentioned you in a post +with the title “New Post”. Click on the button to view this post: + +View post [http://webapp:3000/post/p1/new-post] + +See you soon on ocelot.social [https://ocelot.social]! + +– The ocelot.social Team + + +PS: If you don't want to receive e-mails anymore, change your notification +settings [http://webapp:3000/settings/notifications]! + + +ocelot.social Community [https://ocelot.social]", + "to": "user@example.org", +} +`; + +exports[`sendNotificationMail English post_in_group template 1`] = ` +{ + "attachments": [], + "from": "ocelot.social ", + "html": " + + + + + + + + +
+
+
+
+
+

Hello Jenny Rostock,

+
+
+

someone wrote a new post with the title “New Post” in one of your groups. Click on the button to view this post:

View post +
+

See you soon on ocelot.social!

+

– The ocelot.social Team


+

PS: If you don't want to receive e-mails anymore, change your notification settings!

+
+
+ +
+ +", + "subject": "ocelot.social – Notification: New post in group", + "text": "HELLO JENNY ROSTOCK, + +someone wrote a new post with the title “New Post” in one of your groups. Click +on the button to view this post: + +View post [http://webapp:3000/post/p1/new-post] + +See you soon on ocelot.social [https://ocelot.social]! + +– The ocelot.social Team + + +PS: If you don't want to receive e-mails anymore, change your notification +settings [http://webapp:3000/settings/notifications]! + + +ocelot.social Community [https://ocelot.social]", + "to": "user@example.org", +} +`; + +exports[`sendNotificationMail English removed_user_from_group template 1`] = ` +{ + "attachments": [], + "from": "ocelot.social ", + "html": " + + + + + + + + +
+
+
+
+
+

Hello Jenny Rostock,

+
+
+

you have been removed from the group “The Group”.

+
+

See you soon on ocelot.social!

+

– The ocelot.social Team


+

PS: If you don't want to receive e-mails anymore, change your notification settings!

+
+
+ +
+ +", + "subject": "ocelot.social – Notification: Removed from group", + "text": "HELLO JENNY ROSTOCK, + +you have been removed from the group “The Group”. + +See you soon on ocelot.social [https://ocelot.social]! + +– The ocelot.social Team + + +PS: If you don't want to receive e-mails anymore, change your notification +settings [http://webapp:3000/settings/notifications]! + + +ocelot.social Community [https://ocelot.social]", + "to": "user@example.org", +} +`; + +exports[`sendNotificationMail English user_joined_group template 1`] = ` +{ + "attachments": [], + "from": "ocelot.social ", + "html": " + + + + + + + + +
+
+
+
+
+

Hello Jenny Rostock,

+
+
+

Peter Lustig joined the group “The Group”. Click on the button to view this group: +

View group +
+

See you soon on ocelot.social!

+

– The ocelot.social Team


+

PS: If you don't want to receive e-mails anymore, change your notification settings!

+
+
+ +
+ +", + "subject": "ocelot.social – Notification: User joined group", + "text": "HELLO JENNY ROSTOCK, + +Peter Lustig [http://webapp:3000/user/u2/peter-lustig] joined the group “The +Group”. Click on the button to view this group: + +View group [http://webapp:3000/group/g1/the-group] + +See you soon on ocelot.social [https://ocelot.social]! + +– The ocelot.social Team + + +PS: If you don't want to receive e-mails anymore, change your notification +settings [http://webapp:3000/settings/notifications]! + + +ocelot.social Community [https://ocelot.social]", + "to": "user@example.org", +} +`; + +exports[`sendNotificationMail English user_left_group template 1`] = ` +{ + "attachments": [], + "from": "ocelot.social ", + "html": " + + + + + + + + +
+
+
+
+
+

Hello Jenny Rostock,

+
+
+

Peter Lustig left the group “The Group”. Click on the button to view this group: +

View group +
+

See you soon on ocelot.social!

+

– The ocelot.social Team


+

PS: If you don't want to receive e-mails anymore, change your notification settings!

+
+
+ +
+ +", + "subject": "ocelot.social – Notification: User left group", + "text": "HELLO JENNY ROSTOCK, + +Peter Lustig [http://webapp:3000/user/u2/peter-lustig] left the group “The +Group”. Click on the button to view this group: + +View group [http://webapp:3000/group/g1/the-group] + +See you soon on ocelot.social [https://ocelot.social]! + +– The ocelot.social Team + + +PS: If you don't want to receive e-mails anymore, change your notification +settings [http://webapp:3000/settings/notifications]! + + +ocelot.social Community [https://ocelot.social]", + "to": "user@example.org", +} +`; + +exports[`sendNotificationMail German changed_group_member_role template 1`] = ` +{ + "attachments": [], + "from": "ocelot.social ", + "html": " + + + + + + + + +
+
+
+
+
+

Hallo Jenny Rostock,

+
+
+

deine Rolle in der Gruppe „The Group“ wurde geändert. Klicke auf den Knopf, um diese Gruppe zu sehen:

Gruppe ansehen +
+

Bis bald bei ocelot.social!

+

– Dein ocelot.social Team


+

PS: Möchtest du keine E-Mails mehr erhalten, dann ändere deine Benachrichtigungseinstellungen!

+
+
+ +
+ +", + "subject": "ocelot.social – Benachrichtigung: Rolle in Gruppe geändert", + "text": "HALLO JENNY ROSTOCK, + +deine Rolle in der Gruppe „The Group“ wurde geändert. Klicke auf den Knopf, um +diese Gruppe zu sehen: + +Gruppe ansehen [http://webapp:3000/group/g1/the-group] + +Bis bald bei ocelot.social [https://ocelot.social]! + +– Dein ocelot.social Team + + +PS: Möchtest du keine E-Mails mehr erhalten, dann ändere deine +Benachrichtigungseinstellungen [http://webapp:3000/settings/notifications]! + + +ocelot.social Community [https://ocelot.social]", + "to": "user@example.org", +} +`; + +exports[`sendNotificationMail German commented_on_post template 1`] = ` +{ + "attachments": [], + "from": "ocelot.social ", + "html": " + + + + + + + + +
+
+
+
+
+

Hallo Jenny Rostock,

+
+
+

Peter Lustig hat einen Beitrag den du beobachtest mit dem Titel „New Post“ kommentiert. Klicke auf den Knopf, um diesen Kommentar zu sehen: +

Kommentar ansehen +
+

Bis bald bei ocelot.social!

+

– Dein ocelot.social Team


+

PS: Möchtest du keine E-Mails mehr erhalten, dann ändere deine Benachrichtigungseinstellungen!

+
+
+ +
+ +", + "subject": "ocelot.social – Benachrichtigung: Neuer Kommentar zu Beitrag", + "text": "HALLO JENNY ROSTOCK, + +Peter Lustig [http://webapp:3000/user/u2/peter-lustig] hat einen Beitrag den du +beobachtest mit dem Titel „New Post“ kommentiert. Klicke auf den Knopf, um +diesen Kommentar zu sehen: + +Kommentar ansehen [http://webapp:3000/post/p1/new-post#commentId-c1] + +Bis bald bei ocelot.social [https://ocelot.social]! + +– Dein ocelot.social Team + + +PS: Möchtest du keine E-Mails mehr erhalten, dann ändere deine +Benachrichtigungseinstellungen [http://webapp:3000/settings/notifications]! + + +ocelot.social Community [https://ocelot.social]", + "to": "user@example.org", +} +`; + +exports[`sendNotificationMail German followed_user_posted template 1`] = ` +{ + "attachments": [], + "from": "ocelot.social ", + "html": " + + + + + + + + +
+
+
+
+
+

Hallo Jenny Rostock,

+
+
+

Peter Lustig, ein Nutzer dem du folgst, hat einen neuen Beitrag mit dem Titel „New Post“ geschrieben. Klicke auf den Knopf, um diesen Beitrag zu sehen: +

Beitrag ansehen +
+

Bis bald bei ocelot.social!

+

– Dein ocelot.social Team


+

PS: Möchtest du keine E-Mails mehr erhalten, dann ändere deine Benachrichtigungseinstellungen!

+
+
+ +
+ +", + "subject": "ocelot.social – Benachrichtigung: Neuer Beitrag von gefolgtem Nutzer", + "text": "HALLO JENNY ROSTOCK, + +Peter Lustig [http://webapp:3000/user/u2/peter-lustig], ein Nutzer dem du +folgst, hat einen neuen Beitrag mit dem Titel „New Post“ geschrieben. Klicke auf +den Knopf, um diesen Beitrag zu sehen: + +Beitrag ansehen [http://webapp:3000/post/p1/new-post] + +Bis bald bei ocelot.social [https://ocelot.social]! + +– Dein ocelot.social Team + + +PS: Möchtest du keine E-Mails mehr erhalten, dann ändere deine +Benachrichtigungseinstellungen [http://webapp:3000/settings/notifications]! + + +ocelot.social Community [https://ocelot.social]", + "to": "user@example.org", +} +`; + +exports[`sendNotificationMail German mentioned_in_comment template 1`] = ` +{ + "attachments": [], + "from": "ocelot.social ", + "html": " + + + + + + + + +
+
+
+
+
+

Hallo Jenny Rostock,

+
+
+

Peter Lustig hat dich in einem Kommentar zu dem Beitrag mit dem Titel „New Post“ erwähnt. Klicke auf den Knopf, um den Kommentar zu sehen: +

Kommentar ansehen +
+

Bis bald bei ocelot.social!

+

– Dein ocelot.social Team


+

PS: Möchtest du keine E-Mails mehr erhalten, dann ändere deine Benachrichtigungseinstellungen!

+
+
+ +
+ +", + "subject": "ocelot.social – Benachrichtigung: Erwähnung in Kommentar", + "text": "HALLO JENNY ROSTOCK, + +Peter Lustig [http://webapp:3000/user/u2/peter-lustig] hat dich in einem +Kommentar zu dem Beitrag mit dem Titel „New Post“ erwähnt. Klicke auf den Knopf, +um den Kommentar zu sehen: + +Kommentar ansehen [http://webapp:3000/post/p1/new-post#commentId-c1] + +Bis bald bei ocelot.social [https://ocelot.social]! + +– Dein ocelot.social Team + + +PS: Möchtest du keine E-Mails mehr erhalten, dann ändere deine +Benachrichtigungseinstellungen [http://webapp:3000/settings/notifications]! + + +ocelot.social Community [https://ocelot.social]", + "to": "user@example.org", +} +`; + +exports[`sendNotificationMail German mentioned_in_post template 1`] = ` +{ + "attachments": [], + "from": "ocelot.social ", + "html": " + + + + + + + + +
+
+
+
+
+

Hallo Jenny Rostock,

+
+
+

Peter Lustig hat Dich in einem Beitrag mit dem Titel „New Post“ erwähnt. Klicke auf den Knopf, um den Beitrag zu sehen: +

Beitrag ansehen +
+

Bis bald bei ocelot.social!

+

– Dein ocelot.social Team


+

PS: Möchtest du keine E-Mails mehr erhalten, dann ändere deine Benachrichtigungseinstellungen!

+
+
+ +
+ +", + "subject": "ocelot.social – Benachrichtigung: Erwähnung in Beitrag", + "text": "HALLO JENNY ROSTOCK, + +Peter Lustig [http://webapp:3000/user/u2/peter-lustig] hat Dich in einem Beitrag +mit dem Titel „New Post“ erwähnt. Klicke auf den Knopf, um den Beitrag zu sehen: + +Beitrag ansehen [http://webapp:3000/post/p1/new-post] + +Bis bald bei ocelot.social [https://ocelot.social]! + +– Dein ocelot.social Team + + +PS: Möchtest du keine E-Mails mehr erhalten, dann ändere deine +Benachrichtigungseinstellungen [http://webapp:3000/settings/notifications]! + + +ocelot.social Community [https://ocelot.social]", + "to": "user@example.org", +} +`; + +exports[`sendNotificationMail German post_in_group template 1`] = ` +{ + "attachments": [], + "from": "ocelot.social ", + "html": " + + + + + + + + +
+
+
+
+
+

Hallo Jenny Rostock,

+
+
+

jemand hat einen neuen Beitrag mit dem Titel „New Post“ in einer deiner Gruppen geschrieben. Klicke auf den Knopf, um diesen Beitrag zu sehen:

Beitrag ansehen +
+

Bis bald bei ocelot.social!

+

– Dein ocelot.social Team


+

PS: Möchtest du keine E-Mails mehr erhalten, dann ändere deine Benachrichtigungseinstellungen!

+
+
+ +
+ +", + "subject": "ocelot.social – Benachrichtigung: Neuer Beitrag in Gruppe", + "text": "HALLO JENNY ROSTOCK, + +jemand hat einen neuen Beitrag mit dem Titel „New Post“ in einer deiner Gruppen +geschrieben. Klicke auf den Knopf, um diesen Beitrag zu sehen: + +Beitrag ansehen [http://webapp:3000/post/p1/new-post] + +Bis bald bei ocelot.social [https://ocelot.social]! + +– Dein ocelot.social Team + + +PS: Möchtest du keine E-Mails mehr erhalten, dann ändere deine +Benachrichtigungseinstellungen [http://webapp:3000/settings/notifications]! + + +ocelot.social Community [https://ocelot.social]", + "to": "user@example.org", +} +`; + +exports[`sendNotificationMail German removed_user_from_group template 1`] = ` +{ + "attachments": [], + "from": "ocelot.social ", + "html": " + + + + + + + + +
+
+
+
+
+

Hallo Jenny Rostock,

+
+
+

du wurdest aus der Gruppe „The Group“ entfernt.

+
+

Bis bald bei ocelot.social!

+

– Dein ocelot.social Team


+

PS: Möchtest du keine E-Mails mehr erhalten, dann ändere deine Benachrichtigungseinstellungen!

+
+
+ +
+ +", + "subject": "ocelot.social – Benachrichtigung: Aus Gruppe entfernt", + "text": "HALLO JENNY ROSTOCK, + +du wurdest aus der Gruppe „The Group“ entfernt. + +Bis bald bei ocelot.social [https://ocelot.social]! + +– Dein ocelot.social Team + + +PS: Möchtest du keine E-Mails mehr erhalten, dann ändere deine +Benachrichtigungseinstellungen [http://webapp:3000/settings/notifications]! + + +ocelot.social Community [https://ocelot.social]", + "to": "user@example.org", +} +`; + +exports[`sendNotificationMail German user_joined_group template 1`] = ` +{ + "attachments": [], + "from": "ocelot.social ", + "html": " + + + + + + + + +
+
+
+
+
+

Hallo Jenny Rostock,

+
+
+

Peter Lustig ist der Gruppe „The Group“ beigetreten. Klicke auf den Knopf, um diese Gruppe zu sehen: +

Gruppe ansehen +
+

Bis bald bei ocelot.social!

+

– Dein ocelot.social Team


+

PS: Möchtest du keine E-Mails mehr erhalten, dann ändere deine Benachrichtigungseinstellungen!

+
+
+ +
+ +", + "subject": "ocelot.social – Benachrichtigung: Nutzer tritt Gruppe bei", + "text": "HALLO JENNY ROSTOCK, + +Peter Lustig [http://webapp:3000/user/u2/peter-lustig] ist der Gruppe „The +Group“ beigetreten. Klicke auf den Knopf, um diese Gruppe zu sehen: + +Gruppe ansehen [http://webapp:3000/group/g1/the-group] + +Bis bald bei ocelot.social [https://ocelot.social]! + +– Dein ocelot.social Team + + +PS: Möchtest du keine E-Mails mehr erhalten, dann ändere deine +Benachrichtigungseinstellungen [http://webapp:3000/settings/notifications]! + + +ocelot.social Community [https://ocelot.social]", + "to": "user@example.org", +} +`; + +exports[`sendNotificationMail German user_left_group template 1`] = ` +{ + "attachments": [], + "from": "ocelot.social ", + "html": " + + + + + + + + +
+
+
+
+
+

Hallo Jenny Rostock,

+
+
+

Peter Lustig hat die Gruppe „The Group“ verlassen. Klicke auf den Knopf, um diese Gruppe zu sehen: +

Gruppe ansehen +
+

Bis bald bei ocelot.social!

+

– Dein ocelot.social Team


+

PS: Möchtest du keine E-Mails mehr erhalten, dann ändere deine Benachrichtigungseinstellungen!

+
+
+ +
+ +", + "subject": "ocelot.social – Benachrichtigung: Nutzer verlässt Gruppe", + "text": "HALLO JENNY ROSTOCK, + +Peter Lustig [http://webapp:3000/user/u2/peter-lustig] hat die Gruppe „The +Group“ verlassen. Klicke auf den Knopf, um diese Gruppe zu sehen: + +Gruppe ansehen [http://webapp:3000/group/g1/the-group] + +Bis bald bei ocelot.social [https://ocelot.social]! + +– Dein ocelot.social Team + + +PS: Möchtest du keine E-Mails mehr erhalten, dann ändere deine +Benachrichtigungseinstellungen [http://webapp:3000/settings/notifications]! + + +ocelot.social Community [https://ocelot.social]", + "to": "user@example.org", +} +`; diff --git a/backend/src/emails/__snapshots__/sendRegistrationMail.spec.ts.snap b/backend/src/emails/__snapshots__/sendRegistrationMail.spec.ts.snap new file mode 100644 index 000000000..d4a1ded8a --- /dev/null +++ b/backend/src/emails/__snapshots__/sendRegistrationMail.spec.ts.snap @@ -0,0 +1,559 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`sendRegistrationMail with invite code English renders correctly 1`] = ` +{ + "attachments": [], + "from": "ocelot.social ", + "html": " + + + + + + + + +
+
+
+
+
+

Welcome to ocelot.social!

+
+
+

Thank you for joining our cause – it's awesome to have you on board. There's just one tiny step missing before we can start shaping the world together … Please confirm your e-mail address by clicking the button below:

Confirm your e-mail address +

If the above button doesn't work, you can also copy the following code into your browser window: 123456

+

However, this only works if you have registered through our website.

+

If you didn't sign up for ocelot.social we recommend you to check it out! It's a social network from people for people who want to connect and change the world together. +

+

PS: If you ignore this e-mail we will not create an account for you. ;)

+
+

See you soon on ocelot.social!

+

– The ocelot.social Team

+
+
+ +
+ +", + "subject": "Welcome to ocelot.social", + "text": "WELCOME TO OCELOT.SOCIAL! + +Thank you for joining our cause – it's awesome to have you on board. There's +just one tiny step missing before we can start shaping the world together … +Please confirm your e-mail address by clicking the button below: + +Confirm your e-mail address +[http://webapp:3000/registration?email=user%40example.org&nonce=123456&inviteCode=welcome&method=invite-code] + +If the above button doesn't work, you can also copy the following code into your +browser window: 123456 + +However, this only works if you have registered through our website. + +If you didn't sign up for ocelot.social we recommend you to check it out! It's a +social network from people for people who want to connect and change the world +together. + +PS: If you ignore this e-mail we will not create an account for you. ;) + +See you soon on ocelot.social [https://ocelot.social]! + +– The ocelot.social Team + + +ocelot.social Community [https://ocelot.social]", + "to": "user@example.org", +} +`; + +exports[`sendRegistrationMail with invite code German renders correctly 1`] = ` +{ + "attachments": [], + "from": "ocelot.social ", + "html": " + + + + + + + + +
+
+
+
+
+

Willkommen bei ocelot.social!

+
+
+

Danke, dass du dich angemeldet hast – wir freuen uns, dich dabei zu haben. Jetzt fehlt nur noch eine Kleinigkeit, bevor wir gemeinsam die Welt verbessern können … Bitte bestätige Deine E-Mail Adresse:

Bestätige Deine E-Mail Adresse +

Sollte der Button für Dich nicht funktionieren, kannst Du auch folgenden Code in Dein Browserfenster kopieren: 123456

+

Das funktioniert allerdings nur, wenn du Dich über unsere Website registriert hast.

+

Falls Du Dich nicht selbst bei ocelot.social angemeldet hast, schau doch mal vorbei! Wir sind ein gemeinnütziges Aktionsnetzwerk – von Menschen für Menschen. +

+

PS: Wenn Du keinen Account bei uns möchtest, kannst Du diese E-Mail einfach ignorieren. ;)

+
+

Bis bald bei ocelot.social!

+

– Dein ocelot.social Team

+
+
+ +
+ +", + "subject": "Willkommen bei ocelot.social", + "text": "WILLKOMMEN BEI OCELOT.SOCIAL! + +Danke, dass du dich angemeldet hast – wir freuen uns, dich dabei zu haben. Jetzt +fehlt nur noch eine Kleinigkeit, bevor wir gemeinsam die Welt verbessern können +… Bitte bestätige Deine E-Mail Adresse: + +Bestätige Deine E-Mail Adresse +[http://webapp:3000/registration?email=user%40example.org&nonce=123456&inviteCode=welcome&method=invite-code] + +Sollte der Button für Dich nicht funktionieren, kannst Du auch folgenden Code in +Dein Browserfenster kopieren: 123456 + +Das funktioniert allerdings nur, wenn du Dich über unsere Website registriert +hast. + +Falls Du Dich nicht selbst bei ocelot.social angemeldet hast, schau doch mal +vorbei! Wir sind ein gemeinnütziges Aktionsnetzwerk – von Menschen für Menschen. + +PS: Wenn Du keinen Account bei uns möchtest, kannst Du diese E-Mail einfach +ignorieren. ;) + +Bis bald bei ocelot.social [https://ocelot.social]! + +– Dein ocelot.social Team + + +ocelot.social Community [https://ocelot.social]", + "to": "user@example.org", +} +`; + +exports[`sendRegistrationMail without invite code English renders correctly 1`] = ` +{ + "attachments": [], + "from": "ocelot.social ", + "html": " + + + + + + + + +
+
+
+
+
+

Welcome to ocelot.social!

+
+
+

Thank you for joining our cause – it's awesome to have you on board. There's just one tiny step missing before we can start shaping the world together … Please confirm your e-mail address by clicking the button below:

Confirm your e-mail address +

If the above button doesn't work, you can also copy the following code into your browser window: 123456

+

However, this only works if you have registered through our website.

+

If you didn't sign up for ocelot.social we recommend you to check it out! It's a social network from people for people who want to connect and change the world together. +

+

PS: If you ignore this e-mail we will not create an account for you. ;)

+
+

See you soon on ocelot.social!

+

– The ocelot.social Team

+
+
+ +
+ +", + "subject": "Welcome to ocelot.social", + "text": "WELCOME TO OCELOT.SOCIAL! + +Thank you for joining our cause – it's awesome to have you on board. There's +just one tiny step missing before we can start shaping the world together … +Please confirm your e-mail address by clicking the button below: + +Confirm your e-mail address +[http://webapp:3000/registration?email=user%40example.org&nonce=123456&method=invite-mail] + +If the above button doesn't work, you can also copy the following code into your +browser window: 123456 + +However, this only works if you have registered through our website. + +If you didn't sign up for ocelot.social we recommend you to check it out! It's a +social network from people for people who want to connect and change the world +together. + +PS: If you ignore this e-mail we will not create an account for you. ;) + +See you soon on ocelot.social [https://ocelot.social]! + +– The ocelot.social Team + + +ocelot.social Community [https://ocelot.social]", + "to": "user@example.org", +} +`; + +exports[`sendRegistrationMail without invite code German renders correctly 1`] = ` +{ + "attachments": [], + "from": "ocelot.social ", + "html": " + + + + + + + + +
+
+
+
+
+

Willkommen bei ocelot.social!

+
+
+

Danke, dass du dich angemeldet hast – wir freuen uns, dich dabei zu haben. Jetzt fehlt nur noch eine Kleinigkeit, bevor wir gemeinsam die Welt verbessern können … Bitte bestätige Deine E-Mail Adresse:

Bestätige Deine E-Mail Adresse +

Sollte der Button für Dich nicht funktionieren, kannst Du auch folgenden Code in Dein Browserfenster kopieren: 123456

+

Das funktioniert allerdings nur, wenn du Dich über unsere Website registriert hast.

+

Falls Du Dich nicht selbst bei ocelot.social angemeldet hast, schau doch mal vorbei! Wir sind ein gemeinnütziges Aktionsnetzwerk – von Menschen für Menschen. +

+

PS: Wenn Du keinen Account bei uns möchtest, kannst Du diese E-Mail einfach ignorieren. ;)

+
+

Bis bald bei ocelot.social!

+

– Dein ocelot.social Team

+
+
+ +
+ +", + "subject": "Willkommen bei ocelot.social", + "text": "WILLKOMMEN BEI OCELOT.SOCIAL! + +Danke, dass du dich angemeldet hast – wir freuen uns, dich dabei zu haben. Jetzt +fehlt nur noch eine Kleinigkeit, bevor wir gemeinsam die Welt verbessern können +… Bitte bestätige Deine E-Mail Adresse: + +Bestätige Deine E-Mail Adresse +[http://webapp:3000/registration?email=user%40example.org&nonce=123456&method=invite-mail] + +Sollte der Button für Dich nicht funktionieren, kannst Du auch folgenden Code in +Dein Browserfenster kopieren: 123456 + +Das funktioniert allerdings nur, wenn du Dich über unsere Website registriert +hast. + +Falls Du Dich nicht selbst bei ocelot.social angemeldet hast, schau doch mal +vorbei! Wir sind ein gemeinnütziges Aktionsnetzwerk – von Menschen für Menschen. + +PS: Wenn Du keinen Account bei uns möchtest, kannst Du diese E-Mail einfach +ignorieren. ;) + +Bis bald bei ocelot.social [https://ocelot.social]! + +– Dein ocelot.social Team + + +ocelot.social Community [https://ocelot.social]", + "to": "user@example.org", +} +`; diff --git a/backend/src/emails/__snapshots__/sendResetPasswordMail.spec.ts.snap b/backend/src/emails/__snapshots__/sendResetPasswordMail.spec.ts.snap new file mode 100644 index 000000000..da62c9a34 --- /dev/null +++ b/backend/src/emails/__snapshots__/sendResetPasswordMail.spec.ts.snap @@ -0,0 +1,260 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`sendResetPasswordMail English renders correctly 1`] = ` +{ + "attachments": [], + "from": "ocelot.social ", + "html": " + + + + + + + + +
+
+
+
+
+

Hello Jenny Rostock,

+
+
+

So, you forgot your password? No problem! Just click the button below to reset it within the next 24 hours:

Confirm your e-mail address +

If you didn't request a new password feel free to ignore this e-mail.

+

If the above button doesn't work you can also copy the following code into your browser window: 123456

+
+

See you soon on ocelot.social!

+

– The ocelot.social Team

+
+
+ +
+ +", + "subject": "Reset Password ocelot.social", + "text": "HELLO JENNY ROSTOCK, + +So, you forgot your password? No problem! Just click the button below to reset +it within the next 24 hours: + +Confirm your e-mail address +[http://webapp:3000/password-reset/change-password?email=user%40example.org&nonce=123456] + +If you didn't request a new password feel free to ignore this e-mail. + +If the above button doesn't work you can also copy the following code into your +browser window: 123456 + +See you soon on ocelot.social [https://ocelot.social]! + +– The ocelot.social Team + + +ocelot.social Community [https://ocelot.social]", + "to": "user@example.org", +} +`; + +exports[`sendResetPasswordMail German renders correctly 1`] = ` +{ + "attachments": [], + "from": "ocelot.social ", + "html": " + + + + + + + + +
+
+
+
+
+

Hallo Jenny Rostock,

+
+
+

Du hast also dein Passwort vergessen? Kein Problem! Mit Klick auf diesen Button kannst du innerhalb der nächsten 24 Stunden dein Passwort zurücksetzen:

Bestätige Deine E-Mail Adresse +

Falls du kein neues Passwort angefordert hast, kannst du diese E-Mail einfach ignorieren.

+

Sollte der Button für dich nicht funktionieren, kannst du auch folgenden Code in Dein Browserfenster kopieren: 123456

+
+

Bis bald bei ocelot.social!

+

– Dein ocelot.social Team

+
+
+ +
+ +", + "subject": "Neues Passwort ocelot.social", + "text": "HALLO JENNY ROSTOCK, + +Du hast also dein Passwort vergessen? Kein Problem! Mit Klick auf diesen Button +kannst du innerhalb der nächsten 24 Stunden dein Passwort zurücksetzen: + +Bestätige Deine E-Mail Adresse +[http://webapp:3000/password-reset/change-password?email=user%40example.org&nonce=123456] + +Falls du kein neues Passwort angefordert hast, kannst du diese E-Mail einfach +ignorieren. + +Sollte der Button für dich nicht funktionieren, kannst du auch folgenden Code in +Dein Browserfenster kopieren: 123456 + +Bis bald bei ocelot.social [https://ocelot.social]! + +– Dein ocelot.social Team + + +ocelot.social Community [https://ocelot.social]", + "to": "user@example.org", +} +`; diff --git a/backend/src/emails/__snapshots__/sendWrongEmail.spec.ts.snap b/backend/src/emails/__snapshots__/sendWrongEmail.spec.ts.snap new file mode 100644 index 000000000..b2052d808 --- /dev/null +++ b/backend/src/emails/__snapshots__/sendWrongEmail.spec.ts.snap @@ -0,0 +1,255 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`sendWrongEmail English renders correctly 1`] = ` +{ + "attachments": [], + "from": "ocelot.social ", + "html": " + + + + + + + + +
+
+
+
+
+

Welcome to ocelot.social!

+
+
+

You requested a password reset but unfortunately we couldn't find an account associated with your e-mail address. Did you maybe use another one when you signed up?

Try a different e-mail +

If you don't have an account at ocelot.social yet or if you didn't want to reset your password, please ignore this e-mail. +

+
+

See you soon on ocelot.social!

+

– The ocelot.social Team

+
+
+ +
+ +", + "subject": "Wrong E-mail? ocelot.social", + "text": "WELCOME TO OCELOT.SOCIAL! + +You requested a password reset but unfortunately we couldn't find an account +associated with your e-mail address. Did you maybe use another one when you +signed up? + +Try a different e-mail [http://webapp:3000/password-reset/request] + +If you don't have an account at ocelot.social yet or if you didn't want to reset +your password, please ignore this e-mail. + +See you soon on ocelot.social [https://ocelot.social]! + +– The ocelot.social Team + + +ocelot.social Community [https://ocelot.social]", + "to": "user@example.org", +} +`; + +exports[`sendWrongEmail German renders correctly 1`] = ` +{ + "attachments": [], + "from": "ocelot.social ", + "html": " + + + + + + + + +
+
+
+
+
+

Willkommen bei ocelot.social!

+
+
+

Du hast bei uns ein neues Passwort angefordert – leider haben wir aber keinen Account mit deiner E-Mailadresse gefunden. Kann es sein, dass du mit einer anderen Adresse bei uns angemeldet bist?

Versuch' es mit einer anderen E-Mail +

Wenn du noch keinen Account bei ocelot.social hast oder dein Password gar nicht ändern willst, kannst du diese E-Mail einfach ignorieren! +

+
+

Bis bald bei ocelot.social!

+

– Dein ocelot.social Team

+
+
+ +
+ +", + "subject": "Falsche Mailaddresse? ocelot.social", + "text": "WILLKOMMEN BEI OCELOT.SOCIAL! + +Du hast bei uns ein neues Passwort angefordert – leider haben wir aber keinen +Account mit deiner E-Mailadresse gefunden. Kann es sein, dass du mit einer +anderen Adresse bei uns angemeldet bist? + +Versuch' es mit einer anderen E-Mail [http://webapp:3000/password-reset/request] + +Wenn du noch keinen Account bei ocelot.social hast oder dein Password gar nicht +ändern willst, kannst du diese E-Mail einfach ignorieren! + +Bis bald bei ocelot.social [https://ocelot.social]! + +– Dein ocelot.social Team + + +ocelot.social Community [https://ocelot.social]", + "to": "user@example.org", +} +`; diff --git a/backend/src/emails/locales/de.json b/backend/src/emails/locales/de.json new file mode 100644 index 000000000..9e0ce843a --- /dev/null +++ b/backend/src/emails/locales/de.json @@ -0,0 +1,71 @@ +{ + "notification": "Benachrichtigung", + "subjects": { + "changedGroupMemberRole": "Rolle in Gruppe geändert", + "chatMessage": "Neue Chat Nachricht", + "commentedOnPost": "Neuer Kommentar zu Beitrag", + "followedUserPosted": "Neuer Beitrag von gefolgtem Nutzer", + "mentionedInComment": "Erwähnung in Kommentar", + "mentionedInPost": "Erwähnung in Beitrag", + "newEmail": "Neue E-Mail Addresse", + "removedUserFromGroup": "Aus Gruppe entfernt", + "postInGroup": "Neuer Beitrag in Gruppe", + "resetPassword": "Neues Passwort", + "userJoinedGroup": "Nutzer tritt Gruppe bei", + "userLeftGroup": "Nutzer verlässt Gruppe", + "wrongEmail": "Falsche Mailaddresse?" + }, + "registration": { + "introduction": "Danke, dass du dich angemeldet hast – wir freuen uns, dich dabei zu haben. Jetzt fehlt nur noch eine Kleinigkeit, bevor wir gemeinsam die Welt verbessern können … Bitte bestätige Deine E-Mail Adresse:", + "codeHint": "Sollte der Button für Dich nicht funktionieren, kannst Du auch folgenden Code in Dein Browserfenster kopieren: ", + "codeHintException": "Das funktioniert allerdings nur, wenn du Dich über unsere Website registriert hast.", + "notYouStart": "Falls Du Dich nicht selbst bei ", + "notYouEnd": " angemeldet hast, schau doch mal vorbei! Wir sind ein gemeinnütziges Aktionsnetzwerk – von Menschen für Menschen.", + "ps": "PS: Wenn Du keinen Account bei uns möchtest, kannst Du diese E-Mail einfach ignorieren. ;)" + }, + "emailVerification": { + "codeHint": "Sollte der Button für Dich nicht funktionieren, kannst Du auch folgenden Code in Dein Browserfenster kopieren: ", + "introduction": "Du möchtest also deine E-Mail ändern? Kein Problem! Mit Klick auf diesen Button kannst Du Deine neue E-Mail Adresse bestätigen:", + "doNotChange": "Falls Du deine E-Mail Adresse doch nicht ändern möchtest, kannst du diese Nachricht einfach ignorieren. " + }, + "buttons": { + "confirmEmail": "Bestätige Deine E-Mail Adresse", + "resetPassword": "Passwort zurücksetzen", + "tryAgain": "Versuch' es mit einer anderen E-Mail", + "verifyEmail": "E-Mail Adresse bestätigen", + "viewChat": "Chat anzeigen", + "viewComment": "Kommentar ansehen", + "viewGroup": "Gruppe ansehen", + "viewPost": "Beitrag ansehen" + }, + "general": { + "greeting": "Hallo", + "seeYou": "Bis bald bei ", + "yourTeam": "– Dein {team} Team", + "settingsHint": "PS: Möchtest du keine E-Mails mehr erhalten, dann ändere deine ", + "settingsName": "Benachrichtigungseinstellungen", + "welcome": "Willkommen bei" + }, + "resetPassword": { + "codeHint": "Sollte der Button für dich nicht funktionieren, kannst du auch folgenden Code in Dein Browserfenster kopieren: ", + "ignore": "Falls du kein neues Passwort angefordert hast, kannst du diese E-Mail einfach ignorieren.", + "introduction": "Du hast also dein Passwort vergessen? Kein Problem! Mit Klick auf diesen Button kannst du innerhalb der nächsten 24 Stunden dein Passwort zurücksetzen:" + }, + "wrongEmail": { + "codeHint": "Sollte der Button für Dich nicht funktionieren, kannst Du auch folgenden Code in Dein Browserfenster kopieren: ", + "ignoreEnd": " hast oder dein Password gar nicht ändern willst, kannst du diese E-Mail einfach ignorieren!", + "ignoreStart": "Wenn du noch keinen Account bei ", + "introduction": "Du hast bei uns ein neues Passwort angefordert – leider haben wir aber keinen Account mit deiner E-Mailadresse gefunden. Kann es sein, dass du mit einer anderen Adresse bei uns angemeldet bist?" + }, + "changedGroupMemberRole": "deine Rolle in der Gruppe „{groupName}“ wurde geändert. Klicke auf den Knopf, um diese Gruppe zu sehen:", + "chatMessageStart": "du hast eine neue Chat-Nachricht von ", + "chatMessageEnd": " erhalten.", + "commentedOnPost": " hat einen Beitrag den du beobachtest mit dem Titel „{postTitle}“ kommentiert. Klicke auf den Knopf, um diesen Kommentar zu sehen:", + "followedUserPosted": ", ein Nutzer dem du folgst, hat einen neuen Beitrag mit dem Titel „{postTitle}“ geschrieben. Klicke auf den Knopf, um diesen Beitrag zu sehen:", + "mentionedInComment": " hat dich in einem Kommentar zu dem Beitrag mit dem Titel „{postTitle}“ erwähnt. Klicke auf den Knopf, um den Kommentar zu sehen:", + "mentionedInPost": " hat Dich in einem Beitrag mit dem Titel „{postTitle}“ erwähnt. Klicke auf den Knopf, um den Beitrag zu sehen:", + "postInGroup": "jemand hat einen neuen Beitrag mit dem Titel „{postTitle}“ in einer deiner Gruppen geschrieben. Klicke auf den Knopf, um diesen Beitrag zu sehen:", + "removedUserFromGroup": "du wurdest aus der Gruppe „{groupName}“ entfernt.", + "userJoinedGroup": " ist der Gruppe „{groupName}“ beigetreten. Klicke auf den Knopf, um diese Gruppe zu sehen:", + "userLeftGroup": " hat die Gruppe „{groupName}“ verlassen. Klicke auf den Knopf, um diese Gruppe zu sehen:" +} diff --git a/backend/src/emails/locales/en.json b/backend/src/emails/locales/en.json new file mode 100644 index 000000000..30ca64655 --- /dev/null +++ b/backend/src/emails/locales/en.json @@ -0,0 +1,70 @@ +{ + "notification": "Notification", + "subjects": { + "changedGroupMemberRole": "Role in group changed", + "chatMessage": "New chat message", + "commentedOnPost": "New comment on post", + "followedUserPosted": "New post by followd user", + "mentionedInComment": "Mentioned in comment", + "mentionedInPost": "Mentioned in post", + "newEmail": "New E-Mail Address", + "removedUserFromGroup": "Removed from group", + "postInGroup": "New post in group", + "resetPassword": "Reset Password", + "userJoinedGroup": "User joined group", + "userLeftGroup": "User left group", + "wrongEmail": "Wrong E-mail?" + }, + "registration": { + "introduction": "Thank you for joining our cause – it's awesome to have you on board. There's just one tiny step missing before we can start shaping the world together … Please confirm your e-mail address by clicking the button below:", + "codeHint": "If the above button doesn't work, you can also copy the following code into your browser window: ", + "codeHintException": "However, this only works if you have registered through our website.", + "notYouStart": "If you didn't sign up for ", + "notYouEnd": " we recommend you to check it out! It's a social network from people for people who want to connect and change the world together.", + "ps": "PS: If you ignore this e-mail we will not create an account for you. ;)" + }, + "emailVerification": { + "codeHint": "If the above button doesn't work, you can also copy the following code into your browser window: ", + "introduction": "So, you want to change your e-mail? No problem! Just click the button below to verify your new address:", + "doNotChange": "If you don't want to change your e-mail address feel free to ignore this message. " + }, + "buttons": { + "confirmEmail": "Confirm your e-mail address", + "resetPassword": "Reset password", + "tryAgain": "Try a different e-mail", + "verifyEmail": "Verify e-mail address", + "viewChat": "Show Chat", + "viewComment": "View comment", + "viewGroup": "View group", + "viewPost": "View post" + }, + "general": { + "greeting": "Hello", + "seeYou": "See you soon on ", + "yourTeam": "– The {team} Team", + "settingsHint": "PS: If you don't want to receive e-mails anymore, change your ", + "settingsName": "notification settings", + "welcome": "Welcome to" + }, + "resetPassword": { + "codeHint": "If the above button doesn't work you can also copy the following code into your browser window: ", + "ignore": "If you didn't request a new password feel free to ignore this e-mail.", + "introduction": "So, you forgot your password? No problem! Just click the button below to reset it within the next 24 hours:" + }, + "wrongEmail": { + "ignoreEnd": " yet or if you didn't want to reset your password, please ignore this e-mail.", + "ignoreStart": "If you don't have an account at ", + "introduction": "You requested a password reset but unfortunately we couldn't find an account associated with your e-mail address. Did you maybe use another one when you signed up?" + }, + "changedGroupMemberRole": "your role in the group “{groupName}” has been changed. Click on the button to view this group:", + "chatMessageStart": "you have received a new chat message from ", + "chatMessageEnd": ".", + "commentedOnPost": " commented on a post that you are observing with the title “{postTitle}”. Click on the button to view this comment:", + "followedUserPosted": ", a user you are following, wrote a new post with the title “{postTitle}”. Click on the button to view this post:", + "mentionedInComment": " mentioned you in a comment to the post with the title “{postTitle}”. Click on the button to view this comment:", + "mentionedInPost": " mentioned you in a post with the title “{postTitle}”. Click on the button to view this post:", + "removedUserFromGroup": "you have been removed from the group “{groupName}”.", + "postInGroup": "someone wrote a new post with the title “{postTitle}” in one of your groups. Click on the button to view this post:", + "userJoinedGroup": " joined the group “{groupName}”. Click on the button to view this group:", + "userLeftGroup": " left the group “{groupName}”. Click on the button to view this group:" +} diff --git a/backend/src/emails/sendChatMessageMail.spec.ts b/backend/src/emails/sendChatMessageMail.spec.ts new file mode 100644 index 000000000..45835bbc3 --- /dev/null +++ b/backend/src/emails/sendChatMessageMail.spec.ts @@ -0,0 +1,87 @@ +import { sendChatMessageMail } from './sendEmail' + +const senderUser = { + allowEmbedIframes: false, + createdAt: '2025-04-30T00:16:49.610Z', + deleted: false, + disabled: false, + emailNotificationsChatMessage: true, + emailNotificationsCommentOnObservedPost: true, + emailNotificationsFollowingUsers: true, + emailNotificationsGroupMemberJoined: true, + emailNotificationsGroupMemberLeft: true, + emailNotificationsGroupMemberRemoved: true, + emailNotificationsGroupMemberRoleChanged: true, + emailNotificationsMention: true, + emailNotificationsPostInGroup: true, + encryptedPassword: '$2b$10$n.WujXapJrvn498lS97MD.gn8QwjWI9xlf8ckEYYtMTOPadMidcbG', + id: 'chatSender', + locale: 'en', + name: 'chatSender', + role: 'user', + showShoutsPublicly: false, + slug: 'chatsender', + termsAndConditionsAgreedAt: '2019-08-01T10:47:19.212Z', + termsAndConditionsAgreedVersion: '0.0.1', + updatedAt: '2025-04-30T00:16:49.610Z', +} + +const recipientUser = { + allowEmbedIframes: false, + createdAt: '2025-04-30T00:16:49.716Z', + deleted: false, + disabled: false, + emailNotificationsChatMessage: true, + emailNotificationsCommentOnObservedPost: true, + emailNotificationsFollowingUsers: true, + emailNotificationsGroupMemberJoined: true, + emailNotificationsGroupMemberLeft: true, + emailNotificationsGroupMemberRemoved: true, + emailNotificationsGroupMemberRoleChanged: true, + emailNotificationsMention: true, + emailNotificationsPostInGroup: true, + encryptedPassword: '$2b$10$KOrCHvEB5CM7D.P3VcX2z.pSSBZKZhPqHW/QKym6V1S6fiG..xtBq', + id: 'chatReceiver', + locale: 'en', + name: 'chatReceiver', + role: 'user', + showShoutsPublicly: false, + slug: 'chatreceiver', + termsAndConditionsAgreedAt: '2019-08-01T10:47:19.212Z', + termsAndConditionsAgreedVersion: '0.0.1', + updatedAt: '2025-04-30T00:16:49.716Z', +} + +describe('sendChatMessageMail', () => { + describe('English', () => { + beforeEach(() => { + recipientUser.locale = 'en' + }) + + it('chat_message template', async () => { + await expect( + sendChatMessageMail({ + email: 'user@example.org', + senderUser, + recipientUser, + }), + ).resolves.toMatchSnapshot() + }) + }) + + describe('German', () => { + beforeEach(() => { + recipientUser.locale = 'de' + }) + + it('chat_message template', async () => { + await expect( + sendChatMessageMail({ + email: 'user@example.org', + senderUser, + recipientUser, + }), + ).resolves.toMatchSnapshot() + }) + }) +}) diff --git a/backend/src/emails/sendEmail.ts b/backend/src/emails/sendEmail.ts new file mode 100644 index 000000000..580cc2f58 --- /dev/null +++ b/backend/src/emails/sendEmail.ts @@ -0,0 +1,321 @@ +/* eslint-disable @typescript-eslint/no-unsafe-assignment */ + +/* eslint-disable @typescript-eslint/no-unsafe-member-access */ +/* eslint-disable @typescript-eslint/no-unsafe-argument */ +/* eslint-disable @typescript-eslint/restrict-template-expressions */ +import path from 'node:path' + +import Email from 'email-templates' +import { createTransport } from 'nodemailer' + +// import type Email as EmailType from '@types/email-templates' + +import CONFIG, { nodemailerTransportOptions } from '@config/index' +import logosWebapp from '@config/logos' +import metadata from '@config/metadata' +import { UserDbProperties } from '@db/types/User' + +const welcomeImageUrl = new URL(logosWebapp.LOGO_WELCOME_PATH, CONFIG.CLIENT_URI) +const settingsUrl = new URL('/settings/notifications', CONFIG.CLIENT_URI) + +const defaultParams = { + welcomeImageUrl, + APPLICATION_NAME: CONFIG.APPLICATION_NAME, + ORGANIZATION_NAME: metadata.ORGANIZATION_NAME, + ORGANIZATION_URL: CONFIG.ORGANIZATION_URL, + supportUrl: CONFIG.SUPPORT_URL, + settingsUrl, + renderSettingsUrl: true, +} + +const from = `${CONFIG.APPLICATION_NAME} <${CONFIG.EMAIL_DEFAULT_SENDER}>` + +const transport = createTransport(nodemailerTransportOptions) + +const email = new Email({ + message: { + from, + }, + transport, + i18n: { + locales: ['en', 'de'], + defaultLocale: 'en', + retryInDefaultLocale: false, + directory: path.join(__dirname, 'locales'), + updateFiles: false, + objectNotation: true, + mustacheConfig: { + tags: ['{', '}'], + disable: false, + }, + }, + send: CONFIG.SEND_MAIL, + preview: false, + // This is very useful to see the emails sent by the unit tests + /* + preview: { + open: { + app: 'brave-browser', + }, + }, + */ +}) + +interface OriginalMessage { + to: string + from: string + attachments: string[] + subject: string + html: string + text: string +} + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export const sendNotificationMail = async (notification: any): Promise => { + const locale = notification?.to?.locale + const to = notification?.email + const name = notification?.to?.name + const template = notification?.reason + + try { + const { originalMessage } = await email.send({ + template: path.join(__dirname, 'templates', template), + message: { + to, + }, + locals: { + ...defaultParams, + locale, + name, + postTitle: + notification?.from?.__typename === 'Comment' + ? notification?.from?.post?.title + : notification?.from?.title, + postUrl: new URL( + notification?.from?.__typename === 'Comment' + ? `/post/${notification?.from?.post?.id}/${notification?.from?.post?.slug}` + : `/post/${notification?.from?.id}/${notification?.from?.slug}`, + CONFIG.CLIENT_URI, + ), + postAuthorName: + notification?.from?.__typename === 'Comment' + ? undefined + : notification?.from?.author?.name, + postAuthorUrl: + notification?.from?.__typename === 'Comment' + ? undefined + : new URL( + `user/${notification?.from?.author?.id}/${notification?.from?.author?.slug}`, + CONFIG.CLIENT_URI, + ), + commenterName: + notification?.from?.__typename === 'Comment' + ? notification?.from?.author?.name + : undefined, + commenterUrl: + notification?.from?.__typename === 'Comment' + ? new URL( + `/user/${notification?.from?.author?.id}/${notification?.from?.author?.slug}`, + CONFIG.CLIENT_URI, + ) + : undefined, + commentUrl: + notification?.from?.__typename === 'Comment' + ? new URL( + `/post/${notification?.from?.post?.id}/${notification?.from?.post?.slug}#commentId-${notification?.from?.id}`, + CONFIG.CLIENT_URI, + ) + : undefined, + // chattingUser: 'SR-71', + // chatUrl: new URL('/chat', CONFIG.CLIENT_URI), + groupUrl: + notification?.from?.__typename === 'Group' + ? new URL( + `/group/${notification?.from?.id}/${notification?.from?.slug}`, + CONFIG.CLIENT_URI, + ) + : undefined, + groupName: + notification?.from?.__typename === 'Group' ? notification?.from?.name : undefined, + groupRelatedUserName: + notification?.from?.__typename === 'Group' ? notification?.relatedUser?.name : undefined, + groupRelatedUserUrl: + notification?.from?.__typename === 'Group' + ? new URL( + `/user/${notification?.relatedUser?.id}/${notification?.relatedUser?.slug}`, + CONFIG.CLIENT_URI, + ) + : undefined, + }, + }) + return originalMessage as OriginalMessage + } catch (error) { + throw new Error(error) + } +} + +export interface ChatMessageEmailInput { + senderUser: UserDbProperties + recipientUser: UserDbProperties + email: string +} + +export const sendChatMessageMail = async ( + data: ChatMessageEmailInput, +): Promise => { + const { senderUser, recipientUser } = data + const to = data.email + try { + const { originalMessage } = await email.send({ + template: path.join(__dirname, 'templates', 'chat_message'), + message: { + to, + }, + locals: { + ...defaultParams, + locale: recipientUser.locale, + name: recipientUser.name, + chattingUser: senderUser.name, + chattingUserUrl: new URL(`/user/${senderUser.id}/${senderUser.slug}`, CONFIG.CLIENT_URI), + chatUrl: new URL('/chat', CONFIG.CLIENT_URI), + }, + }) + return originalMessage as OriginalMessage + } catch (error) { + throw new Error(error) + } +} + +interface VerifyMailInput { + email: string + nonce: string + locale: string +} + +interface RegistrationMailInput extends VerifyMailInput { + inviteCode?: string +} + +export const sendRegistrationMail = async ( + data: RegistrationMailInput, +): Promise => { + const { nonce, locale, inviteCode } = data + const to = data.email + const actionUrl = new URL('/registration', CONFIG.CLIENT_URI) + actionUrl.searchParams.set('email', to) + actionUrl.searchParams.set('nonce', nonce) + if (inviteCode) { + actionUrl.searchParams.set('inviteCode', inviteCode) + actionUrl.searchParams.set('method', 'invite-code') + } else { + actionUrl.searchParams.set('method', 'invite-mail') + } + + try { + const { originalMessage } = await email.send({ + template: path.join(__dirname, 'templates', 'registration'), + message: { + to, + }, + locals: { + ...defaultParams, + locale, + actionUrl, + nonce, + renderSettingsUrl: false, + }, + }) + return originalMessage as OriginalMessage + } catch (error) { + throw new Error(error) + } +} + +interface EmailVerificationInput extends VerifyMailInput { + name: string +} + +export const sendEmailVerification = async ( + data: EmailVerificationInput, +): Promise => { + const { nonce, locale, name } = data + const to = data.email + const actionUrl = new URL('/settings/my-email-address/verify', CONFIG.CLIENT_URI) + actionUrl.searchParams.set('email', to) + actionUrl.searchParams.set('nonce', nonce) + + try { + const { originalMessage } = await email.send({ + template: path.join(__dirname, 'templates', 'emailVerification'), + message: { + to, + }, + locals: { + ...defaultParams, + locale, + actionUrl, + nonce, + name, + renderSettingsUrl: false, + }, + }) + return originalMessage as OriginalMessage + } catch (error) { + throw new Error(error) + } +} + +export const sendResetPasswordMail = async ( + data: EmailVerificationInput, +): Promise => { + const { nonce, locale, name } = data + const to = data.email + const actionUrl = new URL('/password-reset/change-password', CONFIG.CLIENT_URI) + actionUrl.searchParams.set('email', to) + actionUrl.searchParams.set('nonce', nonce) + try { + const { originalMessage } = await email.send({ + template: path.join(__dirname, 'templates', 'resetPassword'), + message: { + to, + }, + locals: { + ...defaultParams, + locale, + actionUrl, + nonce, + name, + renderSettingsUrl: false, + }, + }) + return originalMessage as OriginalMessage + } catch (error) { + throw new Error(error) + } +} + +export const sendWrongEmail = async (data: { + locale: string + email: string +}): Promise => { + const { locale } = data + const to = data.email + const actionUrl = new URL('/password-reset/request', CONFIG.CLIENT_URI) + try { + const { originalMessage } = await email.send({ + template: path.join(__dirname, 'templates', 'wrongEmail'), + message: { + to, + }, + locals: { + ...defaultParams, + locale, + actionUrl, + renderSettingsUrl: false, + }, + }) + return originalMessage as OriginalMessage + } catch (error) { + throw new Error(error) + } +} diff --git a/backend/src/emails/sendEmailVerification.spec.ts b/backend/src/emails/sendEmailVerification.spec.ts new file mode 100644 index 000000000..0863dd9db --- /dev/null +++ b/backend/src/emails/sendEmailVerification.spec.ts @@ -0,0 +1,35 @@ +import { sendEmailVerification } from './sendEmail' + +describe('sendEmailVerification', () => { + const data: { + email: string + nonce: string + locale: string + name: string + } = { + email: 'user@example.org', + nonce: '123456', + locale: 'en', + name: 'User', + } + + describe('English', () => { + beforeEach(() => { + data.locale = 'en' + }) + + it('renders correctly', async () => { + await expect(sendEmailVerification(data)).resolves.toMatchSnapshot() + }) + }) + + describe('German', () => { + beforeEach(() => { + data.locale = 'de' + }) + + it('renders correctly', async () => { + await expect(sendEmailVerification(data)).resolves.toMatchSnapshot() + }) + }) +}) diff --git a/backend/src/emails/sendNotificationMail.spec.ts b/backend/src/emails/sendNotificationMail.spec.ts new file mode 100644 index 000000000..fee641e2e --- /dev/null +++ b/backend/src/emails/sendNotificationMail.spec.ts @@ -0,0 +1,475 @@ +import { sendNotificationMail } from './sendEmail' + +describe('sendNotificationMail', () => { + let locale = 'en' + + describe('English', () => { + beforeEach(() => { + locale = 'en' + }) + + it('followed_user_posted template', async () => { + await expect( + sendNotificationMail({ + reason: 'followed_user_posted', + email: 'user@example.org', + to: { + name: 'Jenny Rostock', + id: 'u1', + slug: 'jenny-rostock', + locale, + }, + from: { + id: 'p1', + slug: 'new-post', + title: 'New Post', + author: { + id: 'u2', + name: 'Peter Lustig', + slug: 'peter-lustig', + }, + }, + }), + ).resolves.toMatchSnapshot() + }) + + it('post_in_group template', async () => { + await expect( + sendNotificationMail({ + reason: 'post_in_group', + email: 'user@example.org', + to: { + name: 'Jenny Rostock', + id: 'u1', + slug: 'jenny-rostock', + locale, + }, + from: { + id: 'p1', + slug: 'new-post', + title: 'New Post', + author: { + id: 'u2', + name: 'Peter Lustig', + slug: 'peter-lustig', + }, + }, + }), + ).resolves.toMatchSnapshot() + }) + + it('mentioned_in_post template', async () => { + await expect( + sendNotificationMail({ + reason: 'mentioned_in_post', + email: 'user@example.org', + to: { + name: 'Jenny Rostock', + id: 'u1', + slug: 'jenny-rostock', + locale, + }, + from: { + id: 'p1', + slug: 'new-post', + title: 'New Post', + author: { + id: 'u2', + name: 'Peter Lustig', + slug: 'peter-lustig', + }, + }, + }), + ).resolves.toMatchSnapshot() + }) + + it('commented_on_post template', async () => { + await expect( + sendNotificationMail({ + reason: 'commented_on_post', + email: 'user@example.org', + to: { + name: 'Jenny Rostock', + id: 'u1', + slug: 'jenny-rostock', + locale, + }, + from: { + __typename: 'Comment', + id: 'c1', + slug: 'new-comment', + author: { + id: 'u2', + name: 'Peter Lustig', + slug: 'peter-lustig', + }, + post: { + id: 'p1', + slug: 'new-post', + title: 'New Post', + }, + }, + }), + ).resolves.toMatchSnapshot() + }) + + it('mentioned_in_comment template', async () => { + await expect( + sendNotificationMail({ + reason: 'mentioned_in_comment', + email: 'user@example.org', + to: { + name: 'Jenny Rostock', + id: 'u1', + slug: 'jenny-rostock', + locale, + }, + from: { + __typename: 'Comment', + id: 'c1', + slug: 'new-comment', + author: { + id: 'u2', + name: 'Peter Lustig', + slug: 'peter-lustig', + }, + post: { + id: 'p1', + slug: 'new-post', + title: 'New Post', + }, + }, + }), + ).resolves.toMatchSnapshot() + }) + + it('changed_group_member_role template', async () => { + await expect( + sendNotificationMail({ + reason: 'changed_group_member_role', + email: 'user@example.org', + to: { + name: 'Jenny Rostock', + id: 'u1', + slug: 'jenny-rostock', + locale, + }, + from: { + __typename: 'Group', + id: 'g1', + slug: 'the-group', + name: 'The Group', + }, + }), + ).resolves.toMatchSnapshot() + }) + + it('user_joined_group template', async () => { + await expect( + sendNotificationMail({ + reason: 'user_joined_group', + email: 'user@example.org', + to: { + name: 'Jenny Rostock', + id: 'u1', + slug: 'jenny-rostock', + locale, + }, + from: { + __typename: 'Group', + id: 'g1', + slug: 'the-group', + name: 'The Group', + }, + relatedUser: { + id: 'u2', + name: 'Peter Lustig', + slug: 'peter-lustig', + }, + }), + ).resolves.toMatchSnapshot() + }) + + it('user_left_group template', async () => { + await expect( + sendNotificationMail({ + reason: 'user_left_group', + email: 'user@example.org', + to: { + name: 'Jenny Rostock', + id: 'u1', + slug: 'jenny-rostock', + locale, + }, + from: { + __typename: 'Group', + id: 'g1', + slug: 'the-group', + name: 'The Group', + }, + relatedUser: { + id: 'u2', + name: 'Peter Lustig', + slug: 'peter-lustig', + }, + }), + ).resolves.toMatchSnapshot() + }) + + it('removed_user_from_group template', async () => { + await expect( + sendNotificationMail({ + reason: 'removed_user_from_group', + email: 'user@example.org', + to: { + name: 'Jenny Rostock', + id: 'u1', + slug: 'jenny-rostock', + locale, + }, + from: { + __typename: 'Group', + id: 'g1', + slug: 'the-group', + name: 'The Group', + }, + }), + ).resolves.toMatchSnapshot() + }) + }) + + describe('German', () => { + beforeEach(() => { + locale = 'de' + }) + + it('followed_user_posted template', async () => { + await expect( + sendNotificationMail({ + reason: 'followed_user_posted', + email: 'user@example.org', + to: { + name: 'Jenny Rostock', + id: 'u1', + slug: 'jenny-rostock', + locale, + }, + from: { + id: 'p1', + slug: 'new-post', + title: 'New Post', + author: { + id: 'u2', + name: 'Peter Lustig', + slug: 'peter-lustig', + }, + }, + }), + ).resolves.toMatchSnapshot() + }) + + it('post_in_group template', async () => { + await expect( + sendNotificationMail({ + reason: 'post_in_group', + email: 'user@example.org', + to: { + name: 'Jenny Rostock', + id: 'u1', + slug: 'jenny-rostock', + locale, + }, + from: { + id: 'p1', + slug: 'new-post', + title: 'New Post', + author: { + id: 'u2', + name: 'Peter Lustig', + slug: 'peter-lustig', + }, + }, + }), + ).resolves.toMatchSnapshot() + }) + + it('mentioned_in_post template', async () => { + await expect( + sendNotificationMail({ + reason: 'mentioned_in_post', + email: 'user@example.org', + to: { + name: 'Jenny Rostock', + id: 'u1', + slug: 'jenny-rostock', + locale, + }, + from: { + id: 'p1', + slug: 'new-post', + title: 'New Post', + author: { + id: 'u2', + name: 'Peter Lustig', + slug: 'peter-lustig', + }, + }, + }), + ).resolves.toMatchSnapshot() + }) + + it('commented_on_post template', async () => { + await expect( + sendNotificationMail({ + reason: 'commented_on_post', + email: 'user@example.org', + to: { + name: 'Jenny Rostock', + id: 'u1', + slug: 'jenny-rostock', + locale, + }, + from: { + __typename: 'Comment', + id: 'c1', + slug: 'new-comment', + author: { + id: 'u2', + name: 'Peter Lustig', + slug: 'peter-lustig', + }, + post: { + id: 'p1', + slug: 'new-post', + title: 'New Post', + }, + }, + }), + ).resolves.toMatchSnapshot() + }) + + it('mentioned_in_comment template', async () => { + await expect( + sendNotificationMail({ + reason: 'mentioned_in_comment', + email: 'user@example.org', + to: { + name: 'Jenny Rostock', + id: 'u1', + slug: 'jenny-rostock', + locale, + }, + from: { + __typename: 'Comment', + id: 'c1', + slug: 'new-comment', + author: { + id: 'u2', + name: 'Peter Lustig', + slug: 'peter-lustig', + }, + post: { + id: 'p1', + slug: 'new-post', + title: 'New Post', + }, + }, + }), + ).resolves.toMatchSnapshot() + }) + + it('changed_group_member_role template', async () => { + await expect( + sendNotificationMail({ + reason: 'changed_group_member_role', + email: 'user@example.org', + to: { + name: 'Jenny Rostock', + id: 'u1', + slug: 'jenny-rostock', + locale, + }, + from: { + __typename: 'Group', + id: 'g1', + slug: 'the-group', + name: 'The Group', + }, + }), + ).resolves.toMatchSnapshot() + }) + + it('user_joined_group template', async () => { + await expect( + sendNotificationMail({ + reason: 'user_joined_group', + email: 'user@example.org', + to: { + name: 'Jenny Rostock', + id: 'u1', + slug: 'jenny-rostock', + locale, + }, + from: { + __typename: 'Group', + id: 'g1', + slug: 'the-group', + name: 'The Group', + }, + relatedUser: { + id: 'u2', + name: 'Peter Lustig', + slug: 'peter-lustig', + }, + }), + ).resolves.toMatchSnapshot() + }) + + it('user_left_group template', async () => { + await expect( + sendNotificationMail({ + reason: 'user_left_group', + email: 'user@example.org', + to: { + name: 'Jenny Rostock', + id: 'u1', + slug: 'jenny-rostock', + locale, + }, + from: { + __typename: 'Group', + id: 'g1', + slug: 'the-group', + name: 'The Group', + }, + relatedUser: { + id: 'u2', + name: 'Peter Lustig', + slug: 'peter-lustig', + }, + }), + ).resolves.toMatchSnapshot() + }) + + it('removed_user_from_group template', async () => { + await expect( + sendNotificationMail({ + reason: 'removed_user_from_group', + email: 'user@example.org', + to: { + name: 'Jenny Rostock', + id: 'u1', + slug: 'jenny-rostock', + locale, + }, + from: { + __typename: 'Group', + id: 'g1', + slug: 'the-group', + name: 'The Group', + }, + }), + ).resolves.toMatchSnapshot() + }) + }) +}) diff --git a/backend/src/emails/sendRegistrationMail.spec.ts b/backend/src/emails/sendRegistrationMail.spec.ts new file mode 100644 index 000000000..ea66771c2 --- /dev/null +++ b/backend/src/emails/sendRegistrationMail.spec.ts @@ -0,0 +1,63 @@ +import { sendRegistrationMail } from './sendEmail' + +describe('sendRegistrationMail', () => { + const data: { + email: string + nonce: string + locale: string + inviteCode?: string + } = { + email: 'user@example.org', + nonce: '123456', + locale: 'en', + inviteCode: 'welcome', + } + + describe('with invite code', () => { + describe('English', () => { + beforeEach(() => { + data.locale = 'en' + data.inviteCode = 'welcome' + }) + + it('renders correctly', async () => { + await expect(sendRegistrationMail(data)).resolves.toMatchSnapshot() + }) + }) + + describe('German', () => { + beforeEach(() => { + data.locale = 'de' + data.inviteCode = 'welcome' + }) + + it('renders correctly', async () => { + await expect(sendRegistrationMail(data)).resolves.toMatchSnapshot() + }) + }) + }) + + describe('without invite code', () => { + describe('English', () => { + beforeEach(() => { + data.locale = 'en' + delete data.inviteCode + }) + + it('renders correctly', async () => { + await expect(sendRegistrationMail(data)).resolves.toMatchSnapshot() + }) + }) + + describe('German', () => { + beforeEach(() => { + data.locale = 'de' + delete data.inviteCode + }) + + it('renders correctly', async () => { + await expect(sendRegistrationMail(data)).resolves.toMatchSnapshot() + }) + }) + }) +}) diff --git a/backend/src/emails/sendResetPasswordMail.spec.ts b/backend/src/emails/sendResetPasswordMail.spec.ts new file mode 100644 index 000000000..e37af2e7b --- /dev/null +++ b/backend/src/emails/sendResetPasswordMail.spec.ts @@ -0,0 +1,35 @@ +import { sendResetPasswordMail } from './sendEmail' + +describe('sendResetPasswordMail', () => { + const data: { + email: string + nonce: string + locale: string + name: string + } = { + email: 'user@example.org', + nonce: '123456', + locale: 'en', + name: 'Jenny Rostock', + } + + describe('English', () => { + beforeEach(() => { + data.locale = 'en' + }) + + it('renders correctly', async () => { + await expect(sendResetPasswordMail(data)).resolves.toMatchSnapshot() + }) + }) + + describe('German', () => { + beforeEach(() => { + data.locale = 'de' + }) + + it('renders correctly', async () => { + await expect(sendResetPasswordMail(data)).resolves.toMatchSnapshot() + }) + }) +}) diff --git a/backend/src/emails/sendWrongEmail.spec.ts b/backend/src/emails/sendWrongEmail.spec.ts new file mode 100644 index 000000000..854d935f9 --- /dev/null +++ b/backend/src/emails/sendWrongEmail.spec.ts @@ -0,0 +1,31 @@ +import { sendWrongEmail } from './sendEmail' + +describe('sendWrongEmail', () => { + const data: { + email: string + locale: string + } = { + email: 'user@example.org', + locale: 'en', + } + + describe('English', () => { + beforeEach(() => { + data.locale = 'en' + }) + + it('renders correctly', async () => { + await expect(sendWrongEmail(data)).resolves.toMatchSnapshot() + }) + }) + + describe('German', () => { + beforeEach(() => { + data.locale = 'de' + }) + + it('renders correctly', async () => { + await expect(sendWrongEmail(data)).resolves.toMatchSnapshot() + }) + }) +}) diff --git a/backend/src/emails/templates/changed_group_member_role/html.pug b/backend/src/emails/templates/changed_group_member_role/html.pug new file mode 100644 index 000000000..acb50546d --- /dev/null +++ b/backend/src/emails/templates/changed_group_member_role/html.pug @@ -0,0 +1,7 @@ +extend ../layout.pug + +block content + .content + - var groupUrl = groupUrl + p= t('changedGroupMemberRole', { groupName }) + a.button(href=groupUrl)= t('buttons.viewGroup') diff --git a/backend/src/emails/templates/changed_group_member_role/subject.pug b/backend/src/emails/templates/changed_group_member_role/subject.pug new file mode 100644 index 000000000..2cd0d345e --- /dev/null +++ b/backend/src/emails/templates/changed_group_member_role/subject.pug @@ -0,0 +1 @@ += `${APPLICATION_NAME} – ${t('notification')}: ${t('subjects.changedGroupMemberRole')}` \ No newline at end of file diff --git a/backend/src/emails/templates/chat_message/html.pug b/backend/src/emails/templates/chat_message/html.pug new file mode 100644 index 000000000..d49581d7d --- /dev/null +++ b/backend/src/emails/templates/chat_message/html.pug @@ -0,0 +1,8 @@ +extend ../layout.pug + +block content + .content + p= t('chatMessageStart') + a.user(href=chattingUserUrl)= chattingUser + = t('chatMessageEnd') + a.button(href=chatUrl)= t('buttons.viewChat') diff --git a/backend/src/emails/templates/chat_message/subject.pug b/backend/src/emails/templates/chat_message/subject.pug new file mode 100644 index 000000000..73206e2d6 --- /dev/null +++ b/backend/src/emails/templates/chat_message/subject.pug @@ -0,0 +1 @@ += `${APPLICATION_NAME} – ${t('notification')}: ${t('subjects.chatMessage')}` \ No newline at end of file diff --git a/backend/src/emails/templates/commented_on_post/html.pug b/backend/src/emails/templates/commented_on_post/html.pug new file mode 100644 index 000000000..b139e0f9d --- /dev/null +++ b/backend/src/emails/templates/commented_on_post/html.pug @@ -0,0 +1,8 @@ +extend ../layout.pug + +block content + .content + p + a.user(href=commenterUrl)= commenterName + = t('commentedOnPost', { postTitle}) + a.button(href=commentUrl)= t('buttons.viewComment') diff --git a/backend/src/emails/templates/commented_on_post/subject.pug b/backend/src/emails/templates/commented_on_post/subject.pug new file mode 100644 index 000000000..6a3d4da35 --- /dev/null +++ b/backend/src/emails/templates/commented_on_post/subject.pug @@ -0,0 +1 @@ += `${APPLICATION_NAME} – ${t('notification')}: ${t('subjects.commentedOnPost')}` \ No newline at end of file diff --git a/backend/src/emails/templates/emailVerification/html.pug b/backend/src/emails/templates/emailVerification/html.pug new file mode 100644 index 000000000..7483106e4 --- /dev/null +++ b/backend/src/emails/templates/emailVerification/html.pug @@ -0,0 +1,10 @@ +extend ../layout.pug + +block content + .content + p= t('emailVerification.introduction') + a.button(href=actionUrl)= t('buttons.verifyEmail') + p= t('emailVerification.doNotChange') + + p= t('emailVerification.codeHint') + span= nonce diff --git a/backend/src/emails/templates/emailVerification/subject.pug b/backend/src/emails/templates/emailVerification/subject.pug new file mode 100644 index 000000000..5fc98a7b9 --- /dev/null +++ b/backend/src/emails/templates/emailVerification/subject.pug @@ -0,0 +1 @@ += `${t('subjects.newEmail')} ${APPLICATION_NAME}` \ No newline at end of file diff --git a/backend/src/emails/templates/followed_user_posted/html.pug b/backend/src/emails/templates/followed_user_posted/html.pug new file mode 100644 index 000000000..1b2a0114f --- /dev/null +++ b/backend/src/emails/templates/followed_user_posted/html.pug @@ -0,0 +1,8 @@ +extend ../layout.pug + +block content + .content + p + a.user(href=postAuthorUrl)= postAuthorName + = t('followedUserPosted', { postTitle }) + a.button(href=postUrl)= t('buttons.viewPost') diff --git a/backend/src/emails/templates/followed_user_posted/subject.pug b/backend/src/emails/templates/followed_user_posted/subject.pug new file mode 100644 index 000000000..0da84b83c --- /dev/null +++ b/backend/src/emails/templates/followed_user_posted/subject.pug @@ -0,0 +1 @@ += `${APPLICATION_NAME} – ${t('notification')}: ${t('subjects.followedUserPosted')}` \ No newline at end of file diff --git a/backend/src/emails/templates/includes/footer.pug b/backend/src/emails/templates/includes/footer.pug new file mode 100644 index 000000000..a8deeac84 --- /dev/null +++ b/backend/src/emails/templates/includes/footer.pug @@ -0,0 +1,5 @@ +footer + .footer + - var organizationUrl = ORGANIZATION_URL + - var organizationName = ORGANIZATION_NAME + a(href=organizationUrl)= organizationName \ No newline at end of file diff --git a/backend/src/emails/templates/includes/greeting.pug b/backend/src/emails/templates/includes/greeting.pug new file mode 100644 index 000000000..6b682fc2d --- /dev/null +++ b/backend/src/emails/templates/includes/greeting.pug @@ -0,0 +1,17 @@ +//- This sets the greeting at the end of every e-mail +.text-block + - var organizationUrl = ORGANIZATION_URL + - var team = APPLICATION_NAME + - var settingsUrl = settingsUrl + - var renderSettingsUrl = renderSettingsUrl + p= t('general.seeYou') + a.organization(href=organizationUrl)= team + | ! + p= t('general.yourTeam', { team }) + + if renderSettingsUrl + br + p= t('general.settingsHint') + a.settings(href=settingsUrl)= t('general.settingsName') + | ! + diff --git a/backend/src/emails/templates/includes/header.pug b/backend/src/emails/templates/includes/header.pug new file mode 100644 index 000000000..09b2b07b7 --- /dev/null +++ b/backend/src/emails/templates/includes/header.pug @@ -0,0 +1,9 @@ +header + .head + - var img = welcomeImageUrl + img.head-logo( + alt="Welcome Image" + loading="lazy" + src=img + ) + diff --git a/backend/src/emails/templates/includes/salutation.pug b/backend/src/emails/templates/includes/salutation.pug new file mode 100644 index 000000000..faca3bb64 --- /dev/null +++ b/backend/src/emails/templates/includes/salutation.pug @@ -0,0 +1 @@ +h2= `${t('general.greeting')} ${name},` diff --git a/backend/src/emails/templates/includes/webflow.css b/backend/src/emails/templates/includes/webflow.css new file mode 100644 index 000000000..1dc1f0b24 --- /dev/null +++ b/backend/src/emails/templates/includes/webflow.css @@ -0,0 +1,69 @@ +body{ + display: block; + font-family: Lato, sans-serif; + font-size: 17px; + text-align: left; + text-align: -webkit-left; + justify-content: center; + padding: 15px; + margin: 0px; +} + +h2 { + margin-top: 25px; + font-size: 25px; + font-weight: normal; + line-height: 22px; + color: #333333; +} + +.container { + max-width: 680px; + margin: 0 auto; + display: block; +} + +.head-logo { + width: 60%; + height: auto; + display: block; + margin-left: auto; + margin-right: auto; +} + +a { + color: #17b53e; +} + +a.button { + background: #17b53e; + font-family: Lato, sans-serif; + font-size: 16px; + line-height: 15px; + text-decoration: none; + text-align:center; + padding: 13px 17px; + color: #ffffff; + display: table; + margin-left: auto; + margin-right: auto; + border-radius: 4px; +} + +span { + color: #17b53e; +} + +.text-block { + margin-top: 20px; + color: #000000; +} + +footer { + padding: 20px; + font-family: Lato, sans-serif; + font-size: 12px; + line-height: 15px; + text-align: center; + color: #888888; +} diff --git a/backend/src/emails/templates/includes/welcome.pug b/backend/src/emails/templates/includes/welcome.pug new file mode 100644 index 000000000..f4ec6f8bd --- /dev/null +++ b/backend/src/emails/templates/includes/welcome.pug @@ -0,0 +1 @@ +h2= `${t('general.welcome')} ${APPLICATION_NAME}!` \ No newline at end of file diff --git a/backend/src/emails/templates/layout.pug b/backend/src/emails/templates/layout.pug new file mode 100644 index 000000000..faaadb5d3 --- /dev/null +++ b/backend/src/emails/templates/layout.pug @@ -0,0 +1,30 @@ +doctype html +html(lang=locale) + head + meta( + content="multipart/html; charset=UTF-8" + http-equiv="content-type" + ) + meta( + name="viewport" + content="width=device-width, initial-scale=1" + ) + style. + .wf-force-outline-none[tabindex="-1"]:focus{outline:none;} + style + include includes/webflow.css + + - var name = name + body + div.container + include includes/header.pug + if name + include includes/salutation.pug + else + include includes/welcome.pug + + .wrapper + block content + include includes/greeting.pug + + include includes/footer.pug diff --git a/backend/src/emails/templates/mentioned_in_comment/html.pug b/backend/src/emails/templates/mentioned_in_comment/html.pug new file mode 100644 index 000000000..a7b9be1de --- /dev/null +++ b/backend/src/emails/templates/mentioned_in_comment/html.pug @@ -0,0 +1,8 @@ +extend ../layout.pug + +block content + .content + p + a.user(href=commenterUrl)= commenterName + = t('mentionedInComment', { postTitle}) + a.button(href=commentUrl)= t('buttons.viewComment') diff --git a/backend/src/emails/templates/mentioned_in_comment/subject.pug b/backend/src/emails/templates/mentioned_in_comment/subject.pug new file mode 100644 index 000000000..70d094e59 --- /dev/null +++ b/backend/src/emails/templates/mentioned_in_comment/subject.pug @@ -0,0 +1 @@ += `${APPLICATION_NAME} – ${t('notification')}: ${t('subjects.mentionedInComment')}` \ No newline at end of file diff --git a/backend/src/emails/templates/mentioned_in_post/html.pug b/backend/src/emails/templates/mentioned_in_post/html.pug new file mode 100644 index 000000000..5a31c7258 --- /dev/null +++ b/backend/src/emails/templates/mentioned_in_post/html.pug @@ -0,0 +1,8 @@ +extend ../layout.pug + +block content + .content + p + a.user(href=postAuthorUrl)= postAuthorName + = t('mentionedInPost', { postTitle }) + a.button(href=postUrl)= t('buttons.viewPost') diff --git a/backend/src/emails/templates/mentioned_in_post/subject.pug b/backend/src/emails/templates/mentioned_in_post/subject.pug new file mode 100644 index 000000000..c318630a3 --- /dev/null +++ b/backend/src/emails/templates/mentioned_in_post/subject.pug @@ -0,0 +1 @@ += `${APPLICATION_NAME} – ${t('notification')}: ${t('subjects.mentionedInPost')}` \ No newline at end of file diff --git a/backend/src/emails/templates/post_in_group/html.pug b/backend/src/emails/templates/post_in_group/html.pug new file mode 100644 index 000000000..bc69ed2e9 --- /dev/null +++ b/backend/src/emails/templates/post_in_group/html.pug @@ -0,0 +1,7 @@ +extend ../layout.pug + +block content + .content + - var postUrl = postUrl + p= t('postInGroup', { postTitle}) + a.button(href=postUrl)= t('buttons.viewPost') diff --git a/backend/src/emails/templates/post_in_group/subject.pug b/backend/src/emails/templates/post_in_group/subject.pug new file mode 100644 index 000000000..1f989190d --- /dev/null +++ b/backend/src/emails/templates/post_in_group/subject.pug @@ -0,0 +1 @@ += `${APPLICATION_NAME} – ${t('notification')}: ${t('subjects.postInGroup')}` \ No newline at end of file diff --git a/backend/src/emails/templates/registration/html.pug b/backend/src/emails/templates/registration/html.pug new file mode 100644 index 000000000..b50aaca31 --- /dev/null +++ b/backend/src/emails/templates/registration/html.pug @@ -0,0 +1,15 @@ +extend ../layout.pug + +block content + .content + p= t('registration.introduction') + a.button(href=actionUrl)= t('buttons.confirmEmail') + p= t('registration.codeHint') + span= nonce + p= t('registration.codeHintException') + + p= t('registration.notYouStart') + a(href=ORGANIZATION_LINK)= APPLICATION_NAME + = t('registration.notYouEnd') + + p= t('registration.ps') \ No newline at end of file diff --git a/backend/src/emails/templates/registration/subject.pug b/backend/src/emails/templates/registration/subject.pug new file mode 100644 index 000000000..7e9dbec7f --- /dev/null +++ b/backend/src/emails/templates/registration/subject.pug @@ -0,0 +1 @@ += `${t('general.welcome')} ${APPLICATION_NAME}` \ No newline at end of file diff --git a/backend/src/emails/templates/removed_user_from_group/html.pug b/backend/src/emails/templates/removed_user_from_group/html.pug new file mode 100644 index 000000000..cb991540e --- /dev/null +++ b/backend/src/emails/templates/removed_user_from_group/html.pug @@ -0,0 +1,5 @@ +extend ../layout.pug + +block content + .content + p= t('removedUserFromGroup', { groupName }) diff --git a/backend/src/emails/templates/removed_user_from_group/subject.pug b/backend/src/emails/templates/removed_user_from_group/subject.pug new file mode 100644 index 000000000..c70855f62 --- /dev/null +++ b/backend/src/emails/templates/removed_user_from_group/subject.pug @@ -0,0 +1 @@ += `${APPLICATION_NAME} – ${t('notification')}: ${t('subjects.removedUserFromGroup')}` \ No newline at end of file diff --git a/backend/src/emails/templates/resetPassword/html.pug b/backend/src/emails/templates/resetPassword/html.pug new file mode 100644 index 000000000..f10ee01c2 --- /dev/null +++ b/backend/src/emails/templates/resetPassword/html.pug @@ -0,0 +1,9 @@ +extend ../layout.pug + +block content + .content + p= t('resetPassword.introduction') + a.button(href=actionUrl)= t('buttons.confirmEmail') + p= t('resetPassword.ignore') + p= t('resetPassword.codeHint') + span= nonce diff --git a/backend/src/emails/templates/resetPassword/subject.pug b/backend/src/emails/templates/resetPassword/subject.pug new file mode 100644 index 000000000..047af2052 --- /dev/null +++ b/backend/src/emails/templates/resetPassword/subject.pug @@ -0,0 +1 @@ += `${t('subjects.resetPassword')} ${APPLICATION_NAME}` \ No newline at end of file diff --git a/backend/src/emails/templates/user_joined_group/html.pug b/backend/src/emails/templates/user_joined_group/html.pug new file mode 100644 index 000000000..00bc116a8 --- /dev/null +++ b/backend/src/emails/templates/user_joined_group/html.pug @@ -0,0 +1,8 @@ +extend ../layout.pug + +block content + .content + p + a.user(href=groupRelatedUserUrl)= groupRelatedUserName + = t('userJoinedGroup', { groupName }) + a.button(href=groupUrl)= t('buttons.viewGroup') diff --git a/backend/src/emails/templates/user_joined_group/subject.pug b/backend/src/emails/templates/user_joined_group/subject.pug new file mode 100644 index 000000000..4e2cae4a1 --- /dev/null +++ b/backend/src/emails/templates/user_joined_group/subject.pug @@ -0,0 +1 @@ += `${APPLICATION_NAME} – ${t('notification')}: ${t('subjects.userJoinedGroup')}` \ No newline at end of file diff --git a/backend/src/emails/templates/user_left_group/html.pug b/backend/src/emails/templates/user_left_group/html.pug new file mode 100644 index 000000000..73374e464 --- /dev/null +++ b/backend/src/emails/templates/user_left_group/html.pug @@ -0,0 +1,8 @@ +extend ../layout.pug + +block content + .content + p + a.user(href=groupRelatedUserUrl)= groupRelatedUserName + = t('userLeftGroup', { groupName }) + a.button(href=groupUrl)= t('buttons.viewGroup') diff --git a/backend/src/emails/templates/user_left_group/subject.pug b/backend/src/emails/templates/user_left_group/subject.pug new file mode 100644 index 000000000..52aa6f1a6 --- /dev/null +++ b/backend/src/emails/templates/user_left_group/subject.pug @@ -0,0 +1 @@ += `${APPLICATION_NAME} – ${t('notification')}: ${t('subjects.userLeftGroup')}` \ No newline at end of file diff --git a/backend/src/emails/templates/wrongEmail/html.pug b/backend/src/emails/templates/wrongEmail/html.pug new file mode 100644 index 000000000..79f97833f --- /dev/null +++ b/backend/src/emails/templates/wrongEmail/html.pug @@ -0,0 +1,10 @@ +extend ../layout.pug + +block content + .content + p= t('wrongEmail.introduction') + a.button(href=actionUrl)= t('buttons.tryAgain') + + p= t('wrongEmail.ignoreStart') + a(href=ORGANIZATION_LINK)= APPLICATION_NAME + = t('wrongEmail.ignoreEnd') diff --git a/backend/src/emails/templates/wrongEmail/subject.pug b/backend/src/emails/templates/wrongEmail/subject.pug new file mode 100644 index 000000000..b6bc2d01c --- /dev/null +++ b/backend/src/emails/templates/wrongEmail/subject.pug @@ -0,0 +1 @@ += `${t('subjects.wrongEmail')} ${APPLICATION_NAME}` \ No newline at end of file diff --git a/backend/src/schema/resolvers/Upload.ts b/backend/src/graphql/resolvers/Upload.ts similarity index 100% rename from backend/src/schema/resolvers/Upload.ts rename to backend/src/graphql/resolvers/Upload.ts diff --git a/backend/src/schema/resolvers/badges.spec.ts b/backend/src/graphql/resolvers/badges.spec.ts similarity index 100% rename from backend/src/schema/resolvers/badges.spec.ts rename to backend/src/graphql/resolvers/badges.spec.ts diff --git a/backend/src/schema/resolvers/badges.ts b/backend/src/graphql/resolvers/badges.ts similarity index 100% rename from backend/src/schema/resolvers/badges.ts rename to backend/src/graphql/resolvers/badges.ts diff --git a/backend/src/schema/resolvers/comments.spec.ts b/backend/src/graphql/resolvers/comments.spec.ts similarity index 92% rename from backend/src/schema/resolvers/comments.spec.ts rename to backend/src/graphql/resolvers/comments.spec.ts index a7177d754..9681abe9a 100644 --- a/backend/src/schema/resolvers/comments.spec.ts +++ b/backend/src/graphql/resolvers/comments.spec.ts @@ -2,40 +2,41 @@ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ +import { ApolloServer } from 'apollo-server-express' import { createTestClient } from 'apollo-server-testing' import gql from 'graphql-tag' +import databaseContext from '@context/database' import Factory, { cleanDatabase } from '@db/factories' -import { getNeode, getDriver } from '@db/neo4j' -import createServer from '@src/server' +import createServer, { getContext } from '@src/server' -const driver = getDriver() -const neode = getNeode() +const database = databaseContext() let variables, mutate, authenticatedUser, commentAuthor, newlyCreatedComment +let server: ApolloServer beforeAll(async () => { await cleanDatabase() - const { server } = createServer({ - context: () => { - return { - driver, - user: authenticatedUser, - } - }, - }) + // eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/require-await + const contextUser = async (_req) => authenticatedUser + const context = getContext({ user: contextUser, database }) + + server = createServer({ context }).server + mutate = createTestClient(server).mutate }) afterAll(async () => { await cleanDatabase() - await driver.close() + void server.stop() + void database.driver.close() + database.neode.close() }) beforeEach(async () => { variables = {} - await neode.create('Category', { + await database.neode.create('Category', { id: 'cat9', name: 'Democracy & Politics', icon: 'university', @@ -103,7 +104,7 @@ describe('CreateComment', () => { describe('authenticated', () => { beforeEach(async () => { - const user = await neode.create('User', { name: 'Author' }) + const user = await database.neode.create('User', { name: 'Author' }) authenticatedUser = await user.toJson() }) diff --git a/backend/src/schema/resolvers/comments.ts b/backend/src/graphql/resolvers/comments.ts similarity index 100% rename from backend/src/schema/resolvers/comments.ts rename to backend/src/graphql/resolvers/comments.ts diff --git a/backend/src/schema/resolvers/donations.spec.ts b/backend/src/graphql/resolvers/donations.spec.ts similarity index 100% rename from backend/src/schema/resolvers/donations.spec.ts rename to backend/src/graphql/resolvers/donations.spec.ts diff --git a/backend/src/schema/resolvers/donations.ts b/backend/src/graphql/resolvers/donations.ts similarity index 100% rename from backend/src/schema/resolvers/donations.ts rename to backend/src/graphql/resolvers/donations.ts diff --git a/backend/src/schema/resolvers/emails.spec.ts b/backend/src/graphql/resolvers/emails.spec.ts similarity index 100% rename from backend/src/schema/resolvers/emails.spec.ts rename to backend/src/graphql/resolvers/emails.spec.ts diff --git a/backend/src/schema/resolvers/emails.ts b/backend/src/graphql/resolvers/emails.ts similarity index 98% rename from backend/src/schema/resolvers/emails.ts rename to backend/src/graphql/resolvers/emails.ts index be721dda5..0491c86ad 100644 --- a/backend/src/schema/resolvers/emails.ts +++ b/backend/src/graphql/resolvers/emails.ts @@ -69,6 +69,7 @@ export default { ) return result.records.map((record) => ({ name: record.get('user').properties.name, + locale: record.get('user').properties.locale, ...record.get('email').properties, })) }) diff --git a/backend/src/schema/resolvers/embeds.spec.ts b/backend/src/graphql/resolvers/embeds.spec.ts similarity index 100% rename from backend/src/schema/resolvers/embeds.spec.ts rename to backend/src/graphql/resolvers/embeds.spec.ts diff --git a/backend/src/schema/resolvers/embeds.ts b/backend/src/graphql/resolvers/embeds.ts similarity index 100% rename from backend/src/schema/resolvers/embeds.ts rename to backend/src/graphql/resolvers/embeds.ts diff --git a/backend/src/schema/resolvers/embeds/findProvider.spec.ts b/backend/src/graphql/resolvers/embeds/findProvider.spec.ts similarity index 100% rename from backend/src/schema/resolvers/embeds/findProvider.spec.ts rename to backend/src/graphql/resolvers/embeds/findProvider.spec.ts diff --git a/backend/src/schema/resolvers/embeds/findProvider.ts b/backend/src/graphql/resolvers/embeds/findProvider.ts similarity index 100% rename from backend/src/schema/resolvers/embeds/findProvider.ts rename to backend/src/graphql/resolvers/embeds/findProvider.ts diff --git a/backend/src/schema/resolvers/embeds/scraper.ts b/backend/src/graphql/resolvers/embeds/scraper.ts similarity index 100% rename from backend/src/schema/resolvers/embeds/scraper.ts rename to backend/src/graphql/resolvers/embeds/scraper.ts diff --git a/backend/src/schema/resolvers/filter-posts.spec.ts b/backend/src/graphql/resolvers/filter-posts.spec.ts similarity index 100% rename from backend/src/schema/resolvers/filter-posts.spec.ts rename to backend/src/graphql/resolvers/filter-posts.spec.ts diff --git a/backend/src/schema/resolvers/follow.spec.ts b/backend/src/graphql/resolvers/follow.spec.ts similarity index 100% rename from backend/src/schema/resolvers/follow.spec.ts rename to backend/src/graphql/resolvers/follow.spec.ts diff --git a/backend/src/schema/resolvers/follow.ts b/backend/src/graphql/resolvers/follow.ts similarity index 99% rename from backend/src/schema/resolvers/follow.ts rename to backend/src/graphql/resolvers/follow.ts index d08f243b1..8d69a7d5b 100644 --- a/backend/src/schema/resolvers/follow.ts +++ b/backend/src/graphql/resolvers/follow.ts @@ -1,7 +1,5 @@ /* eslint-disable @typescript-eslint/no-unsafe-argument */ - /* eslint-disable @typescript-eslint/no-unsafe-assignment */ - /* eslint-disable @typescript-eslint/no-unsafe-member-access */ import { getNeode } from '@db/neo4j' diff --git a/backend/src/schema/resolvers/groups.spec.ts b/backend/src/graphql/resolvers/groups.spec.ts similarity index 99% rename from backend/src/schema/resolvers/groups.spec.ts rename to backend/src/graphql/resolvers/groups.spec.ts index 664f57397..545865c20 100644 --- a/backend/src/schema/resolvers/groups.spec.ts +++ b/backend/src/graphql/resolvers/groups.spec.ts @@ -6,8 +6,8 @@ import { createTestClient } from 'apollo-server-testing' import CONFIG from '@config/index' +import databaseContext from '@context/database' import Factory, { cleanDatabase } from '@db/factories' -import { getNeode, getDriver } from '@db/neo4j' import { changeGroupMemberRoleMutation } from '@graphql/queries/changeGroupMemberRoleMutation' import { createGroupMutation } from '@graphql/queries/createGroupMutation' import { groupMembersQuery } from '@graphql/queries/groupMembersQuery' @@ -16,10 +16,7 @@ import { joinGroupMutation } from '@graphql/queries/joinGroupMutation' import { leaveGroupMutation } from '@graphql/queries/leaveGroupMutation' import { removeUserFromGroupMutation } from '@graphql/queries/removeUserFromGroupMutation' import { updateGroupMutation } from '@graphql/queries/updateGroupMutation' -import createServer from '@src/server' - -const driver = getDriver() -const neode = getNeode() +import createServer, { getContext } from '@src/server' let authenticatedUser let user @@ -35,15 +32,12 @@ const descriptionAdditional100 = ' 123456789-123456789-123456789-123456789-123456789-123456789-123456789-123456789-123456789-123456789' let variables = {} -const { server } = createServer({ - context: () => { - return { - driver, - neode, - user: authenticatedUser, - } - }, -}) +const database = databaseContext() +// eslint-disable-next-line @typescript-eslint/no-unsafe-return +const contextUser = async (_req) => authenticatedUser +const context = getContext({ user: contextUser, database }) + +const { server } = createServer({ context }) const { mutate, query } = createTestClient(server) const seedBasicsAndClearAuthentication = async () => { @@ -60,25 +54,25 @@ const seedBasicsAndClearAuthentication = async () => { }, ) await Promise.all([ - neode.create('Category', { + database.neode.create('Category', { id: 'cat4', name: 'Environment & Nature', slug: 'environment-nature', icon: 'tree', }), - neode.create('Category', { + database.neode.create('Category', { id: 'cat9', name: 'Democracy & Politics', slug: 'democracy-politics', icon: 'university', }), - neode.create('Category', { + database.neode.create('Category', { id: 'cat15', name: 'Consumption & Sustainability', slug: 'consumption-sustainability', icon: 'shopping-cart', }), - neode.create('Category', { + database.neode.create('Category', { id: 'cat27', name: 'Animal Protection', slug: 'animal-protection', @@ -241,7 +235,9 @@ beforeAll(async () => { afterAll(async () => { await cleanDatabase() - await driver.close() + void server.stop() + void database.driver.close() + database.neode.close() }) describe('in mode', () => { diff --git a/backend/src/schema/resolvers/groups.ts b/backend/src/graphql/resolvers/groups.ts similarity index 91% rename from backend/src/schema/resolvers/groups.ts rename to backend/src/graphql/resolvers/groups.ts index 96d806bf8..8e24117e1 100644 --- a/backend/src/schema/resolvers/groups.ts +++ b/backend/src/graphql/resolvers/groups.ts @@ -12,6 +12,7 @@ import CONFIG from '@config/index' import { CATEGORIES_MIN, CATEGORIES_MAX } from '@constants/categories' import { DESCRIPTION_WITHOUT_HTML_LENGTH_MIN } from '@constants/groups' import { removeHtmlTags } from '@middleware/helpers/cleanHtml' +import type { Context } from '@src/server' import Resolver, { removeUndefinedNullValuesFromObject, @@ -22,7 +23,7 @@ import { createOrUpdateLocations } from './users/location' export default { Query: { - Group: async (_object, params, context, _resolveInfo) => { + Group: async (_object, params, context: Context, _resolveInfo) => { const { isMember, id, slug, first, offset } = params let pagination = '' const orderBy = 'ORDER BY group.createdAt DESC' @@ -75,10 +76,10 @@ export default { } catch (error) { throw new Error(error) } finally { - session.close() + await session.close() } }, - GroupMembers: async (_object, params, context, _resolveInfo) => { + GroupMembers: async (_object, params, context: Context, _resolveInfo) => { const { id: groupId } = params const session = context.driver.session() const readTxResultPromise = session.readTransaction(async (txc) => { @@ -96,7 +97,7 @@ export default { } catch (error) { throw new Error(error) } finally { - session.close() + await session.close() } }, GroupCount: async (_object, params, context, _resolveInfo) => { @@ -134,7 +135,7 @@ export default { }, }, Mutation: { - CreateGroup: async (_parent, params, context, _resolveInfo) => { + CreateGroup: async (_parent, params, context: Context, _resolveInfo) => { const { categoryIds } = params delete params.categoryIds params.locationName = params.locationName === '' ? null : params.locationName @@ -182,7 +183,7 @@ export default { `, { userId: context.user.id, categoryIds, params }, ) - const [group] = await ownerCreateGroupTransactionResponse.records.map((record) => + const [group] = ownerCreateGroupTransactionResponse.records.map((record) => record.get('group'), ) return group @@ -197,10 +198,10 @@ export default { throw new UserInputError('Group with this slug already exists!') throw new Error(error) } finally { - session.close() + await session.close() } }, - UpdateGroup: async (_parent, params, context, _resolveInfo) => { + UpdateGroup: async (_parent, params, context: Context, _resolveInfo) => { const { categoryIds } = params delete params.categoryIds const { id: groupId, avatar: avatarInput } = params @@ -257,7 +258,7 @@ export default { categoryIds, params, }) - const [group] = await transactionResponse.records.map((record) => record.get('group')) + const [group] = transactionResponse.records.map((record) => record.get('group')) if (avatarInput) { await mergeImage(group, 'AVATAR_IMAGE', avatarInput, { transaction }) } @@ -273,10 +274,10 @@ export default { throw new UserInputError('Group with this slug already exists!') throw new Error(error) } finally { - session.close() + await session.close() } }, - JoinGroup: async (_parent, params, context, _resolveInfo) => { + JoinGroup: async (_parent, params, context: Context, _resolveInfo) => { const { groupId, userId } = params const session = context.driver.session() const writeTxResultPromise = session.writeTransaction(async (transaction) => { @@ -294,7 +295,7 @@ export default { RETURN member {.*, myRoleInGroup: membership.role} ` const transactionResponse = await transaction.run(joinGroupCypher, { groupId, userId }) - const [member] = await transactionResponse.records.map((record) => record.get('member')) + const [member] = transactionResponse.records.map((record) => record.get('member')) return member }) try { @@ -302,10 +303,10 @@ export default { } catch (error) { throw new Error(error) } finally { - session.close() + await session.close() } }, - LeaveGroup: async (_parent, params, context, _resolveInfo) => { + LeaveGroup: async (_parent, params, context: Context, _resolveInfo) => { const { groupId, userId } = params const session = context.driver.session() try { @@ -313,10 +314,10 @@ export default { } catch (error) { throw new Error(error) } finally { - session.close() + await session.close() } }, - ChangeGroupMemberRole: async (_parent, params, context, _resolveInfo) => { + ChangeGroupMemberRole: async (_parent, params, context: Context, _resolveInfo) => { const { groupId, userId, roleInGroup } = params const session = context.driver.session() const writeTxResultPromise = session.writeTransaction(async (transaction) => { @@ -353,7 +354,7 @@ export default { userId, roleInGroup, }) - const [member] = await transactionResponse.records.map((record) => record.get('member')) + const [member] = transactionResponse.records.map((record) => record.get('member')) return member }) try { @@ -361,10 +362,10 @@ export default { } catch (error) { throw new Error(error) } finally { - session.close() + await session.close() } }, - RemoveUserFromGroup: async (_parent, params, context, _resolveInfo) => { + RemoveUserFromGroup: async (_parent, params, context: Context, _resolveInfo) => { const { groupId, userId } = params const session = context.driver.session() try { @@ -372,10 +373,10 @@ export default { } catch (error) { throw new Error(error) } finally { - session.close() + await session.close() } }, - muteGroup: async (_parent, params, context, _resolveInfo) => { + muteGroup: async (_parent, params, context: Context, _resolveInfo) => { const { groupId } = params const userId = context.user.id const session = context.driver.session() @@ -393,7 +394,7 @@ export default { userId, }, ) - const [group] = await transactionResponse.records.map((record) => record.get('group')) + const [group] = transactionResponse.records.map((record) => record.get('group')) return group }) try { @@ -401,10 +402,10 @@ export default { } catch (error) { throw new Error(error) } finally { - session.close() + await session.close() } }, - unmuteGroup: async (_parent, params, context, _resolveInfo) => { + unmuteGroup: async (_parent, params, context: Context, _resolveInfo) => { const { groupId } = params const userId = context.user.id const session = context.driver.session() @@ -422,7 +423,7 @@ export default { userId, }, ) - const [group] = await transactionResponse.records.map((record) => record.get('group')) + const [group] = transactionResponse.records.map((record) => record.get('group')) return group }) try { @@ -430,7 +431,7 @@ export default { } catch (error) { throw new Error(error) } finally { - session.close() + await session.close() } }, }, diff --git a/backend/src/schema/resolvers/helpers/Resolver.ts b/backend/src/graphql/resolvers/helpers/Resolver.ts similarity index 100% rename from backend/src/schema/resolvers/helpers/Resolver.ts rename to backend/src/graphql/resolvers/helpers/Resolver.ts diff --git a/backend/src/schema/resolvers/helpers/createPasswordReset.ts b/backend/src/graphql/resolvers/helpers/createPasswordReset.ts similarity index 100% rename from backend/src/schema/resolvers/helpers/createPasswordReset.ts rename to backend/src/graphql/resolvers/helpers/createPasswordReset.ts diff --git a/backend/src/schema/resolvers/helpers/events.ts b/backend/src/graphql/resolvers/helpers/events.ts similarity index 100% rename from backend/src/schema/resolvers/helpers/events.ts rename to backend/src/graphql/resolvers/helpers/events.ts diff --git a/backend/src/schema/resolvers/helpers/existingEmailAddress.ts b/backend/src/graphql/resolvers/helpers/existingEmailAddress.ts similarity index 100% rename from backend/src/schema/resolvers/helpers/existingEmailAddress.ts rename to backend/src/graphql/resolvers/helpers/existingEmailAddress.ts diff --git a/backend/src/schema/resolvers/helpers/filterForMutedUsers.ts b/backend/src/graphql/resolvers/helpers/filterForMutedUsers.ts similarity index 93% rename from backend/src/schema/resolvers/helpers/filterForMutedUsers.ts rename to backend/src/graphql/resolvers/helpers/filterForMutedUsers.ts index 88d66dd65..967ed2265 100644 --- a/backend/src/schema/resolvers/helpers/filterForMutedUsers.ts +++ b/backend/src/graphql/resolvers/helpers/filterForMutedUsers.ts @@ -4,7 +4,7 @@ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ import { mergeWith, isArray } from 'lodash' -import { getMutedUsers } from '@schema/resolvers/users' +import { getMutedUsers } from '@graphql/resolvers/users' export const filterForMutedUsers = async (params, context) => { if (!context.user) return params diff --git a/backend/src/schema/resolvers/helpers/filterInvisiblePosts.ts b/backend/src/graphql/resolvers/helpers/filterInvisiblePosts.ts similarity index 100% rename from backend/src/schema/resolvers/helpers/filterInvisiblePosts.ts rename to backend/src/graphql/resolvers/helpers/filterInvisiblePosts.ts diff --git a/backend/src/schema/resolvers/helpers/filterPostsOfMyGroups.ts b/backend/src/graphql/resolvers/helpers/filterPostsOfMyGroups.ts similarity index 100% rename from backend/src/schema/resolvers/helpers/filterPostsOfMyGroups.ts rename to backend/src/graphql/resolvers/helpers/filterPostsOfMyGroups.ts diff --git a/backend/src/schema/resolvers/helpers/generateInviteCode.ts b/backend/src/graphql/resolvers/helpers/generateInviteCode.ts similarity index 100% rename from backend/src/schema/resolvers/helpers/generateInviteCode.ts rename to backend/src/graphql/resolvers/helpers/generateInviteCode.ts diff --git a/backend/src/schema/resolvers/helpers/generateNonce.ts b/backend/src/graphql/resolvers/helpers/generateNonce.ts similarity index 100% rename from backend/src/schema/resolvers/helpers/generateNonce.ts rename to backend/src/graphql/resolvers/helpers/generateNonce.ts diff --git a/backend/src/schema/resolvers/helpers/normalizeEmail.ts b/backend/src/graphql/resolvers/helpers/normalizeEmail.ts similarity index 100% rename from backend/src/schema/resolvers/helpers/normalizeEmail.ts rename to backend/src/graphql/resolvers/helpers/normalizeEmail.ts diff --git a/backend/src/schema/resolvers/images.ts b/backend/src/graphql/resolvers/images.ts similarity index 100% rename from backend/src/schema/resolvers/images.ts rename to backend/src/graphql/resolvers/images.ts diff --git a/backend/src/schema/resolvers/images/images.spec.ts b/backend/src/graphql/resolvers/images/images.spec.ts similarity index 100% rename from backend/src/schema/resolvers/images/images.spec.ts rename to backend/src/graphql/resolvers/images/images.spec.ts diff --git a/backend/src/schema/resolvers/images/images.ts b/backend/src/graphql/resolvers/images/images.ts similarity index 97% rename from backend/src/schema/resolvers/images/images.ts rename to backend/src/graphql/resolvers/images/images.ts index 2e76a7889..217d26553 100644 --- a/backend/src/schema/resolvers/images/images.ts +++ b/backend/src/graphql/resolvers/images/images.ts @@ -138,6 +138,9 @@ const s3Upload = async ({ createReadStream, uniqueFilename, mimetype }) => { const s3 = new S3({ region, endpoint }) const s3Location = `original/${uniqueFilename}` + if (!Bucket) { + throw new Error('AWS_BUCKET is undefined') + } const params = { Bucket, Key: s3Location, @@ -160,6 +163,9 @@ const s3Delete = async (url) => { const s3 = new S3({ region, endpoint }) let { pathname } = new URL(url, 'http://example.org') // dummy domain to avoid invalid URL error pathname = pathname.substring(1) // remove first character '/' + if (!Bucket) { + throw new Error('AWS_BUCKET is undefined') + } const params = { Bucket, Key: pathname, diff --git a/backend/src/schema/resolvers/index.ts b/backend/src/graphql/resolvers/index.ts similarity index 100% rename from backend/src/schema/resolvers/index.ts rename to backend/src/graphql/resolvers/index.ts diff --git a/backend/src/schema/resolvers/inviteCodes.spec.ts b/backend/src/graphql/resolvers/inviteCodes.spec.ts similarity index 100% rename from backend/src/schema/resolvers/inviteCodes.spec.ts rename to backend/src/graphql/resolvers/inviteCodes.spec.ts diff --git a/backend/src/schema/resolvers/inviteCodes.ts b/backend/src/graphql/resolvers/inviteCodes.ts similarity index 100% rename from backend/src/schema/resolvers/inviteCodes.ts rename to backend/src/graphql/resolvers/inviteCodes.ts diff --git a/backend/src/graphql/resolvers/locations.spec.ts b/backend/src/graphql/resolvers/locations.spec.ts new file mode 100644 index 000000000..8b3c5b779 --- /dev/null +++ b/backend/src/graphql/resolvers/locations.spec.ts @@ -0,0 +1,302 @@ +/* eslint-disable @typescript-eslint/no-unsafe-call */ +/* eslint-disable @typescript-eslint/no-unsafe-member-access */ +/* eslint-disable @typescript-eslint/no-unsafe-assignment */ +import { createTestClient } from 'apollo-server-testing' +import gql from 'graphql-tag' + +import Factory, { cleanDatabase } from '@db/factories' +import { getNeode, getDriver } from '@db/neo4j' +import createServer from '@src/server' + +let query, mutate, authenticatedUser + +const driver = getDriver() +const neode = getNeode() + +beforeAll(async () => { + await cleanDatabase() + + const { server } = createServer({ + context: () => { + return { + driver, + neode, + user: authenticatedUser, + } + }, + }) + query = createTestClient(server).query + mutate = createTestClient(server).mutate +}) + +afterAll(async () => { + await cleanDatabase() + await driver.close() +}) + +// TODO: avoid database clean after each test in the future if possible for performance and flakyness reasons by filling the database step by step, see issue https://github.com/Ocelot-Social-Community/Ocelot-Social/issues/4543 +afterEach(async () => { + await cleanDatabase() +}) + +describe('resolvers', () => { + describe('Location', () => { + describe('custom mutation, not handled by neo4j-graphql-js', () => { + let variables + const updateUserMutation = gql` + mutation ($id: ID!, $name: String) { + UpdateUser(id: $id, name: $name) { + name + location { + name: nameRU + nameEN + } + } + } + ` + + beforeEach(async () => { + variables = { + id: 'u47', + name: 'John Doughnut', + } + const Paris = await Factory.build('location', { + id: 'region.9397217726497330', + name: 'Paris', + type: 'region', + lng: 2.35183, + lat: 48.85658, + nameEN: 'Paris', + }) + + const user = await Factory.build('user', { + id: 'u47', + name: 'John Doe', + }) + await user.relateTo(Paris, 'isIn') + authenticatedUser = await user.toJson() + }) + + it('returns `null` if location translation is not available', async () => { + await expect(mutate({ mutation: updateUserMutation, variables })).resolves.toMatchObject({ + data: { + UpdateUser: { + name: 'John Doughnut', + location: { + name: null, + nameEN: 'Paris', + }, + }, + }, + errors: undefined, + }) + }) + }) + }) +}) + +const distanceToMeQuery = gql` + query ($id: ID!) { + User(id: $id) { + location { + distanceToMe + } + } + } +` +let user, myPlaceUser, otherPlaceUser, noCordsPlaceUser, noPlaceUser + +describe('distanceToMe', () => { + beforeEach(async () => { + const Hamburg = await Factory.build('location', { + id: 'region.5127278006398860', + name: 'Hamburg', + type: 'region', + lng: 10.0, + lat: 53.55, + nameES: 'Hamburgo', + nameFR: 'Hambourg', + nameIT: 'Amburgo', + nameEN: 'Hamburg', + namePT: 'Hamburgo', + nameDE: 'Hamburg', + nameNL: 'Hamburg', + namePL: 'Hamburg', + nameRU: 'Гамбург', + }) + const Germany = await Factory.build('location', { + id: 'country.10743216036480410', + name: 'Germany', + type: 'country', + namePT: 'Alemanha', + nameDE: 'Deutschland', + nameES: 'Alemania', + nameNL: 'Duitsland', + namePL: 'Niemcy', + nameFR: 'Allemagne', + nameIT: 'Germania', + nameEN: 'Germany', + nameRU: 'Германия', + }) + const Paris = await Factory.build('location', { + id: 'region.9397217726497330', + name: 'Paris', + type: 'region', + lng: 2.35183, + lat: 48.85658, + nameES: 'París', + nameFR: 'Paris', + nameIT: 'Parigi', + nameEN: 'Paris', + namePT: 'Paris', + nameDE: 'Paris', + nameNL: 'Parijs', + namePL: 'Paryż', + nameRU: 'Париж', + }) + + user = await Factory.build('user', { + id: 'user', + role: 'user', + }) + await user.relateTo(Hamburg, 'isIn') + + myPlaceUser = await Factory.build('user', { + id: 'myPlaceUser', + role: 'user', + }) + await myPlaceUser.relateTo(Hamburg, 'isIn') + + otherPlaceUser = await Factory.build('user', { + id: 'otherPlaceUser', + role: 'user', + }) + await otherPlaceUser.relateTo(Paris, 'isIn') + + noCordsPlaceUser = await Factory.build('user', { + id: 'noCordsPlaceUser', + role: 'user', + }) + await noCordsPlaceUser.relateTo(Germany, 'isIn') + + noPlaceUser = await Factory.build('user', { + id: 'noPlaceUser', + role: 'user', + }) + }) + + describe('query the field', () => { + describe('for self user', () => { + it('returns 0', async () => { + authenticatedUser = await user.toJson() + const targetUser = await user.toJson() + await expect( + query({ query: distanceToMeQuery, variables: { id: targetUser.id } }), + ).resolves.toEqual( + expect.objectContaining({ + data: { + User: [ + { + location: { + distanceToMe: 0, + }, + }, + ], + }, + errors: undefined, + }), + ) + }) + }) + + describe('for myPlaceUser', () => { + it('returns 0', async () => { + authenticatedUser = await user.toJson() + const targetUser = await myPlaceUser.toJson() + await expect( + query({ query: distanceToMeQuery, variables: { id: targetUser.id } }), + ).resolves.toEqual( + expect.objectContaining({ + data: { + User: [ + { + location: { + distanceToMe: 0, + }, + }, + ], + }, + errors: undefined, + }), + ) + }) + }) + + describe('for otherPlaceUser', () => { + it('returns a number', async () => { + authenticatedUser = await user.toJson() + const targetUser = await otherPlaceUser.toJson() + await expect( + query({ query: distanceToMeQuery, variables: { id: targetUser.id } }), + ).resolves.toEqual( + expect.objectContaining({ + data: { + User: [ + { + location: { + distanceToMe: 746, + }, + }, + ], + }, + errors: undefined, + }), + ) + }) + }) + + describe('for noCordsPlaceUser', () => { + it('returns null', async () => { + authenticatedUser = await user.toJson() + const targetUser = await noCordsPlaceUser.toJson() + await expect( + query({ query: distanceToMeQuery, variables: { id: targetUser.id } }), + ).resolves.toEqual( + expect.objectContaining({ + data: { + User: [ + { + location: { + distanceToMe: null, + }, + }, + ], + }, + errors: undefined, + }), + ) + }) + }) + + describe('for noPlaceUser', () => { + it('returns null location', async () => { + authenticatedUser = await user.toJson() + const targetUser = await noPlaceUser.toJson() + await expect( + query({ query: distanceToMeQuery, variables: { id: targetUser.id } }), + ).resolves.toEqual( + expect.objectContaining({ + data: { + User: [ + { + location: null, + }, + ], + }, + errors: undefined, + }), + ) + }) + }) + }) +}) diff --git a/backend/src/graphql/resolvers/locations.ts b/backend/src/graphql/resolvers/locations.ts new file mode 100644 index 000000000..f375f287f --- /dev/null +++ b/backend/src/graphql/resolvers/locations.ts @@ -0,0 +1,61 @@ +/* eslint-disable @typescript-eslint/no-unsafe-return */ +/* eslint-disable @typescript-eslint/no-unsafe-call */ +/* eslint-disable @typescript-eslint/no-unsafe-assignment */ +/* eslint-disable @typescript-eslint/no-unsafe-member-access */ +/* eslint-disable @typescript-eslint/no-unsafe-argument */ +import { UserInputError } from 'apollo-server' + +import Resolver from './helpers/Resolver' +import { queryLocations } from './users/location' + +export default { + Location: { + ...Resolver('Location', { + undefinedToNull: [ + 'nameEN', + 'nameDE', + 'nameFR', + 'nameNL', + 'nameIT', + 'nameES', + 'namePT', + 'namePL', + 'nameRU', + ], + }), + distanceToMe: async (parent, _params, context, _resolveInfo) => { + const session = context.driver.session() + + const query = session.readTransaction(async (transaction) => { + const result = await transaction.run( + ` + MATCH (loc:Location {id: $parent.id}) + MATCH (me:User {id: $user.id})-[:IS_IN]->(meLoc:Location) + WITH + point({latitude: loc.lat, longitude: loc.lng}) as locPoint, + point({latitude: meLoc.lat, longitude: meLoc.lng}) as mePoint + RETURN round(point.distance(locPoint, mePoint) / 1000) as distance + `, + { parent, user: context.user }, + ) + + return result.records.map((record) => record.get('distance'))[0] + }) + + try { + return await query + } finally { + await session.close() + } + }, + }, + Query: { + queryLocations: async (_object, args, _context, _resolveInfo) => { + try { + return queryLocations(args) + } catch (e) { + throw new UserInputError(e.message) + } + }, + }, +} diff --git a/backend/src/schema/resolvers/messages.spec.ts b/backend/src/graphql/resolvers/messages.spec.ts similarity index 96% rename from backend/src/schema/resolvers/messages.spec.ts rename to backend/src/graphql/resolvers/messages.spec.ts index 8061cf460..81799fdf1 100644 --- a/backend/src/schema/resolvers/messages.spec.ts +++ b/backend/src/graphql/resolvers/messages.spec.ts @@ -3,49 +3,47 @@ /* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ +import { ApolloServer } from 'apollo-server-express' import { createTestClient } from 'apollo-server-testing' +import databaseContext from '@context/database' +import pubsubContext from '@context/pubsub' import Factory, { cleanDatabase } from '@db/factories' -import { getNeode, getDriver } from '@db/neo4j' import { createMessageMutation } from '@graphql/queries/createMessageMutation' import { createRoomMutation } from '@graphql/queries/createRoomMutation' import { markMessagesAsSeen } from '@graphql/queries/markMessagesAsSeen' import { messageQuery } from '@graphql/queries/messageQuery' import { roomQuery } from '@graphql/queries/roomQuery' -import createServer, { pubsub } from '@src/server' - -const driver = getDriver() -const neode = getNeode() +import createServer, { getContext } from '@src/server' let query let mutate let authenticatedUser let chattingUser, otherChattingUser, notChattingUser +const database = databaseContext() +const pubsub = pubsubContext() const pubsubSpy = jest.spyOn(pubsub, 'publish') +let server: ApolloServer beforeAll(async () => { await cleanDatabase() - const { server } = createServer({ - context: () => { - return { - driver, - neode, - user: authenticatedUser, - cypherParams: { - currentUserId: authenticatedUser ? authenticatedUser.id : null, - }, - } - }, - }) + // eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/require-await + const contextUser = async (_req) => authenticatedUser + const context = getContext({ user: contextUser, database, pubsub }) + + server = createServer({ context }).server + query = createTestClient(server).query mutate = createTestClient(server).mutate }) afterAll(async () => { await cleanDatabase() - await driver.close() + void server.stop() + void database.driver.close() + database.neode.close() }) describe('Message', () => { diff --git a/backend/src/schema/resolvers/messages.ts b/backend/src/graphql/resolvers/messages.ts similarity index 97% rename from backend/src/schema/resolvers/messages.ts rename to backend/src/graphql/resolvers/messages.ts index c3f362660..6a5a59d27 100644 --- a/backend/src/schema/resolvers/messages.ts +++ b/backend/src/graphql/resolvers/messages.ts @@ -7,7 +7,7 @@ import { withFilter } from 'graphql-subscriptions' import { neo4jgraphql } from 'neo4j-graphql-js' -import { pubsub, CHAT_MESSAGE_ADDED } from '@src/server' +import { CHAT_MESSAGE_ADDED } from '@constants/subscriptions' import Resolver from './helpers/Resolver' @@ -30,7 +30,7 @@ export default { Subscription: { chatMessageAdded: { subscribe: withFilter( - () => pubsub.asyncIterator(CHAT_MESSAGE_ADDED), + (_, __, context) => context.pubsub.asyncIterator(CHAT_MESSAGE_ADDED), (payload, variables, context) => { return payload.userId === context.user?.id }, diff --git a/backend/src/schema/resolvers/moderation.spec.ts b/backend/src/graphql/resolvers/moderation.spec.ts similarity index 100% rename from backend/src/schema/resolvers/moderation.spec.ts rename to backend/src/graphql/resolvers/moderation.spec.ts diff --git a/backend/src/schema/resolvers/moderation.ts b/backend/src/graphql/resolvers/moderation.ts similarity index 100% rename from backend/src/schema/resolvers/moderation.ts rename to backend/src/graphql/resolvers/moderation.ts diff --git a/backend/src/schema/resolvers/notifications.spec.ts b/backend/src/graphql/resolvers/notifications.spec.ts similarity index 100% rename from backend/src/schema/resolvers/notifications.spec.ts rename to backend/src/graphql/resolvers/notifications.spec.ts diff --git a/backend/src/schema/resolvers/notifications.ts b/backend/src/graphql/resolvers/notifications.ts similarity index 97% rename from backend/src/schema/resolvers/notifications.ts rename to backend/src/graphql/resolvers/notifications.ts index 0168558c3..08a7c48f5 100644 --- a/backend/src/schema/resolvers/notifications.ts +++ b/backend/src/graphql/resolvers/notifications.ts @@ -6,13 +6,13 @@ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ import { withFilter } from 'graphql-subscriptions' -import { pubsub, NOTIFICATION_ADDED } from '@src/server' +import { NOTIFICATION_ADDED } from '@constants/subscriptions' export default { Subscription: { notificationAdded: { subscribe: withFilter( - () => pubsub.asyncIterator(NOTIFICATION_ADDED), + (_, __, context) => context.pubsub.asyncIterator(NOTIFICATION_ADDED), (payload, variables, context) => { return payload.notificationAdded.to.id === context.user?.id }, diff --git a/backend/src/schema/resolvers/observePosts.spec.ts b/backend/src/graphql/resolvers/observePosts.spec.ts similarity index 91% rename from backend/src/schema/resolvers/observePosts.spec.ts rename to backend/src/graphql/resolvers/observePosts.spec.ts index 76ad5b058..fd2786fc9 100644 --- a/backend/src/schema/resolvers/observePosts.spec.ts +++ b/backend/src/graphql/resolvers/observePosts.spec.ts @@ -1,20 +1,18 @@ /* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ +import { ApolloServer } from 'apollo-server-express' import { createTestClient } from 'apollo-server-testing' import gql from 'graphql-tag' import CONFIG from '@config/index' +import databaseContext from '@context/database' import Factory, { cleanDatabase } from '@db/factories' -import { getNeode, getDriver } from '@db/neo4j' import { createPostMutation } from '@graphql/queries/createPostMutation' -import createServer from '@src/server' +import createServer, { getContext } from '@src/server' CONFIG.CATEGORIES_ACTIVE = false -const driver = getDriver() -const neode = getNeode() - let query let mutate let authenticatedUser @@ -40,28 +38,27 @@ const postQuery = gql` } ` +const database = databaseContext() + +let server: ApolloServer beforeAll(async () => { await cleanDatabase() - const { server } = createServer({ - context: () => { - return { - driver, - neode, - user: authenticatedUser, - cypherParams: { - currentUserId: authenticatedUser ? authenticatedUser.id : null, - }, - } - }, - }) + // eslint-disable-next-line @typescript-eslint/no-unsafe-return, @typescript-eslint/require-await + const contextUser = async (_req) => authenticatedUser + const context = getContext({ user: contextUser, database }) + + server = createServer({ context }).server + query = createTestClient(server).query mutate = createTestClient(server).mutate }) afterAll(async () => { await cleanDatabase() - await driver.close() + void server.stop() + void database.driver.close() + database.neode.close() }) describe('observing posts', () => { diff --git a/backend/src/schema/resolvers/passwordReset.spec.ts b/backend/src/graphql/resolvers/passwordReset.spec.ts similarity index 97% rename from backend/src/schema/resolvers/passwordReset.spec.ts rename to backend/src/graphql/resolvers/passwordReset.spec.ts index d5d08265c..3bc4d53ba 100644 --- a/backend/src/schema/resolvers/passwordReset.spec.ts +++ b/backend/src/graphql/resolvers/passwordReset.spec.ts @@ -71,14 +71,14 @@ describe('passwordReset', () => { describe('requestPasswordReset', () => { const mutation = gql` - mutation ($email: String!) { - requestPasswordReset(email: $email) + mutation ($email: String!, $locale: String!) { + requestPasswordReset(email: $email, locale: $locale) } ` describe('with invalid email', () => { beforeEach(() => { - variables = { ...variables, email: 'non-existent@example.org' } + variables = { ...variables, email: 'non-existent@example.org', locale: 'de' } }) it('resolves anyways', async () => { @@ -96,7 +96,7 @@ describe('passwordReset', () => { describe('with a valid email', () => { beforeEach(() => { - variables = { ...variables, email: 'user@example.org' } + variables = { ...variables, email: 'user@example.org', locale: 'de' } }) it('resolves', async () => { diff --git a/backend/src/schema/resolvers/passwordReset.ts b/backend/src/graphql/resolvers/passwordReset.ts similarity index 100% rename from backend/src/schema/resolvers/passwordReset.ts rename to backend/src/graphql/resolvers/passwordReset.ts diff --git a/backend/src/schema/resolvers/posts.spec.ts b/backend/src/graphql/resolvers/posts.spec.ts similarity index 99% rename from backend/src/schema/resolvers/posts.spec.ts rename to backend/src/graphql/resolvers/posts.spec.ts index 0a05200fd..7574bef17 100644 --- a/backend/src/schema/resolvers/posts.spec.ts +++ b/backend/src/graphql/resolvers/posts.spec.ts @@ -8,9 +8,9 @@ import gql from 'graphql-tag' import CONFIG from '@config/index' import Factory, { cleanDatabase } from '@db/factories' +import Image from '@db/models/Image' import { getNeode, getDriver } from '@db/neo4j' import { createPostMutation } from '@graphql/queries/createPostMutation' -import Image from '@models/Image' import createServer from '@src/server' CONFIG.CATEGORIES_ACTIVE = true diff --git a/backend/src/schema/resolvers/posts.ts b/backend/src/graphql/resolvers/posts.ts similarity index 100% rename from backend/src/schema/resolvers/posts.ts rename to backend/src/graphql/resolvers/posts.ts diff --git a/backend/src/schema/resolvers/postsInGroups.spec.ts b/backend/src/graphql/resolvers/postsInGroups.spec.ts similarity index 99% rename from backend/src/schema/resolvers/postsInGroups.spec.ts rename to backend/src/graphql/resolvers/postsInGroups.spec.ts index 7cb0bdc76..d50451468 100644 --- a/backend/src/schema/resolvers/postsInGroups.spec.ts +++ b/backend/src/graphql/resolvers/postsInGroups.spec.ts @@ -2,11 +2,12 @@ /* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ +import { ApolloServer } from 'apollo-server-express' import { createTestClient } from 'apollo-server-testing' import CONFIG from '@config/index' +import databaseContext from '@context/database' import Factory, { cleanDatabase } from '@db/factories' -import { getNeode, getDriver } from '@db/neo4j' import { changeGroupMemberRoleMutation } from '@graphql/queries/changeGroupMemberRoleMutation' import { createCommentMutation } from '@graphql/queries/createCommentMutation' import { createGroupMutation } from '@graphql/queries/createGroupMutation' @@ -17,7 +18,7 @@ import { postQuery } from '@graphql/queries/postQuery' import { profilePagePosts } from '@graphql/queries/profilePagePosts' import { searchPosts } from '@graphql/queries/searchPosts' import { signupVerificationMutation } from '@graphql/queries/signupVerificationMutation' -import createServer from '@src/server' +import createServer, { getContext } from '@src/server' CONFIG.CATEGORIES_ACTIVE = false @@ -28,9 +29,6 @@ jest.mock('@constants/groups', () => { } }) -const driver = getDriver() -const neode = getNeode() - let query let mutate let anyUser @@ -42,28 +40,26 @@ let hiddenUser let authenticatedUser let newUser +const database = databaseContext() + +let server: ApolloServer beforeAll(async () => { await cleanDatabase() - const { server } = createServer({ - context: () => { - return { - driver, - neode, - user: authenticatedUser, - cypherParams: { - currentUserId: authenticatedUser ? authenticatedUser.id : null, - }, - } - }, - }) + // eslint-disable-next-line @typescript-eslint/no-unsafe-return + const contextUser = async (_req) => authenticatedUser + const context = getContext({ user: contextUser, database }) + + server = createServer({ context }).server query = createTestClient(server).query mutate = createTestClient(server).mutate }) afterAll(async () => { await cleanDatabase() - await driver.close() + void server.stop() + void database.driver.close() + database.neode.close() }) describe('Posts in Groups', () => { diff --git a/backend/src/schema/resolvers/registration.spec.ts b/backend/src/graphql/resolvers/registration.spec.ts similarity index 97% rename from backend/src/schema/resolvers/registration.spec.ts rename to backend/src/graphql/resolvers/registration.spec.ts index f19c6bf01..d959b348a 100644 --- a/backend/src/schema/resolvers/registration.spec.ts +++ b/backend/src/graphql/resolvers/registration.spec.ts @@ -7,9 +7,9 @@ import gql from 'graphql-tag' import CONFIG from '@config/index' import Factory, { cleanDatabase } from '@db/factories' +import EmailAddress from '@db/models/EmailAddress' +import User from '@db/models/User' import { getDriver, getNeode } from '@db/neo4j' -import EmailAddress from '@models/EmailAddress' -import User from '@models/User' import createServer from '@src/server' const neode = getNeode() @@ -50,14 +50,14 @@ afterEach(async () => { describe('Signup', () => { const mutation = gql` - mutation ($email: String!, $inviteCode: String) { - Signup(email: $email, inviteCode: $inviteCode) { + mutation ($email: String!, $locale: String!, $inviteCode: String) { + Signup(email: $email, locale: $locale, inviteCode: $inviteCode) { email } } ` beforeEach(() => { - variables = { ...variables, email: 'someuser@example.org' } + variables = { ...variables, email: 'someuser@example.org', locale: 'de' } }) describe('unauthenticated', () => { diff --git a/backend/src/schema/resolvers/registration.ts b/backend/src/graphql/resolvers/registration.ts similarity index 97% rename from backend/src/schema/resolvers/registration.ts rename to backend/src/graphql/resolvers/registration.ts index 138a21aea..d37d3663a 100644 --- a/backend/src/schema/resolvers/registration.ts +++ b/backend/src/graphql/resolvers/registration.ts @@ -4,9 +4,9 @@ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ import { UserInputError } from 'apollo-server' +import { hash } from 'bcryptjs' import { getNeode } from '@db/neo4j' -import encryptPassword from '@helpers/encryptPassword' import existingEmailAddress from './helpers/existingEmailAddress' import generateNonce from './helpers/generateNonce' @@ -46,7 +46,8 @@ export default { delete args.nonce delete args.email delete args.inviteCode - args = encryptPassword(args) + args.encryptedPassword = await hash(args.password, 10) + delete args.password const { driver } = context const session = driver.session() diff --git a/backend/src/schema/resolvers/reports.spec.ts b/backend/src/graphql/resolvers/reports.spec.ts similarity index 100% rename from backend/src/schema/resolvers/reports.spec.ts rename to backend/src/graphql/resolvers/reports.spec.ts diff --git a/backend/src/schema/resolvers/reports.ts b/backend/src/graphql/resolvers/reports.ts similarity index 100% rename from backend/src/schema/resolvers/reports.ts rename to backend/src/graphql/resolvers/reports.ts diff --git a/backend/src/schema/resolvers/roles.ts b/backend/src/graphql/resolvers/roles.ts similarity index 100% rename from backend/src/schema/resolvers/roles.ts rename to backend/src/graphql/resolvers/roles.ts diff --git a/backend/src/schema/resolvers/rooms.spec.ts b/backend/src/graphql/resolvers/rooms.spec.ts similarity index 100% rename from backend/src/schema/resolvers/rooms.spec.ts rename to backend/src/graphql/resolvers/rooms.spec.ts diff --git a/backend/src/schema/resolvers/rooms.ts b/backend/src/graphql/resolvers/rooms.ts similarity index 96% rename from backend/src/schema/resolvers/rooms.ts rename to backend/src/graphql/resolvers/rooms.ts index 9c6751695..e3422a5ce 100644 --- a/backend/src/schema/resolvers/rooms.ts +++ b/backend/src/graphql/resolvers/rooms.ts @@ -7,8 +7,7 @@ import { withFilter } from 'graphql-subscriptions' import { neo4jgraphql } from 'neo4j-graphql-js' -// eslint-disable-next-line import/no-cycle -import { pubsub, ROOM_COUNT_UPDATED } from '@src/server' +import { ROOM_COUNT_UPDATED } from '@constants/subscriptions' import Resolver from './helpers/Resolver' @@ -30,7 +29,7 @@ export default { Subscription: { roomCountUpdated: { subscribe: withFilter( - () => pubsub.asyncIterator(ROOM_COUNT_UPDATED), + (_, __, context) => context.pubsub.asyncIterator(ROOM_COUNT_UPDATED), (payload, variables, context) => { return payload.userId === context.user?.id }, diff --git a/backend/src/schema/resolvers/searches.spec.ts b/backend/src/graphql/resolvers/searches.spec.ts similarity index 100% rename from backend/src/schema/resolvers/searches.spec.ts rename to backend/src/graphql/resolvers/searches.spec.ts diff --git a/backend/src/schema/resolvers/searches.ts b/backend/src/graphql/resolvers/searches.ts similarity index 100% rename from backend/src/schema/resolvers/searches.ts rename to backend/src/graphql/resolvers/searches.ts diff --git a/backend/src/schema/resolvers/searches/queryString.spec.ts b/backend/src/graphql/resolvers/searches/queryString.spec.ts similarity index 100% rename from backend/src/schema/resolvers/searches/queryString.spec.ts rename to backend/src/graphql/resolvers/searches/queryString.spec.ts diff --git a/backend/src/schema/resolvers/searches/queryString.ts b/backend/src/graphql/resolvers/searches/queryString.ts similarity index 100% rename from backend/src/schema/resolvers/searches/queryString.ts rename to backend/src/graphql/resolvers/searches/queryString.ts diff --git a/backend/src/schema/resolvers/shout.spec.ts b/backend/src/graphql/resolvers/shout.spec.ts similarity index 100% rename from backend/src/schema/resolvers/shout.spec.ts rename to backend/src/graphql/resolvers/shout.spec.ts diff --git a/backend/src/schema/resolvers/shout.ts b/backend/src/graphql/resolvers/shout.ts similarity index 100% rename from backend/src/schema/resolvers/shout.ts rename to backend/src/graphql/resolvers/shout.ts diff --git a/backend/src/schema/resolvers/socialMedia.spec.ts b/backend/src/graphql/resolvers/socialMedia.spec.ts similarity index 100% rename from backend/src/schema/resolvers/socialMedia.spec.ts rename to backend/src/graphql/resolvers/socialMedia.spec.ts diff --git a/backend/src/schema/resolvers/socialMedia.ts b/backend/src/graphql/resolvers/socialMedia.ts similarity index 99% rename from backend/src/schema/resolvers/socialMedia.ts rename to backend/src/graphql/resolvers/socialMedia.ts index 952e4a27e..2c0cd4c94 100644 --- a/backend/src/schema/resolvers/socialMedia.ts +++ b/backend/src/graphql/resolvers/socialMedia.ts @@ -1,7 +1,5 @@ /* eslint-disable @typescript-eslint/no-unsafe-argument */ - /* eslint-disable @typescript-eslint/no-unsafe-member-access */ - /* eslint-disable @typescript-eslint/no-unsafe-assignment */ import { getNeode } from '@db/neo4j' diff --git a/backend/src/schema/resolvers/statistics.spec.ts b/backend/src/graphql/resolvers/statistics.spec.ts similarity index 100% rename from backend/src/schema/resolvers/statistics.spec.ts rename to backend/src/graphql/resolvers/statistics.spec.ts diff --git a/backend/src/schema/resolvers/statistics.ts b/backend/src/graphql/resolvers/statistics.ts similarity index 100% rename from backend/src/schema/resolvers/statistics.ts rename to backend/src/graphql/resolvers/statistics.ts diff --git a/backend/src/schema/resolvers/transactions/inviteCodes.ts b/backend/src/graphql/resolvers/transactions/inviteCodes.ts similarity index 100% rename from backend/src/schema/resolvers/transactions/inviteCodes.ts rename to backend/src/graphql/resolvers/transactions/inviteCodes.ts diff --git a/backend/src/schema/resolvers/userData.spec.ts b/backend/src/graphql/resolvers/userData.spec.ts similarity index 100% rename from backend/src/schema/resolvers/userData.spec.ts rename to backend/src/graphql/resolvers/userData.spec.ts diff --git a/backend/src/schema/resolvers/userData.ts b/backend/src/graphql/resolvers/userData.ts similarity index 100% rename from backend/src/schema/resolvers/userData.ts rename to backend/src/graphql/resolvers/userData.ts diff --git a/backend/src/schema/resolvers/user_management.spec.ts b/backend/src/graphql/resolvers/user_management.spec.ts similarity index 100% rename from backend/src/schema/resolvers/user_management.spec.ts rename to backend/src/graphql/resolvers/user_management.spec.ts diff --git a/backend/src/schema/resolvers/user_management.ts b/backend/src/graphql/resolvers/user_management.ts similarity index 99% rename from backend/src/schema/resolvers/user_management.ts rename to backend/src/graphql/resolvers/user_management.ts index 7bea1f53c..140a8d53c 100644 --- a/backend/src/schema/resolvers/user_management.ts +++ b/backend/src/graphql/resolvers/user_management.ts @@ -1,7 +1,6 @@ /* eslint-disable @typescript-eslint/require-await */ /* eslint-disable @typescript-eslint/no-unsafe-argument */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ - /* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/no-unsafe-return */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ diff --git a/backend/src/schema/resolvers/users.spec.ts b/backend/src/graphql/resolvers/users.spec.ts similarity index 95% rename from backend/src/schema/resolvers/users.spec.ts rename to backend/src/graphql/resolvers/users.spec.ts index ad37e2024..f65f9eae2 100644 --- a/backend/src/schema/resolvers/users.spec.ts +++ b/backend/src/graphql/resolvers/users.spec.ts @@ -3,14 +3,16 @@ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/require-await */ /* eslint-disable @typescript-eslint/no-unsafe-call */ +import { ApolloServer } from 'apollo-server-express' import { createTestClient } from 'apollo-server-testing' import gql from 'graphql-tag' import { categories } from '@constants/categories' +import databaseContext from '@context/database' +import pubsubContext from '@context/pubsub' import Factory, { cleanDatabase } from '@db/factories' -import { getNeode, getDriver } from '@db/neo4j' -import User from '@models/User' -import createServer from '@src/server' +import User from '@db/models/User' +import createServer, { getContext } from '@src/server' const categoryIds = ['cat9'] let user @@ -21,8 +23,7 @@ let query let mutate let variables -const driver = getDriver() -const neode = getNeode() +const pubsub = pubsubContext() const deleteUserMutation = gql` mutation ($id: ID!, $resource: [Deletable]) { @@ -108,25 +109,28 @@ const resetTrophyBadgesSelected = gql` } ` +const database = databaseContext() + +let server: ApolloServer + beforeAll(async () => { await cleanDatabase() - const { server } = createServer({ - context: () => { - return { - driver, - neode, - user: authenticatedUser, - } - }, - }) - query = createTestClient(server).query - mutate = createTestClient(server).mutate + const contextUser = async (_req) => authenticatedUser + const context = getContext({ user: contextUser, database, pubsub }) + + server = createServer({ context }).server + + const createTestClientResult = createTestClient(server) + query = createTestClientResult.query + mutate = createTestClientResult.mutate }) afterAll(async () => { await cleanDatabase() - await driver.close() + void server.stop() + void database.driver.close() + database.neode.close() }) // TODO: avoid database clean after each test in the future if possible for performance and flakyness reasons by filling the database step by step, see issue https://github.com/Ocelot-Social-Community/Ocelot-Social/issues/4543 @@ -540,10 +544,10 @@ describe('Delete a User as admin', () => { describe('connected `EmailAddress` nodes', () => { it('will be removed completely', async () => { - await expect(neode.all('EmailAddress')).resolves.toHaveLength(2) + await expect(database.neode.all('EmailAddress')).resolves.toHaveLength(2) await mutate({ mutation: deleteUserMutation, variables }) - await expect(neode.all('EmailAddress')).resolves.toHaveLength(1) + await expect(database.neode.all('EmailAddress')).resolves.toHaveLength(1) }) }) @@ -554,9 +558,9 @@ describe('Delete a User as admin', () => { }) it('will be removed completely', async () => { - await expect(neode.all('SocialMedia')).resolves.toHaveLength(1) + await expect(database.neode.all('SocialMedia')).resolves.toHaveLength(1) await mutate({ mutation: deleteUserMutation, variables }) - await expect(neode.all('SocialMedia')).resolves.toHaveLength(0) + await expect(database.neode.all('SocialMedia')).resolves.toHaveLength(0) }) }) }) @@ -1041,8 +1045,8 @@ describe('updateOnlineStatus', () => { ) const cypher = 'MATCH (u:User {id: $id}) RETURN u' - const result = await neode.cypher(cypher, { id: authenticatedUser.id }) - const dbUser = neode.hydrateFirst(result, 'u', neode.model('User')) + const result = await database.neode.cypher(cypher, { id: authenticatedUser.id }) + const dbUser = database.neode.hydrateFirst(result, 'u', database.neode.model('User')) await expect(dbUser.toJson()).resolves.toMatchObject({ lastOnlineStatus: 'online', }) @@ -1067,8 +1071,8 @@ describe('updateOnlineStatus', () => { ) const cypher = 'MATCH (u:User {id: $id}) RETURN u' - const result = await neode.cypher(cypher, { id: authenticatedUser.id }) - const dbUser = neode.hydrateFirst(result, 'u', neode.model('User')) + const result = await database.neode.cypher(cypher, { id: authenticatedUser.id }) + const dbUser = database.neode.hydrateFirst(result, 'u', database.neode.model('User')) await expect(dbUser.toJson()).resolves.toMatchObject({ lastOnlineStatus: 'away', awaySince: expect.any(String), @@ -1083,8 +1087,12 @@ describe('updateOnlineStatus', () => { ) const cypher = 'MATCH (u:User {id: $id}) RETURN u' - const result = await neode.cypher(cypher, { id: authenticatedUser.id }) - const dbUser = neode.hydrateFirst(result, 'u', neode.model('User')) + const result = await database.neode.cypher(cypher, { id: authenticatedUser.id }) + const dbUser = database.neode.hydrateFirst( + result, + 'u', + database.neode.model('User'), + ) await expect(dbUser.toJson()).resolves.toMatchObject({ lastOnlineStatus: 'away', awaySince: expect.any(String), @@ -1098,8 +1106,8 @@ describe('updateOnlineStatus', () => { }), ) - const result2 = await neode.cypher(cypher, { id: authenticatedUser.id }) - const dbUser2 = neode.hydrateFirst(result2, 'u', neode.model('User')) + const result2 = await database.neode.cypher(cypher, { id: authenticatedUser.id }) + const dbUser2 = database.neode.hydrateFirst(result2, 'u', database.neode.model('User')) await expect(dbUser2.toJson()).resolves.toMatchObject({ lastOnlineStatus: 'away', awaySince, diff --git a/backend/src/schema/resolvers/users.ts b/backend/src/graphql/resolvers/users.ts similarity index 100% rename from backend/src/schema/resolvers/users.ts rename to backend/src/graphql/resolvers/users.ts diff --git a/backend/src/schema/resolvers/users/location.spec.ts b/backend/src/graphql/resolvers/users/location.spec.ts similarity index 100% rename from backend/src/schema/resolvers/users/location.spec.ts rename to backend/src/graphql/resolvers/users/location.spec.ts diff --git a/backend/src/schema/resolvers/users/location.ts b/backend/src/graphql/resolvers/users/location.ts similarity index 97% rename from backend/src/schema/resolvers/users/location.ts rename to backend/src/graphql/resolvers/users/location.ts index 6dfaede4e..dc515e70d 100644 --- a/backend/src/schema/resolvers/users/location.ts +++ b/backend/src/graphql/resolvers/users/location.ts @@ -12,7 +12,6 @@ import { UserInputError } from 'apollo-server' import request from 'request' import CONFIG from '@config/index' -import asyncForEach from '@helpers/asyncForEach' const fetch = (url) => { return new Promise((resolve, reject) => { @@ -119,7 +118,7 @@ export const createOrUpdateLocations = async (nodeLabel, nodeId, locationName, s } if (data.context) { - await asyncForEach(data.context, async (ctx) => { + for await (const ctx of data.context) { await createLocation(session, ctx) await session.writeTransaction((transaction) => { return transaction.run( @@ -135,7 +134,7 @@ export const createOrUpdateLocations = async (nodeLabel, nodeId, locationName, s ) }) parent = ctx - }) + } } locationId = data.id diff --git a/backend/src/schema/resolvers/users/mutedUsers.spec.ts b/backend/src/graphql/resolvers/users/mutedUsers.spec.ts similarity index 100% rename from backend/src/schema/resolvers/users/mutedUsers.spec.ts rename to backend/src/graphql/resolvers/users/mutedUsers.spec.ts diff --git a/backend/src/schema/resolvers/viewedTeaserCount.spec.ts b/backend/src/graphql/resolvers/viewedTeaserCount.spec.ts similarity index 100% rename from backend/src/schema/resolvers/viewedTeaserCount.spec.ts rename to backend/src/graphql/resolvers/viewedTeaserCount.spec.ts diff --git a/backend/src/schema/index.ts b/backend/src/graphql/schema.ts similarity index 100% rename from backend/src/schema/index.ts rename to backend/src/graphql/schema.ts diff --git a/backend/src/graphql/types/type/EmailAddress.gql b/backend/src/graphql/types/type/EmailAddress.gql index b2e65eafa..261b97207 100644 --- a/backend/src/graphql/types/type/EmailAddress.gql +++ b/backend/src/graphql/types/type/EmailAddress.gql @@ -9,7 +9,11 @@ type Query { } type Mutation { - Signup(email: String!, inviteCode: String = null): EmailAddress + Signup( + email: String! + locale: String! + inviteCode: String = null + ): EmailAddress SignupVerification( nonce: String! email: String! diff --git a/backend/src/graphql/types/type/Location.gql b/backend/src/graphql/types/type/Location.gql index 9cb5c970a..d9c0ec1cc 100644 --- a/backend/src/graphql/types/type/Location.gql +++ b/backend/src/graphql/types/type/Location.gql @@ -14,6 +14,7 @@ type Location { lat: Float lng: Float parent: Location @cypher(statement: "MATCH (this)-[:IS_IN]->(l:Location) RETURN l") + distanceToMe: Int } # This is not smart - we need one location for everything - use the same type everywhere! diff --git a/backend/src/graphql/types/type/User.gql b/backend/src/graphql/types/type/User.gql index 81dd9cf5b..83de35c37 100644 --- a/backend/src/graphql/types/type/User.gql +++ b/backend/src/graphql/types/type/User.gql @@ -245,7 +245,7 @@ type Mutation { updateOnlineStatus(status: OnlineStatus!): Boolean! - requestPasswordReset(email: String!): Boolean! + requestPasswordReset(email: String!, locale: String!): Boolean! resetPassword(email: String!, nonce: String!, newPassword: String!): Boolean! changePassword(oldPassword: String!, newPassword: String!): String! diff --git a/backend/src/helpers/asyncForEach.ts b/backend/src/helpers/asyncForEach.ts deleted file mode 100644 index 354f2cd07..000000000 --- a/backend/src/helpers/asyncForEach.ts +++ /dev/null @@ -1,18 +0,0 @@ -/* eslint-disable @typescript-eslint/no-unsafe-member-access */ -/* eslint-disable @typescript-eslint/no-unsafe-call */ -/* eslint-disable promise/prefer-await-to-callbacks */ -/* eslint-disable security/detect-object-injection */ -/** - * Provide a way to iterate for each element in an array while waiting for async functions to finish - * - * @param array - * @param callback - * @returns {Promise} - */ -async function asyncForEach(array, callback) { - for (let index = 0; index < array.length; index++) { - await callback(array[index], index, array) - } -} - -export default asyncForEach diff --git a/backend/src/helpers/encryptPassword.ts b/backend/src/helpers/encryptPassword.ts deleted file mode 100644 index 1d12556ea..000000000 --- a/backend/src/helpers/encryptPassword.ts +++ /dev/null @@ -1,11 +0,0 @@ -/* eslint-disable @typescript-eslint/no-unsafe-return */ -/* eslint-disable @typescript-eslint/no-unsafe-member-access */ -/* eslint-disable @typescript-eslint/no-unsafe-argument */ -import { hashSync } from 'bcryptjs' - -export default function (args) { - // eslint-disable-next-line n/no-sync - args.encryptedPassword = hashSync(args.password, 10) - delete args.password - return args -} diff --git a/backend/src/helpers/jest.ts b/backend/src/helpers/jest.ts deleted file mode 100644 index 5594eb348..000000000 --- a/backend/src/helpers/jest.ts +++ /dev/null @@ -1,10 +0,0 @@ -/* eslint-disable @typescript-eslint/no-unsafe-argument */ -/* eslint-disable promise/avoid-new */ -// sometime we have to wait to check a db state by having a look into the db in a certain moment -// or we wait a bit to check if we missed to set an await somewhere -// see: https://www.sitepoint.com/delay-sleep-pause-wait/ -export function sleep(ms) { - return new Promise((resolve) => setTimeout(resolve, ms)) -} -// usage – 4 seconds for example -// await sleep(4 * 1000) diff --git a/backend/src/helpers/walkRecursive.ts b/backend/src/helpers/walkRecursive.ts deleted file mode 100644 index 5874ca3af..000000000 --- a/backend/src/helpers/walkRecursive.ts +++ /dev/null @@ -1,36 +0,0 @@ -/* eslint-disable @typescript-eslint/no-unsafe-argument */ -/* eslint-disable @typescript-eslint/no-unsafe-return */ -/* eslint-disable @typescript-eslint/no-unsafe-call */ -/* eslint-disable @typescript-eslint/no-unsafe-member-access */ -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ -/* eslint-disable promise/prefer-await-to-callbacks */ -/* eslint-disable security/detect-object-injection */ -/** - * iterate through all fields and replace it with the callback result - * @property data Array - * @property fields Array - * @property fieldName String - * @property callback Function - */ -function walkRecursive(data, fields, fieldName, callback, _key?) { - if (!Array.isArray(fields)) { - throw new Error('please provide an fields array for the walkRecursive helper') - } - const fieldDef = fields.find((f) => f.field === _key) - if (data && typeof data === 'string' && fieldDef) { - if (!fieldDef.excludes?.includes(fieldName)) data = callback(data, _key) - } else if (data && Array.isArray(data)) { - // go into the rabbit hole and dig through that array - data.forEach((res, index) => { - data[index] = walkRecursive(data[index], fields, fieldName, callback, index) - }) - } else if (data && typeof data === 'object') { - // lets get some keys and stir them - Object.keys(data).forEach((k) => { - data[k] = walkRecursive(data[k], fields, fieldName, callback, k) - }) - } - return data -} - -export default walkRecursive diff --git a/backend/src/index.ts b/backend/src/index.ts index 3da4ec7ae..612141733 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -1,5 +1,5 @@ /* eslint-disable @typescript-eslint/restrict-template-expressions */ -/* eslint-disable @typescript-eslint/no-unsafe-argument */ + import CONFIG from './config' import createServer from './server' diff --git a/backend/src/jwt/decode.spec.ts b/backend/src/jwt/decode.spec.ts index 0cd52a5d5..cbb220b5b 100644 --- a/backend/src/jwt/decode.spec.ts +++ b/backend/src/jwt/decode.spec.ts @@ -2,8 +2,8 @@ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ import Factory, { cleanDatabase } from '@db/factories' +import User from '@db/models/User' import { getDriver, getNeode } from '@db/neo4j' -import User from '@models/User' import decode from './decode' import encode from './encode' diff --git a/backend/src/middleware/helpers/email/sendMail.ts b/backend/src/middleware/helpers/email/sendMail.ts deleted file mode 100644 index a7d223f1c..000000000 --- a/backend/src/middleware/helpers/email/sendMail.ts +++ /dev/null @@ -1,80 +0,0 @@ -/* eslint-disable @typescript-eslint/require-await */ -/* eslint-disable @typescript-eslint/no-unsafe-argument */ -/* eslint-disable @typescript-eslint/restrict-plus-operands */ -/* eslint-disable @typescript-eslint/no-unsafe-call */ -/* eslint-disable @typescript-eslint/no-unsafe-member-access */ -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ -import nodemailer from 'nodemailer' -import { htmlToText } from 'nodemailer-html-to-text' - -import CONFIG from '@config/index' -import { cleanHtml } from '@middleware/helpers/cleanHtml' - -const hasEmailConfig = CONFIG.SMTP_HOST && CONFIG.SMTP_PORT -const hasAuthData = CONFIG.SMTP_USERNAME && CONFIG.SMTP_PASSWORD -const hasDKIMData = - CONFIG.SMTP_DKIM_DOMAINNAME && CONFIG.SMTP_DKIM_KEYSELECTOR && CONFIG.SMTP_DKIM_PRIVATKEY - -const transporter = nodemailer.createTransport({ - host: CONFIG.SMTP_HOST, - port: CONFIG.SMTP_PORT, - ignoreTLS: CONFIG.SMTP_IGNORE_TLS, - secure: CONFIG.SMTP_SECURE, // true for 465, false for other ports - pool: true, - maxConnections: CONFIG.SMTP_MAX_CONNECTIONS, - maxMessages: CONFIG.SMTP_MAX_MESSAGES, - auth: hasAuthData && { - user: CONFIG.SMTP_USERNAME, - pass: CONFIG.SMTP_PASSWORD, - }, - dkim: hasDKIMData && { - domainName: CONFIG.SMTP_DKIM_DOMAINNAME, - keySelector: CONFIG.SMTP_DKIM_KEYSELECTOR, - privateKey: CONFIG.SMTP_DKIM_PRIVATKEY, - }, -}) - -// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-empty-function -let sendMailCallback: any = async () => {} -if (!hasEmailConfig) { - if (!CONFIG.TEST) { - // eslint-disable-next-line no-console - console.log('Warning: Middlewares will not try to send mails.') - // TODO: disable e-mail logging on database seeding? - // TODO: implement general logging like 'log4js', see Gradido project: https://github.com/gradido/gradido/blob/master/backend/log4js-config.json - sendMailCallback = async (templateArgs) => { - // eslint-disable-next-line no-console - console.log('--- Log Unsend E-Mail ---') - // eslint-disable-next-line no-console - console.log('To: ' + templateArgs.to) - // eslint-disable-next-line no-console - console.log('From: ' + templateArgs.from) - // eslint-disable-next-line no-console - console.log('Subject: ' + templateArgs.subject) - // eslint-disable-next-line no-console - console.log('Content:') - // eslint-disable-next-line no-console - console.log( - cleanHtml(templateArgs.html, 'dummyKey', { - allowedTags: ['a'], - allowedAttributes: { a: ['href'] }, - // eslint-disable-next-line @typescript-eslint/no-explicit-any - } as any).replace(/&/g, '&'), - ) - } - } -} else { - sendMailCallback = async (templateArgs) => { - transporter.use( - 'compile', - htmlToText({ - ignoreImage: true, - wordwrap: false, - }), - ) - - await transporter.sendMail(templateArgs) - } -} - -export const sendMail = sendMailCallback diff --git a/backend/src/middleware/helpers/email/templateBuilder.spec.ts b/backend/src/middleware/helpers/email/templateBuilder.spec.ts deleted file mode 100644 index 85608b55a..000000000 --- a/backend/src/middleware/helpers/email/templateBuilder.spec.ts +++ /dev/null @@ -1,283 +0,0 @@ -/* eslint-disable @typescript-eslint/require-await */ -/* eslint-disable @typescript-eslint/no-unsafe-return */ -/* eslint-disable @typescript-eslint/no-unsafe-call */ -/* eslint-disable @typescript-eslint/no-unsafe-member-access */ -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ -/* eslint-disable @typescript-eslint/no-unsafe-argument */ -import CONFIG from '@config/index' -import logosWebapp from '@config/logos' - -import { - signupTemplate, - emailVerificationTemplate, - resetPasswordTemplate, - wrongAccountTemplate, - notificationTemplate, - chatMessageTemplate, -} from './templateBuilder' - -const englishHint = 'English version below!' -const welcomeImageUrl = new URL(logosWebapp.LOGO_WELCOME_PATH, CONFIG.CLIENT_URI) -const supportUrl = CONFIG.SUPPORT_URL.toString() -let actionUrl, name, settingsUrl - -const signupTemplateData = () => ({ - email: 'test@example.org', - variables: { - nonce: '12345', - inviteCode: 'AAAAAA', - }, -}) -const emailVerificationTemplateData = () => ({ - email: 'test@example.org', - variables: { - nonce: '12345', - name: 'Mr Example', - }, -}) -const resetPasswordTemplateData = () => ({ - email: 'test@example.org', - variables: { - nonce: '12345', - name: 'Mr Example', - }, -}) -const chatMessageTemplateData = { - email: 'test@example.org', - variables: { - senderUser: { - name: 'Sender', - }, - recipientUser: { - name: 'Recipient', - }, - }, -} -const wrongAccountTemplateData = () => ({ - email: 'test@example.org', - variables: {}, -}) -const notificationTemplateData = (locale) => ({ - email: 'test@example.org', - variables: { - notification: { - to: { name: 'Mr Example', locale }, - }, - }, -}) -const textsStandard = [ - { - templPropName: 'from', - isContaining: false, - text: CONFIG.EMAIL_DEFAULT_SENDER, - }, - { - templPropName: 'to', - isContaining: false, - text: 'test@example.org', - }, - // is contained in html - welcomeImageUrl.toString(), - CONFIG.ORGANIZATION_URL, - CONFIG.APPLICATION_NAME, -] -const testEmailData = (emailTemplate, templateBuilder, templateData, texts) => { - if (!emailTemplate) { - emailTemplate = templateBuilder(templateData) - } - texts.forEach((element) => { - if (typeof element === 'object') { - if (element.isContaining) { - expect(emailTemplate[element.templPropName]).toEqual(expect.stringContaining(element.text)) - } else { - expect(emailTemplate[element.templPropName]).toEqual(element.text) - } - } else { - expect(emailTemplate.html).toEqual(expect.stringContaining(element)) - } - }) - return emailTemplate -} - -describe('templateBuilder', () => { - describe('signupTemplate', () => { - describe('multi language', () => { - it('e-mail is build with all data', () => { - const subject = `Willkommen, Bienvenue, Welcome to ${CONFIG.APPLICATION_NAME}!` - const actionUrl = new URL('/registration', CONFIG.CLIENT_URI).toString() - const theSignupTemplateData = signupTemplateData() - const enContent = "Thank you for joining our cause – it's awesome to have you on board." - const deContent = - 'Danke, dass Du dich angemeldet hast – wir freuen uns, Dich dabei zu haben.' - testEmailData(null, signupTemplate, theSignupTemplateData, [ - ...textsStandard, - { - templPropName: 'subject', - isContaining: false, - text: subject, - }, - englishHint, - actionUrl, - theSignupTemplateData.variables.nonce, - theSignupTemplateData.variables.inviteCode, - enContent, - deContent, - supportUrl, - ]) - }) - }) - }) - - describe('emailVerificationTemplate', () => { - describe('multi language', () => { - it('e-mail is build with all data', () => { - const subject = 'Neue E-Mail Adresse | New E-Mail Address' - const actionUrl = new URL('/settings/my-email-address/verify', CONFIG.CLIENT_URI).toString() - const theEmailVerificationTemplateData = emailVerificationTemplateData() - const enContent = 'So, you want to change your e-mail? No problem!' - const deContent = 'Du möchtest also deine E-Mail ändern? Kein Problem!' - testEmailData(null, emailVerificationTemplate, theEmailVerificationTemplateData, [ - ...textsStandard, - { - templPropName: 'subject', - isContaining: false, - text: subject, - }, - englishHint, - actionUrl, - theEmailVerificationTemplateData.variables.nonce, - theEmailVerificationTemplateData.variables.name, - enContent, - deContent, - supportUrl, - ]) - }) - }) - }) - - describe('resetPasswordTemplate', () => { - describe('multi language', () => { - it('e-mail is build with all data', () => { - const subject = 'Neues Passwort | Reset Password' - const actionUrl = new URL('/password-reset/change-password', CONFIG.CLIENT_URI).toString() - const theResetPasswordTemplateData = resetPasswordTemplateData() - const enContent = 'So, you forgot your password? No problem!' - const deContent = 'Du hast also dein Passwort vergessen? Kein Problem!' - testEmailData(null, resetPasswordTemplate, theResetPasswordTemplateData, [ - ...textsStandard, - { - templPropName: 'subject', - isContaining: false, - text: subject, - }, - englishHint, - actionUrl, - theResetPasswordTemplateData.variables.nonce, - theResetPasswordTemplateData.variables.name, - enContent, - deContent, - supportUrl, - ]) - }) - }) - }) - - describe('chatMessageTemplate', () => { - describe('multi language', () => { - it('e-mail is build with all data', () => { - const subject = `Neue Chat-Nachricht | New chat message - ${chatMessageTemplateData.variables.senderUser.name}` - const actionUrl = new URL('/chat', CONFIG.CLIENT_URI).toString() - const enContent = `You have received a new chat message from ${chatMessageTemplateData.variables.senderUser.name}.` - const deContent = `Du hast eine neue Chat-Nachricht von ${chatMessageTemplateData.variables.senderUser.name} erhalten.` - testEmailData(null, chatMessageTemplate, chatMessageTemplateData, [ - ...textsStandard, - { - templPropName: 'subject', - isContaining: false, - text: subject, - }, - englishHint, - actionUrl, - chatMessageTemplateData.variables.senderUser, - chatMessageTemplateData.variables.recipientUser, - enContent, - deContent, - supportUrl, - ]) - }) - }) - }) - - describe('wrongAccountTemplate', () => { - describe('multi language', () => { - it('e-mail is build with all data', () => { - const subject = 'Falsche Mailadresse? | Wrong E-mail?' - const actionUrl = new URL('/password-reset/request', CONFIG.CLIENT_URI).toString() - const theWrongAccountTemplateData = wrongAccountTemplateData() - const enContent = - "You requested a password reset but unfortunately we couldn't find an account associated with your e-mail address." - const deContent = - 'Du hast bei uns ein neues Passwort angefordert – leider haben wir aber keinen Account mit Deiner E-Mailadresse gefunden.' - testEmailData(null, wrongAccountTemplate, theWrongAccountTemplateData, [ - ...textsStandard, - { - templPropName: 'subject', - isContaining: false, - text: subject, - }, - englishHint, - actionUrl, - enContent, - deContent, - supportUrl, - ]) - }) - }) - }) - - describe('notificationTemplate', () => { - beforeEach(() => { - actionUrl = new URL('/notifications', CONFIG.CLIENT_URI).toString() - name = notificationTemplateData('en').variables.notification.to.name - settingsUrl = new URL('/settings/notifications', CONFIG.CLIENT_URI) - }) - - describe('en', () => { - it('e-mail is build with all data', () => { - const subject = `${CONFIG.APPLICATION_NAME} – Notification` - const content = 'You received at least one notification. Click on this button to view them:' - testEmailData(null, notificationTemplate, notificationTemplateData('en'), [ - ...textsStandard, - { - templPropName: 'subject', - isContaining: false, - text: subject, - }, - actionUrl, - name, - content, - settingsUrl, - ]) - }) - }) - - describe('de', () => { - it('e-mail is build with all data', async () => { - const subject = `${CONFIG.APPLICATION_NAME} – Benachrichtigung` - const content = `Du hast mindestens eine Benachrichtigung erhalten. Klick auf diesen Button, um sie anzusehen:` - testEmailData(null, notificationTemplate, notificationTemplateData('de'), [ - ...textsStandard, - { - templPropName: 'subject', - isContaining: false, - text: subject, - }, - actionUrl, - name, - content, - settingsUrl, - ]) - }) - }) - }) -}) diff --git a/backend/src/middleware/helpers/email/templateBuilder.ts b/backend/src/middleware/helpers/email/templateBuilder.ts deleted file mode 100644 index ffceb49f6..000000000 --- a/backend/src/middleware/helpers/email/templateBuilder.ts +++ /dev/null @@ -1,140 +0,0 @@ -/* eslint-disable @typescript-eslint/restrict-template-expressions */ -/* eslint-disable @typescript-eslint/no-unsafe-call */ -/* eslint-disable @typescript-eslint/no-unsafe-member-access */ -/* eslint-disable @typescript-eslint/no-unsafe-argument */ -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ -/* eslint-disable import/no-namespace */ -import mustache from 'mustache' - -import CONFIG from '@config/index' -import logosWebapp from '@config/logos' -import metadata from '@config/metadata' - -import * as templates from './templates' -import * as templatesDE from './templates/de' -import * as templatesEN from './templates/en' - -const from = CONFIG.EMAIL_DEFAULT_SENDER -const welcomeImageUrl = new URL(logosWebapp.LOGO_WELCOME_PATH, CONFIG.CLIENT_URI) - -const defaultParams = { - welcomeImageUrl, - APPLICATION_NAME: CONFIG.APPLICATION_NAME, - ORGANIZATION_NAME: metadata.ORGANIZATION_NAME, - ORGANIZATION_URL: CONFIG.ORGANIZATION_URL, - supportUrl: CONFIG.SUPPORT_URL, -} -const englishHint = 'English version below!' - -export const signupTemplate = ({ email, variables: { nonce, inviteCode = null } }) => { - const subject = `Willkommen, Bienvenue, Welcome to ${CONFIG.APPLICATION_NAME}!` - // dev format example: http://localhost:3000/registration?method=invite-mail&email=huss%40pjannto.com&nonce=64853 - const actionUrl = new URL('/registration', CONFIG.CLIENT_URI) - actionUrl.searchParams.set('email', email) - actionUrl.searchParams.set('nonce', nonce) - if (inviteCode) { - actionUrl.searchParams.set('inviteCode', inviteCode) - actionUrl.searchParams.set('method', 'invite-code') - } else { - actionUrl.searchParams.set('method', 'invite-mail') - } - const renderParams = { ...defaultParams, englishHint, actionUrl, nonce, subject } - - return { - from, - to: email, - subject, - html: mustache.render(templates.layout, renderParams, { content: templates.signup }), - } -} - -export const emailVerificationTemplate = ({ email, variables: { nonce, name } }) => { - const subject = 'Neue E-Mail Adresse | New E-Mail Address' - const actionUrl = new URL('/settings/my-email-address/verify', CONFIG.CLIENT_URI) - actionUrl.searchParams.set('email', email) - actionUrl.searchParams.set('nonce', nonce) - const renderParams = { ...defaultParams, englishHint, actionUrl, name, nonce, subject } - - return { - from, - to: email, - subject, - html: mustache.render(templates.layout, renderParams, { content: templates.emailVerification }), - } -} - -export const resetPasswordTemplate = ({ email, variables: { nonce, name } }) => { - const subject = 'Neues Passwort | Reset Password' - const actionUrl = new URL('/password-reset/change-password', CONFIG.CLIENT_URI) - actionUrl.searchParams.set('nonce', nonce) - actionUrl.searchParams.set('email', email) - const renderParams = { ...defaultParams, englishHint, actionUrl, name, nonce, subject } - - return { - from, - to: email, - subject, - html: mustache.render(templates.layout, renderParams, { content: templates.passwordReset }), - } -} - -export const chatMessageTemplate = ({ email, variables: { senderUser, recipientUser } }) => { - const subject = `Neue Chat-Nachricht | New chat message - ${senderUser.name}` - const actionUrl = new URL('/chat', CONFIG.CLIENT_URI) - const renderParams = { - ...defaultParams, - subject, - englishHint, - actionUrl, - senderUser, - recipientUser, - } - - return { - from, - to: email, - subject, - html: mustache.render(templates.layout, renderParams, { content: templates.chatMessage }), - } -} - -export const wrongAccountTemplate = ({ email, _variables = {} }) => { - const subject = 'Falsche Mailadresse? | Wrong E-mail?' - const actionUrl = new URL('/password-reset/request', CONFIG.CLIENT_URI) - const renderParams = { ...defaultParams, englishHint, actionUrl } - - return { - from, - to: email, - subject, - html: mustache.render(templates.layout, renderParams, { content: templates.wrongAccount }), - } -} - -export const notificationTemplate = ({ email, variables: { notification } }) => { - const actionUrl = new URL('/notifications', CONFIG.CLIENT_URI) - const settingsUrl = new URL('/settings/notifications', CONFIG.CLIENT_URI) - const renderParams = { ...defaultParams, name: notification.to.name, settingsUrl, actionUrl } - let content - switch (notification.to.locale) { - case 'de': - content = templatesDE.notification - break - case 'en': - content = templatesEN.notification - break - - default: - content = templatesEN.notification - break - } - const subjectUnrendered = content.split('\n')[0].split('"')[1] - const subject = mustache.render(subjectUnrendered, renderParams, {}) - - return { - from, - to: email, - subject, - html: mustache.render(templates.layout, renderParams, { content }), - } -} diff --git a/backend/src/middleware/helpers/email/templates/chatMessage.html b/backend/src/middleware/helpers/email/templates/chatMessage.html deleted file mode 100644 index 49fc69bf2..000000000 --- a/backend/src/middleware/helpers/email/templates/chatMessage.html +++ /dev/null @@ -1,105 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/backend/src/middleware/helpers/email/templates/de/index.ts b/backend/src/middleware/helpers/email/templates/de/index.ts deleted file mode 100644 index 4aa323b9f..000000000 --- a/backend/src/middleware/helpers/email/templates/de/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -/* eslint-disable @typescript-eslint/no-unsafe-argument */ -/* eslint-disable security/detect-non-literal-fs-filename */ -import fs from 'node:fs' -import path from 'node:path' - -// eslint-disable-next-line n/no-sync -const readFile = (fileName) => fs.readFileSync(path.join(__dirname, fileName), 'utf-8') - -export const notification = readFile('./notification.html') diff --git a/backend/src/middleware/helpers/email/templates/de/notification.html b/backend/src/middleware/helpers/email/templates/de/notification.html deleted file mode 100644 index a54943310..000000000 --- a/backend/src/middleware/helpers/email/templates/de/notification.html +++ /dev/null @@ -1,83 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/backend/src/middleware/helpers/email/templates/emailVerification.html b/backend/src/middleware/helpers/email/templates/emailVerification.html deleted file mode 100644 index 35ce27e5a..000000000 --- a/backend/src/middleware/helpers/email/templates/emailVerification.html +++ /dev/null @@ -1,186 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/backend/src/middleware/helpers/email/templates/en/index.ts b/backend/src/middleware/helpers/email/templates/en/index.ts deleted file mode 100644 index 4aa323b9f..000000000 --- a/backend/src/middleware/helpers/email/templates/en/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -/* eslint-disable @typescript-eslint/no-unsafe-argument */ -/* eslint-disable security/detect-non-literal-fs-filename */ -import fs from 'node:fs' -import path from 'node:path' - -// eslint-disable-next-line n/no-sync -const readFile = (fileName) => fs.readFileSync(path.join(__dirname, fileName), 'utf-8') - -export const notification = readFile('./notification.html') diff --git a/backend/src/middleware/helpers/email/templates/en/notification.html b/backend/src/middleware/helpers/email/templates/en/notification.html deleted file mode 100644 index 168b21864..000000000 --- a/backend/src/middleware/helpers/email/templates/en/notification.html +++ /dev/null @@ -1,83 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/backend/src/middleware/helpers/email/templates/index.ts b/backend/src/middleware/helpers/email/templates/index.ts deleted file mode 100644 index 9a64192ce..000000000 --- a/backend/src/middleware/helpers/email/templates/index.ts +++ /dev/null @@ -1,15 +0,0 @@ -/* eslint-disable @typescript-eslint/no-unsafe-argument */ -/* eslint-disable security/detect-non-literal-fs-filename */ -import fs from 'node:fs' -import path from 'node:path' - -// eslint-disable-next-line n/no-sync -const readFile = (fileName) => fs.readFileSync(path.join(__dirname, fileName), 'utf-8') - -export const signup = readFile('./signup.html') -export const passwordReset = readFile('./resetPassword.html') -export const wrongAccount = readFile('./wrongAccount.html') -export const emailVerification = readFile('./emailVerification.html') -export const chatMessage = readFile('./chatMessage.html') - -export const layout = readFile('./layout.html') diff --git a/backend/src/middleware/helpers/email/templates/layout.html b/backend/src/middleware/helpers/email/templates/layout.html deleted file mode 100644 index 0c68d6309..000000000 --- a/backend/src/middleware/helpers/email/templates/layout.html +++ /dev/null @@ -1,197 +0,0 @@ - - - - - - - - - - {{ subject }} - - - - - - - - - - - - - - - - - -
- - - - - -
- - - diff --git a/backend/src/middleware/helpers/email/templates/resetPassword.html b/backend/src/middleware/helpers/email/templates/resetPassword.html deleted file mode 100644 index 43c45455e..000000000 --- a/backend/src/middleware/helpers/email/templates/resetPassword.html +++ /dev/null @@ -1,185 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/backend/src/middleware/helpers/email/templates/signup.html b/backend/src/middleware/helpers/email/templates/signup.html deleted file mode 100644 index 4bf17fd61..000000000 --- a/backend/src/middleware/helpers/email/templates/signup.html +++ /dev/null @@ -1,214 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/backend/src/middleware/helpers/email/templates/wrongAccount.html b/backend/src/middleware/helpers/email/templates/wrongAccount.html deleted file mode 100644 index e8f71e9ea..000000000 --- a/backend/src/middleware/helpers/email/templates/wrongAccount.html +++ /dev/null @@ -1,185 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/backend/src/middleware/index.ts b/backend/src/middleware/index.ts index 37fd33ef9..cc3af6bfc 100644 --- a/backend/src/middleware/index.ts +++ b/backend/src/middleware/index.ts @@ -1,7 +1,5 @@ /* eslint-disable @typescript-eslint/no-unsafe-argument */ -/* eslint-disable @typescript-eslint/restrict-template-expressions */ -/* eslint-disable @typescript-eslint/no-unsafe-call */ -/* eslint-disable @typescript-eslint/no-unsafe-member-access */ + /* eslint-disable @typescript-eslint/no-unsafe-assignment */ import { applyMiddleware, IMiddleware } from 'graphql-middleware' @@ -15,7 +13,6 @@ import hashtags from './hashtags/hashtagsMiddleware' import includedFields from './includedFieldsMiddleware' import languages from './languages/languages' import login from './login/loginMiddleware' -// eslint-disable-next-line import/no-cycle import notifications from './notifications/notificationsMiddleware' import orderBy from './orderByMiddleware' import permissions from './permissionsMiddleware' diff --git a/backend/src/middleware/login/loginMiddleware.ts b/backend/src/middleware/login/loginMiddleware.ts index b67e5f60a..35f3df702 100644 --- a/backend/src/middleware/login/loginMiddleware.ts +++ b/backend/src/middleware/login/loginMiddleware.ts @@ -2,43 +2,41 @@ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ -import { sendMail } from '@middleware/helpers/email/sendMail' import { - signupTemplate, - resetPasswordTemplate, - wrongAccountTemplate, - emailVerificationTemplate, -} from '@middleware/helpers/email/templateBuilder' + sendRegistrationMail, + sendEmailVerification, + sendResetPasswordMail, +} from '@src/emails/sendEmail' const sendSignupMail = async (resolve, root, args, context, resolveInfo) => { - const { inviteCode } = args + const { inviteCode, locale } = args const response = await resolve(root, args, context, resolveInfo) const { email, nonce } = response if (nonce) { // emails that already exist do not have a nonce - if (inviteCode) { - await sendMail(signupTemplate({ email, variables: { nonce, inviteCode } })) - } else { - await sendMail(signupTemplate({ email, variables: { nonce } })) - } + await sendRegistrationMail({ email, nonce, locale, inviteCode }) } delete response.nonce return response } const sendPasswordResetMail = async (resolve, root, args, context, resolveInfo) => { - const { email } = args + const { email, locale } = args const { email: userFound, nonce, name } = await resolve(root, args, context, resolveInfo) - const template = userFound ? resetPasswordTemplate : wrongAccountTemplate - await sendMail(template({ email, variables: { nonce, name } })) + if (userFound) { + await sendResetPasswordMail({ email, nonce, name, locale }) + } else { + // this is an antifeature allowing unauthenticated users to spam any email with wrong-email notifications + // await sendWrongEmail({ email, locale }) + } return true } const sendEmailVerificationMail = async (resolve, root, args, context, resolveInfo) => { const response = await resolve(root, args, context, resolveInfo) - const { email, nonce, name } = response + const { email, nonce, name, locale } = response if (nonce) { - await sendMail(emailVerificationTemplate({ email, variables: { nonce, name } })) + await sendEmailVerification({ email, nonce, name, locale }) } delete response.nonce return response diff --git a/backend/src/middleware/notifications/notificationsMiddleware.emails.spec.ts b/backend/src/middleware/notifications/notificationsMiddleware.emails.spec.ts index 79d95e43e..27aeb8cf4 100644 --- a/backend/src/middleware/notifications/notificationsMiddleware.emails.spec.ts +++ b/backend/src/middleware/notifications/notificationsMiddleware.emails.spec.ts @@ -1,33 +1,30 @@ -/* eslint-disable @typescript-eslint/no-unsafe-argument */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/require-await */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/no-unsafe-return */ +import { ApolloServer } from 'apollo-server-express' import { createTestClient } from 'apollo-server-testing' import gql from 'graphql-tag' +import databaseContext from '@context/database' import Factory, { cleanDatabase } from '@db/factories' -import { getNeode, getDriver } from '@db/neo4j' import { createGroupMutation } from '@graphql/queries/createGroupMutation' import { joinGroupMutation } from '@graphql/queries/joinGroupMutation' import CONFIG from '@src/config' -import createServer from '@src/server' +import createServer, { getContext } from '@src/server' CONFIG.CATEGORIES_ACTIVE = false -const sendMailMock: (notification) => void = jest.fn() -jest.mock('@middleware/helpers/email/sendMail', () => ({ - sendMail: (notification) => sendMailMock(notification), +const sendNotificationMailMock: (notification) => void = jest.fn() +jest.mock('@src/emails/sendEmail', () => ({ + sendNotificationMail: (notification) => sendNotificationMailMock(notification), })) -let server, query, mutate, authenticatedUser, emaillessMember +let query, mutate, authenticatedUser, emaillessMember let postAuthor, groupMember -const driver = getDriver() -const neode = getNeode() - const mentionString = ` @group-member @email-less-member` @@ -97,22 +94,18 @@ const markAllAsRead = async () => `, }) +const database = databaseContext() + +let server: ApolloServer + beforeAll(async () => { await cleanDatabase() - const createServerResult = createServer({ - context: () => { - return { - user: authenticatedUser, - neode, - driver, - cypherParams: { - currentUserId: authenticatedUser ? authenticatedUser.id : null, - }, - } - }, - }) - server = createServerResult.server + const contextUser = async (_req) => authenticatedUser + const context = getContext({ user: contextUser, database }) + + server = createServer({ context }).server + const createTestClientResult = createTestClient(server) query = createTestClientResult.query mutate = createTestClientResult.mutate @@ -120,7 +113,9 @@ beforeAll(async () => { afterAll(async () => { await cleanDatabase() - await driver.close() + void server.stop() + void database.driver.close() + database.neode.close() }) describe('emails sent for notifications', () => { @@ -149,7 +144,7 @@ describe('emails sent for notifications', () => { password: '1234', }, ) - emaillessMember = await neode.create('User', { + emaillessMember = await database.neode.create('User', { id: 'email-less-member', name: 'Email-less Member', slug: 'email-less-member', @@ -213,7 +208,13 @@ describe('emails sent for notifications', () => { }) it('sends only one email', () => { - expect(sendMailMock).toHaveBeenCalledTimes(1) + expect(sendNotificationMailMock).toHaveBeenCalledTimes(1) + expect(sendNotificationMailMock).toHaveBeenCalledWith( + expect.objectContaining({ + reason: 'mentioned_in_post', + email: 'group.member@example.org', + }), + ) }) it('sends 3 notifications', async () => { @@ -285,7 +286,13 @@ describe('emails sent for notifications', () => { }) it('sends only one email', () => { - expect(sendMailMock).toHaveBeenCalledTimes(1) + expect(sendNotificationMailMock).toHaveBeenCalledTimes(1) + expect(sendNotificationMailMock).toHaveBeenCalledWith( + expect.objectContaining({ + reason: 'followed_user_posted', + email: 'group.member@example.org', + }), + ) }) it('sends 3 notifications', async () => { @@ -358,7 +365,13 @@ describe('emails sent for notifications', () => { }) it('sends only one email', () => { - expect(sendMailMock).toHaveBeenCalledTimes(1) + expect(sendNotificationMailMock).toHaveBeenCalledTimes(1) + expect(sendNotificationMailMock).toHaveBeenCalledWith( + expect.objectContaining({ + reason: 'post_in_group', + email: 'group.member@example.org', + }), + ) }) it('sends 3 notifications', async () => { @@ -432,7 +445,7 @@ describe('emails sent for notifications', () => { }) it('sends NO email', () => { - expect(sendMailMock).not.toHaveBeenCalled() + expect(sendNotificationMailMock).not.toHaveBeenCalled() }) it('sends 3 notifications', async () => { @@ -526,7 +539,13 @@ describe('emails sent for notifications', () => { }) it('sends only one email', () => { - expect(sendMailMock).toHaveBeenCalledTimes(1) + expect(sendNotificationMailMock).toHaveBeenCalledTimes(1) + expect(sendNotificationMailMock).toHaveBeenCalledWith( + expect.objectContaining({ + reason: 'mentioned_in_comment', + email: 'group.member@example.org', + }), + ) }) it('sends 2 notifications', async () => { @@ -608,7 +627,13 @@ describe('emails sent for notifications', () => { }) it('sends only one email', () => { - expect(sendMailMock).toHaveBeenCalledTimes(1) + expect(sendNotificationMailMock).toHaveBeenCalledTimes(1) + expect(sendNotificationMailMock).toHaveBeenCalledWith( + expect.objectContaining({ + reason: 'mentioned_in_comment', + email: 'group.member@example.org', + }), + ) }) it('sends 2 notifications', async () => { @@ -691,7 +716,7 @@ describe('emails sent for notifications', () => { }) it('sends NO email', () => { - expect(sendMailMock).not.toHaveBeenCalled() + expect(sendNotificationMailMock).not.toHaveBeenCalled() }) it('sends 2 notifications', async () => { diff --git a/backend/src/middleware/notifications/notificationsMiddleware.followed-users.spec.ts b/backend/src/middleware/notifications/notificationsMiddleware.followed-users.spec.ts index 21d4a14a0..3bb0d48e3 100644 --- a/backend/src/middleware/notifications/notificationsMiddleware.followed-users.spec.ts +++ b/backend/src/middleware/notifications/notificationsMiddleware.followed-users.spec.ts @@ -1,31 +1,28 @@ /* eslint-disable @typescript-eslint/no-unsafe-call */ -/* eslint-disable @typescript-eslint/no-unsafe-argument */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ - +/* eslint-disable @typescript-eslint/no-unsafe-return */ +import { ApolloServer } from 'apollo-server-express' import { createTestClient } from 'apollo-server-testing' import gql from 'graphql-tag' +import databaseContext from '@context/database' import Factory, { cleanDatabase } from '@db/factories' -import { getNeode, getDriver } from '@db/neo4j' import { createGroupMutation } from '@graphql/queries/createGroupMutation' import CONFIG from '@src/config' -import createServer from '@src/server' +import createServer, { getContext } from '@src/server' CONFIG.CATEGORIES_ACTIVE = false -const sendMailMock: (notification) => void = jest.fn() -jest.mock('@middleware/helpers/email/sendMail', () => ({ - sendMail: (notification) => sendMailMock(notification), +const sendNotificationMailMock: (notification) => void = jest.fn() +jest.mock('@src/emails/sendEmail', () => ({ + sendNotificationMail: (notification) => sendNotificationMailMock(notification), })) -let server, query, mutate, authenticatedUser +let query, mutate, authenticatedUser let postAuthor, firstFollower, secondFollower, thirdFollower, emaillessFollower -const driver = getDriver() -const neode = getNeode() - const createPostMutation = gql` mutation ($id: ID, $title: String!, $content: String!, $groupId: ID) { CreatePost(id: $id, title: $title, content: $content, groupId: $groupId) { @@ -71,22 +68,19 @@ const followUserMutation = gql` } ` +const database = databaseContext() + +let server: ApolloServer + beforeAll(async () => { await cleanDatabase() - const createServerResult = createServer({ - context: () => { - return { - user: authenticatedUser, - neode, - driver, - cypherParams: { - currentUserId: authenticatedUser ? authenticatedUser.id : null, - }, - } - }, - }) - server = createServerResult.server + // eslint-disable-next-line @typescript-eslint/require-await + const contextUser = async (_req) => authenticatedUser + const context = getContext({ user: contextUser, database }) + + server = createServer({ context }).server + const createTestClientResult = createTestClient(server) query = createTestClientResult.query mutate = createTestClientResult.mutate @@ -94,7 +88,9 @@ beforeAll(async () => { afterAll(async () => { await cleanDatabase() - await driver.close() + void server.stop() + void database.driver.close() + database.neode.close() }) describe('following users notifications', () => { @@ -147,7 +143,7 @@ describe('following users notifications', () => { password: '1234', }, ) - emaillessFollower = await neode.create('User', { + emaillessFollower = await database.neode.create('User', { id: 'email-less-follower', name: 'Email-less Follower', slug: 'email-less-follower', @@ -272,17 +268,17 @@ describe('following users notifications', () => { }) it('sends only two emails, as second follower has emails disabled and email-less follower has no email', () => { - expect(sendMailMock).toHaveBeenCalledTimes(2) - expect(sendMailMock).toHaveBeenCalledWith( + expect(sendNotificationMailMock).toHaveBeenCalledTimes(2) + expect(sendNotificationMailMock).toHaveBeenCalledWith( expect.objectContaining({ - html: expect.stringContaining('Hello First Follower'), - to: 'first-follower@example.org', + email: 'first-follower@example.org', + reason: 'followed_user_posted', }), ) - expect(sendMailMock).toHaveBeenCalledWith( + expect(sendNotificationMailMock).toHaveBeenCalledWith( expect.objectContaining({ - html: expect.stringContaining('Hello Third Follower'), - to: 'third-follower@example.org', + email: 'third-follower@example.org', + reason: 'followed_user_posted', }), ) }) diff --git a/backend/src/middleware/notifications/notificationsMiddleware.mentions-in-groups.spec.ts b/backend/src/middleware/notifications/notificationsMiddleware.mentions-in-groups.spec.ts index 96c7e9d18..9eb26e57f 100644 --- a/backend/src/middleware/notifications/notificationsMiddleware.mentions-in-groups.spec.ts +++ b/backend/src/middleware/notifications/notificationsMiddleware.mentions-in-groups.spec.ts @@ -1,34 +1,31 @@ -/* eslint-disable @typescript-eslint/no-unsafe-argument */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/require-await */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/no-unsafe-return */ +import { ApolloServer } from 'apollo-server-express' import { createTestClient } from 'apollo-server-testing' import gql from 'graphql-tag' +import databaseContext from '@context/database' import Factory, { cleanDatabase } from '@db/factories' -import { getNeode, getDriver } from '@db/neo4j' import { changeGroupMemberRoleMutation } from '@graphql/queries/changeGroupMemberRoleMutation' import { createGroupMutation } from '@graphql/queries/createGroupMutation' import { joinGroupMutation } from '@graphql/queries/joinGroupMutation' import CONFIG from '@src/config' -import createServer from '@src/server' +import createServer, { getContext } from '@src/server' CONFIG.CATEGORIES_ACTIVE = false -const sendMailMock: (notification) => void = jest.fn() -jest.mock('@middleware/helpers/email/sendMail', () => ({ - sendMail: (notification) => sendMailMock(notification), +const sendNotificationMailMock: (notification) => void = jest.fn() +jest.mock('@src/emails/sendEmail', () => ({ + sendNotificationMail: (notification) => sendNotificationMailMock(notification), })) -let server, query, mutate, authenticatedUser +let query, mutate, authenticatedUser let postAuthor, groupMember, pendingMember, noMember, emaillessMember -const driver = getDriver() -const neode = getNeode() - const mentionString = ` @no-member @pending-member @@ -93,22 +90,18 @@ const markAllAsRead = async () => `, }) +const database = databaseContext() + +let server: ApolloServer + beforeAll(async () => { await cleanDatabase() - const createServerResult = createServer({ - context: () => { - return { - user: authenticatedUser, - neode, - driver, - cypherParams: { - currentUserId: authenticatedUser ? authenticatedUser.id : null, - }, - } - }, - }) - server = createServerResult.server + const contextUser = async (_req) => authenticatedUser + const context = getContext({ user: contextUser, database }) + + server = createServer({ context }).server + const createTestClientResult = createTestClient(server) query = createTestClientResult.query mutate = createTestClientResult.mutate @@ -116,7 +109,9 @@ beforeAll(async () => { afterAll(async () => { await cleanDatabase() - await driver.close() + void server.stop() + void database.driver.close() + database.neode.close() }) describe('mentions in groups', () => { @@ -169,7 +164,7 @@ describe('mentions in groups', () => { password: '1234', }, ) - emaillessMember = await neode.create('User', { + emaillessMember = await database.neode.create('User', { id: 'email-less-member', name: 'Email-less Member', slug: 'email-less-member', @@ -399,7 +394,25 @@ describe('mentions in groups', () => { }) it('sends only 3 emails, one for each user with an email', () => { - expect(sendMailMock).toHaveBeenCalledTimes(3) + expect(sendNotificationMailMock).toHaveBeenCalledTimes(3) + expect(sendNotificationMailMock).toHaveBeenCalledWith( + expect.objectContaining({ + email: 'group.member@example.org', + reason: 'mentioned_in_post', + }), + ) + expect(sendNotificationMailMock).toHaveBeenCalledWith( + expect.objectContaining({ + email: 'no.member@example.org', + reason: 'mentioned_in_post', + }), + ) + expect(sendNotificationMailMock).toHaveBeenCalledWith( + expect.objectContaining({ + email: 'pending.member@example.org', + reason: 'mentioned_in_post', + }), + ) }) }) @@ -495,7 +508,13 @@ describe('mentions in groups', () => { }) it('sends only 1 email', () => { - expect(sendMailMock).toHaveBeenCalledTimes(1) + expect(sendNotificationMailMock).toHaveBeenCalledTimes(1) + expect(sendNotificationMailMock).toHaveBeenCalledWith( + expect.objectContaining({ + email: 'group.member@example.org', + reason: 'mentioned_in_post', + }), + ) }) }) @@ -591,7 +610,13 @@ describe('mentions in groups', () => { }) it('sends only 1 email', () => { - expect(sendMailMock).toHaveBeenCalledTimes(1) + expect(sendNotificationMailMock).toHaveBeenCalledTimes(1) + expect(sendNotificationMailMock).toHaveBeenCalledWith( + expect.objectContaining({ + email: 'group.member@example.org', + reason: 'mentioned_in_post', + }), + ) }) }) @@ -675,7 +700,19 @@ describe('mentions in groups', () => { }) it('sends 2 emails', () => { - expect(sendMailMock).toHaveBeenCalledTimes(3) + expect(sendNotificationMailMock).toHaveBeenCalledTimes(3) + expect(sendNotificationMailMock).toHaveBeenCalledWith( + expect.objectContaining({ + email: 'group.member@example.org', + reason: 'mentioned_in_comment', + }), + ) + expect(sendNotificationMailMock).toHaveBeenCalledWith( + expect.objectContaining({ + email: 'no.member@example.org', + reason: 'mentioned_in_comment', + }), + ) }) }) @@ -766,7 +803,13 @@ describe('mentions in groups', () => { }) it('sends 1 email', () => { - expect(sendMailMock).toHaveBeenCalledTimes(1) + expect(sendNotificationMailMock).toHaveBeenCalledTimes(1) + expect(sendNotificationMailMock).toHaveBeenCalledWith( + expect.objectContaining({ + email: 'group.member@example.org', + reason: 'mentioned_in_comment', + }), + ) }) }) @@ -857,7 +900,13 @@ describe('mentions in groups', () => { }) it('sends 1 email', () => { - expect(sendMailMock).toHaveBeenCalledTimes(1) + expect(sendNotificationMailMock).toHaveBeenCalledTimes(1) + expect(sendNotificationMailMock).toHaveBeenCalledWith( + expect.objectContaining({ + email: 'group.member@example.org', + reason: 'mentioned_in_comment', + }), + ) }) }) }) diff --git a/backend/src/middleware/notifications/notificationsMiddleware.observing-posts.spec.ts b/backend/src/middleware/notifications/notificationsMiddleware.observing-posts.spec.ts index a0864fe07..e8c25a16f 100644 --- a/backend/src/middleware/notifications/notificationsMiddleware.observing-posts.spec.ts +++ b/backend/src/middleware/notifications/notificationsMiddleware.observing-posts.spec.ts @@ -1,29 +1,28 @@ +/* eslint-disable @typescript-eslint/require-await */ +/* eslint-disable @typescript-eslint/no-unsafe-return */ /* eslint-disable @typescript-eslint/no-unsafe-call */ -/* eslint-disable @typescript-eslint/no-unsafe-argument */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ +import { ApolloServer } from 'apollo-server-express' import { createTestClient } from 'apollo-server-testing' import gql from 'graphql-tag' import CONFIG from '@config/index' +import databaseContext from '@context/database' import Factory, { cleanDatabase } from '@db/factories' -import { getNeode, getDriver } from '@db/neo4j' -import createServer from '@src/server' +import createServer, { getContext } from '@src/server' CONFIG.CATEGORIES_ACTIVE = false -const sendMailMock: (notification) => void = jest.fn() -jest.mock('@middleware/helpers/email/sendMail', () => ({ - sendMail: (notification) => sendMailMock(notification), +const sendNotificationMailMock: (notification) => void = jest.fn() +jest.mock('@src/emails/sendEmail', () => ({ + sendNotificationMail: (notification) => sendNotificationMailMock(notification), })) -let server, query, mutate, authenticatedUser +let query, mutate, authenticatedUser let postAuthor, firstCommenter, secondCommenter, emaillessObserver -const driver = getDriver() -const neode = getNeode() - const createPostMutation = gql` mutation ($id: ID, $title: String!, $content: String!) { CreatePost(id: $id, title: $title, content: $content) { @@ -78,23 +77,18 @@ const toggleObservePostMutation = gql` } } ` +const database = databaseContext() + +let server: ApolloServer beforeAll(async () => { await cleanDatabase() - const createServerResult = createServer({ - context: () => { - return { - user: authenticatedUser, - neode, - driver, - cypherParams: { - currentUserId: authenticatedUser ? authenticatedUser.id : null, - }, - } - }, - }) - server = createServerResult.server + const contextUser = async (_req) => authenticatedUser + const context = getContext({ user: contextUser, database }) + + server = createServer({ context }).server + const createTestClientResult = createTestClient(server) query = createTestClientResult.query mutate = createTestClientResult.mutate @@ -102,7 +96,9 @@ beforeAll(async () => { afterAll(async () => { await cleanDatabase() - await driver.close() + void server.stop() + void database.driver.close() + database.neode.close() }) describe('notifications for users that observe a post', () => { @@ -143,7 +139,7 @@ describe('notifications for users that observe a post', () => { password: '1234', }, ) - emaillessObserver = await neode.create('User', { + emaillessObserver = await database.neode.create('User', { id: 'email-less-observer', name: 'Email-less Observer', slug: 'email-less-observer', @@ -217,10 +213,11 @@ describe('notifications for users that observe a post', () => { }) it('sends one email', () => { - expect(sendMailMock).toHaveBeenCalledTimes(1) - expect(sendMailMock).toHaveBeenCalledWith( + expect(sendNotificationMailMock).toHaveBeenCalledTimes(1) + expect(sendNotificationMailMock).toHaveBeenCalledWith( expect.objectContaining({ - to: 'post-author@example.org', + email: 'post-author@example.org', + reason: 'commented_on_post', }), ) }) @@ -307,15 +304,17 @@ describe('notifications for users that observe a post', () => { }) it('sends two emails', () => { - expect(sendMailMock).toHaveBeenCalledTimes(2) - expect(sendMailMock).toHaveBeenCalledWith( + expect(sendNotificationMailMock).toHaveBeenCalledTimes(2) + expect(sendNotificationMailMock).toHaveBeenCalledWith( expect.objectContaining({ - to: 'post-author@example.org', + email: 'post-author@example.org', + reason: 'commented_on_post', }), ) - expect(sendMailMock).toHaveBeenCalledWith( + expect(sendNotificationMailMock).toHaveBeenCalledWith( expect.objectContaining({ - to: 'first-commenter@example.org', + email: 'first-commenter@example.org', + reason: 'commented_on_post', }), ) }) @@ -421,10 +420,11 @@ describe('notifications for users that observe a post', () => { }) it('sends one email', () => { - expect(sendMailMock).toHaveBeenCalledTimes(1) - expect(sendMailMock).toHaveBeenCalledWith( + expect(sendNotificationMailMock).toHaveBeenCalledTimes(1) + expect(sendNotificationMailMock).toHaveBeenCalledWith( expect.objectContaining({ - to: 'second-commenter@example.org', + email: 'second-commenter@example.org', + reason: 'commented_on_post', }), ) }) diff --git a/backend/src/middleware/notifications/notificationsMiddleware.online-status.spec.ts b/backend/src/middleware/notifications/notificationsMiddleware.online-status.spec.ts index 3a47d376d..1cbb6a2a1 100644 --- a/backend/src/middleware/notifications/notificationsMiddleware.online-status.spec.ts +++ b/backend/src/middleware/notifications/notificationsMiddleware.online-status.spec.ts @@ -1,21 +1,20 @@ /* eslint-disable @typescript-eslint/no-unsafe-call */ -/* eslint-disable @typescript-eslint/no-unsafe-argument */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/no-unsafe-return */ import { createTestClient } from 'apollo-server-testing' import gql from 'graphql-tag' +import databaseContext from '@context/database' import Factory, { cleanDatabase } from '@db/factories' -import { getNeode, getDriver } from '@db/neo4j' import CONFIG from '@src/config' -import createServer from '@src/server' +import createServer, { getContext } from '@src/server' CONFIG.CATEGORIES_ACTIVE = false -const sendMailMock: (notification) => void = jest.fn() -jest.mock('@middleware/helpers/email/sendMail', () => ({ - sendMail: (notification) => sendMailMock(notification), +const sendNotificationMailMock: (notification) => void = jest.fn() +jest.mock('@src/emails/sendEmail', () => ({ + sendNotificationMail: (notification) => sendNotificationMailMock(notification), })) let isUserOnlineMock = jest.fn().mockReturnValue(false) @@ -23,13 +22,10 @@ jest.mock('../helpers/isUserOnline', () => ({ isUserOnline: () => isUserOnlineMock(), })) -let server, mutate, authenticatedUser +let mutate, authenticatedUser let postAuthor -const driver = getDriver() -const neode = getNeode() - const createPostMutation = gql` mutation ($id: ID, $title: String!, $content: String!, $groupId: ID) { CreatePost(id: $id, title: $title, content: $content, groupId: $groupId) { @@ -40,29 +36,24 @@ const createPostMutation = gql` } ` +const database = databaseContext() + beforeAll(async () => { await cleanDatabase() - const createServerResult = createServer({ - context: () => { - return { - user: authenticatedUser, - neode, - driver, - cypherParams: { - currentUserId: authenticatedUser ? authenticatedUser.id : null, - }, - } - }, - }) - server = createServerResult.server + // eslint-disable-next-line @typescript-eslint/require-await + const contextUser = async (_req) => authenticatedUser + const context = getContext({ user: contextUser, database }) + + const { server } = createServer({ context }) + const createTestClientResult = createTestClient(server) mutate = createTestClientResult.mutate }) afterAll(async () => { await cleanDatabase() - await driver.close() + await database.driver.close() }) afterEach(async () => { @@ -118,7 +109,7 @@ describe('online status and sending emails', () => { }) it('sends NO email to the other user', () => { - expect(sendMailMock).not.toBeCalled() + expect(sendNotificationMailMock).not.toBeCalled() }) }) }) @@ -144,7 +135,7 @@ describe('online status and sending emails', () => { }) it('sends email to the other user', () => { - expect(sendMailMock).toBeCalledTimes(1) + expect(sendNotificationMailMock).toBeCalledTimes(1) }) }) }) diff --git a/backend/src/middleware/notifications/notificationsMiddleware.posts-in-groups.spec.ts b/backend/src/middleware/notifications/notificationsMiddleware.posts-in-groups.spec.ts index 25aef2e2b..9a7e830ef 100644 --- a/backend/src/middleware/notifications/notificationsMiddleware.posts-in-groups.spec.ts +++ b/backend/src/middleware/notifications/notificationsMiddleware.posts-in-groups.spec.ts @@ -1,34 +1,31 @@ -/* eslint-disable @typescript-eslint/no-unsafe-argument */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/require-await */ /* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/no-unsafe-return */ +import { ApolloServer } from 'apollo-server-express' import { createTestClient } from 'apollo-server-testing' import gql from 'graphql-tag' +import databaseContext from '@context/database' import Factory, { cleanDatabase } from '@db/factories' -import { getNeode, getDriver } from '@db/neo4j' import { changeGroupMemberRoleMutation } from '@graphql/queries/changeGroupMemberRoleMutation' import { createGroupMutation } from '@graphql/queries/createGroupMutation' import { joinGroupMutation } from '@graphql/queries/joinGroupMutation' import CONFIG from '@src/config' -import createServer from '@src/server' +import createServer, { getContext } from '@src/server' CONFIG.CATEGORIES_ACTIVE = false -const sendMailMock: (notification) => void = jest.fn() -jest.mock('@middleware/helpers/email/sendMail', () => ({ - sendMail: (notification) => sendMailMock(notification), +const sendNotificationMailMock: (notification) => void = jest.fn() +jest.mock('@src/emails/sendEmail', () => ({ + sendNotificationMail: (notification) => sendNotificationMailMock(notification), })) -let server, query, mutate, authenticatedUser +let query, mutate, authenticatedUser let postAuthor, groupMember, pendingMember, emaillessMember -const driver = getDriver() -const neode = getNeode() - const createPostMutation = gql` mutation ($id: ID, $title: String!, $content: String!, $groupId: ID) { CreatePost(id: $id, title: $title, content: $content, groupId: $groupId) { @@ -95,22 +92,17 @@ const markAllAsRead = async () => `, }) +const database = databaseContext() + +let server: ApolloServer beforeAll(async () => { await cleanDatabase() - const createServerResult = createServer({ - context: () => { - return { - user: authenticatedUser, - neode, - driver, - cypherParams: { - currentUserId: authenticatedUser ? authenticatedUser.id : null, - }, - } - }, - }) - server = createServerResult.server + const contextUser = async (_req) => authenticatedUser + const context = getContext({ user: contextUser, database }) + + server = createServer({ context }).server + const createTestClientResult = createTestClient(server) query = createTestClientResult.query mutate = createTestClientResult.mutate @@ -118,7 +110,9 @@ beforeAll(async () => { afterAll(async () => { await cleanDatabase() - await driver.close() + void server.stop() + void database.driver.close() + database.neode.close() }) describe('notify group members of new posts in group', () => { @@ -143,7 +137,7 @@ describe('notify group members of new posts in group', () => { slug: 'group-member', }, { - email: 'test2@example.org', + email: 'group.member@example.org', password: '1234', }, ) @@ -159,7 +153,7 @@ describe('notify group members of new posts in group', () => { password: '1234', }, ) - emaillessMember = await neode.create('User', { + emaillessMember = await database.neode.create('User', { id: 'email-less-member', name: 'Email-less Member', slug: 'email-less-member', @@ -301,7 +295,13 @@ describe('notify group members of new posts in group', () => { }) it('sends one email', () => { - expect(sendMailMock).toHaveBeenCalledTimes(1) + expect(sendNotificationMailMock).toHaveBeenCalledTimes(1) + expect(sendNotificationMailMock).toHaveBeenCalledWith( + expect.objectContaining({ + reason: 'post_in_group', + email: 'group.member@example.org', + }), + ) }) describe('group member mutes group', () => { @@ -343,7 +343,7 @@ describe('notify group members of new posts in group', () => { }) it('sends NO email', () => { - expect(sendMailMock).not.toHaveBeenCalled() + expect(sendNotificationMailMock).not.toHaveBeenCalled() }) describe('group member unmutes group again but disables email', () => { @@ -398,7 +398,7 @@ describe('notify group members of new posts in group', () => { }) it('sends NO email', () => { - expect(sendMailMock).not.toHaveBeenCalled() + expect(sendNotificationMailMock).not.toHaveBeenCalled() }) }) }) @@ -439,7 +439,7 @@ describe('notify group members of new posts in group', () => { }) it('sends NO email', () => { - expect(sendMailMock).not.toHaveBeenCalled() + expect(sendNotificationMailMock).not.toHaveBeenCalled() }) }) @@ -479,7 +479,7 @@ describe('notify group members of new posts in group', () => { }) it('sends NO email', () => { - expect(sendMailMock).not.toHaveBeenCalled() + expect(sendNotificationMailMock).not.toHaveBeenCalled() }) }) }) diff --git a/backend/src/middleware/notifications/notificationsMiddleware.spec.ts b/backend/src/middleware/notifications/notificationsMiddleware.spec.ts index ab0a6a5b2..56fddfd74 100644 --- a/backend/src/middleware/notifications/notificationsMiddleware.spec.ts +++ b/backend/src/middleware/notifications/notificationsMiddleware.spec.ts @@ -4,11 +4,13 @@ /* eslint-disable @typescript-eslint/no-unsafe-argument */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/no-unsafe-return */ +import { ApolloServer } from 'apollo-server-express' import { createTestClient } from 'apollo-server-testing' import gql from 'graphql-tag' +import databaseContext from '@context/database' +import pubsubContext from '@context/pubsub' import Factory, { cleanDatabase } from '@db/factories' -import { getNeode, getDriver } from '@db/neo4j' import { changeGroupMemberRoleMutation } from '@graphql/queries/changeGroupMemberRoleMutation' import { createGroupMutation } from '@graphql/queries/createGroupMutation' import { createMessageMutation } from '@graphql/queries/createMessageMutation' @@ -16,18 +18,13 @@ import { createRoomMutation } from '@graphql/queries/createRoomMutation' import { joinGroupMutation } from '@graphql/queries/joinGroupMutation' import { leaveGroupMutation } from '@graphql/queries/leaveGroupMutation' import { removeUserFromGroupMutation } from '@graphql/queries/removeUserFromGroupMutation' -import createServer, { pubsub } from '@src/server' +import createServer, { getContext } from '@src/server' -const sendMailMock: (notification) => void = jest.fn() -jest.mock('@middleware/helpers/email/sendMail', () => ({ - sendMail: (notification) => sendMailMock(notification), -})) - -const chatMessageTemplateMock = jest.fn() -const notificationTemplateMock = jest.fn() -jest.mock('../helpers/email/templateBuilder', () => ({ - chatMessageTemplate: () => chatMessageTemplateMock(), - notificationTemplate: () => notificationTemplateMock(), +const sendChatMessageMailMock: (notification) => void = jest.fn() +const sendNotificationMailMock: (notification) => void = jest.fn() +jest.mock('@src/emails/sendEmail', () => ({ + sendChatMessageMail: (notification) => sendChatMessageMailMock(notification), + sendNotificationMail: (notification) => sendNotificationMailMock(notification), })) let isUserOnlineMock = jest.fn() @@ -35,12 +32,12 @@ jest.mock('../helpers/isUserOnline', () => ({ isUserOnline: () => isUserOnlineMock(), })) +const database = databaseContext() +const pubsub = pubsubContext() const pubsubSpy = jest.spyOn(pubsub, 'publish') -let server, query, mutate, notifiedUser, authenticatedUser +let query, mutate, notifiedUser, authenticatedUser -const driver = getDriver() -const neode = getNeode() const categoryIds = ['cat9'] const createPostMutation = gql` mutation ($id: ID, $title: String!, $postContent: String!, $categoryIds: [ID]!) { @@ -68,19 +65,16 @@ const createCommentMutation = gql` } ` +let server: ApolloServer + beforeAll(async () => { await cleanDatabase() - const createServerResult = createServer({ - context: () => { - return { - user: authenticatedUser, - neode, - driver, - } - }, - }) - server = createServerResult.server + const contextUser = async (_req) => authenticatedUser + const context = getContext({ user: contextUser, database, pubsub }) + + server = createServer({ context }).server + const createTestClientResult = createTestClient(server) query = createTestClientResult.query mutate = createTestClientResult.mutate @@ -88,7 +82,9 @@ beforeAll(async () => { afterAll(async () => { await cleanDatabase() - await driver.close() + void server.stop() + void database.driver.close() + database.neode.close() }) beforeEach(async () => { @@ -104,7 +100,7 @@ beforeEach(async () => { password: '1234', }, ) - await neode.create('Category', { + await database.neode.create('Category', { id: 'cat9', name: 'Democracy & Politics', icon: 'university', @@ -239,8 +235,13 @@ describe('notifications', () => { ) // Mail - expect(sendMailMock).toHaveBeenCalledTimes(1) - expect(notificationTemplateMock).toHaveBeenCalledTimes(1) + expect(sendNotificationMailMock).toHaveBeenCalledTimes(1) + expect(sendNotificationMailMock).toHaveBeenCalledWith( + expect.objectContaining({ + reason: 'commented_on_post', + email: 'test@example.org', + }), + ) }) describe('if I have disabled `emailNotificationsCommentOnObservedPost`', () => { @@ -275,8 +276,7 @@ describe('notifications', () => { ) // No Mail - expect(sendMailMock).not.toHaveBeenCalled() - expect(notificationTemplateMock).not.toHaveBeenCalled() + expect(sendNotificationMailMock).not.toHaveBeenCalled() }) }) @@ -397,8 +397,13 @@ describe('notifications', () => { }) // Mail - expect(sendMailMock).toHaveBeenCalledTimes(1) - expect(notificationTemplateMock).toHaveBeenCalledTimes(1) + expect(sendNotificationMailMock).toHaveBeenCalledTimes(1) + expect(sendNotificationMailMock).toHaveBeenCalledWith( + expect.objectContaining({ + reason: 'mentioned_in_post', + email: 'test@example.org', + }), + ) }) describe('if I have disabled `emailNotificationsMention`', () => { @@ -433,8 +438,7 @@ describe('notifications', () => { }) // Mail - expect(sendMailMock).not.toHaveBeenCalled() - expect(notificationTemplateMock).not.toHaveBeenCalled() + expect(sendNotificationMailMock).not.toHaveBeenCalled() }) }) @@ -940,8 +944,7 @@ describe('notifications', () => { userId: 'chatReceiver', }) - expect(sendMailMock).not.toHaveBeenCalled() - expect(chatMessageTemplateMock).not.toHaveBeenCalled() + expect(sendChatMessageMailMock).not.toHaveBeenCalled() }) }) @@ -976,8 +979,20 @@ describe('notifications', () => { userId: 'chatReceiver', }) - expect(sendMailMock).toHaveBeenCalledTimes(1) - expect(chatMessageTemplateMock).toHaveBeenCalledTimes(1) + expect(sendChatMessageMailMock).toHaveBeenCalledTimes(1) + expect(sendChatMessageMailMock).toHaveBeenCalledWith({ + email: 'user@example.org', + senderUser: expect.objectContaining({ + name: 'chatSender', + slug: 'chatsender', + id: 'chatSender', + }), + recipientUser: expect.objectContaining({ + name: 'chatReceiver', + slug: 'chatreceiver', + id: 'chatReceiver', + }), + }) }) }) @@ -997,8 +1012,7 @@ describe('notifications', () => { expect(pubsubSpy).not.toHaveBeenCalled() expect(pubsubSpy).not.toHaveBeenCalled() - expect(sendMailMock).not.toHaveBeenCalled() - expect(chatMessageTemplateMock).not.toHaveBeenCalled() + expect(sendChatMessageMailMock).not.toHaveBeenCalled() }) }) @@ -1018,8 +1032,7 @@ describe('notifications', () => { expect(pubsubSpy).not.toHaveBeenCalled() expect(pubsubSpy).not.toHaveBeenCalled() - expect(sendMailMock).not.toHaveBeenCalled() - expect(chatMessageTemplateMock).not.toHaveBeenCalled() + expect(sendChatMessageMailMock).not.toHaveBeenCalled() }) }) @@ -1055,8 +1068,7 @@ describe('notifications', () => { userId: 'chatReceiver', }) - expect(sendMailMock).not.toHaveBeenCalled() - expect(chatMessageTemplateMock).not.toHaveBeenCalled() + expect(sendChatMessageMailMock).not.toHaveBeenCalled() }) }) }) @@ -1136,8 +1148,13 @@ describe('notifications', () => { }) // Mail - expect(sendMailMock).toHaveBeenCalledTimes(1) - expect(notificationTemplateMock).toHaveBeenCalledTimes(1) + expect(sendNotificationMailMock).toHaveBeenCalledTimes(1) + expect(sendNotificationMailMock).toHaveBeenCalledWith( + expect.objectContaining({ + reason: 'user_joined_group', + email: 'owner@example.org', + }), + ) }) describe('if the group owner has disabled `emailNotificationsGroupMemberJoined`', () => { @@ -1169,8 +1186,7 @@ describe('notifications', () => { }) // Mail - expect(sendMailMock).not.toHaveBeenCalled() - expect(notificationTemplateMock).not.toHaveBeenCalled() + expect(sendNotificationMailMock).not.toHaveBeenCalled() }) }) }) @@ -1239,8 +1255,19 @@ describe('notifications', () => { }) // Mail - expect(sendMailMock).toHaveBeenCalledTimes(2) - expect(notificationTemplateMock).toHaveBeenCalledTimes(2) + expect(sendNotificationMailMock).toHaveBeenCalledTimes(2) + expect(sendNotificationMailMock).toHaveBeenCalledWith( + expect.objectContaining({ + reason: 'user_joined_group', + email: 'owner@example.org', + }), + ) + expect(sendNotificationMailMock).toHaveBeenCalledWith( + expect.objectContaining({ + reason: 'user_left_group', + email: 'owner@example.org', + }), + ) }) describe('if the group owner has disabled `emailNotificationsGroupMemberLeft`', () => { @@ -1284,8 +1311,7 @@ describe('notifications', () => { }) // Mail - expect(sendMailMock).toHaveBeenCalledTimes(1) - expect(notificationTemplateMock).toHaveBeenCalledTimes(1) + expect(sendNotificationMailMock).toHaveBeenCalledTimes(1) }) }) }) @@ -1344,8 +1370,13 @@ describe('notifications', () => { }) // Mail - expect(sendMailMock).toHaveBeenCalledTimes(1) - expect(notificationTemplateMock).toHaveBeenCalledTimes(1) + expect(sendNotificationMailMock).toHaveBeenCalledTimes(1) + expect(sendNotificationMailMock).toHaveBeenCalledWith( + expect.objectContaining({ + reason: 'changed_group_member_role', + email: 'test@example.org', + }), + ) }) describe('if the group member has disabled `emailNotificationsGroupMemberRoleChanged`', () => { @@ -1377,8 +1408,7 @@ describe('notifications', () => { }) // Mail - expect(sendMailMock).not.toHaveBeenCalled() - expect(notificationTemplateMock).not.toHaveBeenCalled() + expect(sendNotificationMailMock).not.toHaveBeenCalled() }) }) }) @@ -1436,8 +1466,13 @@ describe('notifications', () => { }) // Mail - expect(sendMailMock).toHaveBeenCalledTimes(1) - expect(notificationTemplateMock).toHaveBeenCalledTimes(1) + expect(sendNotificationMailMock).toHaveBeenCalledTimes(1) + expect(sendNotificationMailMock).toHaveBeenCalledWith( + expect.objectContaining({ + reason: 'removed_user_from_group', + email: 'test@example.org', + }), + ) }) describe('if the previous group member has disabled `emailNotificationsGroupMemberRemoved`', () => { @@ -1469,8 +1504,7 @@ describe('notifications', () => { }) // Mail - expect(sendMailMock).not.toHaveBeenCalled() - expect(notificationTemplateMock).not.toHaveBeenCalled() + expect(sendNotificationMailMock).not.toHaveBeenCalled() }) }) }) diff --git a/backend/src/middleware/notifications/notificationsMiddleware.ts b/backend/src/middleware/notifications/notificationsMiddleware.ts index 4926dc94e..559c72b06 100644 --- a/backend/src/middleware/notifications/notificationsMiddleware.ts +++ b/backend/src/middleware/notifications/notificationsMiddleware.ts @@ -4,16 +4,15 @@ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable security/detect-object-injection */ -import { sendMail } from '@middleware/helpers/email/sendMail' import { - chatMessageTemplate, - notificationTemplate, -} from '@middleware/helpers/email/templateBuilder' + NOTIFICATION_ADDED, + ROOM_COUNT_UPDATED, + CHAT_MESSAGE_ADDED, +} from '@constants/subscriptions' +import { getUnreadRoomsCount } from '@graphql/resolvers/rooms' import { isUserOnline } from '@middleware/helpers/isUserOnline' import { validateNotifyUsers } from '@middleware/validation/validationMiddleware' -// eslint-disable-next-line import/no-cycle -import { getUnreadRoomsCount } from '@schema/resolvers/rooms' -import { pubsub, NOTIFICATION_ADDED, ROOM_COUNT_UPDATED, CHAT_MESSAGE_ADDED } from '@src/server' +import { sendNotificationMail, sendChatMessageMail } from '@src/emails/sendEmail' import extractMentionedUsers from './mentions/extractMentionedUsers' @@ -25,19 +24,14 @@ const publishNotifications = async ( ): Promise => { const notifications = await notificationsPromise notifications.forEach((notificationAdded) => { - pubsub.publish(NOTIFICATION_ADDED, { notificationAdded }) + context.pubsub.publish(NOTIFICATION_ADDED, { notificationAdded }) if ( notificationAdded.email && // no primary email was found (notificationAdded.to[emailNotificationSetting] ?? true) && !isUserOnline(notificationAdded.to) && !emailsSent.includes(notificationAdded.email) ) { - sendMail( - notificationTemplate({ - email: notificationAdded.email, - variables: { notification: notificationAdded }, - }), - ) + void sendNotificationMail(notificationAdded) emailsSent.push(notificationAdded.email) } }) @@ -482,18 +476,18 @@ const handleCreateMessage = async (resolve, root, args, context, resolveInfo) => // send subscriptions const roomCountUpdated = await getUnreadRoomsCount(recipientUser.id, session) - void pubsub.publish(ROOM_COUNT_UPDATED, { + void context.pubsub.publish(ROOM_COUNT_UPDATED, { roomCountUpdated, userId: recipientUser.id, }) - void pubsub.publish(CHAT_MESSAGE_ADDED, { + void context.pubsub.publish(CHAT_MESSAGE_ADDED, { chatMessageAdded: message, userId: recipientUser.id, }) // Send EMail if we found a user(not blocked) and he is not considered online if (recipientUser.emailNotificationsChatMessage !== false && !isUserOnline(recipientUser)) { - void sendMail(chatMessageTemplate({ email, variables: { senderUser, recipientUser } })) + void sendChatMessageMail({ email, senderUser, recipientUser }) } } diff --git a/backend/src/middleware/permissionsMiddleware.spec.ts b/backend/src/middleware/permissionsMiddleware.spec.ts index ca45005fe..e8089b7f3 100644 --- a/backend/src/middleware/permissionsMiddleware.spec.ts +++ b/backend/src/middleware/permissionsMiddleware.spec.ts @@ -177,8 +177,8 @@ describe('authorization', () => { describe('access Signup', () => { const signupMutation = gql` - mutation ($email: String!, $inviteCode: String) { - Signup(email: $email, inviteCode: $inviteCode) { + mutation ($email: String!, $locale: String!, $inviteCode: String) { + Signup(email: $email, locale: $locale, inviteCode: $inviteCode) { email } } @@ -189,6 +189,7 @@ describe('authorization', () => { variables = { email: 'some@email.org', inviteCode: 'ABCDEF', + locale: 'de', } CONFIG.INVITE_REGISTRATION = false CONFIG.PUBLIC_REGISTRATION = false @@ -231,6 +232,7 @@ describe('authorization', () => { variables = { email: 'some@email.org', inviteCode: 'ABCDEF', + locale: 'de', } CONFIG.INVITE_REGISTRATION = false CONFIG.PUBLIC_REGISTRATION = true @@ -269,6 +271,7 @@ describe('authorization', () => { variables = { email: 'some@email.org', inviteCode: 'ABCDEF', + locale: 'de', } authenticatedUser = null }) @@ -288,6 +291,7 @@ describe('authorization', () => { variables = { email: 'some@email.org', inviteCode: 'no valid invite code', + locale: 'de', } authenticatedUser = null }) diff --git a/backend/src/middleware/permissionsMiddleware.ts b/backend/src/middleware/permissionsMiddleware.ts index 3897a61e9..5725b2d98 100644 --- a/backend/src/middleware/permissionsMiddleware.ts +++ b/backend/src/middleware/permissionsMiddleware.ts @@ -7,9 +7,9 @@ import { rule, shield, deny, allow, or, and } from 'graphql-shield' import CONFIG from '@config/index' +import SocialMedia from '@db/models/SocialMedia' import { getNeode } from '@db/neo4j' -import SocialMedia from '@models/SocialMedia' -import { validateInviteCode } from '@schema/resolvers/transactions/inviteCodes' +import { validateInviteCode } from '@graphql/resolvers/transactions/inviteCodes' const debug = !!CONFIG.DEBUG const allowExternalErrors = true @@ -483,6 +483,9 @@ export default shield( email: or(isMyOwn, isAdmin), emailNotificationSettings: isMyOwn, }, + Location: { + distanceToMe: isAuthenticated, + }, Report: isModerator, }, { diff --git a/backend/src/middleware/sluggifyMiddleware.ts b/backend/src/middleware/sluggifyMiddleware.ts index 92c2c1367..0a45521f0 100644 --- a/backend/src/middleware/sluggifyMiddleware.ts +++ b/backend/src/middleware/sluggifyMiddleware.ts @@ -1,18 +1,18 @@ -/* eslint-disable @typescript-eslint/restrict-template-expressions */ /* eslint-disable @typescript-eslint/no-unsafe-call */ -/* eslint-disable @typescript-eslint/no-unsafe-member-access */ -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ + /* eslint-disable @typescript-eslint/no-unsafe-return */ +import type { Context } from '@src/server' + import uniqueSlug from './slugify/uniqueSlug' -const isUniqueFor = (context, type) => { - return async (slug) => { +const isUniqueFor = (context: Context, type: string) => { + return async (slug: string) => { const session = context.driver.session() try { const existingSlug = await session.readTransaction((transaction) => { return transaction.run( ` - MATCH(p:${type} {slug: $slug }) + MATCH(p:${type} {slug: $slug }) RETURN p.slug `, { slug }, @@ -20,26 +20,50 @@ const isUniqueFor = (context, type) => { }) return existingSlug.records.length === 0 } finally { - session.close() + await session.close() } } } export default { Mutation: { - SignupVerification: async (resolve, root, args, context, info) => { + SignupVerification: async ( + resolve, + root, + args: { slug: string; name: string }, + context: Context, + info, + ) => { args.slug = args.slug || (await uniqueSlug(args.name, isUniqueFor(context, 'User'))) return resolve(root, args, context, info) }, - CreateGroup: async (resolve, root, args, context, info) => { + CreateGroup: async ( + resolve, + root, + args: { slug: string; name: string }, + context: Context, + info, + ) => { args.slug = args.slug || (await uniqueSlug(args.name, isUniqueFor(context, 'Group'))) return resolve(root, args, context, info) }, - CreatePost: async (resolve, root, args, context, info) => { + CreatePost: async ( + resolve, + root, + args: { slug: string; title: string }, + context: Context, + info, + ) => { args.slug = args.slug || (await uniqueSlug(args.title, isUniqueFor(context, 'Post'))) return resolve(root, args, context, info) }, - UpdatePost: async (resolve, root, args, context, info) => { + UpdatePost: async ( + resolve, + root, + args: { slug: string; title: string }, + context: Context, + info, + ) => { // TODO: is this absolutely correct? what happens if "args.title" is not defined? may it works accidentally, because "args.title" or "args.slug" is always send? args.slug = args.slug || (await uniqueSlug(args.title, isUniqueFor(context, 'Post'))) return resolve(root, args, context, info) diff --git a/backend/src/middleware/slugify/uniqueSlug.spec.ts b/backend/src/middleware/slugify/uniqueSlug.spec.ts index 659a439c2..8259583cd 100644 --- a/backend/src/middleware/slugify/uniqueSlug.spec.ts +++ b/backend/src/middleware/slugify/uniqueSlug.spec.ts @@ -14,9 +14,11 @@ describe('uniqueSlug', () => { }) it('slugify null string', async () => { - const string = null + const nullString = null const isUnique = jest.fn().mockResolvedValue(true) - await expect(uniqueSlug(string, isUnique)).resolves.toEqual('anonymous') + await expect(uniqueSlug(nullString as unknown as string, isUnique)).resolves.toEqual( + 'anonymous', + ) }) it('Converts umlaut to a two letter equivalent', async () => { diff --git a/backend/src/middleware/slugify/uniqueSlug.ts b/backend/src/middleware/slugify/uniqueSlug.ts index e24b15eb3..8f540a6ab 100644 --- a/backend/src/middleware/slugify/uniqueSlug.ts +++ b/backend/src/middleware/slugify/uniqueSlug.ts @@ -1,18 +1,15 @@ -/* eslint-disable @typescript-eslint/restrict-template-expressions */ -/* eslint-disable @typescript-eslint/no-unsafe-return */ -/* eslint-disable @typescript-eslint/no-unsafe-call */ -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ import slugify from 'slug' -export default async function uniqueSlug(string, isUnique) { - const slug = slugify(string || 'anonymous', { +type IsUnique = (slug: string) => Promise +export default async function uniqueSlug(str: string, isUnique: IsUnique) { + const slug = slugify(str || 'anonymous', { lower: true, multicharmap: { Ä: 'AE', ä: 'ae', Ö: 'OE', ö: 'oe', Ü: 'UE', ü: 'ue', ß: 'ss' }, }) if (await isUnique(slug)) return slug let count = 0 - let uniqueSlug + let uniqueSlug: string do { count += 1 uniqueSlug = `${slug}-${count}` diff --git a/backend/src/middleware/xssMiddleware.ts b/backend/src/middleware/xssMiddleware.ts index 31ded633c..e8beb5463 100644 --- a/backend/src/middleware/xssMiddleware.ts +++ b/backend/src/middleware/xssMiddleware.ts @@ -1,13 +1,42 @@ +/* eslint-disable security/detect-object-injection */ +/* eslint-disable @typescript-eslint/no-unsafe-argument */ +/* eslint-disable promise/prefer-await-to-callbacks */ /* eslint-disable @typescript-eslint/require-await */ /* eslint-disable @typescript-eslint/no-unsafe-member-access */ /* eslint-disable @typescript-eslint/no-unsafe-call */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ /* eslint-disable @typescript-eslint/no-unsafe-return */ -import walkRecursive from '@helpers/walkRecursive' - import { cleanHtml } from './helpers/cleanHtml' -// exclamation mark separetes field names, that should not be sanitized +/** + * iterate through all fields and replace it with the callback result + * @property data Array + * @property fields Array + * @property fieldName String + * @property callback Function + */ +const walkRecursive = (data, fields, fieldName, callback, _key?) => { + if (!Array.isArray(fields)) { + throw new Error('please provide an fields array for the walkRecursive helper') + } + const fieldDef = fields.find((f) => f.field === _key) + if (data && typeof data === 'string' && fieldDef) { + if (!fieldDef.excludes?.includes(fieldName)) data = callback(data, _key) + } else if (data && Array.isArray(data)) { + // go into the rabbit hole and dig through that array + data.forEach((res, index) => { + data[index] = walkRecursive(data[index], fields, fieldName, callback, index) + }) + } else if (data && typeof data === 'object') { + // lets get some keys and stir them + Object.keys(data).forEach((k) => { + data[k] = walkRecursive(data[k], fields, fieldName, callback, k) + }) + } + return data +} + +// exclamation mark separates field names, that should not be sanitized const fields = [ { field: 'content', excludes: ['CreateMessage', 'Message'] }, { field: 'contentExcerpt' }, diff --git a/backend/src/schema/resolvers/locations.spec.ts b/backend/src/schema/resolvers/locations.spec.ts deleted file mode 100644 index aed85da54..000000000 --- a/backend/src/schema/resolvers/locations.spec.ts +++ /dev/null @@ -1,95 +0,0 @@ -/* eslint-disable @typescript-eslint/no-unsafe-call */ -/* eslint-disable @typescript-eslint/no-unsafe-member-access */ -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ -import { createTestClient } from 'apollo-server-testing' -import gql from 'graphql-tag' - -import Factory, { cleanDatabase } from '@db/factories' -import { getNeode, getDriver } from '@db/neo4j' -import createServer from '@src/server' - -let mutate, authenticatedUser - -const driver = getDriver() -const neode = getNeode() - -beforeAll(async () => { - await cleanDatabase() - - const { server } = createServer({ - context: () => { - return { - driver, - neode, - user: authenticatedUser, - } - }, - }) - mutate = createTestClient(server).mutate -}) - -afterAll(async () => { - await cleanDatabase() - await driver.close() -}) - -// TODO: avoid database clean after each test in the future if possible for performance and flakyness reasons by filling the database step by step, see issue https://github.com/Ocelot-Social-Community/Ocelot-Social/issues/4543 -afterEach(async () => { - await cleanDatabase() -}) - -describe('resolvers', () => { - describe('Location', () => { - describe('custom mutation, not handled by neo4j-graphql-js', () => { - let variables - const updateUserMutation = gql` - mutation ($id: ID!, $name: String) { - UpdateUser(id: $id, name: $name) { - name - location { - name: nameRU - nameEN - } - } - } - ` - - beforeEach(async () => { - variables = { - id: 'u47', - name: 'John Doughnut', - } - const Paris = await Factory.build('location', { - id: 'region.9397217726497330', - name: 'Paris', - type: 'region', - lng: 2.35183, - lat: 48.85658, - nameEN: 'Paris', - }) - - const user = await Factory.build('user', { - id: 'u47', - name: 'John Doe', - }) - await user.relateTo(Paris, 'isIn') - authenticatedUser = await user.toJson() - }) - - it('returns `null` if location translation is not available', async () => { - await expect(mutate({ mutation: updateUserMutation, variables })).resolves.toMatchObject({ - data: { - UpdateUser: { - name: 'John Doughnut', - location: { - name: null, - nameEN: 'Paris', - }, - }, - }, - errors: undefined, - }) - }) - }) - }) -}) diff --git a/backend/src/schema/resolvers/locations.ts b/backend/src/schema/resolvers/locations.ts deleted file mode 100644 index bcefa2337..000000000 --- a/backend/src/schema/resolvers/locations.ts +++ /dev/null @@ -1,33 +0,0 @@ -/* eslint-disable @typescript-eslint/no-unsafe-member-access */ -/* eslint-disable @typescript-eslint/no-unsafe-argument */ -import { UserInputError } from 'apollo-server' - -import Resolver from './helpers/Resolver' -import { queryLocations } from './users/location' - -export default { - Location: { - ...Resolver('Location', { - undefinedToNull: [ - 'nameEN', - 'nameDE', - 'nameFR', - 'nameNL', - 'nameIT', - 'nameES', - 'namePT', - 'namePL', - 'nameRU', - ], - }), - }, - Query: { - queryLocations: async (_object, args, _context, _resolveInfo) => { - try { - return queryLocations(args) - } catch (e) { - throw new UserInputError(e.message) - } - }, - }, -} diff --git a/backend/src/server.ts b/backend/src/server.ts index 372ec964b..1f98aab2d 100644 --- a/backend/src/server.ts +++ b/backend/src/server.ts @@ -10,62 +10,55 @@ import http from 'node:http' import { ApolloServer } from 'apollo-server-express' import bodyParser from 'body-parser' import express from 'express' -import { RedisPubSub } from 'graphql-redis-subscriptions' -import { PubSub } from 'graphql-subscriptions' import { graphqlUploadExpress } from 'graphql-upload' import helmet from 'helmet' -import Redis from 'ioredis' + +import databaseContext from '@context/database' +import pubsubContext from '@context/pubsub' import CONFIG from './config' -import { getNeode, getDriver } from './db/neo4j' +import schema from './graphql/schema' import decode from './jwt/decode' -// eslint-disable-next-line import/no-cycle import middleware from './middleware' -import schema from './schema' -export const NOTIFICATION_ADDED = 'NOTIFICATION_ADDED' -export const CHAT_MESSAGE_ADDED = 'CHAT_MESSAGE_ADDED' -export const ROOM_COUNT_UPDATED = 'ROOM_COUNT_UPDATED' -const { REDIS_DOMAIN, REDIS_PORT, REDIS_PASSWORD } = CONFIG -let prodPubsub, devPubsub -const options = { - host: REDIS_DOMAIN, - port: REDIS_PORT, - password: REDIS_PASSWORD, - retryStrategy: (times) => { - return Math.min(times * 50, 2000) - }, -} -if (options.host && options.port && options.password) { - prodPubsub = new RedisPubSub({ - publisher: new Redis(options), - subscriber: new Redis(options), - }) -} else { - devPubsub = new PubSub() -} -export const pubsub = prodPubsub || devPubsub -const driver = getDriver() -const neode = getNeode() +const serverDatabase = databaseContext() +const serverPubsub = pubsubContext() -const getContext = async (req) => { - const user = await decode(driver, req.headers.authorization) - return { - driver, - neode, - user, - req, - cypherParams: { - currentUserId: user ? user.id : null, - }, +const databaseUser = async (req) => decode(serverDatabase.driver, req.headers.authorization) + +export const getContext = + ( + { + database = serverDatabase, + pubsub = serverPubsub, + user = databaseUser, + }: { + database?: ReturnType + pubsub?: ReturnType + user?: (any) => Promise + } = { database: serverDatabase, pubsub: serverPubsub, user: databaseUser }, + ) => + async (req) => { + const u = await user(req) + return { + database, + driver: database.driver, + neode: database.neode, + pubsub, + user: u, + req, + cypherParams: { + currentUserId: u ? u.id : null, + }, + } } -} + export const context = async (options) => { const { connection, req } = options if (connection) { return connection.context } else { - return getContext(req) + return getContext()(req) } } @@ -74,9 +67,7 @@ const createServer = (options?) => { context, schema: middleware(schema), subscriptions: { - onConnect: (connectionParams, _webSocket) => { - return getContext(connectionParams) - }, + onConnect: (connectionParams) => getContext()(connectionParams), }, debug: !!CONFIG.DEBUG, uploads: false, @@ -88,11 +79,10 @@ const createServer = (options?) => { return error }, } - const server = new ApolloServer(Object.assign({}, defaults, options)) + const server = new ApolloServer(Object.assign(defaults, options)) const app = express() - app.set('driver', driver) // TODO: this exception is required for the graphql playground, since the playground loads external resources // See: https://github.com/graphql/graphql-playground/issues/1283 app.use( @@ -112,3 +102,4 @@ const createServer = (options?) => { } export default createServer +export type Context = Awaited>> diff --git a/backend/tsconfig.json b/backend/tsconfig.json index 7de3aad0c..7da05a2f0 100644 --- a/backend/tsconfig.json +++ b/backend/tsconfig.json @@ -11,7 +11,7 @@ // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */ /* Language and Environment */ - "target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ + "target": "es2016" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */, // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */ // "jsx": "preserve", /* Specify what JSX code is generated. */ // "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */ @@ -25,20 +25,20 @@ // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */ /* Modules */ - "module": "commonjs", /* Specify what module code is generated. */ + "module": "commonjs" /* Specify what module code is generated. */, // "rootDir": "./", /* Specify the root folder within your source files. */ // "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */ // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */ - "paths": { /* Specify a set of entries that re-map imports to additional lookup locations. */ + /* Specify a set of entries that re-map imports to additional lookup locations. */ + "paths": { "@config/*": ["./src/config/*"], "@constants/*": ["./src/constants/*"], + "@context/*": ["./src/context/*"], "@db/*": ["./src/db/*"], "@graphql/*": ["./src/graphql/*"], "@helpers/*": ["./src/helpers/*"], "@jwt/*": ["./src/jwt/*"], "@middleware/*": ["./src/middleware/*"], - "@models/*": ["./src/models/*"], - "@schema/*": ["./src/schema/*"], "@src/*": ["./src/*"], "@root/*": ["./*"] }, @@ -67,7 +67,7 @@ // "sourceMap": true, /* Create source map files for emitted JavaScript files. */ // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */ // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */ - "outDir": "./build", /* Specify an output folder for all emitted files. */ + "outDir": "./build" /* Specify an output folder for all emitted files. */, // "removeComments": true, /* Disable emitting comments. */ // "noEmit": true, /* Disable emitting files from a compilation. */ // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */ @@ -89,19 +89,19 @@ // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */ // "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */ // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */ - "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */ + "esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */, // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */ - "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */ + "forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */, /* Type Checking */ - "strict": true, /* Enable all strict type-checking options. */ - "noImplicitAny": false, /* Enable error reporting for expressions and declarations with an implied 'any' type. */ + "strict": true /* Enable all strict type-checking options. */, + "noImplicitAny": false /* Enable error reporting for expressions and declarations with an implied 'any' type. */, // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */ // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */ // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */ // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */ // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */ - "useUnknownInCatchVariables": false, /* Default catch clause variables as 'unknown' instead of 'any'. */ + "useUnknownInCatchVariables": false /* Default catch clause variables as 'unknown' instead of 'any'. */, // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */ // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */ // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */ @@ -116,6 +116,6 @@ /* Completeness */ // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */ - "skipLibCheck": true /* Skip type checking all .d.ts files. */ - }, + "skipLibCheck": true /* Skip type checking all .d.ts files. */ + } } diff --git a/backend/yarn.lock b/backend/yarn.lock index c702695e1..4d6b2e2d3 100644 --- a/backend/yarn.lock +++ b/backend/yarn.lock @@ -64,22 +64,6 @@ "@csstools/css-tokenizer" "^3.0.3" lru-cache "^10.4.3" -"@babel/cli@~7.27.0": - version "7.27.0" - resolved "https://registry.yarnpkg.com/@babel/cli/-/cli-7.27.0.tgz#076603b25fc7dd88298ea94ab249c8237c7e71cc" - integrity sha512-bZfxn8DRxwiVzDO5CEeV+7IqXeCkzI4yYnrQbpwjT76CUyossQc6RYE7n+xfm0/2k40lPaCpW0FhxYs7EBAetw== - dependencies: - "@jridgewell/trace-mapping" "^0.3.25" - commander "^6.2.0" - convert-source-map "^2.0.0" - fs-readdir-recursive "^1.1.0" - glob "^7.2.0" - make-dir "^2.1.0" - slash "^2.0.0" - optionalDependencies: - "@nicolo-ribaudo/chokidar-2" "2.1.8-no-fsevents.3" - chokidar "^3.6.0" - "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.26.2": version "7.26.2" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.26.2.tgz#4b5fab97d33338eff916235055f0ebc21e573a85" @@ -89,12 +73,12 @@ js-tokens "^4.0.0" picocolors "^1.0.0" -"@babel/compat-data@^7.22.6", "@babel/compat-data@^7.26.5", "@babel/compat-data@^7.26.8": +"@babel/compat-data@^7.26.5": version "7.26.8" resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.26.8.tgz#821c1d35641c355284d4a870b8a4a7b0c141e367" integrity sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ== -"@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.23.9", "@babel/core@^7.26.10": +"@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.23.9": version "7.26.10" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.26.10.tgz#5c876f83c8c4dcb233ee4b670c0606f2ac3000f9" integrity sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ== @@ -126,21 +110,7 @@ "@jridgewell/trace-mapping" "^0.3.25" jsesc "^3.0.2" -"@babel/helper-annotate-as-pure@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz#e7f06737b197d580a01edf75d97e2c8be99d3882" - integrity sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg== - dependencies: - "@babel/types" "^7.22.5" - -"@babel/helper-annotate-as-pure@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz#d8eac4d2dc0d7b6e11fa6e535332e0d3184f06b4" - integrity sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g== - dependencies: - "@babel/types" "^7.25.9" - -"@babel/helper-compilation-targets@^7.22.6", "@babel/helper-compilation-targets@^7.25.9", "@babel/helper-compilation-targets@^7.26.5": +"@babel/helper-compilation-targets@^7.26.5": version "7.26.5" resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.26.5.tgz#75d92bb8d8d51301c0d49e52a65c9a7fe94514d8" integrity sha512-IXuyn5EkouFJscIDuFF5EsiSolseme1s0CZB+QxVugqJLYmKdxI1VfIBOst0SUu4rnk2Z7kqTwmoO1lp3HIfnA== @@ -151,67 +121,6 @@ lru-cache "^5.1.1" semver "^6.3.1" -"@babel/helper-create-class-features-plugin@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.25.9.tgz#7644147706bb90ff613297d49ed5266bde729f83" - integrity sha512-UTZQMvt0d/rSz6KI+qdu7GQze5TIajwTS++GUozlw8VBJDEOAqSXwm1WvmYEZwqdqSGQshRocPDqrt4HBZB3fQ== - dependencies: - "@babel/helper-annotate-as-pure" "^7.25.9" - "@babel/helper-member-expression-to-functions" "^7.25.9" - "@babel/helper-optimise-call-expression" "^7.25.9" - "@babel/helper-replace-supers" "^7.25.9" - "@babel/helper-skip-transparent-expression-wrappers" "^7.25.9" - "@babel/traverse" "^7.25.9" - semver "^6.3.1" - -"@babel/helper-create-regexp-features-plugin@^7.18.6": - version "7.22.9" - resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.22.9.tgz#9d8e61a8d9366fe66198f57c40565663de0825f6" - integrity sha512-+svjVa/tFwsNSG4NEy1h85+HQ5imbT92Q5/bgtS7P0GTQlP8WuFdqsiABmQouhiFGyV66oGxZFpeYHza1rNsKw== - dependencies: - "@babel/helper-annotate-as-pure" "^7.22.5" - regexpu-core "^5.3.1" - semver "^6.3.1" - -"@babel/helper-create-regexp-features-plugin@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.25.9.tgz#3e8999db94728ad2b2458d7a470e7770b7764e26" - integrity sha512-ORPNZ3h6ZRkOyAa/SaHU+XsLZr0UQzRwuDQ0cczIA17nAzZ+85G5cVkOJIj7QavLZGSe8QXUmNFxSZzjcZF9bw== - dependencies: - "@babel/helper-annotate-as-pure" "^7.25.9" - regexpu-core "^6.1.1" - semver "^6.3.1" - -"@babel/helper-define-polyfill-provider@^0.6.1": - version "0.6.1" - resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.1.tgz#fadc63f0c2ff3c8d02ed905dcea747c5b0fb74fd" - integrity sha512-o7SDgTJuvx5vLKD6SFvkydkSMBvahDKGiNJzG22IZYXhiqoe9efY7zocICBgzHV4IRg5wdgl2nEL/tulKIEIbA== - dependencies: - "@babel/helper-compilation-targets" "^7.22.6" - "@babel/helper-plugin-utils" "^7.22.5" - debug "^4.1.1" - lodash.debounce "^4.0.8" - resolve "^1.14.2" - -"@babel/helper-define-polyfill-provider@^0.6.3": - version "0.6.3" - resolved "https://registry.yarnpkg.com/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.6.3.tgz#f4f2792fae2ef382074bc2d713522cf24e6ddb21" - integrity sha512-HK7Bi+Hj6H+VTHA3ZvBis7V/6hu9QuTrnMXNybfUf2iiuU/N97I8VjB+KbhFF8Rld/Lx5MzoCwPCpPjfK+n8Cg== - dependencies: - "@babel/helper-compilation-targets" "^7.22.6" - "@babel/helper-plugin-utils" "^7.22.5" - debug "^4.1.1" - lodash.debounce "^4.0.8" - resolve "^1.14.2" - -"@babel/helper-member-expression-to-functions@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.25.9.tgz#9dfffe46f727005a5ea29051ac835fb735e4c1a3" - integrity sha512-wbfdZ9w5vk0C0oyHqAJbc62+vet5prjj01jjJ8sKn3j9h3MQQlflEdXYvuqRWjHnM12coDEqiC1IRCi0U/EKwQ== - dependencies: - "@babel/traverse" "^7.25.9" - "@babel/types" "^7.25.9" - "@babel/helper-module-imports@^7.25.9": version "7.25.9" resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz#e7f8d20602ebdbf9ebbea0a0751fb0f2a4141715" @@ -220,7 +129,7 @@ "@babel/traverse" "^7.25.9" "@babel/types" "^7.25.9" -"@babel/helper-module-transforms@^7.25.9", "@babel/helper-module-transforms@^7.26.0": +"@babel/helper-module-transforms@^7.26.0": version "7.26.0" resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz#8ce54ec9d592695e58d84cd884b7b5c6a2fdeeae" integrity sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw== @@ -229,44 +138,11 @@ "@babel/helper-validator-identifier" "^7.25.9" "@babel/traverse" "^7.25.9" -"@babel/helper-optimise-call-expression@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.25.9.tgz#3324ae50bae7e2ab3c33f60c9a877b6a0146b54e" - integrity sha512-FIpuNaz5ow8VyrYcnXQTDRGvV6tTjkNtCK/RYNDXGSLlUD6cBuQTSw43CShGxjvfBTfcUA/r6UhUCbtYqkhcuQ== - dependencies: - "@babel/types" "^7.25.9" - -"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.19.0", "@babel/helper-plugin-utils@^7.22.5", "@babel/helper-plugin-utils@^7.24.0", "@babel/helper-plugin-utils@^7.25.9", "@babel/helper-plugin-utils@^7.26.5", "@babel/helper-plugin-utils@^7.8.0": +"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.19.0", "@babel/helper-plugin-utils@^7.24.0", "@babel/helper-plugin-utils@^7.8.0": version "7.26.5" resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.26.5.tgz#18580d00c9934117ad719392c4f6585c9333cc35" integrity sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg== -"@babel/helper-remap-async-to-generator@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.25.9.tgz#e53956ab3d5b9fb88be04b3e2f31b523afd34b92" - integrity sha512-IZtukuUeBbhgOcaW2s06OXTzVNJR0ybm4W5xC1opWFFJMZbwRj5LCk+ByYH7WdZPZTt8KnFwA8pvjN2yqcPlgw== - dependencies: - "@babel/helper-annotate-as-pure" "^7.25.9" - "@babel/helper-wrap-function" "^7.25.9" - "@babel/traverse" "^7.25.9" - -"@babel/helper-replace-supers@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.25.9.tgz#ba447224798c3da3f8713fc272b145e33da6a5c5" - integrity sha512-IiDqTOTBQy0sWyeXyGSC5TBJpGFXBkRynjBeXsvbhQFKj2viwJC76Epz35YLU1fpe/Am6Vppb7W7zM4fPQzLsQ== - dependencies: - "@babel/helper-member-expression-to-functions" "^7.25.9" - "@babel/helper-optimise-call-expression" "^7.25.9" - "@babel/traverse" "^7.25.9" - -"@babel/helper-skip-transparent-expression-wrappers@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.25.9.tgz#0b2e1b62d560d6b1954893fd2b705dc17c91f0c9" - integrity sha512-K4Du3BFa3gvyhzgPcntrkDgZzQaq6uozzcpGbOO1OEJaI+EJdqWIMTLgFgQf6lrfiDFo5FU+BxKepI9RmZqahA== - dependencies: - "@babel/traverse" "^7.25.9" - "@babel/types" "^7.25.9" - "@babel/helper-string-parser@^7.25.9": version "7.25.9" resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz#1aabb72ee72ed35789b4bbcad3ca2862ce614e8c" @@ -282,15 +158,6 @@ resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz#86e45bd8a49ab7e03f276577f96179653d41da72" integrity sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw== -"@babel/helper-wrap-function@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.25.9.tgz#d99dfd595312e6c894bd7d237470025c85eea9d0" - integrity sha512-ETzz9UTjQSTmw39GboatdymDq4XIQbR8ySgVrylRhPOFpsd+JrKHIuF0de7GCWmem+T4uC5z7EZguod7Wj4A4g== - dependencies: - "@babel/template" "^7.25.9" - "@babel/traverse" "^7.25.9" - "@babel/types" "^7.25.9" - "@babel/helpers@^7.26.10": version "7.27.0" resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.27.0.tgz#53d156098defa8243eab0f32fa17589075a1b808" @@ -299,76 +166,13 @@ "@babel/template" "^7.27.0" "@babel/types" "^7.27.0" -"@babel/node@~7.26.0": - version "7.26.0" - resolved "https://registry.yarnpkg.com/@babel/node/-/node-7.26.0.tgz#4b126f4eb56a05a97990a650fae0a24ad9b0c583" - integrity sha512-5ASMjh42hbnqyCOK68Q5chh1jKAqn91IswFTN+niwt4FLABhEWCT1tEuuo6mlNQ4WG/oFQLvJ71PaHAKtWtJyA== - dependencies: - "@babel/register" "^7.25.9" - commander "^6.2.0" - core-js "^3.30.2" - node-environment-flags "^1.0.5" - regenerator-runtime "^0.14.0" - v8flags "^3.1.1" - -"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.23.9", "@babel/parser@^7.26.10", "@babel/parser@^7.27.0", "@babel/parser@^7.7.0": +"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.23.9", "@babel/parser@^7.26.10", "@babel/parser@^7.27.0", "@babel/parser@^7.6.0", "@babel/parser@^7.9.6": version "7.27.0" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.27.0.tgz#3d7d6ee268e41d2600091cbd4e145ffee85a44ec" integrity sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg== dependencies: "@babel/types" "^7.27.0" -"@babel/plugin-bugfix-firefox-class-in-computed-class-key@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.25.9.tgz#cc2e53ebf0a0340777fff5ed521943e253b4d8fe" - integrity sha512-ZkRyVkThtxQ/J6nv3JFYv1RYY+JT5BvU0y3k5bWrmuG4woXypRa4PXmm9RhOwodRkYFWqC0C0cqcJ4OqR7kW+g== - dependencies: - "@babel/helper-plugin-utils" "^7.25.9" - "@babel/traverse" "^7.25.9" - -"@babel/plugin-bugfix-safari-class-field-initializer-scope@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.25.9.tgz#af9e4fb63ccb8abcb92375b2fcfe36b60c774d30" - integrity sha512-MrGRLZxLD/Zjj0gdU15dfs+HH/OXvnw/U4jJD8vpcP2CJQapPEv1IWwjc/qMg7ItBlPwSv1hRBbb7LeuANdcnw== - dependencies: - "@babel/helper-plugin-utils" "^7.25.9" - -"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.25.9.tgz#e8dc26fcd616e6c5bf2bd0d5a2c151d4f92a9137" - integrity sha512-2qUwwfAFpJLZqxd02YW9btUCZHl+RFvdDkNfZwaIJrvB8Tesjsk8pEQkTvGwZXLqXUx/2oyY3ySRhm6HOXuCug== - dependencies: - "@babel/helper-plugin-utils" "^7.25.9" - -"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.25.9.tgz#807a667f9158acac6f6164b4beb85ad9ebc9e1d1" - integrity sha512-6xWgLZTJXwilVjlnV7ospI3xi+sl8lN8rXXbBD6vYn3UYDlGsag8wrZkKcSI8G6KgqKP7vNFaDgeDnfAABq61g== - dependencies: - "@babel/helper-plugin-utils" "^7.25.9" - "@babel/helper-skip-transparent-expression-wrappers" "^7.25.9" - "@babel/plugin-transform-optional-chaining" "^7.25.9" - -"@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.25.9.tgz#de7093f1e7deaf68eadd7cc6b07f2ab82543269e" - integrity sha512-aLnMXYPnzwwqhYSCyXfKkIkYgJ8zv9RK+roo9DkTXz38ynIhd9XCbN08s3MGvqL2MYGVUGdRQLL/JqBIeJhJBg== - dependencies: - "@babel/helper-plugin-utils" "^7.25.9" - "@babel/traverse" "^7.25.9" - -"@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2": - version "7.21.0-placeholder-for-preset-env.2" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz#7844f9289546efa9febac2de4cfe358a050bd703" - integrity sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w== - -"@babel/plugin-proposal-throw-expressions@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-throw-expressions/-/plugin-proposal-throw-expressions-7.25.9.tgz#9bfba4a4b775dbadfc5ca91a9f22097754142b56" - integrity sha512-Zw62DP6cdbXXEtTNMWYY10rIOPGAWPk8qdqM+AT3JbHtFq8ook0JXJCWdQJTlSVACHo0R6lvoNKO9B1ZVkjClg== - dependencies: - "@babel/helper-plugin-utils" "^7.25.9" - "@babel/plugin-syntax-async-generators@^7.8.4": version "7.8.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d" @@ -390,20 +194,6 @@ dependencies: "@babel/helper-plugin-utils" "^7.12.13" -"@babel/plugin-syntax-import-assertions@^7.26.0": - version "7.26.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.26.0.tgz#620412405058efa56e4a564903b79355020f445f" - integrity sha512-QCWT5Hh830hK5EQa7XzuqIkQU9tT/whqbDz7kuaZMHFl1inRRg7JnuAEOQ0Ur0QUl0NufCk1msK2BeY79Aj/eg== - dependencies: - "@babel/helper-plugin-utils" "^7.25.9" - -"@babel/plugin-syntax-import-attributes@^7.26.0": - version "7.26.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.26.0.tgz#3b1412847699eea739b4f2602c74ce36f6b0b0f7" - integrity sha512-e2dttdsJ1ZTpi3B9UYGLw41hifAubg19AtCu/2I/F1QNVclOBr1dYpTdmdyZ84Xiz43BS/tCUkMAZNLv12Pi+A== - dependencies: - "@babel/helper-plugin-utils" "^7.25.9" - "@babel/plugin-syntax-import-meta@^7.8.3": version "7.10.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz#ee601348c370fa334d2207be158777496521fd51" @@ -481,501 +271,6 @@ dependencies: "@babel/helper-plugin-utils" "^7.19.0" -"@babel/plugin-syntax-unicode-sets-regex@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-unicode-sets-regex/-/plugin-syntax-unicode-sets-regex-7.18.6.tgz#d49a3b3e6b52e5be6740022317580234a6a47357" - integrity sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg== - dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" - -"@babel/plugin-transform-arrow-functions@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.25.9.tgz#7821d4410bee5daaadbb4cdd9a6649704e176845" - integrity sha512-6jmooXYIwn9ca5/RylZADJ+EnSxVUS5sjeJ9UPk6RWRzXCmOJCy6dqItPJFpw2cuCangPK4OYr5uhGKcmrm5Qg== - dependencies: - "@babel/helper-plugin-utils" "^7.25.9" - -"@babel/plugin-transform-async-generator-functions@^7.26.8": - version "7.26.8" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.26.8.tgz#5e3991135e3b9c6eaaf5eff56d1ae5a11df45ff8" - integrity sha512-He9Ej2X7tNf2zdKMAGOsmg2MrFc+hfoAhd3po4cWfo/NWjzEAKa0oQruj1ROVUdl0e6fb6/kE/G3SSxE0lRJOg== - dependencies: - "@babel/helper-plugin-utils" "^7.26.5" - "@babel/helper-remap-async-to-generator" "^7.25.9" - "@babel/traverse" "^7.26.8" - -"@babel/plugin-transform-async-to-generator@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.25.9.tgz#c80008dacae51482793e5a9c08b39a5be7e12d71" - integrity sha512-NT7Ejn7Z/LjUH0Gv5KsBCxh7BH3fbLTV0ptHvpeMvrt3cPThHfJfst9Wrb7S8EvJ7vRTFI7z+VAvFVEQn/m5zQ== - dependencies: - "@babel/helper-module-imports" "^7.25.9" - "@babel/helper-plugin-utils" "^7.25.9" - "@babel/helper-remap-async-to-generator" "^7.25.9" - -"@babel/plugin-transform-block-scoped-functions@^7.26.5": - version "7.26.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.26.5.tgz#3dc4405d31ad1cbe45293aa57205a6e3b009d53e" - integrity sha512-chuTSY+hq09+/f5lMj8ZSYgCFpppV2CbYrhNFJ1BFoXpiWPnnAb7R0MqrafCpN8E1+YRrtM1MXZHJdIx8B6rMQ== - dependencies: - "@babel/helper-plugin-utils" "^7.26.5" - -"@babel/plugin-transform-block-scoping@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.25.9.tgz#c33665e46b06759c93687ca0f84395b80c0473a1" - integrity sha512-1F05O7AYjymAtqbsFETboN1NvBdcnzMerO+zlMyJBEz6WkMdejvGWw9p05iTSjC85RLlBseHHQpYaM4gzJkBGg== - dependencies: - "@babel/helper-plugin-utils" "^7.25.9" - -"@babel/plugin-transform-class-properties@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.25.9.tgz#a8ce84fedb9ad512549984101fa84080a9f5f51f" - integrity sha512-bbMAII8GRSkcd0h0b4X+36GksxuheLFjP65ul9w6C3KgAamI3JqErNgSrosX6ZPj+Mpim5VvEbawXxJCyEUV3Q== - dependencies: - "@babel/helper-create-class-features-plugin" "^7.25.9" - "@babel/helper-plugin-utils" "^7.25.9" - -"@babel/plugin-transform-class-static-block@^7.26.0": - version "7.26.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.26.0.tgz#6c8da219f4eb15cae9834ec4348ff8e9e09664a0" - integrity sha512-6J2APTs7BDDm+UMqP1useWqhcRAXo0WIoVj26N7kPFB6S73Lgvyka4KTZYIxtgYXiN5HTyRObA72N2iu628iTQ== - dependencies: - "@babel/helper-create-class-features-plugin" "^7.25.9" - "@babel/helper-plugin-utils" "^7.25.9" - -"@babel/plugin-transform-classes@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.25.9.tgz#7152457f7880b593a63ade8a861e6e26a4469f52" - integrity sha512-mD8APIXmseE7oZvZgGABDyM34GUmK45Um2TXiBUt7PnuAxrgoSVf123qUzPxEr/+/BHrRn5NMZCdE2m/1F8DGg== - dependencies: - "@babel/helper-annotate-as-pure" "^7.25.9" - "@babel/helper-compilation-targets" "^7.25.9" - "@babel/helper-plugin-utils" "^7.25.9" - "@babel/helper-replace-supers" "^7.25.9" - "@babel/traverse" "^7.25.9" - globals "^11.1.0" - -"@babel/plugin-transform-computed-properties@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.25.9.tgz#db36492c78460e534b8852b1d5befe3c923ef10b" - integrity sha512-HnBegGqXZR12xbcTHlJ9HGxw1OniltT26J5YpfruGqtUHlz/xKf/G2ak9e+t0rVqrjXa9WOhvYPz1ERfMj23AA== - dependencies: - "@babel/helper-plugin-utils" "^7.25.9" - "@babel/template" "^7.25.9" - -"@babel/plugin-transform-destructuring@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.25.9.tgz#966ea2595c498224340883602d3cfd7a0c79cea1" - integrity sha512-WkCGb/3ZxXepmMiX101nnGiU+1CAdut8oHyEOHxkKuS1qKpU2SMXE2uSvfz8PBuLd49V6LEsbtyPhWC7fnkgvQ== - dependencies: - "@babel/helper-plugin-utils" "^7.25.9" - -"@babel/plugin-transform-dotall-regex@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.25.9.tgz#bad7945dd07734ca52fe3ad4e872b40ed09bb09a" - integrity sha512-t7ZQ7g5trIgSRYhI9pIJtRl64KHotutUJsh4Eze5l7olJv+mRSg4/MmbZ0tv1eeqRbdvo/+trvJD/Oc5DmW2cA== - dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.25.9" - "@babel/helper-plugin-utils" "^7.25.9" - -"@babel/plugin-transform-duplicate-keys@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.25.9.tgz#8850ddf57dce2aebb4394bb434a7598031059e6d" - integrity sha512-LZxhJ6dvBb/f3x8xwWIuyiAHy56nrRG3PeYTpBkkzkYRRQ6tJLu68lEF5VIqMUZiAV7a8+Tb78nEoMCMcqjXBw== - dependencies: - "@babel/helper-plugin-utils" "^7.25.9" - -"@babel/plugin-transform-duplicate-named-capturing-groups-regex@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.25.9.tgz#6f7259b4de127721a08f1e5165b852fcaa696d31" - integrity sha512-0UfuJS0EsXbRvKnwcLjFtJy/Sxc5J5jhLHnFhy7u4zih97Hz6tJkLU+O+FMMrNZrosUPxDi6sYxJ/EA8jDiAog== - dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.25.9" - "@babel/helper-plugin-utils" "^7.25.9" - -"@babel/plugin-transform-dynamic-import@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.25.9.tgz#23e917de63ed23c6600c5dd06d94669dce79f7b8" - integrity sha512-GCggjexbmSLaFhqsojeugBpeaRIgWNTcgKVq/0qIteFEqY2A+b9QidYadrWlnbWQUrW5fn+mCvf3tr7OeBFTyg== - dependencies: - "@babel/helper-plugin-utils" "^7.25.9" - -"@babel/plugin-transform-exponentiation-operator@^7.26.3": - version "7.26.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.26.3.tgz#e29f01b6de302c7c2c794277a48f04a9ca7f03bc" - integrity sha512-7CAHcQ58z2chuXPWblnn1K6rLDnDWieghSOEmqQsrBenH0P9InCUtOJYD89pvngljmZlJcz3fcmgYsXFNGa1ZQ== - dependencies: - "@babel/helper-plugin-utils" "^7.25.9" - -"@babel/plugin-transform-export-namespace-from@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.25.9.tgz#90745fe55053394f554e40584cda81f2c8a402a2" - integrity sha512-2NsEz+CxzJIVOPx2o9UsW1rXLqtChtLoVnwYHHiB04wS5sgn7mrV45fWMBX0Kk+ub9uXytVYfNP2HjbVbCB3Ww== - dependencies: - "@babel/helper-plugin-utils" "^7.25.9" - -"@babel/plugin-transform-for-of@^7.26.9": - version "7.26.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.26.9.tgz#27231f79d5170ef33b5111f07fe5cafeb2c96a56" - integrity sha512-Hry8AusVm8LW5BVFgiyUReuoGzPUpdHQQqJY5bZnbbf+ngOHWuCuYFKw/BqaaWlvEUrF91HMhDtEaI1hZzNbLg== - dependencies: - "@babel/helper-plugin-utils" "^7.26.5" - "@babel/helper-skip-transparent-expression-wrappers" "^7.25.9" - -"@babel/plugin-transform-function-name@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.25.9.tgz#939d956e68a606661005bfd550c4fc2ef95f7b97" - integrity sha512-8lP+Yxjv14Vc5MuWBpJsoUCd3hD6V9DgBon2FVYL4jJgbnVQ9fTgYmonchzZJOVNgzEgbxp4OwAf6xz6M/14XA== - dependencies: - "@babel/helper-compilation-targets" "^7.25.9" - "@babel/helper-plugin-utils" "^7.25.9" - "@babel/traverse" "^7.25.9" - -"@babel/plugin-transform-json-strings@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.25.9.tgz#c86db407cb827cded902a90c707d2781aaa89660" - integrity sha512-xoTMk0WXceiiIvsaquQQUaLLXSW1KJ159KP87VilruQm0LNNGxWzahxSS6T6i4Zg3ezp4vA4zuwiNUR53qmQAw== - dependencies: - "@babel/helper-plugin-utils" "^7.25.9" - -"@babel/plugin-transform-literals@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.25.9.tgz#1a1c6b4d4aa59bc4cad5b6b3a223a0abd685c9de" - integrity sha512-9N7+2lFziW8W9pBl2TzaNht3+pgMIRP74zizeCSrtnSKVdUl8mAjjOP2OOVQAfZ881P2cNjDj1uAMEdeD50nuQ== - dependencies: - "@babel/helper-plugin-utils" "^7.25.9" - -"@babel/plugin-transform-logical-assignment-operators@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.25.9.tgz#b19441a8c39a2fda0902900b306ea05ae1055db7" - integrity sha512-wI4wRAzGko551Y8eVf6iOY9EouIDTtPb0ByZx+ktDGHwv6bHFimrgJM/2T021txPZ2s4c7bqvHbd+vXG6K948Q== - dependencies: - "@babel/helper-plugin-utils" "^7.25.9" - -"@babel/plugin-transform-member-expression-literals@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.25.9.tgz#63dff19763ea64a31f5e6c20957e6a25e41ed5de" - integrity sha512-PYazBVfofCQkkMzh2P6IdIUaCEWni3iYEerAsRWuVd8+jlM1S9S9cz1dF9hIzyoZ8IA3+OwVYIp9v9e+GbgZhA== - dependencies: - "@babel/helper-plugin-utils" "^7.25.9" - -"@babel/plugin-transform-modules-amd@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.25.9.tgz#49ba478f2295101544abd794486cd3088dddb6c5" - integrity sha512-g5T11tnI36jVClQlMlt4qKDLlWnG5pP9CSM4GhdRciTNMRgkfpo5cR6b4rGIOYPgRRuFAvwjPQ/Yk+ql4dyhbw== - dependencies: - "@babel/helper-module-transforms" "^7.25.9" - "@babel/helper-plugin-utils" "^7.25.9" - -"@babel/plugin-transform-modules-commonjs@^7.26.3": - version "7.26.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.26.3.tgz#8f011d44b20d02c3de44d8850d971d8497f981fb" - integrity sha512-MgR55l4q9KddUDITEzEFYn5ZsGDXMSsU9E+kh7fjRXTIC3RHqfCo8RPRbyReYJh44HQ/yomFkqbOFohXvDCiIQ== - dependencies: - "@babel/helper-module-transforms" "^7.26.0" - "@babel/helper-plugin-utils" "^7.25.9" - -"@babel/plugin-transform-modules-systemjs@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.25.9.tgz#8bd1b43836269e3d33307151a114bcf3ba6793f8" - integrity sha512-hyss7iIlH/zLHaehT+xwiymtPOpsiwIIRlCAOwBB04ta5Tt+lNItADdlXw3jAWZ96VJ2jlhl/c+PNIQPKNfvcA== - dependencies: - "@babel/helper-module-transforms" "^7.25.9" - "@babel/helper-plugin-utils" "^7.25.9" - "@babel/helper-validator-identifier" "^7.25.9" - "@babel/traverse" "^7.25.9" - -"@babel/plugin-transform-modules-umd@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.25.9.tgz#6710079cdd7c694db36529a1e8411e49fcbf14c9" - integrity sha512-bS9MVObUgE7ww36HEfwe6g9WakQ0KF07mQF74uuXdkoziUPfKyu/nIm663kz//e5O1nPInPFx36z7WJmJ4yNEw== - dependencies: - "@babel/helper-module-transforms" "^7.25.9" - "@babel/helper-plugin-utils" "^7.25.9" - -"@babel/plugin-transform-named-capturing-groups-regex@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.25.9.tgz#454990ae6cc22fd2a0fa60b3a2c6f63a38064e6a" - integrity sha512-oqB6WHdKTGl3q/ItQhpLSnWWOpjUJLsOCLVyeFgeTktkBSCiurvPOsyt93gibI9CmuKvTUEtWmG5VhZD+5T/KA== - dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.25.9" - "@babel/helper-plugin-utils" "^7.25.9" - -"@babel/plugin-transform-new-target@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.25.9.tgz#42e61711294b105c248336dcb04b77054ea8becd" - integrity sha512-U/3p8X1yCSoKyUj2eOBIx3FOn6pElFOKvAAGf8HTtItuPyB+ZeOqfn+mvTtg9ZlOAjsPdK3ayQEjqHjU/yLeVQ== - dependencies: - "@babel/helper-plugin-utils" "^7.25.9" - -"@babel/plugin-transform-nullish-coalescing-operator@^7.26.6": - version "7.26.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.26.6.tgz#fbf6b3c92cb509e7b319ee46e3da89c5bedd31fe" - integrity sha512-CKW8Vu+uUZneQCPtXmSBUC6NCAUdya26hWCElAWh5mVSlSRsmiCPUUDKb3Z0szng1hiAJa098Hkhg9o4SE35Qw== - dependencies: - "@babel/helper-plugin-utils" "^7.26.5" - -"@babel/plugin-transform-numeric-separator@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.25.9.tgz#bfed75866261a8b643468b0ccfd275f2033214a1" - integrity sha512-TlprrJ1GBZ3r6s96Yq8gEQv82s8/5HnCVHtEJScUj90thHQbwe+E5MLhi2bbNHBEJuzrvltXSru+BUxHDoog7Q== - dependencies: - "@babel/helper-plugin-utils" "^7.25.9" - -"@babel/plugin-transform-object-rest-spread@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.25.9.tgz#0203725025074164808bcf1a2cfa90c652c99f18" - integrity sha512-fSaXafEE9CVHPweLYw4J0emp1t8zYTXyzN3UuG+lylqkvYd7RMrsOQ8TYx5RF231be0vqtFC6jnx3UmpJmKBYg== - dependencies: - "@babel/helper-compilation-targets" "^7.25.9" - "@babel/helper-plugin-utils" "^7.25.9" - "@babel/plugin-transform-parameters" "^7.25.9" - -"@babel/plugin-transform-object-super@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.25.9.tgz#385d5de135162933beb4a3d227a2b7e52bb4cf03" - integrity sha512-Kj/Gh+Rw2RNLbCK1VAWj2U48yxxqL2x0k10nPtSdRa0O2xnHXalD0s+o1A6a0W43gJ00ANo38jxkQreckOzv5A== - dependencies: - "@babel/helper-plugin-utils" "^7.25.9" - "@babel/helper-replace-supers" "^7.25.9" - -"@babel/plugin-transform-optional-catch-binding@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.25.9.tgz#10e70d96d52bb1f10c5caaac59ac545ea2ba7ff3" - integrity sha512-qM/6m6hQZzDcZF3onzIhZeDHDO43bkNNlOX0i8n3lR6zLbu0GN2d8qfM/IERJZYauhAHSLHy39NF0Ctdvcid7g== - dependencies: - "@babel/helper-plugin-utils" "^7.25.9" - -"@babel/plugin-transform-optional-chaining@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.25.9.tgz#e142eb899d26ef715435f201ab6e139541eee7dd" - integrity sha512-6AvV0FsLULbpnXeBjrY4dmWF8F7gf8QnvTEoO/wX/5xm/xE1Xo8oPuD3MPS+KS9f9XBEAWN7X1aWr4z9HdOr7A== - dependencies: - "@babel/helper-plugin-utils" "^7.25.9" - "@babel/helper-skip-transparent-expression-wrappers" "^7.25.9" - -"@babel/plugin-transform-parameters@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.25.9.tgz#b856842205b3e77e18b7a7a1b94958069c7ba257" - integrity sha512-wzz6MKwpnshBAiRmn4jR8LYz/g8Ksg0o80XmwZDlordjwEk9SxBzTWC7F5ef1jhbrbOW2DJ5J6ayRukrJmnr0g== - dependencies: - "@babel/helper-plugin-utils" "^7.25.9" - -"@babel/plugin-transform-private-methods@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.25.9.tgz#847f4139263577526455d7d3223cd8bda51e3b57" - integrity sha512-D/JUozNpQLAPUVusvqMxyvjzllRaF8/nSrP1s2YGQT/W4LHK4xxsMcHjhOGTS01mp9Hda8nswb+FblLdJornQw== - dependencies: - "@babel/helper-create-class-features-plugin" "^7.25.9" - "@babel/helper-plugin-utils" "^7.25.9" - -"@babel/plugin-transform-private-property-in-object@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.25.9.tgz#9c8b73e64e6cc3cbb2743633885a7dd2c385fe33" - integrity sha512-Evf3kcMqzXA3xfYJmZ9Pg1OvKdtqsDMSWBDzZOPLvHiTt36E75jLDQo5w1gtRU95Q4E5PDttrTf25Fw8d/uWLw== - dependencies: - "@babel/helper-annotate-as-pure" "^7.25.9" - "@babel/helper-create-class-features-plugin" "^7.25.9" - "@babel/helper-plugin-utils" "^7.25.9" - -"@babel/plugin-transform-property-literals@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.25.9.tgz#d72d588bd88b0dec8b62e36f6fda91cedfe28e3f" - integrity sha512-IvIUeV5KrS/VPavfSM/Iu+RE6llrHrYIKY1yfCzyO/lMXHQ+p7uGhonmGVisv6tSBSVgWzMBohTcvkC9vQcQFA== - dependencies: - "@babel/helper-plugin-utils" "^7.25.9" - -"@babel/plugin-transform-regenerator@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.25.9.tgz#03a8a4670d6cebae95305ac6defac81ece77740b" - integrity sha512-vwDcDNsgMPDGP0nMqzahDWE5/MLcX8sv96+wfX7as7LoF/kr97Bo/7fI00lXY4wUXYfVmwIIyG80fGZ1uvt2qg== - dependencies: - "@babel/helper-plugin-utils" "^7.25.9" - regenerator-transform "^0.15.2" - -"@babel/plugin-transform-regexp-modifiers@^7.26.0": - version "7.26.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.26.0.tgz#2f5837a5b5cd3842a919d8147e9903cc7455b850" - integrity sha512-vN6saax7lrA2yA/Pak3sCxuD6F5InBjn9IcrIKQPjpsLvuHYLVroTxjdlVRHjjBWxKOqIwpTXDkOssYT4BFdRw== - dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.25.9" - "@babel/helper-plugin-utils" "^7.25.9" - -"@babel/plugin-transform-reserved-words@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.25.9.tgz#0398aed2f1f10ba3f78a93db219b27ef417fb9ce" - integrity sha512-7DL7DKYjn5Su++4RXu8puKZm2XBPHyjWLUidaPEkCUBbE7IPcsrkRHggAOOKydH1dASWdcUBxrkOGNxUv5P3Jg== - dependencies: - "@babel/helper-plugin-utils" "^7.25.9" - -"@babel/plugin-transform-shorthand-properties@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.25.9.tgz#bb785e6091f99f826a95f9894fc16fde61c163f2" - integrity sha512-MUv6t0FhO5qHnS/W8XCbHmiRWOphNufpE1IVxhK5kuN3Td9FT1x4rx4K42s3RYdMXCXpfWkGSbCSd0Z64xA7Ng== - dependencies: - "@babel/helper-plugin-utils" "^7.25.9" - -"@babel/plugin-transform-spread@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.25.9.tgz#24a35153931b4ba3d13cec4a7748c21ab5514ef9" - integrity sha512-oNknIB0TbURU5pqJFVbOOFspVlrpVwo2H1+HUIsVDvp5VauGGDP1ZEvO8Nn5xyMEs3dakajOxlmkNW7kNgSm6A== - dependencies: - "@babel/helper-plugin-utils" "^7.25.9" - "@babel/helper-skip-transparent-expression-wrappers" "^7.25.9" - -"@babel/plugin-transform-sticky-regex@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.25.9.tgz#c7f02b944e986a417817b20ba2c504dfc1453d32" - integrity sha512-WqBUSgeVwucYDP9U/xNRQam7xV8W5Zf+6Eo7T2SRVUFlhRiMNFdFz58u0KZmCVVqs2i7SHgpRnAhzRNmKfi2uA== - dependencies: - "@babel/helper-plugin-utils" "^7.25.9" - -"@babel/plugin-transform-template-literals@^7.26.8": - version "7.26.8" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.26.8.tgz#966b15d153a991172a540a69ad5e1845ced990b5" - integrity sha512-OmGDL5/J0CJPJZTHZbi2XpO0tyT2Ia7fzpW5GURwdtp2X3fMmN8au/ej6peC/T33/+CRiIpA8Krse8hFGVmT5Q== - dependencies: - "@babel/helper-plugin-utils" "^7.26.5" - -"@babel/plugin-transform-typeof-symbol@^7.26.7": - version "7.26.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.26.7.tgz#d0e33acd9223744c1e857dbd6fa17bd0a3786937" - integrity sha512-jfoTXXZTgGg36BmhqT3cAYK5qkmqvJpvNrPhaK/52Vgjhw4Rq29s9UqpWWV0D6yuRmgiFH/BUVlkl96zJWqnaw== - dependencies: - "@babel/helper-plugin-utils" "^7.26.5" - -"@babel/plugin-transform-unicode-escapes@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.25.9.tgz#a75ef3947ce15363fccaa38e2dd9bc70b2788b82" - integrity sha512-s5EDrE6bW97LtxOcGj1Khcx5AaXwiMmi4toFWRDP9/y0Woo6pXC+iyPu/KuhKtfSrNFd7jJB+/fkOtZy6aIC6Q== - dependencies: - "@babel/helper-plugin-utils" "^7.25.9" - -"@babel/plugin-transform-unicode-property-regex@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.25.9.tgz#a901e96f2c1d071b0d1bb5dc0d3c880ce8f53dd3" - integrity sha512-Jt2d8Ga+QwRluxRQ307Vlxa6dMrYEMZCgGxoPR8V52rxPyldHu3hdlHspxaqYmE7oID5+kB+UKUB/eWS+DkkWg== - dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.25.9" - "@babel/helper-plugin-utils" "^7.25.9" - -"@babel/plugin-transform-unicode-regex@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.25.9.tgz#5eae747fe39eacf13a8bd006a4fb0b5d1fa5e9b1" - integrity sha512-yoxstj7Rg9dlNn9UQxzk4fcNivwv4nUYz7fYXBaKxvw/lnmPuOm/ikoELygbYq68Bls3D/D+NBPHiLwZdZZ4HA== - dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.25.9" - "@babel/helper-plugin-utils" "^7.25.9" - -"@babel/plugin-transform-unicode-sets-regex@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.25.9.tgz#65114c17b4ffc20fa5b163c63c70c0d25621fabe" - integrity sha512-8BYqO3GeVNHtx69fdPshN3fnzUNLrWdHhk/icSwigksJGczKSizZ+Z6SBCxTs723Fr5VSNorTIK7a+R2tISvwQ== - dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.25.9" - "@babel/helper-plugin-utils" "^7.25.9" - -"@babel/preset-env@~7.26.9": - version "7.26.9" - resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.26.9.tgz#2ec64e903d0efe743699f77a10bdf7955c2123c3" - integrity sha512-vX3qPGE8sEKEAZCWk05k3cpTAE3/nOYca++JA+Rd0z2NCNzabmYvEiSShKzm10zdquOIAVXsy2Ei/DTW34KlKQ== - dependencies: - "@babel/compat-data" "^7.26.8" - "@babel/helper-compilation-targets" "^7.26.5" - "@babel/helper-plugin-utils" "^7.26.5" - "@babel/helper-validator-option" "^7.25.9" - "@babel/plugin-bugfix-firefox-class-in-computed-class-key" "^7.25.9" - "@babel/plugin-bugfix-safari-class-field-initializer-scope" "^7.25.9" - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.25.9" - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.25.9" - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly" "^7.25.9" - "@babel/plugin-proposal-private-property-in-object" "7.21.0-placeholder-for-preset-env.2" - "@babel/plugin-syntax-import-assertions" "^7.26.0" - "@babel/plugin-syntax-import-attributes" "^7.26.0" - "@babel/plugin-syntax-unicode-sets-regex" "^7.18.6" - "@babel/plugin-transform-arrow-functions" "^7.25.9" - "@babel/plugin-transform-async-generator-functions" "^7.26.8" - "@babel/plugin-transform-async-to-generator" "^7.25.9" - "@babel/plugin-transform-block-scoped-functions" "^7.26.5" - "@babel/plugin-transform-block-scoping" "^7.25.9" - "@babel/plugin-transform-class-properties" "^7.25.9" - "@babel/plugin-transform-class-static-block" "^7.26.0" - "@babel/plugin-transform-classes" "^7.25.9" - "@babel/plugin-transform-computed-properties" "^7.25.9" - "@babel/plugin-transform-destructuring" "^7.25.9" - "@babel/plugin-transform-dotall-regex" "^7.25.9" - "@babel/plugin-transform-duplicate-keys" "^7.25.9" - "@babel/plugin-transform-duplicate-named-capturing-groups-regex" "^7.25.9" - "@babel/plugin-transform-dynamic-import" "^7.25.9" - "@babel/plugin-transform-exponentiation-operator" "^7.26.3" - "@babel/plugin-transform-export-namespace-from" "^7.25.9" - "@babel/plugin-transform-for-of" "^7.26.9" - "@babel/plugin-transform-function-name" "^7.25.9" - "@babel/plugin-transform-json-strings" "^7.25.9" - "@babel/plugin-transform-literals" "^7.25.9" - "@babel/plugin-transform-logical-assignment-operators" "^7.25.9" - "@babel/plugin-transform-member-expression-literals" "^7.25.9" - "@babel/plugin-transform-modules-amd" "^7.25.9" - "@babel/plugin-transform-modules-commonjs" "^7.26.3" - "@babel/plugin-transform-modules-systemjs" "^7.25.9" - "@babel/plugin-transform-modules-umd" "^7.25.9" - "@babel/plugin-transform-named-capturing-groups-regex" "^7.25.9" - "@babel/plugin-transform-new-target" "^7.25.9" - "@babel/plugin-transform-nullish-coalescing-operator" "^7.26.6" - "@babel/plugin-transform-numeric-separator" "^7.25.9" - "@babel/plugin-transform-object-rest-spread" "^7.25.9" - "@babel/plugin-transform-object-super" "^7.25.9" - "@babel/plugin-transform-optional-catch-binding" "^7.25.9" - "@babel/plugin-transform-optional-chaining" "^7.25.9" - "@babel/plugin-transform-parameters" "^7.25.9" - "@babel/plugin-transform-private-methods" "^7.25.9" - "@babel/plugin-transform-private-property-in-object" "^7.25.9" - "@babel/plugin-transform-property-literals" "^7.25.9" - "@babel/plugin-transform-regenerator" "^7.25.9" - "@babel/plugin-transform-regexp-modifiers" "^7.26.0" - "@babel/plugin-transform-reserved-words" "^7.25.9" - "@babel/plugin-transform-shorthand-properties" "^7.25.9" - "@babel/plugin-transform-spread" "^7.25.9" - "@babel/plugin-transform-sticky-regex" "^7.25.9" - "@babel/plugin-transform-template-literals" "^7.26.8" - "@babel/plugin-transform-typeof-symbol" "^7.26.7" - "@babel/plugin-transform-unicode-escapes" "^7.25.9" - "@babel/plugin-transform-unicode-property-regex" "^7.25.9" - "@babel/plugin-transform-unicode-regex" "^7.25.9" - "@babel/plugin-transform-unicode-sets-regex" "^7.25.9" - "@babel/preset-modules" "0.1.6-no-external-plugins" - babel-plugin-polyfill-corejs2 "^0.4.10" - babel-plugin-polyfill-corejs3 "^0.11.0" - babel-plugin-polyfill-regenerator "^0.6.1" - core-js-compat "^3.40.0" - semver "^6.3.1" - -"@babel/preset-modules@0.1.6-no-external-plugins": - version "0.1.6-no-external-plugins" - resolved "https://registry.yarnpkg.com/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz#ccb88a2c49c817236861fee7826080573b8a923a" - integrity sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - "@babel/types" "^7.4.4" - esutils "^2.0.2" - -"@babel/register@^7.23.7", "@babel/register@^7.25.9": - version "7.25.9" - resolved "https://registry.yarnpkg.com/@babel/register/-/register-7.25.9.tgz#1c465acf7dc983d70ccc318eb5b887ecb04f021b" - integrity sha512-8D43jXtGsYmEeDvm4MWHYUpWf8iiXgWYx3fW7E7Wb7Oe6FWqJPl5K6TuFW0dOwNZzEE5rjlaSJYH9JjrUKJszA== - dependencies: - clone-deep "^4.0.1" - find-cache-dir "^2.0.0" - make-dir "^2.1.0" - pirates "^4.0.6" - source-map-support "^0.5.16" - -"@babel/regjsgen@^0.8.0": - version "0.8.0" - resolved "https://registry.yarnpkg.com/@babel/regjsgen/-/regjsgen-0.8.0.tgz#f0ba69b075e1f05fb2825b7fad991e7adbb18310" - integrity sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA== - "@babel/runtime-corejs2@^7.5.5": version "7.5.5" resolved "https://registry.yarnpkg.com/@babel/runtime-corejs2/-/runtime-corejs2-7.5.5.tgz#c3214c08ef20341af4187f1c9fbdc357fbec96b2" @@ -992,14 +287,14 @@ core-js-pure "^3.30.2" regenerator-runtime "^0.14.0" -"@babel/runtime@^7.5.5", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2": +"@babel/runtime@^7.5.5", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2": version "7.12.5" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.12.5.tgz#410e7e487441e1b360c29be715d870d9b985882e" integrity sha512-plcc+hbExy3McchJCEQG3knOsuh3HH+Prx1P6cLIkET/0dLuQDEnrT+s27Axgc9bqfsmNUNHfscgMUdBpC9xfg== dependencies: regenerator-runtime "^0.13.4" -"@babel/template@^7.25.9", "@babel/template@^7.26.9", "@babel/template@^7.27.0", "@babel/template@^7.3.3": +"@babel/template@^7.26.9", "@babel/template@^7.27.0", "@babel/template@^7.3.3": version "7.27.0" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.27.0.tgz#b253e5406cc1df1c57dcd18f11760c2dbf40c0b4" integrity sha512-2ncevenBqXI6qRMukPlXwHKHchC7RyMuu4xv5JBXRfOGVcTy1mXCD12qrp7Jsoxll1EV3+9sE4GugBVRjT2jFA== @@ -1008,7 +303,7 @@ "@babel/parser" "^7.27.0" "@babel/types" "^7.27.0" -"@babel/traverse@^7.25.9", "@babel/traverse@^7.26.10", "@babel/traverse@^7.26.8", "@babel/traverse@^7.7.0": +"@babel/traverse@^7.25.9", "@babel/traverse@^7.26.10": version "7.27.0" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.27.0.tgz#11d7e644779e166c0442f9a07274d02cd91d4a70" integrity sha512-19lYZFzYVQkkHkl4Cy4WrAVcqBkgvV2YM2TU3xG6DIwO7O3ecbDPfW3yM3bjAGcqcQHi+CCtjMR3dIEHxsd6bA== @@ -1021,7 +316,7 @@ debug "^4.3.1" globals "^11.1.0" -"@babel/types@^7.0.0", "@babel/types@^7.20.7", "@babel/types@^7.22.5", "@babel/types@^7.25.9", "@babel/types@^7.26.10", "@babel/types@^7.27.0", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4", "@babel/types@^7.7.0": +"@babel/types@^7.0.0", "@babel/types@^7.20.7", "@babel/types@^7.25.9", "@babel/types@^7.26.10", "@babel/types@^7.27.0", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.6.1", "@babel/types@^7.9.6": version "7.27.0" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.27.0.tgz#ef9acb6b06c3173f6632d993ecb6d4ae470b4559" integrity sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg== @@ -1113,6 +408,13 @@ dependencies: eslint-visitor-keys "^3.4.3" +"@eslint-community/eslint-utils@^4.5.1": + version "4.7.0" + resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz#607084630c6c033992a082de6e6fbc1a8b52175a" + integrity sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw== + dependencies: + eslint-visitor-keys "^3.4.3" + "@eslint-community/regexpp@^4.11.0": version "4.12.1" resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.12.1.tgz#cfc6cffe39df390a3841cde2abccf92eaa7ae0e0" @@ -1181,6 +483,13 @@ resolved "https://registry.yarnpkg.com/@hapi/address/-/address-2.1.4.tgz#5d67ed43f3fd41a69d4b9ff7b56e7c0d1d0a81e5" integrity sha512-QD1PhQk+s31P1ixsX0H0Suoupp3VMXzIVMSwobR3F3MSUO2YCV0B7xqLcUw/Bh8yuvd3LhpyqLQWTNcRmp6IdQ== +"@hapi/boom@^10.0.0": + version "10.0.1" + resolved "https://registry.yarnpkg.com/@hapi/boom/-/boom-10.0.1.tgz#ebb14688275ae150aa6af788dbe482e6a6062685" + integrity sha512-ERcCZaEjdH3OgSJlyjVk8pHIFeus91CjKP3v+MpgBNp5IvGzP2l/bRiD78nqYcKPaZdbKkK5vDBVPd2ohHBlsA== + dependencies: + "@hapi/hoek" "^11.0.2" + "@hapi/bourne@1.x.x": version "1.3.2" resolved "https://registry.yarnpkg.com/@hapi/bourne/-/bourne-1.3.2.tgz#0a7095adea067243ce3283e1b56b8a8f453b242a" @@ -1191,6 +500,11 @@ resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-8.5.1.tgz#fde96064ca446dec8c55a8c2f130957b070c6e06" integrity sha512-yN7kbciD87WzLGc5539Tn0sApjyiGHAJgKvG9W8C7O+6c7qmoQMfVs0W4bX17eqz6C78QJqqFrtgdK5EWf6Qow== +"@hapi/hoek@^11.0.2": + version "11.0.7" + resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-11.0.7.tgz#56a920793e0a42d10e530da9a64cc0d3919c4002" + integrity sha512-HV5undWkKzcB4RZUusqOpcgxOaq6VOAH7zhhIr2g3G8NF/MlFO75SjOr2NfuSx0Mh40+1FqCkagKLJRykUWoFQ== + "@hapi/joi@^15.1.1": version "15.1.1" resolved "https://registry.yarnpkg.com/@hapi/joi/-/joi-15.1.1.tgz#c675b8a71296f02833f8d6d243b34c57b8ce19d7" @@ -1526,6 +840,77 @@ resolved "https://registry.yarnpkg.com/@kikobeats/time-span/-/time-span-1.0.5.tgz#9f7c5d48b08da02115dbf3d85ca11a6a6f8bfdeb" integrity sha512-txRAdmi35N1wnsLS1AO5mTlbY5Cv5/61WXqek2y3L9Q7u4mgdUVq819so5xe753hL5gYeLzlWoJ/VJfXg9nx8g== +"@ladjs/consolidate@^1.0.4": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@ladjs/consolidate/-/consolidate-1.0.4.tgz#31d9604a0e3de6616aeba062c4390c5aa0e5c04d" + integrity sha512-ErvBg5acSqns86V/xW7gjqqnBBs6thnpMB0gGc3oM7WHsV8PWrnBtKI6dumHDT3UT/zEOfGzp7dmSFqWoCXKWQ== + +"@ladjs/country-language@^0.2.1": + version "0.2.1" + resolved "https://registry.yarnpkg.com/@ladjs/country-language/-/country-language-0.2.1.tgz#553f776fa1eb295d0344ed06525a945f94cdafaa" + integrity sha512-e3AmT7jUnfNE6e2mx2+cPYiWdFW3McySDGRhQEYE6SksjZTMj0PTp+R9x1xG89tHRTsyMNJFl9J4HtZPWZzi1Q== + dependencies: + underscore "~1.13.1" + underscore.deep "~0.5.1" + +"@ladjs/country-language@^1.0.1": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@ladjs/country-language/-/country-language-1.0.3.tgz#1131b524c6242567dfc4ce61401ff7a62e91b155" + integrity sha512-FJROu9/hh4eqVAGDyfL8vpv6Vb0qKHX1ozYLRZ+beUzD5xFf+3r0J+SVIWKviEa7W524Qvqou+ta1WrsRgzxGw== + +"@ladjs/i18n@^8.0.3": + version "8.0.3" + resolved "https://registry.yarnpkg.com/@ladjs/i18n/-/i18n-8.0.3.tgz#e2abb0726ff24fd9a8d6e37d5ca351b079974069" + integrity sha512-QYeYGz6uJaH41ZVyNoI2Lt2NyfcpKwpDIBMx3psaE1NBJn8P+jk1m0EIjphfYvnRMnl/QyBpn98FfcTUjTkuBw== + dependencies: + "@hapi/boom" "^10.0.0" + "@ladjs/country-language" "^1.0.1" + boolean "3.2.0" + i18n "^0.15.0" + i18n-locales "^0.0.5" + lodash "^4.17.21" + multimatch "5" + punycode "^2.1.1" + qs "^6.11.0" + titleize "2" + tlds "^1.231.0" + +"@messageformat/core@^3.0.0": + version "3.4.0" + resolved "https://registry.yarnpkg.com/@messageformat/core/-/core-3.4.0.tgz#2814c23383dec7bddf535d54f2a03e410165ca9f" + integrity sha512-NgCFubFFIdMWJGN5WuQhHCNmzk7QgiVfrViFxcS99j7F5dDS5EP6raR54I+2ydhe4+5/XTn/YIEppFaqqVWHsw== + dependencies: + "@messageformat/date-skeleton" "^1.0.0" + "@messageformat/number-skeleton" "^1.0.0" + "@messageformat/parser" "^5.1.0" + "@messageformat/runtime" "^3.0.1" + make-plural "^7.0.0" + safe-identifier "^0.4.1" + +"@messageformat/date-skeleton@^1.0.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@messageformat/date-skeleton/-/date-skeleton-1.1.0.tgz#3bad068cbf5873d14592cfc7a73dd4d8615e2739" + integrity sha512-rmGAfB1tIPER+gh3p/RgA+PVeRE/gxuQ2w4snFWPF5xtb5mbWR7Cbw7wCOftcUypbD6HVoxrVdyyghPm3WzP5A== + +"@messageformat/number-skeleton@^1.0.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@messageformat/number-skeleton/-/number-skeleton-1.2.0.tgz#e7c245c41a1b2722bc59dad68f4d454f761bc9b4" + integrity sha512-xsgwcL7J7WhlHJ3RNbaVgssaIwcEyFkBqxHdcdaiJzwTZAWEOD8BuUFxnxV9k5S0qHN3v/KzUpq0IUpjH1seRg== + +"@messageformat/parser@^5.1.0": + version "5.1.1" + resolved "https://registry.yarnpkg.com/@messageformat/parser/-/parser-5.1.1.tgz#ca7d6c18e9f3f6b6bc984a465dac16da00106055" + integrity sha512-3p0YRGCcTUCYvBKLIxtDDyrJ0YijGIwrTRu1DT8gIviIDZru8H23+FkY6MJBzM1n9n20CiM4VeDYuBsrrwnLjg== + dependencies: + moo "^0.5.1" + +"@messageformat/runtime@^3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@messageformat/runtime/-/runtime-3.0.1.tgz#94d1f6c43265c28ef7aed98ecfcc0968c6c849ac" + integrity sha512-6RU5ol2lDtO8bD9Yxe6CZkl0DArdv0qkuoZC+ZwowU+cdRlVE1157wjCmlA5Rsf1Xc/brACnsZa5PZpEDfTFFg== + dependencies: + make-plural "^7.0.0" + "@metascraper/helpers@5.46.11", "@metascraper/helpers@^5.34.4": version "5.46.11" resolved "https://registry.yarnpkg.com/@metascraper/helpers/-/helpers-5.46.11.tgz#d55f77623227887a1ee52be3f4ea20174c36ec72" @@ -1588,11 +973,6 @@ "@emnapi/runtime" "^1.4.0" "@tybys/wasm-util" "^0.9.0" -"@nicolo-ribaudo/chokidar-2@2.1.8-no-fsevents.3": - version "2.1.8-no-fsevents.3" - resolved "https://registry.yarnpkg.com/@nicolo-ribaudo/chokidar-2/-/chokidar-2-2.1.8-no-fsevents.3.tgz#323d72dd25103d0c4fbdce89dadf574a787b1f9b" - integrity sha512-s88O1aVtXftvp5bCPB7WnmXc5IwOZZ7YPuwNPt+GtOOXpPvad1LfbmjYv+qII7zP6RU2QGnqve27dnLycEnyEQ== - "@nodelib/fs.scandir@2.1.5": version "2.1.5" resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" @@ -1705,6 +1085,14 @@ resolved "https://registry.yarnpkg.com/@rtsao/scc/-/scc-1.1.0.tgz#927dd2fae9bc3361403ac2c7a00c32ddce9ad7e8" integrity sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g== +"@selderee/plugin-htmlparser2@^0.11.0": + version "0.11.0" + resolved "https://registry.yarnpkg.com/@selderee/plugin-htmlparser2/-/plugin-htmlparser2-0.11.0.tgz#d5b5e29a7ba6d3958a1972c7be16f4b2c188c517" + integrity sha512-P33hHGdldxGabLFjPPpaTxVolMrzrcegejx+0GxjrIb9Zv48D8yAIA/QTDR2dFl7Uz7urX8aX6+5bCZslr+gWQ== + dependencies: + domhandler "^5.0.3" + selderee "^0.11.0" + "@sentry/apm@5.15.4": version "5.15.4" resolved "https://registry.yarnpkg.com/@sentry/apm/-/apm-5.15.4.tgz#59af766d2bb4c9d98eda5ddba7a32a79ecc807a2" @@ -1929,6 +1317,15 @@ dependencies: "@types/express" "*" +"@types/email-templates@^10.0.4": + version "10.0.4" + resolved "https://registry.yarnpkg.com/@types/email-templates/-/email-templates-10.0.4.tgz#3181fd540e76e6b90b8b3e0a5a1afbc803ef7797" + integrity sha512-8O2bdGPO6RYgH2DrnFAcuV++s+8KNA5e2Erjl6UxgKRVsBH9zXu2YLrLyOBRMn2VyEYmzgF+6QQUslpVhj0y/g== + dependencies: + "@types/html-to-text" "*" + "@types/nodemailer" "*" + juice "^8.0.0" + "@types/express-serve-static-core@*": version "4.17.7" resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.7.tgz#dfe61f870eb549dc6d7e12050901847c7d7e915b" @@ -1982,6 +1379,11 @@ "@types/koa" "*" graphql "^14.5.3" +"@types/html-to-text@*": + version "9.0.4" + resolved "https://registry.yarnpkg.com/@types/html-to-text/-/html-to-text-9.0.4.tgz#4a83dd8ae8bfa91457d0b1ffc26f4d0537eff58c" + integrity sha512-pUY3cKH/Nm2yYrEmDlPR1mR7yszjGx4DrwPjQ702C4/D5CwHuZTgZdIdwPkRbcuhs7BAh2L5rg3CL5cbRiGTCQ== + "@types/http-assert@*": version "1.5.1" resolved "https://registry.yarnpkg.com/@types/http-assert/-/http-assert-1.5.1.tgz#d775e93630c2469c2f980fc27e3143240335db3b" @@ -2076,6 +1478,11 @@ resolved "https://registry.yarnpkg.com/@types/mime/-/mime-2.0.2.tgz#857a118d8634c84bba7ae14088e4508490cd5da5" integrity sha512-4kPlzbljFcsttWEq6aBW0OZe6BDajAmyvr2xknBG92tejQnvdGtT9+kXSZ580DqpxY9qG2xeQVF9Dq0ymUTo5Q== +"@types/minimatch@^3.0.3": + version "3.0.5" + resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.5.tgz#1001cc5e6a3704b83c236027e77f2f58ea010f40" + integrity sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ== + "@types/node-fetch@2.5.7": version "2.5.7" resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.5.7.tgz#20a2afffa882ab04d44ca786449a276f9f6bbf3c" @@ -2084,10 +1491,10 @@ "@types/node" "*" form-data "^3.0.0" -"@types/node@*", "@types/node@>=6", "@types/node@^22.15.2": - version "22.15.2" - resolved "https://registry.yarnpkg.com/@types/node/-/node-22.15.2.tgz#1db55aa64618ee93a58c8912f74beefe44aca905" - integrity sha512-uKXqKN9beGoMdBfcaTY1ecwz6ctxuJAcUlwE55938g0ZJ8lRxwAZqRz2AJ4pzpt5dHdTPMB863UZ0ESiFUcP7A== +"@types/node@*", "@types/node@^22.15.3": + version "22.15.3" + resolved "https://registry.yarnpkg.com/@types/node/-/node-22.15.3.tgz#b7fb9396a8ec5b5dfb1345d8ac2502060e9af68b" + integrity sha512-lX7HFZeHf4QG/J7tBZqrCAXwz9J5RD56Y6MpP0eJkka8p+K0RY/yBTW7CYFJ4VGCclxqOLKmiGP5juQc6MKgcw== dependencies: undici-types "~6.21.0" @@ -2096,6 +1503,13 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.26.tgz#a8a119960bff16b823be4c617da028570779bcfd" integrity sha512-myMwkO2Cr82kirHY8uknNRHEVtn0wV3DTQfkrjx17jmkstDRZ24gNUdl8AHXVyVclTYI/bNjgTPTAWvWLqXqkw== +"@types/nodemailer@*": + version "6.4.17" + resolved "https://registry.yarnpkg.com/@types/nodemailer/-/nodemailer-6.4.17.tgz#5c82a42aee16a3dd6ea31446a1bd6a447f1ac1a4" + integrity sha512-I9CCaIp6DTldEg7vyUTZi8+9Vo0hi1/T8gv3C89yk1rSAAzoKQ8H8ki/jBYJSFoH/BisgLP8tkZMlQ91CIquww== + dependencies: + "@types/node" "*" + "@types/qs@*": version "6.9.3" resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.3.tgz#b755a0934564a200d3efdf88546ec93c369abd03" @@ -2126,6 +1540,11 @@ "@types/express-serve-static-core" "*" "@types/mime" "*" +"@types/slug@^5.0.9": + version "5.0.9" + resolved "https://registry.yarnpkg.com/@types/slug/-/slug-5.0.9.tgz#e5b213a9d7797d40d362ba85e2a7bbcd4df4ed40" + integrity sha512-6Yp8BSplP35Esa/wOG1wLNKiqXevpQTEF/RcL/NV6BBQaMmZh4YlDwCgrrFSoUE4xAGvnKd5c+lkQJmPrBAzfQ== + "@types/stack-utils@^2.0.0": version "2.0.1" resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c" @@ -2160,11 +1579,6 @@ resolved "https://registry.yarnpkg.com/@types/yup/-/yup-0.26.34.tgz#199329f9fee5074a7385b4a4a25d1559db628aef" integrity sha512-/zH/Yuwl2vC5fgPE+Qtv67iI/o50qLJJsR0KVc86cpDlY2IsRv3yJop1v/9hfNrgy7J8J5BpJM4BMhyFE3QE4w== -"@types/zen-observable@^0.8.0": - version "0.8.0" - resolved "https://registry.yarnpkg.com/@types/zen-observable/-/zen-observable-0.8.0.tgz#8b63ab7f1aa5321248aad5ac890a485656dcea4d" - integrity sha512-te5lMAWii1uEJ4FwLjzdlbw3+n0FZNOvFXHxQDKeT0dilh7HOzdMzV2TrJVUzq8ep7J4Na8OUYPRLSQkJHAlrg== - "@typescript-eslint/eslint-plugin@^5.62.0": version "5.62.0" resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz#aeef0328d172b9e37d9bab6dbc13b87ed88977db" @@ -2386,14 +1800,6 @@ resolved "https://registry.yarnpkg.com/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.7.0.tgz#c130ae8c0ce56dd1fe952d44fe95a6f9a91cccb6" integrity sha512-Jf14pKofg58DIwcZv4Wt9AyVVe7bSJP8ODz+EP9nG/rho08FQzan0VOJk1g6/BNE1RkoYd+lRTWK+/BgH12qoQ== -"@wry/context@^0.4.0": - version "0.4.4" - resolved "https://registry.yarnpkg.com/@wry/context/-/context-0.4.4.tgz#e50f5fa1d6cfaabf2977d1fda5ae91717f8815f8" - integrity sha512-LrKVLove/zw6h2Md/KZyWxIkFM6AoyKp71OqpH9Hiip1csjPVoD3tPxlbQUNxEnHENks3UGgNpSBCAfq9KWuag== - dependencies: - "@types/node" ">=6" - tslib "^1.9.3" - "@wry/equality@^0.1.2": version "0.1.11" resolved "https://registry.yarnpkg.com/@wry/equality/-/equality-0.1.11.tgz#35cb156e4a96695aa81a9ecc4d03787bc17f1790" @@ -2437,6 +1843,16 @@ acorn-walk@^8.1.1: resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.2.0.tgz#741210f2e2426454508853a2f44d0ab83b7f69c1" integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA== +acorn@^7.1.1: + version "7.4.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" + integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== + +acorn@^8.14.0, acorn@^8.5.0: + version "8.14.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.14.1.tgz#721d5dc10f7d5b5609a891773d47731796935dfb" + integrity sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg== + acorn@^8.4.1: version "8.9.0" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.9.0.tgz#78a16e3b2bcc198c10822786fa6679e245db5b59" @@ -2483,6 +1899,19 @@ ajv@^6.12.3, ajv@^6.12.4: json-schema-traverse "^0.4.1" uri-js "^4.2.2" +alce@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/alce/-/alce-1.2.0.tgz#a8be2dacaac42494612f18dc09db691f3dea4aab" + integrity sha512-XppPf2S42nO2WhvKzlwzlfcApcXHzjlod30pKmcWjRgLOtqoe5DMuqdiYoM6AgyXksc6A6pV4v1L/WW217e57w== + dependencies: + esprima "^1.2.0" + estraverse "^1.5.0" + +ansi-colors@^4.1.1: + version "4.1.3" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.3.tgz#37611340eb2243e70cc604cad35d63270d48781b" + integrity sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw== + ansi-escapes@^4.2.1: version "4.3.2" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" @@ -2495,11 +1924,6 @@ ansi-regex@^5.0.1: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== -ansi-regex@^6.0.1: - version "6.1.0" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.1.0.tgz#95ec409c69619d6cb1b8b34f14b660ef28ebd654" - integrity sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA== - ansi-styles@^3.1.0: version "3.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" @@ -2519,11 +1943,6 @@ ansi-styles@^5.0.0: resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== -ansi-styles@^6.1.0: - version "6.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" - integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== - anymatch@^3.0.3, anymatch@~3.1.2: version "3.1.3" resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" @@ -2540,39 +1959,6 @@ apollo-cache-control@^0.11.4: apollo-server-env "^2.4.5" apollo-server-plugin-base "^0.10.2" -apollo-cache-inmemory@~1.6.6: - version "1.6.6" - resolved "https://registry.yarnpkg.com/apollo-cache-inmemory/-/apollo-cache-inmemory-1.6.6.tgz#56d1f2a463a6b9db32e9fa990af16d2a008206fd" - integrity sha512-L8pToTW/+Xru2FFAhkZ1OA9q4V4nuvfoPecBM34DecAugUZEBhI2Hmpgnzq2hTKZ60LAMrlqiASm0aqAY6F8/A== - dependencies: - apollo-cache "^1.3.5" - apollo-utilities "^1.3.4" - optimism "^0.10.0" - ts-invariant "^0.4.0" - tslib "^1.10.0" - -apollo-cache@1.3.5, apollo-cache@^1.3.5: - version "1.3.5" - resolved "https://registry.yarnpkg.com/apollo-cache/-/apollo-cache-1.3.5.tgz#9dbebfc8dbe8fe7f97ba568a224bca2c5d81f461" - integrity sha512-1XoDy8kJnyWY/i/+gLTEbYLnoiVtS8y7ikBr/IfmML4Qb+CM7dEEbIUOjnY716WqmZ/UpXIxTfJsY7rMcqiCXA== - dependencies: - apollo-utilities "^1.3.4" - tslib "^1.10.0" - -apollo-client@~2.6.10: - version "2.6.10" - resolved "https://registry.yarnpkg.com/apollo-client/-/apollo-client-2.6.10.tgz#86637047b51d940c8eaa771a4ce1b02df16bea6a" - integrity sha512-jiPlMTN6/5CjZpJOkGeUV0mb4zxx33uXWdj/xQCfAMkuNAC3HN7CvYDyMHHEzmcQ5GV12LszWoQ/VlxET24CtA== - dependencies: - "@types/zen-observable" "^0.8.0" - apollo-cache "1.3.5" - apollo-link "^1.0.0" - apollo-utilities "1.3.4" - symbol-observable "^1.0.2" - ts-invariant "^0.4.0" - tslib "^1.10.0" - zen-observable "^0.8.0" - apollo-datasource@^0.7.2: version "0.7.2" resolved "https://registry.yarnpkg.com/apollo-datasource/-/apollo-datasource-0.7.2.tgz#1662ee93453a9b89af6f73ce561bde46b41ebf31" @@ -2614,15 +2000,7 @@ apollo-graphql@^0.6.0: apollo-env "^0.6.5" lodash.sortby "^4.7.0" -apollo-link-context@~1.0.20: - version "1.0.20" - resolved "https://registry.yarnpkg.com/apollo-link-context/-/apollo-link-context-1.0.20.tgz#1939ac5dc65d6dff0c855ee53521150053c24676" - integrity sha512-MLLPYvhzNb8AglNsk2NcL9AvhO/Vc9hn2ZZuegbhRHGet3oGr0YH9s30NS9+ieoM0sGT11p7oZ6oAILM/kiRBA== - dependencies: - apollo-link "^1.2.14" - tslib "^1.9.3" - -apollo-link-http-common@^0.2.14, apollo-link-http-common@^0.2.16: +apollo-link-http-common@^0.2.14: version "0.2.16" resolved "https://registry.yarnpkg.com/apollo-link-http-common/-/apollo-link-http-common-0.2.16.tgz#756749dafc732792c8ca0923f9a40564b7c59ecc" integrity sha512-2tIhOIrnaF4UbQHf7kjeQA/EmSorB7+HyJIIrUjJOKBgnXwuexi8aMecRlqTIDWcyVXCeqLhUnztMa6bOH/jTg== @@ -2631,16 +2009,7 @@ apollo-link-http-common@^0.2.14, apollo-link-http-common@^0.2.16: ts-invariant "^0.4.0" tslib "^1.9.3" -apollo-link-http@~1.5.17: - version "1.5.17" - resolved "https://registry.yarnpkg.com/apollo-link-http/-/apollo-link-http-1.5.17.tgz#499e9f1711bf694497f02c51af12d82de5d8d8ba" - integrity sha512-uWcqAotbwDEU/9+Dm9e1/clO7hTB2kQ/94JYcGouBVLjoKmTeJTUPQKcJGpPwUjZcSqgYicbFqQSoJIW0yrFvg== - dependencies: - apollo-link "^1.2.14" - apollo-link-http-common "^0.2.16" - tslib "^1.9.3" - -apollo-link@^1.0.0, apollo-link@^1.2.12, apollo-link@^1.2.14: +apollo-link@^1.2.12, apollo-link@^1.2.14: version "1.2.14" resolved "https://registry.yarnpkg.com/apollo-link/-/apollo-link-1.2.14.tgz#3feda4b47f9ebba7f4160bef8b977ba725b684d9" integrity sha512-p67CMEFP7kOG1JZ0ZkYZwRDa369w5PIjtMjvrQd/HnIV8FRsHRqLqK+oAZQnFa1DDdZtOtHTi+aMIW6EatC2jg== @@ -2807,7 +2176,7 @@ apollo-upload-client@^13.0.0: apollo-link-http-common "^0.2.14" extract-files "^8.0.0" -apollo-utilities@1.3.4, apollo-utilities@^1.0.1, apollo-utilities@^1.3.0, apollo-utilities@^1.3.4: +apollo-utilities@^1.0.1, apollo-utilities@^1.3.0: version "1.3.4" resolved "https://registry.yarnpkg.com/apollo-utilities/-/apollo-utilities-1.3.4.tgz#6129e438e8be201b6c55b0f13ce49d2c7175c9cf" integrity sha512-pk2hiWrCXMAy2fRPwEyhvka+mqwzeP60Jr1tRYi5xru+3ko94HI9o6lK0CT33/w4RDlxWchmdhDCrvdr+pHCig== @@ -2860,6 +2229,11 @@ array-buffer-byte-length@^1.0.1: call-bind "^1.0.5" is-array-buffer "^3.0.4" +array-differ@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/array-differ/-/array-differ-3.0.0.tgz#3cbb3d0f316810eafcc47624734237d6aee4ae6b" + integrity sha512-THtfYS6KtME/yIAhKjZ2ul7XI96lQGHRputJQHO80LAWQnuGP4iCIN8vdMRboGbIEYBwU33q8Tch1os2+X0kMg== + array-flatten@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" @@ -2941,6 +2315,16 @@ arraybuffer.prototype.slice@^1.0.3: is-array-buffer "^3.0.4" is-shared-array-buffer "^1.0.2" +arrify@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/arrify/-/arrify-2.0.1.tgz#c9655e9331e0abcd588d2a7cad7e9956f66701fa" + integrity sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug== + +asap@~2.0.3: + version "2.0.6" + resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" + integrity sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA== + asn1@~0.2.3: version "0.2.6" resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.6.tgz#0d3a7bb6e64e02a90c0303b31f292868ea09a08d" @@ -2948,6 +2332,11 @@ asn1@~0.2.3: dependencies: safer-buffer "~2.1.0" +assert-never@^1.2.1: + version "1.4.0" + resolved "https://registry.yarnpkg.com/assert-never/-/assert-never-1.4.0.tgz#b0d4988628c87f35eb94716cc54422a63927e175" + integrity sha512-5oJg84os6NMQNl27T9LnZkvvqzvAnHu03ShCnoj6bsJwS7L8AO4lf+C/XjK/nvzEqQB744moC6V128RucQd1jA== + assert-plus@1.0.0, assert-plus@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" @@ -3036,24 +2425,7 @@ aws4@^1.8.0: resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.12.0.tgz#ce1c9d143389679e253b314241ea9aa5cec980d3" integrity sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg== -babel-core@~7.0.0-0: - version "7.0.0-bridge.0" - resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-7.0.0-bridge.0.tgz#95a492ddd90f9b4e9a4a1da14eb335b87b634ece" - integrity sha512-poPX9mZH/5CSanm50Q+1toVci6pv5KSRv/5TWCwtzQS5XEwn40BcCrgIeMFWP9CKKIniKXNxoIOnOq4VVlGXhg== - -babel-eslint@~10.1.0: - version "10.1.0" - resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-10.1.0.tgz#6968e568a910b78fb3779cdd8b6ac2f479943232" - integrity sha512-ifWaTHQ0ce+448CYop8AdrQiBsGrnC+bMgfyKFdi6EsPLTAWG+QfyDeM6OH+FmWnKvEq5NnBMLvlBUPKQZoDSg== - dependencies: - "@babel/code-frame" "^7.0.0" - "@babel/parser" "^7.7.0" - "@babel/traverse" "^7.7.0" - "@babel/types" "^7.7.0" - eslint-visitor-keys "^1.0.0" - resolve "^1.12.0" - -babel-jest@^29.7.0, babel-jest@~29.7.0: +babel-jest@^29.7.0: version "29.7.0" resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-29.7.0.tgz#f4369919225b684c56085998ac63dbd05be020d5" integrity sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg== @@ -3087,37 +2459,6 @@ babel-plugin-jest-hoist@^29.6.3: "@types/babel__core" "^7.1.14" "@types/babel__traverse" "^7.0.6" -babel-plugin-polyfill-corejs2@^0.4.10: - version "0.4.10" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.10.tgz#276f41710b03a64f6467433cab72cbc2653c38b1" - integrity sha512-rpIuu//y5OX6jVU+a5BCn1R5RSZYWAl2Nar76iwaOdycqb6JPxediskWFMMl7stfwNJR4b7eiQvh5fB5TEQJTQ== - dependencies: - "@babel/compat-data" "^7.22.6" - "@babel/helper-define-polyfill-provider" "^0.6.1" - semver "^6.3.1" - -babel-plugin-polyfill-corejs3@^0.11.0: - version "0.11.1" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.11.1.tgz#4e4e182f1bb37c7ba62e2af81d8dd09df31344f6" - integrity sha512-yGCqvBT4rwMczo28xkH/noxJ6MZ4nJfkVYdoDaC/utLtWrXxv27HVrzAeSbqR8SxDsp46n0YF47EbHoixy6rXQ== - dependencies: - "@babel/helper-define-polyfill-provider" "^0.6.3" - core-js-compat "^3.40.0" - -babel-plugin-polyfill-regenerator@^0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.6.1.tgz#4f08ef4c62c7a7f66a35ed4c0d75e30506acc6be" - integrity sha512-JfTApdE++cgcTWjsiCQlLyFBMbTUft9ja17saCc93lgV33h4tuCVj7tlvu//qpLwaG+3yEz7/KhahGrUMkVq9g== - dependencies: - "@babel/helper-define-polyfill-provider" "^0.6.1" - -babel-plugin-transform-runtime@^6.23.0: - version "6.23.0" - resolved "https://registry.yarnpkg.com/babel-plugin-transform-runtime/-/babel-plugin-transform-runtime-6.23.0.tgz#88490d446502ea9b8e7efb0fe09ec4d99479b1ee" - integrity sha1-iEkNRGUC6puOfvsP4J7E2ZR5se4= - dependencies: - babel-runtime "^6.22.0" - babel-preset-current-node-syntax@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz#b4399239b89b2a011f9ddbe3e4f401fc40cff73b" @@ -3144,13 +2485,12 @@ babel-preset-jest@^29.6.3: babel-plugin-jest-hoist "^29.6.3" babel-preset-current-node-syntax "^1.0.0" -babel-runtime@^6.22.0: - version "6.26.0" - resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.26.0.tgz#965c7058668e82b55d7bfe04ff2337bc8b5647fe" - integrity sha1-llxwWGaOgrVde/4E/yM3vItWR/4= +babel-walk@3.0.0-canary-5: + version "3.0.0-canary-5" + resolved "https://registry.yarnpkg.com/babel-walk/-/babel-walk-3.0.0-canary-5.tgz#f66ecd7298357aee44955f235a6ef54219104b11" + integrity sha512-GAwkz0AihzY5bkwIY5QDR+LvsRQgB/B+1foMPvi0FZPMl5fjD7ICiznUiBdLYMH1QYe6vqu4gWYytZOccLouFw== dependencies: - core-js "^2.4.0" - regenerator-runtime "^0.11.0" + "@babel/types" "^7.9.6" backo2@^1.0.2: version "1.0.2" @@ -3227,6 +2567,11 @@ boolbase@^1.0.0: resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24= +boolean@3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/boolean/-/boolean-3.2.0.tgz#9e5294af4e98314494cbb17979fa54ca159f116b" + integrity sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw== + brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" @@ -3259,16 +2604,6 @@ browserslist@^4.24.0: node-releases "^2.0.18" update-browserslist-db "^1.1.0" -browserslist@^4.24.3: - version "4.24.4" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.24.4.tgz#c6b2865a3f08bcb860a0e827389003b9fe686e4b" - integrity sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A== - dependencies: - caniuse-lite "^1.0.30001688" - electron-to-chromium "^1.5.73" - node-releases "^2.0.19" - update-browserslist-db "^1.1.1" - bs-logger@^0.2.6: version "0.2.6" resolved "https://registry.yarnpkg.com/bs-logger/-/bs-logger-0.2.6.tgz#eb7d365307a72cf974cc6cda76b68354ad336bd8" @@ -3430,11 +2765,6 @@ caniuse-lite@^1.0.30001663: resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001669.tgz#fda8f1d29a8bfdc42de0c170d7f34a9cf19ed7a3" integrity sha512-DlWzFDJqstqtIVx1zeSpIMLjunf5SmwOw0N2Ck/QSQdS8PLS4+9HrLaYei4w8BIAL7IB/UEDu889d8vhCTPA0w== -caniuse-lite@^1.0.30001688: - version "1.0.30001700" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001700.tgz#26cd429cf09b4fd4e745daf4916039c794d720f6" - integrity sha512-2S6XIXwaE7K7erT8dY+kLQcpa5ms63XlRkMkReXjle+kf6c5g38vyMl+Z5y8dSxOFDhcFe+nxnn261PLxBSQsQ== - caseless@~0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" @@ -3449,6 +2779,14 @@ chalk@2.3.0: escape-string-regexp "^1.0.5" supports-color "^4.0.0" +chalk@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4" + integrity sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" @@ -3462,6 +2800,24 @@ char-regex@^1.0.2: resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf" integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw== +character-parser@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/character-parser/-/character-parser-2.2.0.tgz#c7ce28f36d4bcd9744e5ffc2c5fcde1c73261fc0" + integrity sha512-+UqJQjFEFaTAs3bNsF2j2kEN1baG/zghZbdqoYEDxGZtJo9LBzl1A+m0D4n3qKx8N2FNv8/Xp6yV9mQmBuptaw== + dependencies: + is-regex "^1.0.3" + +cheerio-select@^1.5.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/cheerio-select/-/cheerio-select-1.6.0.tgz#489f36604112c722afa147dedd0d4609c09e1696" + integrity sha512-eq0GdBvxVFbqWgmCm7M3XGs1I8oLy/nExUnh6oLqmBditPO9AqQJrkslDpMun/hZ0yyTs8L0m85OHp4ho6Qm9g== + dependencies: + css-select "^4.3.0" + css-what "^6.0.1" + domelementtype "^2.2.0" + domhandler "^4.3.1" + domutils "^2.8.0" + cheerio-select@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/cheerio-select/-/cheerio-select-2.1.0.tgz#4d8673286b8126ca2a8e42740d5e3c4884ae21b4" @@ -3474,6 +2830,32 @@ cheerio-select@^2.1.0: domhandler "^5.0.3" domutils "^3.0.1" +cheerio@1.0.0-rc.10: + version "1.0.0-rc.10" + resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-1.0.0-rc.10.tgz#2ba3dcdfcc26e7956fc1f440e61d51c643379f3e" + integrity sha512-g0J0q/O6mW8z5zxQ3A8E8J1hUgp4SMOvEoW/x84OwyHKe/Zccz83PVT4y5Crcr530FV6NgmKI1qvGTKVl9XXVw== + dependencies: + cheerio-select "^1.5.0" + dom-serializer "^1.3.2" + domhandler "^4.2.0" + htmlparser2 "^6.1.0" + parse5 "^6.0.1" + parse5-htmlparser2-tree-adapter "^6.0.1" + tslib "^2.2.0" + +cheerio@1.0.0-rc.12: + version "1.0.0-rc.12" + resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-1.0.0-rc.12.tgz#788bf7466506b1c6bf5fae51d24a2c4d62e47683" + integrity sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q== + dependencies: + cheerio-select "^2.1.0" + dom-serializer "^2.0.0" + domhandler "^5.0.3" + domutils "^3.0.1" + htmlparser2 "^8.0.1" + parse5 "^7.0.0" + parse5-htmlparser2-tree-adapter "^7.0.0" + cheerio@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-1.0.0.tgz#1ede4895a82f26e8af71009f961a9b8cb60d6a81" @@ -3491,7 +2873,7 @@ cheerio@~1.0.0: undici "^6.19.5" whatwg-mimetype "^4.0.0" -chokidar@^3.5.2, chokidar@^3.5.3, chokidar@^3.6.0: +chokidar@^3.5.2, chokidar@^3.5.3: version "3.6.0" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.6.0.tgz#197c6cc669ef2a8dc5e7b4d97ee4e092c3eb0d5b" integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw== @@ -3530,6 +2912,11 @@ ci-info@^3.2.0: resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.8.0.tgz#81408265a5380c929f0bc665d62256628ce9ef91" integrity sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw== +ci-info@^3.8.0: + version "3.9.0" + resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.9.0.tgz#4279a62028a7b1f262f3473fc9605f5e218c59b4" + integrity sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ== + cjs-module-lexer@^1.0.0: version "1.2.2" resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz#9f84ba3244a512f3a54e5277e8eef4c489864e40" @@ -3557,15 +2944,6 @@ cliui@^8.0.1: strip-ansi "^6.0.1" wrap-ansi "^7.0.0" -clone-deep@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387" - integrity sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ== - dependencies: - is-plain-object "^2.0.4" - kind-of "^6.0.2" - shallow-clone "^3.0.0" - clone-response@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b" @@ -3629,7 +3007,7 @@ commander@^2.20.3: resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== -commander@^6.2.0: +commander@^6.1.0: version "6.2.1" resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.1.tgz#0792eb682dfbc325999bb2b84fddddba110ac73c" integrity sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA== @@ -3639,11 +3017,6 @@ commander@^9.0.0: resolved "https://registry.yarnpkg.com/commander/-/commander-9.5.0.tgz#bc08d1eb5cedf7ccb797a96199d41c7bc3e60d30" integrity sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ== -commondir@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" - integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs= - concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" @@ -3659,6 +3032,14 @@ condense-whitespace@~2.0.0: resolved "https://registry.yarnpkg.com/condense-whitespace/-/condense-whitespace-2.0.0.tgz#94e9644938f66aa7be4b8849f8f0b3cec97d6b3a" integrity sha512-Ath9o58/0rxZXbyoy3zZgrVMoIemi30sukG/btuMKCLyqfQt3dNOWc9N3EHEMa2Q3i0tXQPDJluYFLwy7pJuQw== +constantinople@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/constantinople/-/constantinople-4.0.1.tgz#0def113fa0e4dc8de83331a5cf79c8b325213151" + integrity sha512-vCrqcSIq4//Gx74TXXCGnHpulY1dskqLTFGDmhrGxzeXL8lF8kvXv6mpNWlJj1uD4DW23D4ljAqbY4RRaaUZIw== + dependencies: + "@babel/parser" "^7.6.0" + "@babel/types" "^7.6.1" + content-disposition@0.5.4: version "0.5.4" resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" @@ -3708,24 +3089,17 @@ cookie@^0.7.1: resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.7.2.tgz#556369c472a2ba910f2979891b526b3436237ed7" integrity sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w== -core-js-compat@^3.40.0: - version "3.40.0" - resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.40.0.tgz#7485912a5a4a4315c2fdb2cbdc623e6881c88b38" - integrity sha512-0XEDpr5y5mijvw8Lbc6E5AkjrHfp7eEoPlu36SWeAbcL8fn1G1ANe8DBlo2XoNN89oVpxWwOjYIPVzR4ZvsKCQ== - dependencies: - browserslist "^4.24.3" - core-js-pure@^3.30.2: version "3.40.0" resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.40.0.tgz#d9a019e9160f9b042eeb6abb92242680089d486e" integrity sha512-AtDzVIgRrmRKQai62yuSIN5vNiQjcJakJb4fbhVw3ehxx7Lohphvw9SGNWKhLFqSxC4ilD0g/L1huAYFQU3Q6A== -core-js@^2.4.0, core-js@^2.6.5: +core-js@^2.6.5: version "2.6.9" resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.9.tgz#6b4b214620c834152e179323727fc19741b084f2" integrity sha512-HOpZf6eXmnl7la+cUdMnLvUxKNqLUzJvgIziQ0DiF3JwSImNphIqdGqzj6hIKyX04MmV0poclQ7+wjWvxQyR2A== -core-js@^3.0.1, core-js@^3.30.2: +core-js@^3.0.1: version "3.36.1" resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.36.1.tgz#c97a7160ebd00b2de19e62f4bbd3406ab720e578" integrity sha512-BTvUrwxVBezj5SZ3f10ImnX2oRByMxql3EimVqMysepbC9EeMUOpLwdy6Eoili2x6E4kf+ZUB5k/+Jv55alPfA== @@ -3735,7 +3109,7 @@ core-util-is@1.0.2: resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= -cors@^2.8.4, cors@~2.8.5: +cors@^2.8.4: version "2.8.5" resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29" integrity sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g== @@ -3777,6 +3151,17 @@ cross-spawn@^5.0.1: shebang-command "^1.2.0" which "^1.2.9" +cross-spawn@^6.0.0: + version "6.0.6" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.6.tgz#30d0efa0712ddb7eb5a76e1e8721bffafa6b5d57" + integrity sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw== + dependencies: + nice-try "^1.0.4" + path-key "^2.0.1" + semver "^5.5.0" + shebang-command "^1.2.0" + which "^1.2.9" + cross-spawn@^7.0.1, cross-spawn@^7.0.2, cross-spawn@^7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" @@ -3795,6 +3180,17 @@ cross-spawn@^7.0.6: shebang-command "^2.0.0" which "^2.0.1" +css-select@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/css-select/-/css-select-4.3.0.tgz#db7129b2846662fd8628cfc496abb2b59e41529b" + integrity sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ== + dependencies: + boolbase "^1.0.0" + css-what "^6.0.1" + domhandler "^4.3.1" + domutils "^2.8.0" + nth-check "^2.0.1" + css-select@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/css-select/-/css-select-5.1.0.tgz#b8ebd6554c3637ccc76688804ad3f6a6fdaea8a6" @@ -3806,7 +3202,7 @@ css-select@^5.1.0: domutils "^3.0.1" nth-check "^2.0.1" -css-what@^6.1.0: +css-what@^6.0.1, css-what@^6.1.0: version "6.1.0" resolved "https://registry.yarnpkg.com/css-what/-/css-what-6.1.0.tgz#fb5effcf76f1ddea2c81bdfaa4de44e79bac70f4" integrity sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw== @@ -3921,7 +3317,7 @@ debug@2.6.9: dependencies: ms "2.0.0" -debug@4, debug@^4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4, debug@^4.3.5, debug@^4.4.0: +debug@4, debug@^4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4, debug@^4.3.5, debug@^4.4.0: version "4.4.0" resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.0.tgz#2b3f2aea2ffeb776477460267377dc8710faba8a" integrity sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA== @@ -3952,6 +3348,11 @@ dedent@^1.0.0: resolved "https://registry.yarnpkg.com/dedent/-/dedent-1.5.1.tgz#4f3fc94c8b711e9bb2800d185cd6ad20f2a90aff" integrity sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg== +deep-extend@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" + integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== + deep-is@^0.1.3: version "0.1.4" resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" @@ -3967,6 +3368,11 @@ deepmerge@^4.2.2: resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.0.tgz#65491893ec47756d44719ae520e0e2609233b59b" integrity sha512-z2wJZXrmeHdvYJp/Ux55wIjqo81G5Bp4c+oELTW+7ar6SogWHajt5a9gO3s3IDaGSAXjDk0vlQKN3rms8ab3og== +deepmerge@^4.3.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a" + integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A== + defer-to-connect@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-2.0.1.tgz#8016bdb4143e4632b77a3449c6236277de520587" @@ -4044,7 +3450,12 @@ destroy@1.2.0: resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015" integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== -detect-newline@^3.0.0: +detect-indent@^6.0.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-6.1.0.tgz#592485ebbbf6b3b1ab2be175c8393d04ca0d57e6" + integrity sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA== + +detect-newline@^3.0.0, detect-newline@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== @@ -4073,6 +3484,14 @@ dir-glob@^3.0.1: dependencies: path-type "^4.0.0" +display-notification@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/display-notification/-/display-notification-2.0.0.tgz#49fad2e03289b4f668c296e1855c2cf8ba893d49" + integrity sha512-TdmtlAcdqy1NU+j7zlkDdMnCL878zriLaBmoD9quOoq1ySSSGv03l0hXK5CvIFZlIfFI/hizqdQuW+Num7xuhw== + dependencies: + escape-string-applescript "^1.0.0" + run-applescript "^3.0.0" + doctrine@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" @@ -4087,6 +3506,11 @@ doctrine@^3.0.0: dependencies: esutils "^2.0.2" +doctypes@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/doctypes/-/doctypes-1.1.0.tgz#ea80b106a87538774e8a3a4a5afe293de489e0a9" + integrity sha512-LLBi6pEqS6Do3EKQ3J0NqHWV5hhb78Pi8vvESYwyOy2c31ZEZVdtitdzsQsKb7878PEERhzUk0ftqGhG6Mz+pQ== + dom-serializer@^1.0.1: version "1.3.2" resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.3.2.tgz#6206437d32ceefaec7161803230c7a20bc1b4d91" @@ -4096,6 +3520,15 @@ dom-serializer@^1.0.1: domhandler "^4.2.0" entities "^2.0.0" +dom-serializer@^1.3.2: + version "1.4.1" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.4.1.tgz#de5d41b1aea290215dc45a6dae8adcf1d32e2d30" + integrity sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag== + dependencies: + domelementtype "^2.0.1" + domhandler "^4.2.0" + entities "^2.0.0" + dom-serializer@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-2.0.0.tgz#e41b802e1eedf9f6cae183ce5e622d789d7d8e53" @@ -4120,6 +3553,13 @@ domelementtype@^2.3.0: resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.3.0.tgz#5c45e8e869952626331d7aab326d01daf65d589d" integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw== +domhandler@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-3.3.0.tgz#6db7ea46e4617eb15cf875df68b2b8524ce0037a" + integrity sha512-J1C5rIANUbuYK+FuFL98650rihynUOEzRLxW+90bKZRWB6A1X1Tf82GxR1qAWLyfNPRvjqfip3Q5tdYlmAa9lA== + dependencies: + domelementtype "^2.0.1" + domhandler@^4.0.0, domhandler@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.2.0.tgz#f9768a5f034be60a89a27c2e4d0f74eba0d8b059" @@ -4127,6 +3567,13 @@ domhandler@^4.0.0, domhandler@^4.2.0: dependencies: domelementtype "^2.2.0" +domhandler@^4.3.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.3.1.tgz#8d792033416f59d68bc03a5aa7b018c1ca89279c" + integrity sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ== + dependencies: + domelementtype "^2.2.0" + domhandler@^5.0.2, domhandler@^5.0.3: version "5.0.3" resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-5.0.3.tgz#cc385f7f751f1d1fc650c21374804254538c7d31" @@ -4134,6 +3581,15 @@ domhandler@^5.0.2, domhandler@^5.0.3: dependencies: domelementtype "^2.3.0" +domutils@^2.4.2, domutils@^2.8.0: + version "2.8.0" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.8.0.tgz#4437def5db6e2d1f5d6ee859bd95ca7d02048135" + integrity sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A== + dependencies: + dom-serializer "^1.0.1" + domelementtype "^2.2.0" + domhandler "^4.2.0" + domutils@^2.5.2: version "2.7.0" resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.7.0.tgz#8ebaf0c41ebafcf55b0b72ec31c56323712c5442" @@ -4171,11 +3627,6 @@ dunder-proto@^1.0.1: es-errors "^1.3.0" gopd "^1.2.0" -eastasianwidth@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" - integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== - ecc-jsbn@~0.1.1: version "0.1.2" resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" @@ -4208,10 +3659,20 @@ electron-to-chromium@^1.5.28: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.41.tgz#eae1ba6c49a1a61d84cf8263351d3513b2bcc534" integrity sha512-dfdv/2xNjX0P8Vzme4cfzHqnPm5xsZXwsolTYr0eyW18IUmNyG08vL+fttvinTfhKfIKdRoqkDIC9e9iWQCNYQ== -electron-to-chromium@^1.5.73: - version "1.5.103" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.103.tgz#3d02025bc16e96e5edb3ed3ffa2538a11ae682dc" - integrity sha512-P6+XzIkfndgsrjROJWfSvVEgNHtPgbhVyTkwLjUM2HU/h7pZRORgaTlHqfAikqxKmdJMLW8fftrdGWbd/Ds0FA== +email-templates@^12.0.2: + version "12.0.2" + resolved "https://registry.yarnpkg.com/email-templates/-/email-templates-12.0.2.tgz#b4a3b45e0da190aa337a24d7619b464ae9d5ac06" + integrity sha512-lCCnOgapf/h5Lqgz9XGlrkfZQW422MoHBylFvBJxq88VlALA6mt018Mp2reZvyimZ411Dyln+JKMN0Z64D6Bew== + dependencies: + "@ladjs/consolidate" "^1.0.4" + "@ladjs/i18n" "^8.0.3" + get-paths "^0.0.7" + html-to-text "^9.0.5" + juice "^10.0.0" + lodash "^4.17.21" + nodemailer "^6.9.14" + optionalDependencies: + preview-email "^3.0.17" emittery@^0.13.1: version "0.13.1" @@ -4223,11 +3684,6 @@ emoji-regex@^8.0.0: resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== -emoji-regex@^9.2.2: - version "9.2.2" - resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" - integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== - encodeurl@^2.0.0, encodeurl@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-2.0.0.tgz#7b8ea898077d7e409d3ac45474ea38eaf0857a58" @@ -4238,6 +3694,11 @@ encodeurl@~1.0.2: resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= +encoding-japanese@2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/encoding-japanese/-/encoding-japanese-2.2.0.tgz#0ef2d2351250547f432a2dd155453555c16deb59" + integrity sha512-EuJWwlHPZ1LbADuKTClvHtwbaFn4rOD+dRAbWysqEOXRc2Uui0hJInNJrsdH0c+OhJA4nrCBdSkW4DD5YxAo6A== + encoding-sniffer@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/encoding-sniffer/-/encoding-sniffer-0.2.0.tgz#799569d66d443babe82af18c9f403498365ef1d5" @@ -4419,18 +3880,6 @@ es-abstract@^1.22.3, es-abstract@^1.23.0, es-abstract@^1.23.2: unbox-primitive "^1.0.2" which-typed-array "^1.1.15" -es-abstract@^1.5.1: - version "1.13.0" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.13.0.tgz#ac86145fdd5099d8dd49558ccba2eaf9b88e24e9" - integrity sha512-vDZfg/ykNxQVwup/8E1BZhVzFfBxs9NqMzGcvIJrqg5k2/5Za2bWo40dK2J1pgLngZ7c+Shh8lwYtLGyrwPutg== - dependencies: - es-to-primitive "^1.2.0" - function-bind "^1.1.1" - has "^1.0.3" - is-callable "^1.1.4" - is-regex "^1.0.4" - object-keys "^1.0.12" - es-define-property@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.0.tgz#c7faefbdff8b2696cf5f46921edfb77cc4ba3845" @@ -4504,15 +3953,6 @@ es-shim-unscopables@^1.0.2: dependencies: hasown "^2.0.0" -es-to-primitive@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.0.tgz#edf72478033456e8dda8ef09e00ad9650707f377" - integrity sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg== - dependencies: - is-callable "^1.1.4" - is-date-object "^1.0.1" - is-symbol "^1.0.2" - es-to-primitive@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" @@ -4564,16 +4004,26 @@ escalade@^3.1.1: resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== -escalade@^3.1.2, escalade@^3.2.0: +escalade@^3.1.2: version "3.2.0" resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.2.0.tgz#011a3f69856ba189dffa7dc8fcce99d2a87903e5" integrity sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA== +escape-goat@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/escape-goat/-/escape-goat-3.0.0.tgz#e8b5fb658553fe8a3c4959c316c6ebb8c842b19c" + integrity sha512-w3PwNZJwRxlp47QGzhuEBldEqVHHhh8/tIPcl6ecf2Bou99cdAt0knihBV0Ecc7CGxYduXVBDheH1K2oADRlvw== + escape-html@^1.0.3, escape-html@~1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= +escape-string-applescript@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/escape-string-applescript/-/escape-string-applescript-1.0.0.tgz#6f1c2294245d82c63bc03338dc19a94aa8428892" + integrity sha512-4/hFwoYaC6TkpDn9A3pTC52zQPArFeXuIfhUtCGYdauTzXVP9H3BDr3oO/QzQehMpLDC7srvYgfwvImPFGfvBA== + escape-string-regexp@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" @@ -4596,6 +4046,13 @@ eslint-compat-utils@^0.5.1: dependencies: semver "^7.5.4" +eslint-compat-utils@^0.6.4: + version "0.6.5" + resolved "https://registry.yarnpkg.com/eslint-compat-utils/-/eslint-compat-utils-0.6.5.tgz#6b06350a1c947c4514cfa64a170a6bfdbadc7ec2" + integrity sha512-vAUHYzue4YAa2hNACjB8HvUQj5yehAZgiClyFVVom9cP8z5NSFq3PwB/TtJslN2zAMgRX6FCFCjYBbQh71g5RQ== + dependencies: + semver "^7.5.4" + eslint-config-prettier@^10.1.2: version "10.1.2" resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-10.1.2.tgz#31a4b393c40c4180202c27e829af43323bf85276" @@ -4627,6 +4084,13 @@ eslint-import-resolver-typescript@^4.3.4: tinyglobby "^0.2.13" unrs-resolver "^1.6.3" +eslint-json-compat-utils@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/eslint-json-compat-utils/-/eslint-json-compat-utils-0.2.1.tgz#32931d42c723da383712f25177a2c57b9ef5f079" + integrity sha512-YzEodbDyW8DX8bImKhAcCeu/L31Dd/70Bidx2Qex9OFUtgzXLqtfWL4Hr5fM/aCCB8QUZLuJur0S9k6UfgFkfg== + dependencies: + esquery "^1.6.0" + eslint-module-utils@^2.12.0: version "2.12.0" resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.12.0.tgz#fe4cfb948d61f49203d7b08871982b65b9af0b0b" @@ -4675,6 +4139,20 @@ eslint-plugin-jest@^28.11.0: dependencies: "@typescript-eslint/utils" "^6.0.0 || ^7.0.0 || ^8.0.0" +eslint-plugin-jsonc@^2.20.0: + version "2.20.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-jsonc/-/eslint-plugin-jsonc-2.20.0.tgz#7f3ae51abd38176487ba7324dee77578a92e15e0" + integrity sha512-FRgCn9Hzk5eKboCbVMrr9QrhM0eO4G+WKH8IFXoaeqhM/2kuWzbStJn4kkr0VWL8J5H8RYZF+Aoam1vlBaZVkw== + dependencies: + "@eslint-community/eslint-utils" "^4.5.1" + eslint-compat-utils "^0.6.4" + eslint-json-compat-utils "^0.2.1" + espree "^9.6.1 || ^10.3.0" + graphemer "^1.4.0" + jsonc-eslint-parser "^2.4.0" + natural-compare "^1.4.0" + synckit "^0.6.2 || ^0.7.3 || ^0.10.3" + eslint-plugin-n@^17.17.0: version "17.17.0" resolved "https://registry.yarnpkg.com/eslint-plugin-n/-/eslint-plugin-n-17.17.0.tgz#6644433d395c2ecae0b2fe58018807e85d8e0724" @@ -4732,16 +4210,16 @@ eslint-scope@^7.2.2: esrecurse "^4.3.0" estraverse "^5.2.0" -eslint-visitor-keys@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz#e2a82cea84ff246ad6fb57f9bde5b46621459ec2" - integrity sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A== - -eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4.3: +eslint-visitor-keys@^3.0.0, eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4.3: version "3.4.3" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== +eslint-visitor-keys@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz#687bacb2af884fcdda8a6e7d65c606f46a14cd45" + integrity sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw== + eslint@^8.57.1: version "8.57.1" resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.57.1.tgz#7df109654aba7e3bbe5c8eae533c5e461d3c6ca9" @@ -4796,7 +4274,7 @@ esniff@^2.0.1: event-emitter "^0.3.5" type "^2.7.2" -espree@^9.6.0, espree@^9.6.1: +espree@^9.0.0, espree@^9.6.0, espree@^9.6.1: version "9.6.1" resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.1.tgz#a2a17b8e434690a5432f2f8018ce71d331a48c6f" integrity sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ== @@ -4805,6 +4283,20 @@ espree@^9.6.0, espree@^9.6.1: acorn-jsx "^5.3.2" eslint-visitor-keys "^3.4.1" +"espree@^9.6.1 || ^10.3.0": + version "10.3.0" + resolved "https://registry.yarnpkg.com/espree/-/espree-10.3.0.tgz#29267cf5b0cb98735b65e64ba07e0ed49d1eed8a" + integrity sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg== + dependencies: + acorn "^8.14.0" + acorn-jsx "^5.3.2" + eslint-visitor-keys "^4.2.0" + +esprima@^1.2.0: + version "1.2.5" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-1.2.5.tgz#0993502feaf668138325756f30f9a51feeec11e9" + integrity sha512-S9VbPDU0adFErpDai3qDkjq8+G05ONtKzcyNrPKg/ZKa+tf879nX2KexNU95b31UoTJjRLInNBHHHjFPoCd7lQ== + esprima@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" @@ -4817,6 +4309,13 @@ esquery@^1.4.2: dependencies: estraverse "^5.1.0" +esquery@^1.6.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.6.0.tgz#91419234f804d852a82dceec3e16cdc22cf9dae7" + integrity sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg== + dependencies: + estraverse "^5.1.0" + esrecurse@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" @@ -4824,6 +4323,11 @@ esrecurse@^4.3.0: dependencies: estraverse "^5.2.0" +estraverse@^1.5.0: + version "1.9.3" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-1.9.3.tgz#af67f2dc922582415950926091a4005d29c9bb44" + integrity sha512-25w1fMXQrGdoquWnScXZGckOv+Wes+JDnuN/+7ex3SauFRS72r2lFDec0EKPt2YD1wUJ/IrfEex+9yp4hfSOJA== + estraverse@^4.1.1: version "4.3.0" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" @@ -4862,6 +4366,19 @@ events@1.1.1: resolved "https://registry.yarnpkg.com/events/-/events-1.1.1.tgz#9ebdb7635ad099c70dcc4c2a1f5004288e8bd924" integrity sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ= +execa@^0.10.0: + version "0.10.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-0.10.0.tgz#ff456a8f53f90f8eccc71a96d11bdfc7f082cb50" + integrity sha512-7XOMnz8Ynx1gGo/3hyV9loYNPWM94jG3+3T3Y8tsfSstFmETmENCMU/A/zj8Lyaj1lkgEepKepvd6240tBRvlw== + dependencies: + cross-spawn "^6.0.0" + get-stream "^3.0.0" + is-stream "^1.1.0" + npm-run-path "^2.0.0" + p-finally "^1.0.0" + signal-exit "^3.0.0" + strip-eof "^1.0.0" + execa@^0.8.0: version "0.8.0" resolved "https://registry.yarnpkg.com/execa/-/execa-0.8.0.tgz#d8d76bbc1b55217ed190fd6dd49d3c774ecfc8da" @@ -4988,6 +4505,11 @@ ext@^1.7.0: dependencies: type "^2.7.2" +extend-object@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/extend-object/-/extend-object-1.0.0.tgz#42514f84015d1356caf5187969dfb2bc1bda0823" + integrity sha512-0dHDIXC7y7LDmCh/lp1oYkmv73K25AMugQI07r8eFopkW6f7Ufn1q+ETMsJjnV9Am14SlElkqy3O92r6xEaxPw== + extend@~3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" @@ -5044,6 +4566,11 @@ fast-levenshtein@^2.0.6: resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== +fast-printf@^1.6.9: + version "1.6.10" + resolved "https://registry.yarnpkg.com/fast-printf/-/fast-printf-1.6.10.tgz#c44ad871726152159d7a903a5af0d65cf3d75875" + integrity sha512-GwTgG9O4FVIdShhbVF3JxOgSBY2+ePGsu2V/UONgoCPzF9VY6ZdBMKsHKCYQHZwNk3qNouUolRDsgVxcVA5G1w== + fastq@^1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.6.0.tgz#4ec8a38f4ac25f21492673adb7eae9cfef47d1c2" @@ -5114,22 +4641,6 @@ finalhandler@^2.1.0: parseurl "^1.3.3" statuses "^2.0.1" -find-cache-dir@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-2.1.0.tgz#8d0f94cd13fe43c6c7c261a0d86115ca918c05f7" - integrity sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ== - dependencies: - commondir "^1.0.1" - make-dir "^2.0.0" - pkg-dir "^3.0.0" - -find-up@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" - integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg== - dependencies: - locate-path "^3.0.0" - find-up@^4.0.0, find-up@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" @@ -5146,6 +4657,18 @@ find-up@^5.0.0: locate-path "^6.0.0" path-exists "^4.0.0" +fixpack@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/fixpack/-/fixpack-4.0.0.tgz#28b9fb8ca04f89aab382021cfa826b36dc381dfd" + integrity sha512-5SM1+H2CcuJ3gGEwTiVo/+nd/hYpNj9Ch3iMDOQ58ndY+VGQ2QdvaUTkd3otjZvYnd/8LF/HkJ5cx7PBq0orCQ== + dependencies: + alce "1.2.0" + chalk "^3.0.0" + detect-indent "^6.0.0" + detect-newline "^3.1.0" + extend-object "^1.0.0" + rc "^1.2.8" + flat-cache@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11" @@ -5253,11 +4776,6 @@ fs-minipass@^3.0.0: dependencies: minipass "^7.0.3" -fs-readdir-recursive@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz#e32fc030a2ccee44a6b5371308da54be0b397d27" - integrity sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA== - fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" @@ -5360,6 +4878,18 @@ get-package-type@^0.1.0: resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== +get-paths@^0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/get-paths/-/get-paths-0.0.7.tgz#15331086752077cf130166ccd233a1cdbeefcf38" + integrity sha512-0wdJt7C1XKQxuCgouqd+ZvLJ56FQixKoki9MrFaO4EriqzXOiH9gbukaDE1ou08S8Ns3/yDzoBAISNPqj6e6tA== + dependencies: + pify "^4.0.1" + +get-port@5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/get-port/-/get-port-5.1.1.tgz#0469ed07563479de6efb986baf053dcd7d4e3193" + integrity sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ== + get-proto@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/get-proto/-/get-proto-1.0.1.tgz#150b3f2743869ef3e851ec0c49d15b1d14d00ee1" @@ -5447,7 +4977,7 @@ glob@^10.2.2, glob@^10.3.10: package-json-from-dist "^1.0.0" path-scurry "^1.11.1" -glob@^7.1.3, glob@^7.1.4, glob@^7.2.0: +glob@^7.1.3, glob@^7.1.4: version "7.2.3" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== @@ -5762,7 +5292,7 @@ has-values@~2.0.1: dependencies: kind-of "^6.0.2" -has@^1.0.1, has@^1.0.3: +has@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== @@ -5781,7 +5311,7 @@ he@0.5.0: resolved "https://registry.yarnpkg.com/he/-/he-0.5.0.tgz#2c05ffaef90b68e860f3fd2b54ef580989277ee2" integrity sha1-LAX/rvkLaOhg8/0rVO9YCYknfuI= -he@^1.2.0: +he@1.2.0, he@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== @@ -5791,13 +5321,6 @@ helmet@~8.1.0: resolved "https://registry.yarnpkg.com/helmet/-/helmet-8.1.0.tgz#f96d23fedc89e9476ecb5198181009c804b8b38c" integrity sha512-jOiHyAZsmnr8LqoPGmCjYAaiuWwjAPLgY8ZX2XrmHawt99/u1y6RgrZMTeoPfpUbV96HOalYgz1qzkRbw54Pmg== -homedir-polyfill@^1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz#743298cef4e5af3e194161fbadcc2151d3a058e8" - integrity sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA== - dependencies: - parse-passwd "^1.0.0" - html-encoding-sniffer@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz#696df529a7cfd82446369dc5193e590a3735b448" @@ -5820,6 +5343,27 @@ html-to-text@7.1.1: htmlparser2 "^6.1.0" minimist "^1.2.5" +html-to-text@9.0.5, html-to-text@^9.0.5: + version "9.0.5" + resolved "https://registry.yarnpkg.com/html-to-text/-/html-to-text-9.0.5.tgz#6149a0f618ae7a0db8085dca9bbf96d32bb8368d" + integrity sha512-qY60FjREgVZL03vJU6IfMV4GDjGBIoOyvuFdpBDIX9yTlDw0TjxVBQp+P8NvpdIXNJvfWBTNul7fsAQJq2FNpg== + dependencies: + "@selderee/plugin-htmlparser2" "^0.11.0" + deepmerge "^4.3.1" + dom-serializer "^2.0.0" + htmlparser2 "^8.0.2" + selderee "^0.11.0" + +htmlparser2@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-5.0.1.tgz#7daa6fc3e35d6107ac95a4fc08781f091664f6e7" + integrity sha512-vKZZra6CSe9qsJzh0BjBGXo8dvzNsq/oGvsjfRdOrrryfeD9UOBEEQdeoqCRmKZchF5h2zOBMQ6YuQ0uRUmdbQ== + dependencies: + domelementtype "^2.0.1" + domhandler "^3.3.0" + domutils "^2.4.2" + entities "^2.0.0" + htmlparser2@^6.1.0: version "6.1.0" resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-6.1.0.tgz#c4d762b6c3371a05dbe65e94ae43a9f845fb8fb7" @@ -5830,7 +5374,7 @@ htmlparser2@^6.1.0: domutils "^2.5.2" entities "^2.0.0" -htmlparser2@^8.0.0: +htmlparser2@^8.0.0, htmlparser2@^8.0.1, htmlparser2@^8.0.2: version "8.0.2" resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-8.0.2.tgz#f002151705b383e62433b5cf466f5b716edaec21" integrity sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA== @@ -5928,6 +5472,25 @@ human-signals@^2.1.0: resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== +i18n-locales@^0.0.5: + version "0.0.5" + resolved "https://registry.yarnpkg.com/i18n-locales/-/i18n-locales-0.0.5.tgz#8f587e598ab982511d7c7db910cb45b8d93cd96a" + integrity sha512-Kve1AHy6rqyfJHPy8MIvaKBKhHhHPXV+a/TgMkjp3UBhO3gfWR40ZQn8Xy7LI6g3FhmbvkFtv+GCZy6yvuyeHQ== + dependencies: + "@ladjs/country-language" "^0.2.1" + +i18n@^0.15.0: + version "0.15.1" + resolved "https://registry.yarnpkg.com/i18n/-/i18n-0.15.1.tgz#68fb8993c461cc440bc2485d82f72019f2b92de8" + integrity sha512-yue187t8MqUPMHdKjiZGrX+L+xcUsDClGO0Cz4loaKUOK9WrGw5pgan4bv130utOwX7fHE9w2iUeHFalVQWkXA== + dependencies: + "@messageformat/core" "^3.0.0" + debug "^4.3.3" + fast-printf "^1.6.9" + make-plural "^7.0.0" + math-interval-parser "^2.0.1" + mustache "^4.2.0" + iconv-lite@0.4.24: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" @@ -6016,6 +5579,11 @@ inherits@2.0.1: resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1" integrity sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE= +ini@~1.3.0: + version "1.3.8" + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" + integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== + insane@2.6.1: version "2.6.1" resolved "https://registry.yarnpkg.com/insane/-/insane-2.6.1.tgz#c7dcae7b51c20346883b71078fad6ce0483c198f" @@ -6171,6 +5739,13 @@ is-core-module@^2.13.0, is-core-module@^2.15.1: dependencies: hasown "^2.0.2" +is-core-module@^2.16.0: + version "2.16.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.16.1.tgz#2a98801a849f43e2add644fbb6bc6229b19a4ef4" + integrity sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w== + dependencies: + hasown "^2.0.2" + is-data-view@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/is-data-view/-/is-data-view-1.0.1.tgz#4b4d3a511b70f3dc26d42c03ca9ca515d847759f" @@ -6183,6 +5758,19 @@ is-date-object@^1.0.1: resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.2.tgz#bda736f2cd8fd06d32844e7743bfa7494c3bfd7e" integrity sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g== +is-docker@^2.0.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" + integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== + +is-expression@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/is-expression/-/is-expression-4.0.0.tgz#c33155962abf21d0afd2552514d67d2ec16fd2ab" + integrity sha512-zMIXX63sxzG3XrkHkrAPvm/OVZVSCPNkwMHU8oTX7/U3AL78I0QXCEICXUM13BIa8TYGZ68PiTKfQz3yaTNr4A== + dependencies: + acorn "^7.1.1" + object-assign "^4.1.1" + is-extglob@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" @@ -6244,13 +5832,6 @@ is-path-inside@^3.0.3: resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== -is-plain-object@^2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" - integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== - dependencies: - isobject "^3.0.1" - is-plain-object@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-5.0.0.tgz#4427f50ab3429e9025ea7d52e9043a9ef4159344" @@ -6261,7 +5842,7 @@ is-potential-custom-element-name@^1.0.1: resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5" integrity sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ== -is-promise@^2.2.2: +is-promise@^2.0.0, is-promise@^2.2.2: version "2.2.2" resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.2.2.tgz#39ab959ccbf9a774cf079f7b40c7a26f763135f1" integrity sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ== @@ -6271,12 +5852,15 @@ is-promise@^4.0.0: resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-4.0.0.tgz#42ff9f84206c1991d26debf520dd5c01042dd2f3" integrity sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ== -is-regex@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.4.tgz#5517489b547091b0930e095654ced25ee97e9491" - integrity sha1-VRdIm1RwkbCTDglWVM7SXul+lJE= +is-regex@^1.0.3: + version "1.2.1" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.2.1.tgz#76d70a3ed10ef9be48eb577887d74205bf0cad22" + integrity sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g== dependencies: - has "^1.0.1" + call-bound "^1.0.2" + gopd "^1.2.0" + has-tostringtag "^1.0.2" + hasown "^2.0.2" is-regex@^1.1.0: version "1.1.0" @@ -6405,6 +5989,13 @@ is-weakref@^1.0.2: dependencies: call-bind "^1.0.2" +is-wsl@^2.1.1: + version "2.2.0" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" + integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== + dependencies: + is-docker "^2.0.0" + isarray@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" @@ -6435,11 +6026,6 @@ iso-639-3@~2.2.0: resolved "https://registry.yarnpkg.com/iso-639-3/-/iso-639-3-2.2.0.tgz#eb01d7734d61396efec934979e8b0806550837f1" integrity sha512-v9w/U4XDSfXCrXxf4E6ertGC/lTRX8MLLv7XC1j6N5oL3ympe38jp77zgeyMsn3MbufuAAoGeVzDJbOXnPTMhQ== -isobject@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" - integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg== - isobject@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/isobject/-/isobject-4.0.0.tgz#3f1c9155e73b192022a80819bacd0343711697b0" @@ -6895,6 +6481,11 @@ jmespath@0.16.0: resolved "https://registry.yarnpkg.com/jmespath/-/jmespath-0.16.0.tgz#b15b0a85dfd4d930d43e69ed605943c802785076" integrity sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw== +js-stringify@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/js-stringify/-/js-stringify-1.0.2.tgz#1736fddfd9724f28a3682adc6230ae7e4e9679db" + integrity sha512-rtS5ATOo2Q5k1G+DADISilDA6lv79zIiwFd6CcjuIxGKLFm5C+RLImRscVap9k55i+MOZwgliw+NejvkLuGD5g== + js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" @@ -6952,16 +6543,11 @@ jsdom@~26.0.0: ws "^8.18.0" xml-name-validator "^5.0.0" -jsesc@^3.0.2, jsesc@~3.0.2: +jsesc@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-3.0.2.tgz#bb8b09a6597ba426425f2e4a07245c3d00b9343e" integrity sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g== -jsesc@~0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" - integrity sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0= - json-buffer@3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" @@ -7004,6 +6590,16 @@ json5@^2.2.2, json5@^2.2.3, json5@^2.x: resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.3.tgz#78cd6f1a19bdc12b73db5ad0c61efd66c1e29283" integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== +jsonc-eslint-parser@^2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/jsonc-eslint-parser/-/jsonc-eslint-parser-2.4.0.tgz#74ded53f9d716e8d0671bd167bf5391f452d5461" + integrity sha512-WYDyuc/uFcGp6YtM2H0uKmUwieOuzeE/5YocFJLnLfclZ4inf3mRn8ZVy1s7Hxji7Jxm6Ss8gqpexD/GlKoGgg== + dependencies: + acorn "^8.5.0" + eslint-visitor-keys "^3.0.0" + espree "^9.0.0" + semver "^7.3.5" + jsonwebtoken@^8.3.0, jsonwebtoken@~8.5.1: version "8.5.1" resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz#00e71e0b8df54c2121a1f26137df2280673bcc0d" @@ -7030,6 +6626,36 @@ jsprim@^1.2.2: json-schema "0.4.0" verror "1.10.0" +jstransformer@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/jstransformer/-/jstransformer-1.0.0.tgz#ed8bf0921e2f3f1ed4d5c1a44f68709ed24722c3" + integrity sha512-C9YK3Rf8q6VAPDCCU9fnqo3mAfOH6vUGnMcP4AQAYIEpWtfGLpwOTmZ+igtdK5y+VvI2n3CyYSzy4Qh34eq24A== + dependencies: + is-promise "^2.0.0" + promise "^7.0.1" + +juice@^10.0.0: + version "10.0.1" + resolved "https://registry.yarnpkg.com/juice/-/juice-10.0.1.tgz#a1492091ef739e4771b9f60aad1a608b5a8ea3ba" + integrity sha512-ZhJT1soxJCkOiO55/mz8yeBKTAJhRzX9WBO+16ZTqNTONnnVlUPyVBIzQ7lDRjaBdTbid+bAnyIon/GM3yp4cA== + dependencies: + cheerio "1.0.0-rc.12" + commander "^6.1.0" + mensch "^0.3.4" + slick "^1.12.2" + web-resource-inliner "^6.0.1" + +juice@^8.0.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/juice/-/juice-8.1.0.tgz#4ea23362522fe06418229943237ee3751a4fca70" + integrity sha512-FLzurJrx5Iv1e7CfBSZH68dC04EEvXvvVvPYB7Vx1WAuhCp1ZPIMtqxc+WTWxVkpTIC2Ach/GAv0rQbtGf6YMA== + dependencies: + cheerio "1.0.0-rc.10" + commander "^6.1.0" + mensch "^0.3.4" + slick "^1.12.2" + web-resource-inliner "^6.0.1" + jwa@^1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.4.1.tgz#743c32985cb9e98655530d53641b66c8645b039a" @@ -7069,6 +6695,11 @@ languagedetect@^2.0.0: resolved "https://registry.yarnpkg.com/languagedetect/-/languagedetect-2.0.0.tgz#4b8fa2b7593b2a3a02fb1100891041c53238936c" integrity sha512-AZb/liiQ+6ZoTj4f1J0aE6OkzhCo8fyH+tuSaPfSo8YHCWLFJrdSixhtO2TYdIkjcDQNaR4RmGaV2A5FJklDMQ== +leac@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/leac/-/leac-0.6.0.tgz#dcf136e382e666bd2475f44a1096061b70dc0912" + integrity sha512-y+SqErxb8h7nE/fiEX07jsbuhrpO9lL8eca7/Y1nuWV2moNlXhyd59iDGcRf6moVyDMbmTNzL40SUyrFU/yDpg== + leven@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" @@ -7082,6 +6713,26 @@ levn@^0.4.1: prelude-ls "^1.2.1" type-check "~0.4.0" +libbase64@1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/libbase64/-/libbase64-1.3.0.tgz#053314755a05d2e5f08bbfc48d0290e9322f4406" + integrity sha512-GgOXd0Eo6phYgh0DJtjQ2tO8dc0IVINtZJeARPeiIJqge+HdsWSuaDTe8ztQ7j/cONByDZ3zeB325AHiv5O0dg== + +libmime@5.3.6: + version "5.3.6" + resolved "https://registry.yarnpkg.com/libmime/-/libmime-5.3.6.tgz#e6dfc655b6b4614bad90e8e65817957903b56580" + integrity sha512-j9mBC7eiqi6fgBPAGvKCXJKJSIASanYF4EeA4iBzSG0HxQxmXnR3KbyWqTn4CwsKSebqCv2f5XZfAO6sKzgvwA== + dependencies: + encoding-japanese "2.2.0" + iconv-lite "0.6.3" + libbase64 "1.3.0" + libqp "2.1.1" + +libqp@2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/libqp/-/libqp-2.1.1.tgz#f1be767a58f966f500597997cab72cfc1e17abfa" + integrity sha512-0Wd+GPz1O134cP62YU2GTOPNA7Qgl09XwCqM5zpBv87ERCXdfDtyKXvV7c9U22yWJh44QZqBocFnXN11K96qow== + lines-and-columns@^1.1.6: version "1.2.4" resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" @@ -7092,19 +6743,18 @@ linkify-html@^4.2.0: resolved "https://registry.yarnpkg.com/linkify-html/-/linkify-html-4.2.0.tgz#06f78780827d90433424e412976d656912b13fb8" integrity sha512-bVXuLiWmGwvlH95hq6q9DFGqTsQeFSGw/nHmvvjGMZv9T3GqkxuW2d2SOgk/a4DV2ajeS4c37EqlF16cjOj7GA== +linkify-it@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/linkify-it/-/linkify-it-5.0.0.tgz#9ef238bfa6dc70bd8e7f9572b52d369af569b421" + integrity sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ== + dependencies: + uc.micro "^2.0.0" + linkifyjs@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/linkifyjs/-/linkifyjs-4.2.0.tgz#9dd30222b9cbabec9c950e725ec00031c7fa3f08" integrity sha512-pCj3PrQyATaoTYKHrgWRF3SJwsm61udVh+vuls/Rl6SptiDhgE7ziUIudAedRY9QEfynmM7/RmLEfPUyw1HPCw== -locate-path@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" - integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A== - dependencies: - p-locate "^3.0.0" - path-exists "^3.0.0" - locate-path@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" @@ -7124,11 +6774,6 @@ lodash-es@^4.17.11: resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.15.tgz#21bd96839354412f23d7a10340e5eac6ee455d78" integrity sha512-rlrc3yU3+JNOpZ9zj5pQtxnx2THmvRykwL4Xlxoa8I9lHBlVbbyPhgyPMioxVZ4NqyxaVVtaJnzsyOidQIhyyQ== -lodash.debounce@^4.0.8: - version "4.0.8" - resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" - integrity sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow== - lodash.defaults@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz#d09178716ffea4dde9e5fb7b37f6f0802274580c" @@ -7194,7 +6839,7 @@ lodash@4.17.15: resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== -lodash@^4.17.15, lodash@~4.17.11, lodash@~4.17.21: +lodash@^4.17.15, lodash@^4.17.21, lodash@~4.17.11, lodash@~4.17.21: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -7253,13 +6898,30 @@ lru_map@^0.3.3: resolved "https://registry.yarnpkg.com/lru_map/-/lru_map-0.3.3.tgz#b5c8351b9464cbd750335a79650a0ec0e56118dd" integrity sha1-tcg1G5Rky9dQM1p5ZQoOwOVhGN0= -make-dir@^2.0.0, make-dir@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5" - integrity sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA== +mailparser@^3.7.1: + version "3.7.2" + resolved "https://registry.yarnpkg.com/mailparser/-/mailparser-3.7.2.tgz#00feec656e23c0ae805163581b460c2f72ca75d1" + integrity sha512-iI0p2TCcIodR1qGiRoDBBwboSSff50vQAWytM5JRggLfABa4hHYCf3YVujtuzV454xrOP352VsAPIzviqMTo4Q== dependencies: - pify "^4.0.1" - semver "^5.6.0" + encoding-japanese "2.2.0" + he "1.2.0" + html-to-text "9.0.5" + iconv-lite "0.6.3" + libmime "5.3.6" + linkify-it "5.0.0" + mailsplit "5.4.2" + nodemailer "6.9.16" + punycode.js "2.3.1" + tlds "1.255.0" + +mailsplit@5.4.2: + version "5.4.2" + resolved "https://registry.yarnpkg.com/mailsplit/-/mailsplit-5.4.2.tgz#ee2be344bb3511345c0bd6ea72e5657acb8cd83b" + integrity sha512-4cczG/3Iu3pyl8JgQ76dKkisurZTmxMrA4dj/e8d2jKYcFTZ7MxOzg1gTioTDMPuFXwTrVuN/gxhkrO7wLg7qA== + dependencies: + libbase64 "1.3.0" + libmime "5.3.6" + libqp "2.1.1" make-dir@^3.0.0: version "3.1.0" @@ -7291,6 +6953,11 @@ make-fetch-happen@^13.0.0: promise-retry "^2.0.1" ssri "^10.0.0" +make-plural@^7.0.0: + version "7.4.0" + resolved "https://registry.yarnpkg.com/make-plural/-/make-plural-7.4.0.tgz#fa6990dd550dea4de6b20163f74e5ed83d8a8d6d" + integrity sha512-4/gC9KVNTV6pvYg2gFeQYTW3mWaoJt7WZE5vrp1KnQDgW92JtYZnzmZT81oj/dUTqAIu0ufI2x3dkgu3bB1tYg== + makeerror@1.0.12: version "1.0.12" resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.12.tgz#3e5dd2079a82e812e983cc6610c4a2cb0eaa801a" @@ -7298,6 +6965,11 @@ makeerror@1.0.12: dependencies: tmpl "1.0.5" +math-interval-parser@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/math-interval-parser/-/math-interval-parser-2.0.1.tgz#e22cd6d15a0a7f4c03aec560db76513da615bed4" + integrity sha512-VmlAmb0UJwlvMyx8iPhXUDnVW1F9IrGEd9CIOmv+XL8AErCUUuozoDMrgImvnYt2A+53qVX/tPW6YJurMKYsvA== + math-intrinsics@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz#a0dd74be81e2aa5c2f27e65ce283605ee4e2b7f9" @@ -7332,6 +7004,11 @@ memoizee@0.4: next-tick "^1.1.0" timers-ext "^0.1.7" +mensch@^0.3.4: + version "0.3.4" + resolved "https://registry.yarnpkg.com/mensch/-/mensch-0.3.4.tgz#770f91b46cb16ea5b204ee735768c3f0c491fecd" + integrity sha512-IAeFvcOnV9V0Yk+bFhYR07O3yNina9ANIN5MoXBKYJ/RLYPurd2d0yw14MDhpr9/momp0WofT1bPUh3hkzdi/g== + merge-descriptors@1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.3.tgz#d80319a65f3c7935351e5cfdac8f9318504dbed5" @@ -7542,6 +7219,11 @@ mime@3: resolved "https://registry.yarnpkg.com/mime/-/mime-3.0.0.tgz#b374550dca3a0c18443b0c950a6a58f1931cf7a7" integrity sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A== +mime@^2.4.6: + version "2.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-2.6.0.tgz#a2a682a95cd4d0cb1d6257e28f83da7e35800367" + integrity sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg== + mimic-fn@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" @@ -7669,6 +7351,11 @@ moment@2.21.0: resolved "https://registry.yarnpkg.com/moment/-/moment-2.21.0.tgz#2a114b51d2a6ec9e6d83cf803f838a878d8a023a" integrity sha512-TCZ36BjURTeFTM/CwRcViQlfkMvL1/vFISuNLO5GkcVm1+QHfbSiNqZuWeMFjj1/3+uAjXswgRk30j1kkLYJBQ== +moo@^0.5.1: + version "0.5.2" + resolved "https://registry.yarnpkg.com/moo/-/moo-0.5.2.tgz#f9fe82473bc7c184b0d32e2215d3f6e67278733c" + integrity sha512-iSAJLHYKnX41mKcJKjqvnAN9sf0LMDTXDEvFv+ffuRR9a1MIuXLjMNL6EsnDHSkKLTWNqQQ5uo61P4EbU4NU+Q== + ms@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" @@ -7679,6 +7366,17 @@ ms@2.1.3, ms@^2.1.1, ms@^2.1.3: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== +multimatch@5: + version "5.0.0" + resolved "https://registry.yarnpkg.com/multimatch/-/multimatch-5.0.0.tgz#932b800963cea7a31a033328fa1e0c3a1874dbe6" + integrity sha512-ypMKuglUrZUD99Tk2bUQ+xNQj43lPEfAeX2o9cTteAmShXy2VHDJpuwu1o0xqoKCt9jLVAvwyFKdLTPXKAfJyA== + dependencies: + "@types/minimatch" "^3.0.3" + array-differ "^3.0.0" + array-union "^2.1.0" + arrify "^2.0.1" + minimatch "^3.0.4" + mustache@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/mustache/-/mustache-4.2.0.tgz#e5892324d60a12ec9c2a73359edca52972bf6f64" @@ -7805,6 +7503,11 @@ next-tick@^1.1.0: resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.1.0.tgz#1836ee30ad56d67ef281b22bd199f709449b35eb" integrity sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ== +nice-try@^1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" + integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== + no-case@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/no-case/-/no-case-3.0.3.tgz#c21b434c1ffe48b39087e86cfb4d2582e9df18f8" @@ -7813,14 +7516,6 @@ no-case@^3.0.3: lower-case "^2.0.1" tslib "^1.10.0" -node-environment-flags@^1.0.5: - version "1.0.6" - resolved "https://registry.yarnpkg.com/node-environment-flags/-/node-environment-flags-1.0.6.tgz#a30ac13621f6f7d674260a54dede048c3982c088" - integrity sha512-5Evy2epuL+6TM0lCQGpFIj6KwiEsGh1SrHUhTbNX+sLbBtjidPZFAnVK9y5yU1+h//RitLbRHTIMyxQPtxMdHw== - dependencies: - object.getownpropertydescriptors "^2.0.3" - semver "^5.7.0" - node-fetch@^2.1.2, node-fetch@^2.2.0, node-fetch@^2.6.0, node-fetch@^2.7.0: version "2.7.0" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d" @@ -7854,11 +7549,6 @@ node-releases@^2.0.18: resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.18.tgz#f010e8d35e2fe8d6b2944f03f70213ecedc4ca3f" integrity sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g== -node-releases@^2.0.19: - version "2.0.19" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.19.tgz#9e445a52950951ec4d177d843af370b411caf314" - integrity sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw== - nodemailer-html-to-text@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/nodemailer-html-to-text/-/nodemailer-html-to-text-3.2.0.tgz#91b959491fef8f7d91796047abb728aa86d4a12b" @@ -7866,7 +7556,12 @@ nodemailer-html-to-text@^3.2.0: dependencies: html-to-text "7.1.1" -nodemailer@^6.10.1: +nodemailer@6.9.16: + version "6.9.16" + resolved "https://registry.yarnpkg.com/nodemailer/-/nodemailer-6.9.16.tgz#3ebdf6c6f477c571c0facb0727b33892635e0b8b" + integrity sha512-psAuZdTIRN08HKVd/E8ObdV6NO7NTBY3KsC30F7M4H1OnmLCUNaS56FpYxyb26zWLSyYF9Ozch9KYHhHegsiOQ== + +nodemailer@^6.10.1, nodemailer@^6.9.13, nodemailer@^6.9.14: version "6.10.1" resolved "https://registry.yarnpkg.com/nodemailer/-/nodemailer-6.10.1.tgz#cbc434c54238f83a51c07eabd04e2b3e832da623" integrity sha512-Z+iLaBGVaSjbIzQ4pX6XV41HrooLsQ10ZWPUehGmuantvzWoDVBnmsdUcOIDM1t+yPor5pDhVlDESgOMEGxhHA== @@ -8034,14 +7729,6 @@ object.fromentries@^2.0.8: es-abstract "^1.23.2" es-object-atoms "^1.0.0" -object.getownpropertydescriptors@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz#8758c846f5b407adab0f236e0986f14b051caa16" - integrity sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY= - dependencies: - define-properties "^1.1.2" - es-abstract "^1.5.1" - object.getownpropertydescriptors@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.0.tgz#369bf1f9592d8ab89d712dced5cb81c7c5352649" @@ -8089,12 +7776,13 @@ onetime@^5.1.2: dependencies: mimic-fn "^2.1.0" -optimism@^0.10.0: - version "0.10.2" - resolved "https://registry.yarnpkg.com/optimism/-/optimism-0.10.2.tgz#626b6fd28b0923de98ecb36a3fd2d3d4e5632dd9" - integrity sha512-zPfBIxFFWMmQboM9+Z4MSJqc1PXp82v1PFq/GfQaufI69mHKlup7ykGNnfuGIGssXJQkmhSodQ/k9EWwjd8O8A== +open@7: + version "7.4.2" + resolved "https://registry.yarnpkg.com/open/-/open-7.4.2.tgz#b8147e26dcf3e426316c730089fd71edd29c2321" + integrity sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q== dependencies: - "@wry/context" "^0.4.0" + is-docker "^2.0.0" + is-wsl "^2.1.1" optionator@^0.9.3: version "0.9.3" @@ -8113,18 +7801,18 @@ p-cancelable@^2.0.0: resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-2.0.0.tgz#4a3740f5bdaf5ed5d7c3e34882c6fb5d6b266a6e" integrity sha512-wvPXDmbMmu2ksjkB4Z3nZWTSkJEb9lqVdMaCKpZUGJG9TMiNp9XcbG3fn9fPKjem04fJMJnXoyFPk2FmgiaiNg== +p-event@4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/p-event/-/p-event-4.2.0.tgz#af4b049c8acd91ae81083ebd1e6f5cae2044c1b5" + integrity sha512-KXatOjCRXXkSePPb1Nbi0p0m+gQAwdlbhi4wQKJPI1HsMQS9g+Sqp2o+QHziPr7eYJyOZet836KoHEVM1mwOrQ== + dependencies: + p-timeout "^3.1.0" + p-finally@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" integrity sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow== -p-limit@^2.0.0: - version "2.2.1" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.2.1.tgz#aa07a788cc3151c939b5131f63570f0dd2009537" - integrity sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg== - dependencies: - p-try "^2.0.0" - p-limit@^2.2.0: version "2.3.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" @@ -8139,13 +7827,6 @@ p-limit@^3.0.2, p-limit@^3.1.0: dependencies: yocto-queue "^0.1.0" -p-locate@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" - integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ== - dependencies: - p-limit "^2.0.0" - p-locate@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" @@ -8172,11 +7853,25 @@ p-reflect@~2.1.0: resolved "https://registry.yarnpkg.com/p-reflect/-/p-reflect-2.1.0.tgz#5d67c7b3c577c4e780b9451fc9129675bd99fe67" integrity sha512-paHV8NUz8zDHu5lhr/ngGWQiW067DK/+IbJ+RfZ4k+s8y4EKyYCz8pGYWjxCg35eHztpJAt+NUgvN4L+GCbPlg== +p-timeout@^3.0.0, p-timeout@^3.1.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-3.2.0.tgz#c7e17abc971d2a7962ef83626b35d635acf23dfe" + integrity sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg== + dependencies: + p-finally "^1.0.0" + p-try@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== +p-wait-for@3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/p-wait-for/-/p-wait-for-3.2.0.tgz#640429bcabf3b0dd9f492c31539c5718cb6a3f1f" + integrity sha512-wpgERjNkLrBiFmkMEjuZJEWKKDrNfHCKA1OhyN1wg1FrLkULbviEy6py1AyJUgZ72YWFbZ38FIpnqvVqAlDUwA== + dependencies: + p-timeout "^3.0.0" + package-json-from-dist@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz#4f1471a010827a86f94cfd9b0727e36d267de505" @@ -8204,11 +7899,6 @@ parse-ms@^2.1.0: resolved "https://registry.yarnpkg.com/parse-ms/-/parse-ms-2.1.0.tgz#348565a753d4391fa524029956b172cb7753097d" integrity sha512-kHt7kzLoS9VBZfUsiKjv43mr91ea+U05EyKkEtqp7vNbHxmaVuEqN7XxeEVnGrMtYOAxGrDElSi96K7EgO1zCA== -parse-passwd@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/parse-passwd/-/parse-passwd-1.0.0.tgz#6d5b934a456993b23d37f40a382d6f1666a8e5c6" - integrity sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY= - parse-srcset@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/parse-srcset/-/parse-srcset-1.0.2.tgz#f2bd221f6cc970a938d88556abc589caaaa2bde1" @@ -8224,6 +7914,13 @@ parse-uri@~1.0.3: resolved "https://registry.yarnpkg.com/parse-uri/-/parse-uri-1.0.7.tgz#287629a09328a97e398468f21b8a00c4a2d9cc73" integrity sha512-eWuZCMKNlVkXrEoANdXxbmqhu2SQO9jUMCSpdbJDObin0JxISn6e400EWsSRbr/czdKvWKkhZnMKEGUwf/Plmg== +parse5-htmlparser2-tree-adapter@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz#2cdf9ad823321140370d4dbf5d3e92c7c8ddc6e6" + integrity sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA== + dependencies: + parse5 "^6.0.1" + parse5-htmlparser2-tree-adapter@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.0.0.tgz#23c2cc233bcf09bb7beba8b8a69d46b08c62c2f1" @@ -8239,6 +7936,11 @@ parse5-parser-stream@^7.1.2: dependencies: parse5 "^7.0.0" +parse5@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" + integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== + parse5@^7.0.0, parse5@^7.1.2: version "7.1.2" resolved "https://registry.yarnpkg.com/parse5/-/parse5-7.1.2.tgz#0736bebbfd77793823240a23b7fc5e010b7f8e32" @@ -8253,6 +7955,14 @@ parse5@^7.2.1: dependencies: entities "^4.5.0" +parseley@^0.12.0: + version "0.12.1" + resolved "https://registry.yarnpkg.com/parseley/-/parseley-0.12.1.tgz#4afd561d50215ebe259e3e7a853e62f600683aef" + integrity sha512-e6qHKe3a9HWr0oMRVDTRhKce+bRO8VGQR3NyVwcjwrbhMmFCX9KszEV35+rn4AdilFAq9VPxP/Fe1wC9Qjd2lw== + dependencies: + leac "^0.6.0" + peberminta "^0.9.0" + parseurl@^1.3.2, parseurl@^1.3.3, parseurl@~1.3.3: version "1.3.3" resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" @@ -8266,11 +7976,6 @@ pascal-case@^3.1.1: no-case "^3.0.3" tslib "^1.10.0" -path-exists@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" - integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU= - path-exists@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" @@ -8281,7 +7986,7 @@ path-is-absolute@^1.0.0: resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== -path-key@^2.0.0: +path-key@^2.0.0, path-key@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" integrity sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw== @@ -8319,6 +8024,11 @@ path-type@^4.0.0: resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== +peberminta@^0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/peberminta/-/peberminta-0.9.0.tgz#8ec9bc0eb84b7d368126e71ce9033501dca2a352" + integrity sha512-XIxfHpEuSJbITd1H3EeQwpcZbTLHc+VVr8ANI9t5sit565tsI4/xK3KWTUFE2e6QiangUkh3B0jihzmGnNrRsQ== + performance-now@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" @@ -8334,11 +8044,6 @@ picocolors@^1.0.1: resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.0.tgz#5358b76a78cde483ba5cef6a9dc9671440b27d59" integrity sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw== -picocolors@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b" - integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== - picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3, picomatch@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" @@ -8359,18 +8064,6 @@ pirates@^4.0.4: resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.5.tgz#feec352ea5c3268fb23a37c702ab1699f35a5f3b" integrity sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ== -pirates@^4.0.6: - version "4.0.6" - resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.6.tgz#3018ae32ecfcff6c29ba2267cbf21166ac1f36b9" - integrity sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg== - -pkg-dir@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-3.0.0.tgz#2749020f239ed990881b1f71210d51eb6523bea3" - integrity sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw== - dependencies: - find-up "^3.0.0" - pkg-dir@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" @@ -8432,6 +8125,23 @@ pretty-ms@~7.0.1: dependencies: parse-ms "^2.1.0" +preview-email@^3.0.17, preview-email@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/preview-email/-/preview-email-3.1.0.tgz#ee8525d878afef4309ae548116e4a4fe8b119a6d" + integrity sha512-ZtV1YrwscEjlrUzYrTSs6Nwo49JM3pXLM4fFOBSC3wSni+bxaWlw9/Qgk75PZO8M7cX2EybmL2iwvaV3vkAttw== + dependencies: + ci-info "^3.8.0" + display-notification "2.0.0" + fixpack "^4.0.0" + get-port "5.1.1" + mailparser "^3.7.1" + nodemailer "^6.9.13" + open "7" + p-event "4.2.0" + p-wait-for "3.2.0" + pug "^3.0.3" + uuid "^9.0.1" + proc-log@^4.1.0, proc-log@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/proc-log/-/proc-log-4.2.0.tgz#b6f461e4026e75fdfe228b265e9f7a00779d7034" @@ -8445,6 +8155,13 @@ promise-retry@^2.0.1: err-code "^2.0.2" retry "^0.12.0" +promise@^7.0.1: + version "7.3.1" + resolved "https://registry.yarnpkg.com/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf" + integrity sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg== + dependencies: + asap "~2.0.3" + prompts@^2.0.1: version "2.4.2" resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.2.tgz#7b57e73b3a48029ad10ebd44f74b01722a4cb069" @@ -8481,6 +8198,109 @@ pstree.remy@^1.1.8: resolved "https://registry.yarnpkg.com/pstree.remy/-/pstree.remy-1.1.8.tgz#c242224f4a67c21f686839bbdb4ac282b8373d3a" integrity sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w== +pug-attrs@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pug-attrs/-/pug-attrs-3.0.0.tgz#b10451e0348165e31fad1cc23ebddd9dc7347c41" + integrity sha512-azINV9dUtzPMFQktvTXciNAfAuVh/L/JCl0vtPCwvOA21uZrC08K/UnmrL+SXGEVc1FwzjW62+xw5S/uaLj6cA== + dependencies: + constantinople "^4.0.1" + js-stringify "^1.0.2" + pug-runtime "^3.0.0" + +pug-code-gen@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/pug-code-gen/-/pug-code-gen-3.0.3.tgz#58133178cb423fe1716aece1c1da392a75251520" + integrity sha512-cYQg0JW0w32Ux+XTeZnBEeuWrAY7/HNE6TWnhiHGnnRYlCgyAUPoyh9KzCMa9WhcJlJ1AtQqpEYHc+vbCzA+Aw== + dependencies: + constantinople "^4.0.1" + doctypes "^1.1.0" + js-stringify "^1.0.2" + pug-attrs "^3.0.0" + pug-error "^2.1.0" + pug-runtime "^3.0.1" + void-elements "^3.1.0" + with "^7.0.0" + +pug-error@^2.0.0, pug-error@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/pug-error/-/pug-error-2.1.0.tgz#17ea37b587b6443d4b8f148374ec27b54b406e55" + integrity sha512-lv7sU9e5Jk8IeUheHata6/UThZ7RK2jnaaNztxfPYUY+VxZyk/ePVaNZ/vwmH8WqGvDz3LrNYt/+gA55NDg6Pg== + +pug-filters@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/pug-filters/-/pug-filters-4.0.0.tgz#d3e49af5ba8472e9b7a66d980e707ce9d2cc9b5e" + integrity sha512-yeNFtq5Yxmfz0f9z2rMXGw/8/4i1cCFecw/Q7+D0V2DdtII5UvqE12VaZ2AY7ri6o5RNXiweGH79OCq+2RQU4A== + dependencies: + constantinople "^4.0.1" + jstransformer "1.0.0" + pug-error "^2.0.0" + pug-walk "^2.0.0" + resolve "^1.15.1" + +pug-lexer@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/pug-lexer/-/pug-lexer-5.0.1.tgz#ae44628c5bef9b190b665683b288ca9024b8b0d5" + integrity sha512-0I6C62+keXlZPZkOJeVam9aBLVP2EnbeDw3An+k0/QlqdwH6rv8284nko14Na7c0TtqtogfWXcRoFE4O4Ff20w== + dependencies: + character-parser "^2.2.0" + is-expression "^4.0.0" + pug-error "^2.0.0" + +pug-linker@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/pug-linker/-/pug-linker-4.0.0.tgz#12cbc0594fc5a3e06b9fc59e6f93c146962a7708" + integrity sha512-gjD1yzp0yxbQqnzBAdlhbgoJL5qIFJw78juN1NpTLt/mfPJ5VgC4BvkoD3G23qKzJtIIXBbcCt6FioLSFLOHdw== + dependencies: + pug-error "^2.0.0" + pug-walk "^2.0.0" + +pug-load@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pug-load/-/pug-load-3.0.0.tgz#9fd9cda52202b08adb11d25681fb9f34bd41b662" + integrity sha512-OCjTEnhLWZBvS4zni/WUMjH2YSUosnsmjGBB1An7CsKQarYSWQ0GCVyd4eQPMFJqZ8w9xgs01QdiZXKVjk92EQ== + dependencies: + object-assign "^4.1.1" + pug-walk "^2.0.0" + +pug-parser@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/pug-parser/-/pug-parser-6.0.0.tgz#a8fdc035863a95b2c1dc5ebf4ecf80b4e76a1260" + integrity sha512-ukiYM/9cH6Cml+AOl5kETtM9NR3WulyVP2y4HOU45DyMim1IeP/OOiyEWRr6qk5I5klpsBnbuHpwKmTx6WURnw== + dependencies: + pug-error "^2.0.0" + token-stream "1.0.0" + +pug-runtime@^3.0.0, pug-runtime@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/pug-runtime/-/pug-runtime-3.0.1.tgz#f636976204723f35a8c5f6fad6acda2a191b83d7" + integrity sha512-L50zbvrQ35TkpHwv0G6aLSuueDRwc/97XdY8kL3tOT0FmhgG7UypU3VztfV/LATAvmUfYi4wNxSajhSAeNN+Kg== + +pug-strip-comments@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/pug-strip-comments/-/pug-strip-comments-2.0.0.tgz#f94b07fd6b495523330f490a7f554b4ff876303e" + integrity sha512-zo8DsDpH7eTkPHCXFeAk1xZXJbyoTfdPlNR0bK7rpOMuhBYb0f5qUVCO1xlsitYd3w5FQTK7zpNVKb3rZoUrrQ== + dependencies: + pug-error "^2.0.0" + +pug-walk@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/pug-walk/-/pug-walk-2.0.0.tgz#417aabc29232bb4499b5b5069a2b2d2a24d5f5fe" + integrity sha512-yYELe9Q5q9IQhuvqsZNwA5hfPkMJ8u92bQLIMcsMxf/VADjNtEYptU+inlufAFYcWdHlwNfZOEnOOQrZrcyJCQ== + +pug@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/pug/-/pug-3.0.3.tgz#e18324a314cd022883b1e0372b8af3a1a99f7597" + integrity sha512-uBi6kmc9f3SZ3PXxqcHiUZLmIXgfgWooKWXcwSGwQd2Zi5Rb0bT14+8CJjJgI8AB+nndLaNgHGrcc6bPIB665g== + dependencies: + pug-code-gen "^3.0.3" + pug-filters "^4.0.0" + pug-lexer "^5.0.1" + pug-linker "^4.0.0" + pug-load "^3.0.0" + pug-parser "^6.0.0" + pug-runtime "^3.0.1" + pug-strip-comments "^2.0.0" + pump@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" @@ -8489,6 +8309,11 @@ pump@^3.0.0: end-of-stream "^1.1.0" once "^1.3.1" +punycode.js@2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/punycode.js/-/punycode.js-2.3.1.tgz#6b53e56ad75588234e79f4affa90972c7dd8cdb7" + integrity sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA== + punycode2@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/punycode2/-/punycode2-1.0.0.tgz#e2b4b9a9a8ff157d0b84438e203181ee7892dfd8" @@ -8521,7 +8346,7 @@ qs@6.13.0: dependencies: side-channel "^1.0.6" -qs@^6.14.0: +qs@^6.11.0, qs@^6.14.0: version "6.14.0" resolved "https://registry.yarnpkg.com/qs/-/qs-6.14.0.tgz#c63fa40680d2c5c941412a0e899c89af60c0a930" integrity sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w== @@ -8573,6 +8398,16 @@ raw-body@^3.0.0: iconv-lite "0.6.3" unpipe "1.0.0" +rc@^1.2.8: + version "1.2.8" + resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed" + integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== + dependencies: + deep-extend "^0.6.0" + ini "~1.3.0" + minimist "^1.2.0" + strip-json-comments "~2.0.1" + re2@~1.21.4: version "1.21.4" resolved "https://registry.yarnpkg.com/re2/-/re2-1.21.4.tgz#d688edcc40da3cf542ee3a480a8b60e5900dd24d" @@ -8614,30 +8449,6 @@ redis-parser@^3.0.0: dependencies: redis-errors "^1.0.0" -regenerate-unicode-properties@^10.1.0: - version "10.1.0" - resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.0.tgz#7c3192cab6dd24e21cb4461e5ddd7dd24fa8374c" - integrity sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ== - dependencies: - regenerate "^1.4.2" - -regenerate-unicode-properties@^10.2.0: - version "10.2.0" - resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.0.tgz#626e39df8c372338ea9b8028d1f99dc3fd9c3db0" - integrity sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA== - dependencies: - regenerate "^1.4.2" - -regenerate@^1.4.2: - version "1.4.2" - resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a" - integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A== - -regenerator-runtime@^0.11.0: - version "0.11.1" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9" - integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg== - regenerator-runtime@^0.13.2, regenerator-runtime@^0.13.4: version "0.13.4" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.4.tgz#e96bf612a3362d12bb69f7e8f74ffeab25c7ac91" @@ -8648,13 +8459,6 @@ regenerator-runtime@^0.14.0: resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz#356ade10263f685dda125100cd862c1db895327f" integrity sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw== -regenerator-transform@^0.15.2: - version "0.15.2" - resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.15.2.tgz#5bbae58b522098ebdf09bca2f83838929001c7a4" - integrity sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg== - dependencies: - "@babel/runtime" "^7.8.4" - regexp-tree@~0.1.1: version "0.1.27" resolved "https://registry.yarnpkg.com/regexp-tree/-/regexp-tree-0.1.27.tgz#2198f0ef54518ffa743fe74d983b56ffd631b6cd" @@ -8679,49 +8483,6 @@ regexp.prototype.flags@^1.5.2: es-errors "^1.3.0" set-function-name "^2.0.1" -regexpu-core@^5.3.1: - version "5.3.2" - resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-5.3.2.tgz#11a2b06884f3527aec3e93dbbf4a3b958a95546b" - integrity sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ== - dependencies: - "@babel/regjsgen" "^0.8.0" - regenerate "^1.4.2" - regenerate-unicode-properties "^10.1.0" - regjsparser "^0.9.1" - unicode-match-property-ecmascript "^2.0.0" - unicode-match-property-value-ecmascript "^2.1.0" - -regexpu-core@^6.1.1: - version "6.1.1" - resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-6.1.1.tgz#b469b245594cb2d088ceebc6369dceb8c00becac" - integrity sha512-k67Nb9jvwJcJmVpw0jPttR1/zVfnKf8Km0IPatrU/zJ5XeG3+Slx0xLXs9HByJSzXzrlz5EDvN6yLNMDc2qdnw== - dependencies: - regenerate "^1.4.2" - regenerate-unicode-properties "^10.2.0" - regjsgen "^0.8.0" - regjsparser "^0.11.0" - unicode-match-property-ecmascript "^2.0.0" - unicode-match-property-value-ecmascript "^2.1.0" - -regjsgen@^0.8.0: - version "0.8.0" - resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.8.0.tgz#df23ff26e0c5b300a6470cad160a9d090c3a37ab" - integrity sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q== - -regjsparser@^0.11.0: - version "0.11.1" - resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.11.1.tgz#ae55c74f646db0c8fcb922d4da635e33da405149" - integrity sha512-1DHODs4B8p/mQHU9kr+jv8+wIC9mtG4eBHxWxIq5mhjE3D5oORhCc6deRKzTjs9DcfRFmj9BHSDguZklqCGFWQ== - dependencies: - jsesc "~3.0.2" - -regjsparser@^0.9.1: - version "0.9.1" - resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.9.1.tgz#272d05aa10c7c1f67095b1ff0addae8442fc5709" - integrity sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ== - dependencies: - jsesc "~0.5.0" - remove-trailing-separator@^1.0.1: version "1.1.0" resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" @@ -8797,7 +8558,16 @@ resolve.exports@^2.0.0: resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-2.0.2.tgz#f8c934b8e6a13f539e38b7098e2e36134f01e800" integrity sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg== -resolve@^1.12.0, resolve@^1.14.2, resolve@^1.20.0, resolve@^1.22.4: +resolve@^1.15.1: + version "1.22.10" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.10.tgz#b663e83ffb09bbf2386944736baae803029b8b39" + integrity sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w== + dependencies: + is-core-module "^2.16.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + +resolve@^1.20.0, resolve@^1.22.4: version "1.22.8" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d" integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== @@ -8851,6 +8621,13 @@ rrweb-cssom@^0.8.0: resolved "https://registry.yarnpkg.com/rrweb-cssom/-/rrweb-cssom-0.8.0.tgz#3021d1b4352fbf3b614aaeed0bc0d5739abe0bc2" integrity sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw== +run-applescript@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/run-applescript/-/run-applescript-3.2.0.tgz#73fb34ce85d3de8076d511ea767c30d4fdfc918b" + integrity sha512-Ep0RsvAjnRcBX1p5vogbaBdAGu/8j/ewpvGqnQYunnLd9SM0vWcPJewPKNnWFggf0hF0pwIgwV5XK7qQ7UZ8Qg== + dependencies: + execa "^0.10.0" + run-parallel@^1.1.9: version "1.1.9" resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.1.9.tgz#c9dd3a7cf9f4b2c4b6244e173a6ed866e61dd679" @@ -8888,6 +8665,11 @@ safe-buffer@5.2.1, safe-buffer@^5.0.1, safe-buffer@^5.1.2, safe-buffer@~5.2.0: resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== +safe-identifier@^0.4.1: + version "0.4.2" + resolved "https://registry.yarnpkg.com/safe-identifier/-/safe-identifier-0.4.2.tgz#cf6bfca31c2897c588092d1750d30ef501d59fcb" + integrity sha512-6pNbSMW6OhAi9j+N8V+U715yBQsaWJ7eyEUaOrawX+isg5ZxhUlV1NipNtgaKHmFGiABwt+ZF04Ii+3Xjkg+8w== + safe-regex-test@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.0.0.tgz#793b874d524eb3640d1873aad03596db2d4f2295" @@ -8947,7 +8729,19 @@ saxes@^6.0.0: dependencies: xmlchars "^2.2.0" -semver@^5.6.0, semver@^5.7.0: +selderee@^0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/selderee/-/selderee-0.11.0.tgz#6af0c7983e073ad3e35787ffe20cefd9daf0ec8a" + integrity sha512-5TF+l7p4+OsnP8BCCvSyZiSPc4x4//p5uPwK8TCnVPJYRmU2aYKMpOXvw8zM5a5JvuuCGN1jmsMwuU2W02ukfA== + dependencies: + parseley "^0.12.0" + +semver@^5.5.0: + version "5.7.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" + integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== + +semver@^5.6.0: version "5.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== @@ -9072,13 +8866,6 @@ sha.js@^2.4.11: inherits "^2.0.1" safe-buffer "^5.0.1" -shallow-clone@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3" - integrity sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA== - dependencies: - kind-of "^6.0.2" - shebang-command@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" @@ -9184,16 +8971,16 @@ sisteransi@^1.0.5: resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg== -slash@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/slash/-/slash-2.0.0.tgz#de552851a1759df3a8f206535442f5ec4ddeab44" - integrity sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A== - slash@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== +slick@^1.12.2: + version "1.12.2" + resolved "https://registry.yarnpkg.com/slick/-/slick-1.12.2.tgz#bd048ddb74de7d1ca6915faa4a57570b3550c2d7" + integrity sha512-4qdtOGcBjral6YIBCWJ0ljFSKNLz9KkhbWtuGvUyRowl1kxfuE1x/Z/aJcaiilpb3do9bl5K7/1h9XC5wWpY/A== + slug@^8.2.2: version "8.2.2" resolved "https://registry.yarnpkg.com/slug/-/slug-8.2.2.tgz#33b019a857a11fc4773c1e9a9f60e3da651a9e5d" @@ -9249,14 +9036,6 @@ source-map-support@0.5.13: buffer-from "^1.0.0" source-map "^0.6.0" -source-map-support@^0.5.16: - version "0.5.16" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.16.tgz#0ae069e7fe3ba7538c64c98515e35339eac5a042" - integrity sha512-efyLRJDr68D9hBBNIPWFjhpFzURh+KJykQwvMyW5UiZzYwoF6l4YMMDIJJEyFWxWCqfyxLzz6tSfUFR+kXXsVQ== - dependencies: - buffer-from "^1.0.0" - source-map "^0.6.0" - source-map@^0.6.0, source-map@^0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" @@ -9343,23 +9122,14 @@ string-length@^4.0.1: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.1" -string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== +string-width@4.2.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3, string-width@^5.1.2: + version "4.2.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.0.tgz#952182c46cc7b2c313d1596e623992bd163b72b5" + integrity sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg== dependencies: emoji-regex "^8.0.0" is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - -string-width@^5.0.1, string-width@^5.1.2: - version "5.1.2" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" - integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== - dependencies: - eastasianwidth "^0.2.0" - emoji-regex "^9.2.2" - strip-ansi "^7.0.1" + strip-ansi "^6.0.0" string.prototype.trim@^1.2.8: version "1.2.8" @@ -9429,20 +9199,13 @@ string_decoder@^1.3.0: dependencies: ansi-regex "^5.0.1" -strip-ansi@^6.0.0, strip-ansi@^6.0.1: +strip-ansi@6.0.1, strip-ansi@^6.0.0, strip-ansi@^6.0.1, strip-ansi@^7.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== dependencies: ansi-regex "^5.0.1" -strip-ansi@^7.0.1: - version "7.1.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" - integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ== - dependencies: - ansi-regex "^6.0.1" - strip-bom@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" @@ -9468,7 +9231,12 @@ strip-json-comments@^3.1.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== -subscriptions-transport-ws@^0.9.11, subscriptions-transport-ws@^0.9.16, subscriptions-transport-ws@^0.9.19: +strip-json-comments@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + integrity sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ== + +subscriptions-transport-ws@^0.9.11, subscriptions-transport-ws@^0.9.16: version "0.9.19" resolved "https://registry.yarnpkg.com/subscriptions-transport-ws/-/subscriptions-transport-ws-0.9.19.tgz#10ca32f7e291d5ee8eb728b9c02e43c52606cdcf" integrity sha512-dxdemxFFB0ppCLg10FTtRqH/31FNRL1y1BQv8209MK5I4CwALb7iihQg+7p65lFcIl8MHatINWBLOqpgU4Kyyw== @@ -9512,7 +9280,7 @@ supports-preserve-symlinks-flag@^1.0.0: resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== -symbol-observable@^1.0.2, symbol-observable@^1.0.4: +symbol-observable@^1.0.4: version "1.2.0" resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804" integrity sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ== @@ -9535,6 +9303,14 @@ synckit@^0.11.0: "@pkgr/core" "^0.2.0" tslib "^2.8.1" +"synckit@^0.6.2 || ^0.7.3 || ^0.10.3": + version "0.10.3" + resolved "https://registry.yarnpkg.com/synckit/-/synckit-0.10.3.tgz#940aea2c7b6d141a4f74dbdebc81e0958c331a4b" + integrity sha512-R1urvuyiTaWfeCggqEvpDJwAlDVdsT9NM+IP//Tk2x7qHCkSvBk/fwFgw/TLAHzZlrAnnazMcRw0ZD8HlYFTEQ== + dependencies: + "@pkgr/core" "^0.2.0" + tslib "^2.8.1" + tapable@^2.2.0: version "2.2.1" resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0" @@ -9609,15 +9385,25 @@ titleize@1.0.0: resolved "https://registry.yarnpkg.com/titleize/-/titleize-1.0.0.tgz#7d350722061830ba6617631e0cfd3ea08398d95a" integrity sha1-fTUHIgYYMLpmF2MeDP0+oIOY2Vo= +titleize@2: + version "2.1.0" + resolved "https://registry.yarnpkg.com/titleize/-/titleize-2.1.0.tgz#5530de07c22147a0488887172b5bd94f5b30a48f" + integrity sha512-m+apkYlfiQTKLW+sI4vqUkwMEzfgEUEYSqljx1voUE3Wz/z1ZsxyzSxvH2X8uKVrOp7QkByWt0rA6+gvhCKy6g== + +tlds@1.255.0, tlds@^1.242.0: + version "1.255.0" + resolved "https://registry.yarnpkg.com/tlds/-/tlds-1.255.0.tgz#53c2571766c10da95928c716c48dfcf141341e3f" + integrity sha512-tcwMRIioTcF/FcxLev8MJWxCp+GUALRhFEqbDoZrnowmKSGqPrl5pqS+Sut2m8BgJ6S4FExCSSpGffZ0Tks6Aw== + tlds@^1.187.0: version "1.203.1" resolved "https://registry.yarnpkg.com/tlds/-/tlds-1.203.1.tgz#4dc9b02f53de3315bc98b80665e13de3edfc1dfc" integrity sha512-7MUlYyGJ6rSitEZ3r1Q1QNV8uSIzapS8SmmhSusBuIc7uIxPPwsKllEP0GRp1NS6Ik6F+fRZvnjDWm3ecv2hDw== -tlds@^1.242.0: - version "1.255.0" - resolved "https://registry.yarnpkg.com/tlds/-/tlds-1.255.0.tgz#53c2571766c10da95928c716c48dfcf141341e3f" - integrity sha512-tcwMRIioTcF/FcxLev8MJWxCp+GUALRhFEqbDoZrnowmKSGqPrl5pqS+Sut2m8BgJ6S4FExCSSpGffZ0Tks6Aw== +tlds@^1.231.0: + version "1.256.0" + resolved "https://registry.yarnpkg.com/tlds/-/tlds-1.256.0.tgz#4285b41a7ed4fcc7c5eed8516c3a180e892fad36" + integrity sha512-ZmyVB9DAw+FFTmLElGYJgdZFsKLYd/I59Bg9NHkCGPwAbVZNRilFWDMAdX8UG+bHuv7kfursd5XGqo/9wi26lA== tldts-core@^6.1.78: version "6.1.78" @@ -9660,6 +9446,11 @@ toidentifier@1.0.1: resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== +token-stream@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/token-stream/-/token-stream-1.0.0.tgz#cc200eab2613f4166d27ff9afc7ca56d49df6eb4" + integrity sha512-VSsyNPPW74RpHwR8Fc21uubwHY7wMDeJLys2IX5zJNih+OnAnaifKHo+1LHT7DAdloQ7apeaaWg8l7qnf/TnEg== + toposort@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/toposort/-/toposort-2.0.2.tgz#ae21768175d1559d48bef35420b2f4962f09c330" @@ -9828,7 +9619,7 @@ tslib@^1.10.0, tslib@^1.11.1, tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== -tslib@^2.4.0, tslib@^2.8.1: +tslib@^2.2.0, tslib@^2.4.0, tslib@^2.8.1: version "2.8.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f" integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w== @@ -9989,6 +9780,11 @@ typescript@^5.8.3: resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.8.3.tgz#92f8a3e5e3cf497356f4178c34cd65a7f5e8440e" integrity sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ== +uc.micro@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-2.1.0.tgz#f8d3f7d0ec4c3dea35a7e3c8efa4cb8b45c9e7ee" + integrity sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A== + unbox-primitive@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.2.tgz#29032021057d5e6cdbd08c5129c226dff8ed6f9e" @@ -10004,6 +9800,16 @@ undefsafe@^2.0.5: resolved "https://registry.yarnpkg.com/undefsafe/-/undefsafe-2.0.5.tgz#38733b9327bdcd226db889fb723a6efd162e6e2c" integrity sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA== +underscore.deep@~0.5.1: + version "0.5.3" + resolved "https://registry.yarnpkg.com/underscore.deep/-/underscore.deep-0.5.3.tgz#210969d58025339cecabd2a2ad8c3e8925e5c095" + integrity sha512-4OuSOlFNkiVFVc3khkeG112Pdu1gbitMj7t9B9ENb61uFmN70Jq7Iluhi3oflcSgexkKfDdJ5XAJET2gEq6ikA== + +underscore@~1.13.1: + version "1.13.7" + resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.13.7.tgz#970e33963af9a7dda228f17ebe8399e5fbe63a10" + integrity sha512-GMXzWtsc57XAtguZgaQViUOzs0KTkk8ojr3/xAxXLITqf/3EMwxC0inyETfDFjH/Krbhuep0HNbbjI9i/q3F3g== + undici-types@~6.21.0: version "6.21.0" resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.21.0.tgz#691d00af3909be93a7faa13be61b3a5b50ef12cb" @@ -10014,29 +9820,6 @@ undici@^6.19.5: resolved "https://registry.yarnpkg.com/undici/-/undici-6.19.8.tgz#002d7c8a28f8cc3a44ff33c3d4be4d85e15d40e1" integrity sha512-U8uCCl2x9TK3WANvmBavymRzxbfFYG+tAu+fgx3zxQy3qdagQqBLwJVrdyO1TBfUXvfKveMKJZhpvUYoOjM+4g== -unicode-canonical-property-names-ecmascript@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz#301acdc525631670d39f6146e0e77ff6bbdebddc" - integrity sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ== - -unicode-match-property-ecmascript@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz#54fd16e0ecb167cf04cf1f756bdcc92eba7976c3" - integrity sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q== - dependencies: - unicode-canonical-property-names-ecmascript "^2.0.0" - unicode-property-aliases-ecmascript "^2.0.0" - -unicode-match-property-value-ecmascript@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.1.0.tgz#cb5fffdcd16a05124f5a4b0bf7c3770208acbbe0" - integrity sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA== - -unicode-property-aliases-ecmascript@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz#43d41e3be698bd493ef911077c9b131f827e8ccd" - integrity sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w== - unique-filename@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-3.0.0.tgz#48ba7a5a16849f5080d26c760c86cf5cf05770ea" @@ -10096,14 +9879,6 @@ update-browserslist-db@^1.1.0: escalade "^3.1.2" picocolors "^1.0.1" -update-browserslist-db@^1.1.1: - version "1.1.2" - resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.1.2.tgz#97e9c96ab0ae7bcac08e9ae5151d26e6bc6b5580" - integrity sha512-PPypAm5qvlD7XMZC3BujecnaOxwhrtoFR+Dqkk5Aa/6DssiH0ibKoketaj9w8LP7Bont1rYeoV5plxD7RTEPRg== - dependencies: - escalade "^3.2.0" - picocolors "^1.1.1" - uri-js@^4.2.2: version "4.4.1" resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" @@ -10188,7 +9963,7 @@ uuid@^8.0.0: resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== -uuid@~9.0.1: +uuid@^9.0.1, uuid@~9.0.1: version "9.0.1" resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.1.tgz#e188d4c8853cc722220392c424cd637f32293f30" integrity sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA== @@ -10207,12 +9982,10 @@ v8-to-istanbul@^9.0.1: "@types/istanbul-lib-coverage" "^2.0.1" convert-source-map "^2.0.0" -v8flags@^3.1.1: - version "3.1.3" - resolved "https://registry.yarnpkg.com/v8flags/-/v8flags-3.1.3.tgz#fc9dc23521ca20c5433f81cc4eb9b3033bb105d8" - integrity sha512-amh9CCg3ZxkzQ48Mhcb8iX7xpAfYJgePHxWMQCBWECpOSqJUXgY26ncA61UTV0BkPqfhcy6mzwCIoP4ygxpW8w== - dependencies: - homedir-polyfill "^1.0.1" +valid-data-url@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/valid-data-url/-/valid-data-url-3.0.1.tgz#826c1744e71b5632e847dd15dbd45b9fb38aa34f" + integrity sha512-jOWVmzVceKlVVdwjNSenT4PbGghU0SBIizAev8ofZVgivk/TVHXSbNL8LP6M3spZvkR9/QolkyJavGSX5Cs0UA== validator@^13.15.0: version "13.15.0" @@ -10243,6 +10016,11 @@ video-extensions@~1.1.0: resolved "https://registry.yarnpkg.com/video-extensions/-/video-extensions-1.1.0.tgz#eaa86b45f29a853c2b873e9d8e23b513712997d6" integrity sha1-6qhrRfKahTwrhz6djiO1E3Epl9Y= +void-elements@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-3.1.0.tgz#614f7fbf8d801f0bb5f0661f5b2f5785750e4f09" + integrity sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w== + w3c-xmlserializer@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz#f925ba26855158594d907313cedd1476c5967f6c" @@ -10257,6 +10035,18 @@ walker@^1.0.8: dependencies: makeerror "1.0.12" +web-resource-inliner@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/web-resource-inliner/-/web-resource-inliner-6.0.1.tgz#df0822f0a12028805fe80719ed52ab6526886e02" + integrity sha512-kfqDxt5dTB1JhqsCUQVFDj0rmY+4HLwGQIsLPbyrsN9y9WV/1oFDSx3BQ4GfCv9X+jVeQ7rouTqwK53rA/7t8A== + dependencies: + ansi-colors "^4.1.1" + escape-goat "^3.0.0" + htmlparser2 "^5.0.0" + mime "^2.4.6" + node-fetch "^2.6.0" + valid-data-url "^3.0.0" + webidl-conversions@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" @@ -10373,6 +10163,16 @@ whoops@~5.0.1: resolved "https://registry.yarnpkg.com/whoops/-/whoops-5.0.1.tgz#ce2fd6f255aca09b6fd8ec00c99f9761420296d4" integrity sha512-H2sKu1asxnFE2mNUeRzNY0CQuvl+n6iyE6phvzOaBfZblItNKpC1EzKWy2Zx+woZ3qUFK/wbmmNiLeqXwzk+FA== +with@^7.0.0: + version "7.0.2" + resolved "https://registry.yarnpkg.com/with/-/with-7.0.2.tgz#ccee3ad542d25538a7a7a80aad212b9828495bac" + integrity sha512-RNGKj82nUPg3g5ygxkQl0R937xLyho1J24ItRCBTr/m1YnZkzJy1hUiHUJrc/VlsDQzsCnInEGSg3bci0Lmd4w== + dependencies: + "@babel/parser" "^7.9.6" + "@babel/types" "^7.9.6" + assert-never "^1.2.1" + babel-walk "3.0.0-canary-5" + "wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" @@ -10382,7 +10182,7 @@ whoops@~5.0.1: string-width "^4.1.0" strip-ansi "^6.0.0" -wrap-ansi@^7.0.0: +wrap-ansi@7.0.0, wrap-ansi@^7.0.0, wrap-ansi@^8.1.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -10391,15 +10191,6 @@ wrap-ansi@^7.0.0: string-width "^4.1.0" strip-ansi "^6.0.0" -wrap-ansi@^8.1.0: - version "8.1.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" - integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ== - dependencies: - ansi-styles "^6.1.0" - string-width "^5.0.1" - strip-ansi "^7.0.1" - wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" diff --git a/cypress/support/step_definitions/Admin.DonationInfo/the_donation_info_contains_goal_{string}_and_progress_{string}.js b/cypress/support/step_definitions/Admin.DonationInfo/the_donation_info_contains_goal_{string}_and_progress_{string}.js index 454aea44b..a847e3e35 100644 --- a/cypress/support/step_definitions/Admin.DonationInfo/the_donation_info_contains_goal_{string}_and_progress_{string}.js +++ b/cypress/support/step_definitions/Admin.DonationInfo/the_donation_info_contains_goal_{string}_and_progress_{string}.js @@ -1,8 +1,8 @@ -import { When } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -When("the donation info contains goal {string} and progress {string}", (goal, progress) => { +defineStep('the donation info contains goal {string} and progress {string}', (goal, progress) => { cy.get('.top-info-bar') .should('contain', goal) cy.get('.top-info-bar') .should('contain', progress) -}); +}) diff --git a/cypress/support/step_definitions/Admin.DonationInfo/the_donation_info_is_{string}.js b/cypress/support/step_definitions/Admin.DonationInfo/the_donation_info_is_{string}.js index da231f23a..ef6c69767 100644 --- a/cypress/support/step_definitions/Admin.DonationInfo/the_donation_info_is_{string}.js +++ b/cypress/support/step_definitions/Admin.DonationInfo/the_donation_info_is_{string}.js @@ -1,6 +1,6 @@ -import { Then } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -Then("the donation info is {string}", (visibility) => { +defineStep('the donation info is {string}', (visibility) => { cy.get('.top-info-bar') .should(visibility === 'visible' ? 'exist' : 'not.exist') }) diff --git a/cypress/support/step_definitions/Admin.PinPost/I_open_the_content_menu_of_post_{string}.js b/cypress/support/step_definitions/Admin.PinPost/I_open_the_content_menu_of_post_{string}.js index 2b8d00dc9..dca7b9e7f 100644 --- a/cypress/support/step_definitions/Admin.PinPost/I_open_the_content_menu_of_post_{string}.js +++ b/cypress/support/step_definitions/Admin.PinPost/I_open_the_content_menu_of_post_{string}.js @@ -1,6 +1,6 @@ -import { When } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -When("I open the content menu of post {string}", (title) => { +defineStep('I open the content menu of post {string}', (title) => { cy.contains('.post-teaser', title) .find('.content-menu .base-button') .click() diff --git a/cypress/support/step_definitions/Admin.PinPost/the_post_with_title_{string}_has_a_ribbon_for_pinned_posts.js b/cypress/support/step_definitions/Admin.PinPost/the_post_with_title_{string}_has_a_ribbon_for_pinned_posts.js index 3e9f43bc3..5690ac23e 100644 --- a/cypress/support/step_definitions/Admin.PinPost/the_post_with_title_{string}_has_a_ribbon_for_pinned_posts.js +++ b/cypress/support/step_definitions/Admin.PinPost/the_post_with_title_{string}_has_a_ribbon_for_pinned_posts.js @@ -1,9 +1,9 @@ -import { Then } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -Then("the post with title {string} has a ribbon for pinned posts", (title) => { - cy.get(".post-teaser").contains(title) +defineStep('the post with title {string} has a ribbon for pinned posts', (title) => { + cy.get('.post-teaser').contains(title) .parent() .parent() - .find(".ribbon.--pinned") - .should("contain", "Announcement") + .find('.ribbon.--pinned') + .should('contain', 'Announcement') }) diff --git a/cypress/support/step_definitions/Admin.PinPost/there_is_no_button_to_pin_a_post.js b/cypress/support/step_definitions/Admin.PinPost/there_is_no_button_to_pin_a_post.js index 70535b920..5d75e9a87 100644 --- a/cypress/support/step_definitions/Admin.PinPost/there_is_no_button_to_pin_a_post.js +++ b/cypress/support/step_definitions/Admin.PinPost/there_is_no_button_to_pin_a_post.js @@ -1,7 +1,7 @@ -import { Then } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -Then("there is no button to pin a post", () => { - cy.get("a.ds-menu-item-link") - .should('contain', "Report Post") // sanity check - .should('not.contain', "Pin post") +defineStep('there is no button to pin a post', () => { + cy.get('a.ds-menu-item-link') + .should('contain', 'Report Post') // sanity check + .should('not.contain', 'Pin post') }) diff --git a/cypress/support/step_definitions/Internationalization/I_see_a_button_with_the_label_{string}.js b/cypress/support/step_definitions/Internationalization/I_see_a_button_with_the_label_{string}.js index 73a4a5e50..921b0344f 100644 --- a/cypress/support/step_definitions/Internationalization/I_see_a_button_with_the_label_{string}.js +++ b/cypress/support/step_definitions/Internationalization/I_see_a_button_with_the_label_{string}.js @@ -1,5 +1,5 @@ -import { Then } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -Then("I see a button with the label {string}", label => { - cy.contains("button", label); -}); +defineStep('I see a button with the label {string}', label => { + cy.contains('button', label) +}) diff --git a/cypress/support/step_definitions/Internationalization/I_select_{string}_in_the_language_menu.js b/cypress/support/step_definitions/Internationalization/I_select_{string}_in_the_language_menu.js index ba89fd3f5..48e1d4b59 100644 --- a/cypress/support/step_definitions/Internationalization/I_select_{string}_in_the_language_menu.js +++ b/cypress/support/step_definitions/Internationalization/I_select_{string}_in_the_language_menu.js @@ -1,8 +1,8 @@ -import { When } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -When("I select {string} in the language menu", language => { - cy.get(".locale-menu") - .click(); - cy.contains(".locale-menu-popover a", language) - .click(); -}); +defineStep('I select {string} in the language menu', language => { + cy.get('.locale-menu') + .click() + cy.contains('.locale-menu-popover a', language) + .click() +}) diff --git a/cypress/support/step_definitions/Internationalization/the_whole_user_interface_appears_in_{string}.js b/cypress/support/step_definitions/Internationalization/the_whole_user_interface_appears_in_{string}.js index d5a8ac95c..22d5e8c14 100644 --- a/cypress/support/step_definitions/Internationalization/the_whole_user_interface_appears_in_{string}.js +++ b/cypress/support/step_definitions/Internationalization/the_whole_user_interface_appears_in_{string}.js @@ -1,8 +1,8 @@ -import { Then } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' import locales from '../../../../webapp/locales' -Then("the whole user interface appears in {string}", language => { - const { code } = locales.find((entry) => entry.name === language); - cy.get(`html[lang=${code}]`); - cy.getCookie("locale").should("have.property", "value", code); -}); +defineStep('the whole user interface appears in {string}', language => { + const { code } = locales.find((entry) => entry.name === language) + cy.get(`html[lang=${code}]`) + cy.getCookie('locale').should('have.property', 'value', code) +}) diff --git a/cypress/support/step_definitions/Moderation.HidePost/I_should_see_only_{int}_posts_on_the_newsfeed.js b/cypress/support/step_definitions/Moderation.HidePost/I_should_see_only_{int}_posts_on_the_newsfeed.js index 26221ae66..5a0b2cf17 100644 --- a/cypress/support/step_definitions/Moderation.HidePost/I_should_see_only_{int}_posts_on_the_newsfeed.js +++ b/cypress/support/step_definitions/Moderation.HidePost/I_should_see_only_{int}_posts_on_the_newsfeed.js @@ -1,7 +1,7 @@ -import { Then } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -Then("I should see only {int} posts on the newsfeed", posts => { - cy.get(".post-teaser") - .should("have.length", posts); -}); +defineStep('I should see only {int} posts on the newsfeed', posts => { + cy.get('.post-teaser') + .should('have.length', posts) +}) diff --git a/cypress/support/step_definitions/Moderation.HidePost/the_page_{string}_returns_a_404_error_with_a_message.js b/cypress/support/step_definitions/Moderation.HidePost/the_page_{string}_returns_a_404_error_with_a_message.js index 538e8a64d..af813cd6e 100644 --- a/cypress/support/step_definitions/Moderation.HidePost/the_page_{string}_returns_a_404_error_with_a_message.js +++ b/cypress/support/step_definitions/Moderation.HidePost/the_page_{string}_returns_a_404_error_with_a_message.js @@ -1,14 +1,14 @@ -import { Then } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -Then("the page {string} returns a 404 error with a message:", (route, message) => { +defineStep('the page {string} returns a 404 error with a message:', (route, message) => { cy.request({ url: route, failOnStatusCode: false }) - .its("status") - .should("eq", 404); + .its('status') + .should('eq', 404) cy.visit(route, { failOnStatusCode: false - }); - cy.get(".error-message").should("contain", message); -}); + }) + cy.get('.error-message').should('contain', message) +}) diff --git a/cypress/support/step_definitions/Moderation.ReportContent/I_can_t_see_the_moderation_menu_item.js b/cypress/support/step_definitions/Moderation.ReportContent/I_can_t_see_the_moderation_menu_item.js index fcb1cb686..dd929e2d4 100644 --- a/cypress/support/step_definitions/Moderation.ReportContent/I_can_t_see_the_moderation_menu_item.js +++ b/cypress/support/step_definitions/Moderation.ReportContent/I_can_t_see_the_moderation_menu_item.js @@ -1,6 +1,6 @@ -import { Then } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -Then(`I can't see the moderation menu item`, () => { +defineStep(`I can't see the moderation menu item`, () => { cy.get('.avatar-menu-popover') .find('a[href="/settings"]', 'Settings') .should('exist') // OK, the dropdown is actually open diff --git a/cypress/support/step_definitions/Moderation.ReportContent/I_can_visit_the_post_page.js b/cypress/support/step_definitions/Moderation.ReportContent/I_can_visit_the_post_page.js index ce846c39a..2986a8fc8 100644 --- a/cypress/support/step_definitions/Moderation.ReportContent/I_can_visit_the_post_page.js +++ b/cypress/support/step_definitions/Moderation.ReportContent/I_can_visit_the_post_page.js @@ -1,6 +1,6 @@ -import { Then } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -Then('I can visit the post page', () => { +defineStep('I can visit the post page', () => { cy.contains('Fake news').click() cy.location('pathname').should('contain', '/post') .get('.base-card .title').should('contain', 'Fake news') diff --git a/cypress/support/step_definitions/Moderation.ReportContent/I_click_on_Report_Post_from_the_content_menu_of_the_post.js b/cypress/support/step_definitions/Moderation.ReportContent/I_click_on_Report_Post_from_the_content_menu_of_the_post.js index 8588e156a..bcfc362cd 100644 --- a/cypress/support/step_definitions/Moderation.ReportContent/I_click_on_Report_Post_from_the_content_menu_of_the_post.js +++ b/cypress/support/step_definitions/Moderation.ReportContent/I_click_on_Report_Post_from_the_content_menu_of_the_post.js @@ -1,6 +1,6 @@ -import { When } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -When('I click on "Report Post" from the content menu of the post', () => { +defineStep('I click on "Report Post" from the content menu of the post', () => { cy.contains('.base-card', 'The Truth about the Holocaust') .find('.content-menu .base-button') .click() diff --git a/cypress/support/step_definitions/Moderation.ReportContent/I_click_on_the_author.js b/cypress/support/step_definitions/Moderation.ReportContent/I_click_on_the_author.js index 049eb8e72..d0526bebd 100644 --- a/cypress/support/step_definitions/Moderation.ReportContent/I_click_on_the_author.js +++ b/cypress/support/step_definitions/Moderation.ReportContent/I_click_on_the_author.js @@ -1,6 +1,6 @@ -import { When } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -When('I click on the author', () => { +defineStep('I click on the author', () => { cy.get('[data-test="avatarUserLink"]') .click() .url().should('include', '/profile/') diff --git a/cypress/support/step_definitions/Moderation.ReportContent/I_click_on_the_avatar_menu_in_the_top_right_corner.js b/cypress/support/step_definitions/Moderation.ReportContent/I_click_on_the_avatar_menu_in_the_top_right_corner.js index 0bb1e816f..5ce6ca6b6 100644 --- a/cypress/support/step_definitions/Moderation.ReportContent/I_click_on_the_avatar_menu_in_the_top_right_corner.js +++ b/cypress/support/step_definitions/Moderation.ReportContent/I_click_on_the_avatar_menu_in_the_top_right_corner.js @@ -1,7 +1,7 @@ -import { When } from "@badeball/cypress-cucumber-preprocessor"; -import 'cypress-network-idle'; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' +import 'cypress-network-idle' -When("I click on the avatar menu in the top right corner", () => { - cy.get(".avatar-menu").click(); - cy.waitForNetworkIdle(2000); -}); +defineStep('I click on the avatar menu in the top right corner', () => { + cy.get('.avatar-menu').click() + cy.waitForNetworkIdle(2000) +}) diff --git a/cypress/support/step_definitions/Moderation.ReportContent/I_confirm_the_reporting_dialog.js b/cypress/support/step_definitions/Moderation.ReportContent/I_confirm_the_reporting_dialog.js index 970c61c00..085641b94 100644 --- a/cypress/support/step_definitions/Moderation.ReportContent/I_confirm_the_reporting_dialog.js +++ b/cypress/support/step_definitions/Moderation.ReportContent/I_confirm_the_reporting_dialog.js @@ -1,6 +1,6 @@ -import { When } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -When(/^I confirm the reporting dialog .*:$/, message => { +defineStep(/^I confirm the reporting dialog .*:$/, message => { cy.contains(message) // wait for element to become visible cy.get('.ds-modal') .within(() => { diff --git a/cypress/support/step_definitions/Moderation.ReportContent/I_see_all_the_reported_posts_including_from_the_user_who_muted_me.js b/cypress/support/step_definitions/Moderation.ReportContent/I_see_all_the_reported_posts_including_from_the_user_who_muted_me.js index 8989ecf68..eaa06e625 100644 --- a/cypress/support/step_definitions/Moderation.ReportContent/I_see_all_the_reported_posts_including_from_the_user_who_muted_me.js +++ b/cypress/support/step_definitions/Moderation.ReportContent/I_see_all_the_reported_posts_including_from_the_user_who_muted_me.js @@ -1,6 +1,6 @@ -import { Then } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -Then('I see all the reported posts including from the user who muted me', () => { +defineStep('I see all the reported posts including from the user who muted me', () => { cy.get('table tbody').within(() => { cy.contains('tr', 'Fake news') }) diff --git a/cypress/support/step_definitions/Moderation.ReportContent/I_see_all_the_reported_posts_including_the_one_from_above.js b/cypress/support/step_definitions/Moderation.ReportContent/I_see_all_the_reported_posts_including_the_one_from_above.js index f5cb71f91..40d018f6a 100644 --- a/cypress/support/step_definitions/Moderation.ReportContent/I_see_all_the_reported_posts_including_the_one_from_above.js +++ b/cypress/support/step_definitions/Moderation.ReportContent/I_see_all_the_reported_posts_including_the_one_from_above.js @@ -1,6 +1,6 @@ -import { Then } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -Then('I see all the reported posts including the one from above', () => { +defineStep('I see all the reported posts including the one from above', () => { cy.intercept({ method: 'POST', url: '/api', diff --git a/cypress/support/step_definitions/Moderation.ReportContent/each_list_item_links_to_the_post_page.js b/cypress/support/step_definitions/Moderation.ReportContent/each_list_item_links_to_the_post_page.js index e174113fc..99283a117 100644 --- a/cypress/support/step_definitions/Moderation.ReportContent/each_list_item_links_to_the_post_page.js +++ b/cypress/support/step_definitions/Moderation.ReportContent/each_list_item_links_to_the_post_page.js @@ -1,6 +1,6 @@ -import { Then } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -Then('each list item links to the post page', () => { - cy.contains('The Truth about the Holocaust').click(); +defineStep('each list item links to the post page', () => { + cy.contains('The Truth about the Holocaust').click() cy.location('pathname').should('contain', '/post') }) diff --git a/cypress/support/step_definitions/Moderation.ReportContent/somebody_reported_the_following_posts.js b/cypress/support/step_definitions/Moderation.ReportContent/somebody_reported_the_following_posts.js index e924acdeb..191bcd25f 100644 --- a/cypress/support/step_definitions/Moderation.ReportContent/somebody_reported_the_following_posts.js +++ b/cypress/support/step_definitions/Moderation.ReportContent/somebody_reported_the_following_posts.js @@ -1,9 +1,9 @@ -import { Given } from '@badeball/cypress-cucumber-preprocessor' +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' import './../../commands' import './../../factories' import 'cypress-network-idle' -Given('somebody reported the following posts:', table => { +defineStep('somebody reported the following posts:', table => { const reportIdRegex = /^[0-9a-zA-Z]{8}-[0-9a-zA-Z]{4}-[0-9a-zA-Z]{4}-[0-9a-zA-Z]{4}-[0-9a-zA-Z]{12}$/ cy.intercept({ method: 'POST', diff --git a/cypress/support/step_definitions/Moderation.ReportContent/there_is_an_annoying_user_who_has_muted_me.js b/cypress/support/step_definitions/Moderation.ReportContent/there_is_an_annoying_user_who_has_muted_me.js index 8d61afd61..1b0c72ea2 100644 --- a/cypress/support/step_definitions/Moderation.ReportContent/there_is_an_annoying_user_who_has_muted_me.js +++ b/cypress/support/step_definitions/Moderation.ReportContent/there_is_an_annoying_user_who_has_muted_me.js @@ -1,15 +1,15 @@ -import { Given } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -Given("there is an annoying user who has muted me", () => { +defineStep('there is an annoying user who has muted me', () => { cy.neode() - .firstOf("User", { + .firstOf('User', { role: 'moderator' }) .then(mutedUser => { cy.neode() - .firstOf("User", { + .firstOf('User', { id: 'user' }) - .relateTo(mutedUser, "muted"); - }); -}); + .relateTo(mutedUser, 'muted') + }) +}) diff --git a/cypress/support/step_definitions/Notification.Mention/I_start_to_write_a_new_post_with_the_title_{string}_beginning_with.js b/cypress/support/step_definitions/Notification.Mention/I_start_to_write_a_new_post_with_the_title_{string}_beginning_with.js index b8e705c82..b6cd829e1 100644 --- a/cypress/support/step_definitions/Notification.Mention/I_start_to_write_a_new_post_with_the_title_{string}_beginning_with.js +++ b/cypress/support/step_definitions/Notification.Mention/I_start_to_write_a_new_post_with_the_title_{string}_beginning_with.js @@ -1,8 +1,8 @@ -import { When } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -When("I start to write a new post with the title {string} beginning with:", (title, intro) => { +defineStep('I start to write a new post with the title {string} beginning with:', (title, intro) => { cy.get('input[name="title"]') - .type(title); - cy.get(".ProseMirror") - .type(intro); -}); + .type(title) + cy.get('.ProseMirror') + .type(intro) +}) diff --git a/cypress/support/step_definitions/Notification.Mention/mention_{string}_in_the_text.js b/cypress/support/step_definitions/Notification.Mention/mention_{string}_in_the_text.js index e1bd19da0..0997111d8 100644 --- a/cypress/support/step_definitions/Notification.Mention/mention_{string}_in_the_text.js +++ b/cypress/support/step_definitions/Notification.Mention/mention_{string}_in_the_text.js @@ -1,9 +1,9 @@ -import { When } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -When("mention {string} in the text", mention => { - cy.get(".ProseMirror") - .type(" @"); - cy.get(".suggestion-list__item") +defineStep('mention {string} in the text', mention => { + cy.get('.ProseMirror') + .type(' @') + cy.get('.suggestion-list__item') .contains(mention) - .click(); -}); + .click() +}) diff --git a/cypress/support/step_definitions/Notification.Mention/open_the_notification_menu_and_click_on_the_first_item.js b/cypress/support/step_definitions/Notification.Mention/open_the_notification_menu_and_click_on_the_first_item.js index 0143d1ac5..e209909d3 100644 --- a/cypress/support/step_definitions/Notification.Mention/open_the_notification_menu_and_click_on_the_first_item.js +++ b/cypress/support/step_definitions/Notification.Mention/open_the_notification_menu_and_click_on_the_first_item.js @@ -1,10 +1,10 @@ -import { When } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -When("open the notification menu and click on the first item", () => { - cy.get(".notifications-menu") +defineStep('open the notification menu and click on the first item', () => { + cy.get('.notifications-menu') .invoke('show') - .click(); // "invoke('show')" because of the delay for show the menu - cy.get(".notification .link") + .click() // 'invoke('show')' because of the delay for show the menu + cy.get('.notification .link') .first() - .click({force: true}); -}); + .click({force: true}) +}) diff --git a/cypress/support/step_definitions/Notification.Mention/see_{int}_unread_notifications_in_the_top_menu.js b/cypress/support/step_definitions/Notification.Mention/see_{int}_unread_notifications_in_the_top_menu.js index ae1644cef..eebb5b877 100644 --- a/cypress/support/step_definitions/Notification.Mention/see_{int}_unread_notifications_in_the_top_menu.js +++ b/cypress/support/step_definitions/Notification.Mention/see_{int}_unread_notifications_in_the_top_menu.js @@ -1,6 +1,6 @@ -import { Then } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -Then("see {int} unread notifications in the top menu", count => { - cy.get(".notifications-menu") - .should("contain", count); -}); +defineStep('see {int} unread notifications in the top menu', count => { + cy.get('.notifications-menu') + .should('contain', count) +}) diff --git a/cypress/support/step_definitions/Notification.Mention/the_notification_menu_button_links_to_the_all_notifications_page.js b/cypress/support/step_definitions/Notification.Mention/the_notification_menu_button_links_to_the_all_notifications_page.js index a7204978e..3cdbeb305 100644 --- a/cypress/support/step_definitions/Notification.Mention/the_notification_menu_button_links_to_the_all_notifications_page.js +++ b/cypress/support/step_definitions/Notification.Mention/the_notification_menu_button_links_to_the_all_notifications_page.js @@ -1,8 +1,8 @@ -import { Then } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -Then("the notification menu button links to the all notifications page", () => { - cy.get(".notifications-menu") - .click(); - cy.location("pathname") - .should("contain", "/notifications"); -}); +defineStep('the notification menu button links to the all notifications page', () => { + cy.get('.notifications-menu') + .click() + cy.location('pathname') + .should('contain', '/notifications') +}) diff --git a/cypress/support/step_definitions/Notification.Mention/the_unread_counter_is_removed.js b/cypress/support/step_definitions/Notification.Mention/the_unread_counter_is_removed.js index 6c7ff96f0..771d66c3c 100644 --- a/cypress/support/step_definitions/Notification.Mention/the_unread_counter_is_removed.js +++ b/cypress/support/step_definitions/Notification.Mention/the_unread_counter_is_removed.js @@ -1,6 +1,6 @@ -import { Then } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -Then("the unread counter is removed", () => { +defineStep('the unread counter is removed', () => { cy.get('.notifications-menu .counter-icon') - .should('not.exist'); -}); + .should('not.exist') +}) diff --git a/cypress/support/step_definitions/Post.Comment/I_comment_the_following.js b/cypress/support/step_definitions/Post.Comment/I_comment_the_following.js index 495075b60..76d5b2c55 100644 --- a/cypress/support/step_definitions/Post.Comment/I_comment_the_following.js +++ b/cypress/support/step_definitions/Post.Comment/I_comment_the_following.js @@ -1,7 +1,7 @@ -import { When } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -When("I comment the following:", text => { - const comment = text.replace("\n", " ") +defineStep('I comment the following:', text => { + const comment = text.replace('\n', ' ') cy.task('pushValue', { name: 'lastComment', value: comment }) - cy.get(".editor .ProseMirror").type(comment); -}); + cy.get('.editor .ProseMirror').type(comment) +}) diff --git a/cypress/support/step_definitions/Post.Comment/I_should_see_an_abbreviated_version_of_my_comment.js b/cypress/support/step_definitions/Post.Comment/I_should_see_an_abbreviated_version_of_my_comment.js index 67dc9bef8..f27cf795a 100644 --- a/cypress/support/step_definitions/Post.Comment/I_should_see_an_abbreviated_version_of_my_comment.js +++ b/cypress/support/step_definitions/Post.Comment/I_should_see_an_abbreviated_version_of_my_comment.js @@ -1,6 +1,6 @@ -import { Then } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -Then("I should see an abbreviated version of my comment", () => { - cy.get("article.comment-card") - .should("contain", "show more") -}); +defineStep('I should see an abbreviated version of my comment', () => { + cy.get('article.comment-card') + .should('contain', 'show more') +}) diff --git a/cypress/support/step_definitions/Post.Comment/I_should_see_my_comment.js b/cypress/support/step_definitions/Post.Comment/I_should_see_my_comment.js index 7b30ee82d..332379dcc 100644 --- a/cypress/support/step_definitions/Post.Comment/I_should_see_my_comment.js +++ b/cypress/support/step_definitions/Post.Comment/I_should_see_my_comment.js @@ -1,13 +1,13 @@ -import { Then } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -Then("I should see my comment", () => { - cy.get("article.comment-card p") - .should("contain", "Ocelot.social rocks") - .get(".user-teaser span.slug") - .should("contain", "@peter-pan") // specific enough - .get(".profile-avatar img") - .should("have.attr", "src") - .and("contain", 'https://') // some url - .get(".user-teaser > .info > .text") - .should("contain", "today at"); -}); +defineStep('I should see my comment', () => { + cy.get('article.comment-card p') + .should('contain', 'Ocelot.social rocks') + .get('.user-teaser span.slug') + .should('contain', '@peter-pan') // specific enough + .get('.profile-avatar img') + .should('have.attr', 'src') + .and('contain', 'https://') // some url + .get('.user-teaser .info > .text') + .should('contain', 'today at') +}) diff --git a/cypress/support/step_definitions/Post.Comment/I_should_see_the_entirety_of_my_comment.js b/cypress/support/step_definitions/Post.Comment/I_should_see_the_entirety_of_my_comment.js index 9ea48fa05..eb0d3d70f 100644 --- a/cypress/support/step_definitions/Post.Comment/I_should_see_the_entirety_of_my_comment.js +++ b/cypress/support/step_definitions/Post.Comment/I_should_see_the_entirety_of_my_comment.js @@ -1,6 +1,6 @@ -import { Then } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -Then("I should see the entirety of my comment", () => { - cy.get("article.comment-card") - .should("not.contain", "show more") -}); +defineStep('I should see the entirety of my comment', () => { + cy.get('article.comment-card') + .should('not.contain', 'show more') +}) diff --git a/cypress/support/step_definitions/Post.Comment/I_type_in_a_comment_with_{int}_characters.js b/cypress/support/step_definitions/Post.Comment/I_type_in_a_comment_with_{int}_characters.js index ef39bdbf4..e7fcdc70e 100644 --- a/cypress/support/step_definitions/Post.Comment/I_type_in_a_comment_with_{int}_characters.js +++ b/cypress/support/step_definitions/Post.Comment/I_type_in_a_comment_with_{int}_characters.js @@ -1,9 +1,9 @@ -import { When } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -When("I type in a comment with {int} characters", size => { - var c=""; +defineStep('I type in a comment with {int} characters', size => { + var c = '' for (var i = 0; i < size; i++) { - c += "c" + c += 'c' } - cy.get(".editor .ProseMirror").type(c); -}); + cy.get('.editor .ProseMirror').type(c) +}) diff --git a/cypress/support/step_definitions/Post.Comment/it_should_create_a_mention_in_the_CommentForm.js b/cypress/support/step_definitions/Post.Comment/it_should_create_a_mention_in_the_CommentForm.js index 0e52e0f7a..f30c2ccba 100644 --- a/cypress/support/step_definitions/Post.Comment/it_should_create_a_mention_in_the_CommentForm.js +++ b/cypress/support/step_definitions/Post.Comment/it_should_create_a_mention_in_the_CommentForm.js @@ -1,7 +1,7 @@ -import { Then } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -Then("it should create a mention in the CommentForm", () => { - cy.get(".ProseMirror a") +defineStep('it should create a mention in the CommentForm', () => { + cy.get('.ProseMirror a') .should('have.class', 'mention') .should('contain', '@peter-pan') }) diff --git a/cypress/support/step_definitions/Post.Comment/my_comment_should_be_successfully_created.js b/cypress/support/step_definitions/Post.Comment/my_comment_should_be_successfully_created.js index acb94f216..6edb116b8 100644 --- a/cypress/support/step_definitions/Post.Comment/my_comment_should_be_successfully_created.js +++ b/cypress/support/step_definitions/Post.Comment/my_comment_should_be_successfully_created.js @@ -1,5 +1,5 @@ -import { Then } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -Then("my comment should be successfully created", () => { - cy.get(".iziToast-message").contains("Comment submitted!"); -}); +defineStep('my comment should be successfully created', () => { + cy.get('.iziToast-message').contains('Comment submitted!') +}) diff --git a/cypress/support/step_definitions/Post.Comment/the_editor_should_be_cleared.js b/cypress/support/step_definitions/Post.Comment/the_editor_should_be_cleared.js index f6e47313a..5b3ae68e5 100644 --- a/cypress/support/step_definitions/Post.Comment/the_editor_should_be_cleared.js +++ b/cypress/support/step_definitions/Post.Comment/the_editor_should_be_cleared.js @@ -1,5 +1,5 @@ -import { Then } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -Then("the editor should be cleared", () => { - cy.get(".ProseMirror p").should("have.class", "is-empty"); -}); +defineStep('the editor should be cleared', () => { + cy.get('.ProseMirror p').should('have.class', 'is-empty') +}) diff --git a/cypress/support/step_definitions/Post.Create/I_choose_{string}_as_the_title.js b/cypress/support/step_definitions/Post.Create/I_choose_{string}_as_the_title.js index fc57b23a5..18018d357 100644 --- a/cypress/support/step_definitions/Post.Create/I_choose_{string}_as_the_title.js +++ b/cypress/support/step_definitions/Post.Create/I_choose_{string}_as_the_title.js @@ -1,8 +1,8 @@ -import { When } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -When("I choose {string} as the title", title => { +defineStep('I choose {string} as the title', title => { const lastPost = {} - lastPost.title = title.replace("\n", " "); + lastPost.title = title.replace('\n', ' ') cy.task('pushValue', { name: 'lastPost', value: lastPost }) - cy.get('input[name="title"]').type(lastPost.title); -}); + cy.get('input[name="title"]').type(lastPost.title) +}) diff --git a/cypress/support/step_definitions/Post.Create/the_post_was_saved_successfully.js b/cypress/support/step_definitions/Post.Create/the_post_was_saved_successfully.js index 50e414650..4850ab432 100644 --- a/cypress/support/step_definitions/Post.Create/the_post_was_saved_successfully.js +++ b/cypress/support/step_definitions/Post.Create/the_post_was_saved_successfully.js @@ -1,8 +1,8 @@ -import { Then } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -Then("the post was saved successfully", () => { +defineStep('the post was saved successfully', () => { cy.task('getValue', 'lastPost').then(lastPost => { - cy.get(".base-card > .title").should("contain", lastPost.title); - cy.get(".content").should("contain", lastPost.content); + cy.get('.base-card > .title').should('contain', lastPost.title) + cy.get('.content').should('contain', lastPost.content) }) -}); +}) diff --git a/cypress/support/step_definitions/Post.Images/I_add_all_required_fields.js b/cypress/support/step_definitions/Post.Images/I_add_all_required_fields.js index ce2e88a83..e7bc94795 100644 --- a/cypress/support/step_definitions/Post.Images/I_add_all_required_fields.js +++ b/cypress/support/step_definitions/Post.Images/I_add_all_required_fields.js @@ -1,8 +1,8 @@ -import { Then } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -Then("I add all required fields", () => { +defineStep('I add all required fields', () => { cy.get('input[name="title"]') .type('new post') - .get(".editor .ProseMirror") + .get('.editor .ProseMirror') .type('new post content') - }) +}) diff --git a/cypress/support/step_definitions/Post.Images/I_should_be_able_to_{string}_a_teaser_image.js b/cypress/support/step_definitions/Post.Images/I_should_be_able_to_{string}_a_teaser_image.js index 478851f92..04cfeff3d 100644 --- a/cypress/support/step_definitions/Post.Images/I_should_be_able_to_{string}_a_teaser_image.js +++ b/cypress/support/step_definitions/Post.Images/I_should_be_able_to_{string}_a_teaser_image.js @@ -1,29 +1,28 @@ -import { Then } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -Then("I should be able to {string} a teaser image", condition => { - let postTeaserImage = "" +defineStep('I should be able to {string} a teaser image', condition => { + let postTeaserImage = '' switch(condition){ - case "change": - postTeaserImage = "humanconnection.png" - cy.get(".delete-image-button") + case 'change': + postTeaserImage = 'humanconnection.png' + cy.get('.delete-image-button') .click() - cy.get("#postdropzone").selectFile( - { contents: `cypress/fixtures/${postTeaserImage}`, fileName: postTeaserImage, mimeType: "image/png" }, - { action: "drag-drop", force: true } - ).wait(750); - break; - case "add": - postTeaserImage = "onourjourney.png" - cy.get("#postdropzone").selectFile( - { contents: `cypress/fixtures/${postTeaserImage}`, fileName: postTeaserImage, mimeType: "image/png" }, - { action: "drag-drop", force: true } - ).wait(750); - break; - case "remove": - cy.get(".delete-image-button") + cy.get('#postdropzone').selectFile( + { contents: `cypress/fixtures/${postTeaserImage}`, fileName: postTeaserImage, mimeType: 'image/png' }, + { action: 'drag-drop', force: true } + ).wait(750) + break + case 'add': + postTeaserImage = 'onourjourney.png' + cy.get('#postdropzone').selectFile( + { contents: `cypress/fixtures/${postTeaserImage}`, fileName: postTeaserImage, mimeType: 'image/png' }, + { action: 'drag-drop', force: true } + ).wait(750) + break + case 'remove': + cy.get('.delete-image-button') .click() - break; + break } - }) diff --git a/cypress/support/step_definitions/Post.Images/my_post_has_a_teaser_image.js b/cypress/support/step_definitions/Post.Images/my_post_has_a_teaser_image.js index b9ce4b3c7..7858b958c 100644 --- a/cypress/support/step_definitions/Post.Images/my_post_has_a_teaser_image.js +++ b/cypress/support/step_definitions/Post.Images/my_post_has_a_teaser_image.js @@ -1,6 +1,6 @@ -import { When } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -When('my post has a teaser image', () => { +defineStep('my post has a teaser image', () => { cy.get('.contribution-form .image') .should('exist') .and('have.attr', 'src') diff --git a/cypress/support/step_definitions/Post.Images/the_first_image_should_not_be_displayed_anymore.js b/cypress/support/step_definitions/Post.Images/the_first_image_should_not_be_displayed_anymore.js index 6388f4458..f2188a28a 100644 --- a/cypress/support/step_definitions/Post.Images/the_first_image_should_not_be_displayed_anymore.js +++ b/cypress/support/step_definitions/Post.Images/the_first_image_should_not_be_displayed_anymore.js @@ -1,7 +1,7 @@ -import { Then } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -Then("the first image should not be displayed anymore", () => { - cy.get(".hero-image") +defineStep('the first image should not be displayed anymore', () => { + cy.get('.hero-image') .children() .get('.hero-image > .image') .should('have.length', 1) diff --git a/cypress/support/step_definitions/Post.Images/the_post_was_saved_successfully_with_the_{string}_teaser_image.js b/cypress/support/step_definitions/Post.Images/the_post_was_saved_successfully_with_the_{string}_teaser_image.js index c0571068e..fdfb1c84a 100644 --- a/cypress/support/step_definitions/Post.Images/the_post_was_saved_successfully_with_the_{string}_teaser_image.js +++ b/cypress/support/step_definitions/Post.Images/the_post_was_saved_successfully_with_the_{string}_teaser_image.js @@ -1,11 +1,11 @@ -import { Then } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -Then("the post was saved successfully with the {string} teaser image", condition => { - cy.get(".base-card > .title") - .should("contain", condition === 'updated' ? 'to be updated' : 'new post') - .get(".content") - .should("contain", condition === 'updated' ? 'successfully updated' : 'new post content') +defineStep('the post was saved successfully with the {string} teaser image', condition => { + cy.get('.base-card > .title') + .should('contain', condition === 'updated' ? 'to be updated' : 'new post') + .get('.content') + .should('contain', condition === 'updated' ? 'successfully updated' : 'new post content') .get('.post-page img') - .should("have.attr", "src") - .and("contains", condition === 'updated' ? 'humanconnection' : 'onourjourney') + .should('have.attr', 'src') + .and('contains', condition === 'updated' ? 'humanconnection' : 'onourjourney') }) diff --git a/cypress/support/step_definitions/Post.Images/the_{string}_post_was_saved_successfully_without_a_teaser_image.js b/cypress/support/step_definitions/Post.Images/the_{string}_post_was_saved_successfully_without_a_teaser_image.js index 40245df2b..39947d029 100644 --- a/cypress/support/step_definitions/Post.Images/the_{string}_post_was_saved_successfully_without_a_teaser_image.js +++ b/cypress/support/step_definitions/Post.Images/the_{string}_post_was_saved_successfully_without_a_teaser_image.js @@ -1,6 +1,6 @@ -import { Then } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -Then('the {string} post was saved successfully without a teaser image', condition => { +defineStep('the {string} post was saved successfully without a teaser image', condition => { cy.get(".base-card > .title") .should("contain", condition === 'updated' ? 'to be updated' : 'new post') .get(".content") diff --git a/cypress/support/step_definitions/Post/the_post_shows_up_on_the_newsfeed_at_position_{int}.js b/cypress/support/step_definitions/Post/the_post_shows_up_on_the_newsfeed_at_position_{int}.js index f10896a33..59484591f 100644 --- a/cypress/support/step_definitions/Post/the_post_shows_up_on_the_newsfeed_at_position_{int}.js +++ b/cypress/support/step_definitions/Post/the_post_shows_up_on_the_newsfeed_at_position_{int}.js @@ -1,8 +1,7 @@ -import { Then } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -Then("the post shows up on the newsfeed at position {int}", index => { - const selector = `.post-teaser:nth-child(${index}) > .base-card`; - cy.get(selector).should("contain", 'previously created post'); - cy.get(selector).should("contain", 'with some content'); - -}); +defineStep('the post shows up on the newsfeed at position {int}', index => { + const selector = `.post-teaser:nth-child(${index}) > .base-card` + cy.get(selector).should('contain', 'previously created post') + cy.get(selector).should('contain', 'with some content') +}) diff --git a/cypress/support/step_definitions/Search/I_select_a_post_entry.js b/cypress/support/step_definitions/Search/I_select_a_post_entry.js index 26e673499..ddc7d0162 100644 --- a/cypress/support/step_definitions/Search/I_select_a_post_entry.js +++ b/cypress/support/step_definitions/Search/I_select_a_post_entry.js @@ -1,7 +1,7 @@ -import { When } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -When("I select a post entry", () => { - cy.get(".searchable-input .search-post") +defineStep('I select a post entry', () => { + cy.get('.searchable-input .search-post') .first() - .trigger("click"); -}); + .trigger('click') +}) diff --git a/cypress/support/step_definitions/Search/I_select_a_user_entry.js b/cypress/support/step_definitions/Search/I_select_a_user_entry.js index 3d186ffd8..b3df870a9 100644 --- a/cypress/support/step_definitions/Search/I_select_a_user_entry.js +++ b/cypress/support/step_definitions/Search/I_select_a_user_entry.js @@ -1,7 +1,7 @@ -import { Then } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -Then("I select a user entry", () => { - cy.get(".searchable-input .user-teaser") +defineStep('I select a user entry', () => { + cy.get('.searchable-input .user-teaser') .first() - .trigger("click"); + .trigger('click') }) diff --git a/cypress/support/step_definitions/Search/I_should_have_one_item_in_the_select_dropdown.js b/cypress/support/step_definitions/Search/I_should_have_one_item_in_the_select_dropdown.js index 148bb8195..cc52f5985 100644 --- a/cypress/support/step_definitions/Search/I_should_have_one_item_in_the_select_dropdown.js +++ b/cypress/support/step_definitions/Search/I_should_have_one_item_in_the_select_dropdown.js @@ -1,7 +1,7 @@ -import { Then } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -Then("I should have one item in the select dropdown", () => { - cy.get(".searchable-input .ds-select-dropdown").should($li => { - expect($li).to.have.length(1); - }); -}); +defineStep('I should have one item in the select dropdown', () => { + cy.get('.searchable-input .ds-select-dropdown').should($li => { + expect($li).to.have.length(1) + }) +}) diff --git a/cypress/support/step_definitions/Search/I_should_not_see_posts_without_the_searched-for_term_in_the_select_dropdown.js b/cypress/support/step_definitions/Search/I_should_not_see_posts_without_the_searched-for_term_in_the_select_dropdown.js index d2a2bc1df..1e8082d40 100644 --- a/cypress/support/step_definitions/Search/I_should_not_see_posts_without_the_searched-for_term_in_the_select_dropdown.js +++ b/cypress/support/step_definitions/Search/I_should_not_see_posts_without_the_searched-for_term_in_the_select_dropdown.js @@ -1,6 +1,6 @@ -import { Then } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -Then("I should not see posts without the searched-for term in the select dropdown", () => { - cy.get(".ds-select-dropdown") - .should("not.contain","No searched for content"); -}); +defineStep('I should not see posts without the searched-for term in the select dropdown', () => { + cy.get('.ds-select-dropdown') + .should('not.contain','No searched for content') +}) diff --git a/cypress/support/step_definitions/Search/I_should_see_posts_with_the_searched-for_term_in_the_select_dropdown.js b/cypress/support/step_definitions/Search/I_should_see_posts_with_the_searched-for_term_in_the_select_dropdown.js index 41c132dea..23d2c141f 100644 --- a/cypress/support/step_definitions/Search/I_should_see_posts_with_the_searched-for_term_in_the_select_dropdown.js +++ b/cypress/support/step_definitions/Search/I_should_see_posts_with_the_searched-for_term_in_the_select_dropdown.js @@ -1,6 +1,6 @@ -import { Then } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -Then("I should see posts with the searched-for term in the select dropdown", () => { - cy.get(".ds-select-dropdown") - .should("contain","101 Essays that will change the way you think"); -}); +defineStep('I should see posts with the searched-for term in the select dropdown', () => { + cy.get('.ds-select-dropdown') + .should('contain','101 Essays that will change the way you think') +}) diff --git a/cypress/support/step_definitions/Search/I_should_see_the_following_posts_on_the_search_results_page.js b/cypress/support/step_definitions/Search/I_should_see_the_following_posts_on_the_search_results_page.js index 3e5da72f7..57f41bccb 100644 --- a/cypress/support/step_definitions/Search/I_should_see_the_following_posts_on_the_search_results_page.js +++ b/cypress/support/step_definitions/Search/I_should_see_the_following_posts_on_the_search_results_page.js @@ -1,8 +1,8 @@ -import { Then } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -Then("I should see the following posts on the search results page:", table => { +defineStep('I should see the following posts on the search results page:', table => { table.hashes().forEach(({ title }) => { - cy.get(".post-teaser") - .should("contain",title) - }); -}); + cy.get('.post-teaser') + .should('contain',title) + }) +}) diff --git a/cypress/support/step_definitions/Search/I_should_see_the_following_users_in_the_select_dropdown.js b/cypress/support/step_definitions/Search/I_should_see_the_following_users_in_the_select_dropdown.js index 830e19a8d..f4b89de0c 100644 --- a/cypress/support/step_definitions/Search/I_should_see_the_following_users_in_the_select_dropdown.js +++ b/cypress/support/step_definitions/Search/I_should_see_the_following_users_in_the_select_dropdown.js @@ -1,8 +1,8 @@ -import { Then } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -Then("I should see the following users in the select dropdown:", table => { - cy.get(".search-heading").should("contain", "Users"); +defineStep('I should see the following users in the select dropdown:', table => { + cy.get('.search-heading').should('contain', 'Users') table.hashes().forEach(({ slug }) => { - cy.get(".ds-select-dropdown").should("contain", slug); - }); -}); + cy.get('.ds-select-dropdown').should('contain', slug) + }) +}) diff --git a/cypress/support/step_definitions/Search/I_type_{string}_and_press_Enter.js b/cypress/support/step_definitions/Search/I_type_{string}_and_press_Enter.js index 796820ba0..9a94cf756 100644 --- a/cypress/support/step_definitions/Search/I_type_{string}_and_press_Enter.js +++ b/cypress/support/step_definitions/Search/I_type_{string}_and_press_Enter.js @@ -1,8 +1,8 @@ -import { When } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -When("I type {string} and press Enter", value => { - cy.get(".searchable-input .ds-select input") +defineStep('I type {string} and press Enter', value => { + cy.get('.searchable-input .ds-select input') .focus() .type(value) - .type("{enter}", { force: true }); -}); + .type('{enter}', { force: true }) +}) diff --git a/cypress/support/step_definitions/Search/I_type_{string}_and_press_escape.js b/cypress/support/step_definitions/Search/I_type_{string}_and_press_escape.js index 3e2e67be8..e393ba227 100644 --- a/cypress/support/step_definitions/Search/I_type_{string}_and_press_escape.js +++ b/cypress/support/step_definitions/Search/I_type_{string}_and_press_escape.js @@ -1,8 +1,8 @@ -import { When } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -When("I type {string} and press escape", value => { - cy.get(".searchable-input .ds-select input") +defineStep('I type {string} and press escape', value => { + cy.get('.searchable-input .ds-select input') .focus() .type(value) - .type("{esc}"); -}); + .type('{esc}') +}) diff --git a/cypress/support/step_definitions/Search/the_search_field_should_clear.js b/cypress/support/step_definitions/Search/the_search_field_should_clear.js index 10f73959f..1c31bbf99 100644 --- a/cypress/support/step_definitions/Search/the_search_field_should_clear.js +++ b/cypress/support/step_definitions/Search/the_search_field_should_clear.js @@ -1,6 +1,6 @@ -import { Then } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -Then("the search field should clear", () => { - cy.get(".searchable-input .ds-select input") - .should("have.text", ""); -}); +defineStep('the search field should clear', () => { + cy.get('.searchable-input .ds-select input') + .should('have.text', '') +}) diff --git a/cypress/support/step_definitions/Search/the_search_parameter_equals_{string}.js b/cypress/support/step_definitions/Search/the_search_parameter_equals_{string}.js index 0f433cf1f..552dd5738 100644 --- a/cypress/support/step_definitions/Search/the_search_parameter_equals_{string}.js +++ b/cypress/support/step_definitions/Search/the_search_parameter_equals_{string}.js @@ -1,6 +1,6 @@ -import { Then } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -Then("the search parameter equals {string}", search => { - cy.location("search") - .should("eq", search); -}); +defineStep('the search parameter equals {string}', search => { + cy.location('search') + .should('eq', search) +}) diff --git a/cypress/support/step_definitions/User.Authentication/I_am_logged_in_with_username_{string}.js b/cypress/support/step_definitions/User.Authentication/I_am_logged_in_with_username_{string}.js index d4af04ff6..04ca3e59b 100644 --- a/cypress/support/step_definitions/User.Authentication/I_am_logged_in_with_username_{string}.js +++ b/cypress/support/step_definitions/User.Authentication/I_am_logged_in_with_username_{string}.js @@ -1,7 +1,7 @@ -import { Then } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -Then("I am logged in with username {string}", name => { - cy.get(".avatar-menu").click(); - cy.get(".avatar-menu-popover").contains(name); - cy.get(".avatar-menu").click(); // Close menu again -}); +defineStep('I am logged in with username {string}', name => { + cy.get('.avatar-menu').click() + cy.get('.avatar-menu-popover').contains(name) + cy.get('.avatar-menu').click() // Close menu again +}) diff --git a/cypress/support/step_definitions/User.Block/I_block_the_user_{string}.js b/cypress/support/step_definitions/User.Block/I_block_the_user_{string}.js index be82f00d9..8fe506919 100644 --- a/cypress/support/step_definitions/User.Block/I_block_the_user_{string}.js +++ b/cypress/support/step_definitions/User.Block/I_block_the_user_{string}.js @@ -1,11 +1,11 @@ -import { When } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -When("I block the user {string}", name => { +defineStep('I block the user {string}', name => { cy.neode() - .firstOf("User", { name }) + .firstOf('User', { name }) .then(blockedUser => { cy.neode() - .firstOf("User", {id: "id-of-peter-pan"}) - .relateTo(blockedUser, "blocked"); - }); -}); + .firstOf('User', {id: 'id-of-peter-pan'}) + .relateTo(blockedUser, 'blocked') + }) +}) diff --git a/cypress/support/step_definitions/User.Block/I_should_not_see_{string}_button.js b/cypress/support/step_definitions/User.Block/I_should_not_see_{string}_button.js index 791a5aaaf..ae47405f3 100644 --- a/cypress/support/step_definitions/User.Block/I_should_not_see_{string}_button.js +++ b/cypress/support/step_definitions/User.Block/I_should_not_see_{string}_button.js @@ -1,6 +1,6 @@ -import { Then } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -Then('I should not see {string} button', button => { +defineStep('I should not see {string} button', button => { cy.get('.base-card .action-buttons') .should('have.length', 1) }) diff --git a/cypress/support/step_definitions/User.Block/I_should_see_no_users_in_my_blocked_users_list.js b/cypress/support/step_definitions/User.Block/I_should_see_no_users_in_my_blocked_users_list.js index 3e4813fbc..702e07df4 100644 --- a/cypress/support/step_definitions/User.Block/I_should_see_no_users_in_my_blocked_users_list.js +++ b/cypress/support/step_definitions/User.Block/I_should_see_no_users_in_my_blocked_users_list.js @@ -1,6 +1,6 @@ -import { Then } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -Then("I should see no users in my blocked users list", () => { +defineStep('I should see no users in my blocked users list', () => { cy.get('.ds-placeholder') - .should('contain', "So far, you have not blocked anybody.") + .should('contain', 'So far, you have not blocked anybody.') }) diff --git a/cypress/support/step_definitions/User.Block/I_should_see_the_{string}_button.js b/cypress/support/step_definitions/User.Block/I_should_see_the_{string}_button.js index 7e6b7eacb..a6e014130 100644 --- a/cypress/support/step_definitions/User.Block/I_should_see_the_{string}_button.js +++ b/cypress/support/step_definitions/User.Block/I_should_see_the_{string}_button.js @@ -1,6 +1,6 @@ -import { Then } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -Then('I should see the {string} button', button => { +defineStep('I should see the {string} button', button => { cy.get('.base-card .action-buttons .base-button') .should('contain', button) }) diff --git a/cypress/support/step_definitions/User.Block/I_{string}_see_{string}_from_the_content_menu_in_the_user_info_box.js b/cypress/support/step_definitions/User.Block/I_{string}_see_{string}_from_the_content_menu_in_the_user_info_box.js index bcfd9bd7a..fa568efeb 100644 --- a/cypress/support/step_definitions/User.Block/I_{string}_see_{string}_from_the_content_menu_in_the_user_info_box.js +++ b/cypress/support/step_definitions/User.Block/I_{string}_see_{string}_from_the_content_menu_in_the_user_info_box.js @@ -1,7 +1,7 @@ -import { Then } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -Then("I {string} see {string} from the content menu in the user info box", (condition, link) => { - cy.get(".user-content-menu .base-button").click() - cy.get(".popover .ds-menu-item-link") +defineStep('I {string} see {string} from the content menu in the user info box', (condition, link) => { + cy.get('.user-content-menu .base-button').click() + cy.get('.popover .ds-menu-item-link') .should(condition === 'should' ? 'contain' : 'not.contain', link) }) diff --git a/cypress/support/step_definitions/User.Block/a_user_has_blocked_me.js b/cypress/support/step_definitions/User.Block/a_user_has_blocked_me.js index 13b247ccf..a53d6b3c0 100644 --- a/cypress/support/step_definitions/User.Block/a_user_has_blocked_me.js +++ b/cypress/support/step_definitions/User.Block/a_user_has_blocked_me.js @@ -1,15 +1,15 @@ -import { When } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -When("a user has blocked me", () => { +defineStep('a user has blocked me', () => { cy.neode() - .firstOf("User", { - name: "Peter Pan" + .firstOf('User', { + name: 'Peter Pan' }) .then(blockedUser => { cy.neode() - .firstOf("User", { + .firstOf('User', { name: 'Harassing User' }) - .relateTo(blockedUser, "blocked"); - }); -}); + .relateTo(blockedUser, 'blocked') + }) +}) diff --git a/cypress/support/step_definitions/User.Block/they_should_not_see_the_comment_form.js b/cypress/support/step_definitions/User.Block/they_should_not_see_the_comment_form.js index 34aa86aaf..b9dff833d 100644 --- a/cypress/support/step_definitions/User.Block/they_should_not_see_the_comment_form.js +++ b/cypress/support/step_definitions/User.Block/they_should_not_see_the_comment_form.js @@ -1,5 +1,5 @@ -import { Then } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -Then("they should not see the comment form", () => { - cy.get(".base-card").children().should('not.have.class', 'comment-form') +defineStep('they should not see the comment form', () => { + cy.get('.base-card').children().should('not.have.class', 'comment-form') }) diff --git a/cypress/support/step_definitions/User.Block/they_should_see_a_text_explaining_why_commenting_is_not_possible.js b/cypress/support/step_definitions/User.Block/they_should_see_a_text_explaining_why_commenting_is_not_possible.js index 64f0f0fd1..0f282e8fd 100644 --- a/cypress/support/step_definitions/User.Block/they_should_see_a_text_explaining_why_commenting_is_not_possible.js +++ b/cypress/support/step_definitions/User.Block/they_should_see_a_text_explaining_why_commenting_is_not_possible.js @@ -1,5 +1,5 @@ -import { Then } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -Then("they should see a text explaining why commenting is not possible", () => { - cy.get('.ds-placeholder').should('contain', "Commenting is not possible at this time on this post.") +defineStep('they should see a text explaining why commenting is not possible', () => { + cy.get('.ds-placeholder').should('contain', 'Commenting is not possible at this time on this post.') }) diff --git a/cypress/support/step_definitions/User.Mute/I_mute_the_user_{string}.js b/cypress/support/step_definitions/User.Mute/I_mute_the_user_{string}.js index 7b52ca373..b05401d7a 100644 --- a/cypress/support/step_definitions/User.Mute/I_mute_the_user_{string}.js +++ b/cypress/support/step_definitions/User.Mute/I_mute_the_user_{string}.js @@ -1,13 +1,13 @@ -import { When } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -When("I mute the user {string}", name => { +defineStep('I mute the user {string}', name => { cy.neode() - .firstOf("User", { name }) + .firstOf('User', { name }) .then(mutedUser => { cy.neode() - .firstOf("User", { - name: "Peter Pan" + .firstOf('User', { + name: 'Peter Pan' }) - .relateTo(mutedUser, "muted"); - }); -}); + .relateTo(mutedUser, 'muted') + }) +}) diff --git a/cypress/support/step_definitions/User.Mute/the_list_of_posts_of_this_user_is_empty.js b/cypress/support/step_definitions/User.Mute/the_list_of_posts_of_this_user_is_empty.js index 66ac3bdb8..7a2f3d7df 100644 --- a/cypress/support/step_definitions/User.Mute/the_list_of_posts_of_this_user_is_empty.js +++ b/cypress/support/step_definitions/User.Mute/the_list_of_posts_of_this_user_is_empty.js @@ -1,6 +1,6 @@ -import { Then } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -Then("the list of posts of this user is empty", () => { - cy.get(".base-card").not(".post-link"); - cy.get(".main-container").find(".ds-space.hc-empty"); -}); +defineStep('the list of posts of this user is empty', () => { + cy.get('.base-card').not('.post-link') + cy.get('.main-container').find('.ds-space.hc-empty') +}) diff --git a/cypress/support/step_definitions/User.Mute/the_search_should_contain_the_annoying_user.js b/cypress/support/step_definitions/User.Mute/the_search_should_contain_the_annoying_user.js index 7d47c48aa..e47f6f5ac 100644 --- a/cypress/support/step_definitions/User.Mute/the_search_should_contain_the_annoying_user.js +++ b/cypress/support/step_definitions/User.Mute/the_search_should_contain_the_annoying_user.js @@ -1,13 +1,13 @@ -import { Then } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -Then("the search should contain the annoying user", () => { - cy.get(".searchable-input .ds-select-dropdown") +defineStep('the search should contain the annoying user', () => { + cy.get('.searchable-input .ds-select-dropdown') .should($li => { - expect($li).to.have.length(1); + expect($li).to.have.length(1) }) - cy.get(".ds-select-dropdown .user-teaser .slug") - .should("contain", '@annoying-user'); - cy.get(".searchable-input .ds-select input") + cy.get('.ds-select-dropdown .user-teaser .slug') + .should('contain', '@annoying-user') + cy.get('.searchable-input .ds-select input') .focus() - .type("{esc}"); + .type('{esc}') }) diff --git a/cypress/support/step_definitions/User.Mute/the_search_should_not_contain_posts_by_the_annoying_user.js b/cypress/support/step_definitions/User.Mute/the_search_should_not_contain_posts_by_the_annoying_user.js index 1dad99678..cdf29fc7d 100644 --- a/cypress/support/step_definitions/User.Mute/the_search_should_not_contain_posts_by_the_annoying_user.js +++ b/cypress/support/step_definitions/User.Mute/the_search_should_not_contain_posts_by_the_annoying_user.js @@ -1,10 +1,10 @@ -import { Then } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -Then("the search should not contain posts by the annoying user", () => { - cy.get(".searchable-input .ds-select-dropdown").should($li => { - expect($li).to.have.length(1); +defineStep('the search should not contain posts by the annoying user', () => { + cy.get('.searchable-input .ds-select-dropdown').should($li => { + expect($li).to.have.length(1) }) - cy.get(".ds-select-dropdown") - .should("not.have.class", '.search-post') - .should("not.contain", 'Spam') -}); + cy.get('.ds-select-dropdown') + .should('not.have.class', '.search-post') + .should('not.contain', 'Spam') +}) diff --git a/cypress/support/step_definitions/User.SettingNotifications/I_click_on_element_with_ID_{string}.js b/cypress/support/step_definitions/User.SettingNotifications/I_click_on_element_with_ID_{string}.js index 7bdb20e5d..90bc73a01 100644 --- a/cypress/support/step_definitions/User.SettingNotifications/I_click_on_element_with_ID_{string}.js +++ b/cypress/support/step_definitions/User.SettingNotifications/I_click_on_element_with_ID_{string}.js @@ -1,5 +1,5 @@ -import { When } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -When("I click on element with ID {string}", (id) => { +defineStep('I click on element with ID {string}', (id) => { cy.get('#' + id).click() }) diff --git a/cypress/support/step_definitions/User.SettingNotifications/I_click_save.js b/cypress/support/step_definitions/User.SettingNotifications/I_click_save.js index 9412d7912..40a37dc9e 100644 --- a/cypress/support/step_definitions/User.SettingNotifications/I_click_save.js +++ b/cypress/support/step_definitions/User.SettingNotifications/I_click_save.js @@ -1,5 +1,5 @@ -import { Then } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -Then("I click save", () => { - cy.get(".save-button").click() +defineStep('I click save', () => { + cy.get('.save-button').click() }) diff --git a/cypress/support/step_definitions/UserProfile.Avatar/I_cannot_upload_a_picture.js b/cypress/support/step_definitions/UserProfile.Avatar/I_cannot_upload_a_picture.js index 9e44b55ba..792c6462c 100644 --- a/cypress/support/step_definitions/UserProfile.Avatar/I_cannot_upload_a_picture.js +++ b/cypress/support/step_definitions/UserProfile.Avatar/I_cannot_upload_a_picture.js @@ -1,8 +1,8 @@ -import { Then } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -Then("I cannot upload a picture", () => { - cy.get(".base-card") +defineStep('I cannot upload a picture', () => { + cy.get('.base-card') .children() - .should("not.have.id", "customdropzone") - .should("have.class", "profile-avatar"); -}); + .should('not.have.id', 'customdropzone') + .should('have.class', 'profile-avatar') +}) diff --git a/cypress/support/step_definitions/UserProfile.Avatar/I_should_be_able_to_change_my_profile_picture.js b/cypress/support/step_definitions/UserProfile.Avatar/I_should_be_able_to_change_my_profile_picture.js index b1b2401e2..3a175c3fe 100644 --- a/cypress/support/step_definitions/UserProfile.Avatar/I_should_be_able_to_change_my_profile_picture.js +++ b/cypress/support/step_definitions/UserProfile.Avatar/I_should_be_able_to_change_my_profile_picture.js @@ -1,15 +1,15 @@ -import { Then } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -Then("I should be able to change my profile picture", () => { - const avatarUpload = "onourjourney.png"; +defineStep('I should be able to change my profile picture', () => { + const avatarUpload = 'onourjourney.png' - cy.get("#customdropzone").selectFile( - { contents: `cypress/fixtures/${avatarUpload}`, fileName: avatarUpload, mimeType: "image/png" }, - { action: "drag-drop" } - ); - cy.get(".profile-page-avatar img") - .should("have.attr", "src") - .and("contains", "onourjourney"); - cy.contains(".iziToast-message", "Upload successful") - .should("have.length",1); -}); + cy.get('#customdropzone').selectFile( + { contents: `cypress/fixtures/${avatarUpload}`, fileName: avatarUpload, mimeType: 'image/png' }, + { action: 'drag-drop' } + ) + cy.get('.profile-page-avatar img') + .should('have.attr', 'src') + .and('contains', 'onourjourney') + cy.contains('.iziToast-message', 'Upload successful') + .should('have.length',1) +}) diff --git a/cypress/support/step_definitions/UserProfile.ChangePassword/I_can_login_successfully.js b/cypress/support/step_definitions/UserProfile.ChangePassword/I_can_login_successfully.js index 1349b5eb9..ad4fc6076 100644 --- a/cypress/support/step_definitions/UserProfile.ChangePassword/I_can_login_successfully.js +++ b/cypress/support/step_definitions/UserProfile.ChangePassword/I_can_login_successfully.js @@ -1,7 +1,6 @@ -import { Then } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -Then("I can login successfully", () => { - // cy.reload(); - cy.get(".iziToast-wrapper") - .should("contain", "You are logged in!"); -}); +defineStep('I can login successfully', () => { + cy.get('.iziToast-wrapper') + .should('contain', 'You are logged in!') +}) diff --git a/cypress/support/step_definitions/UserProfile.ChangePassword/I_cannot_login_anymore.js b/cypress/support/step_definitions/UserProfile.ChangePassword/I_cannot_login_anymore.js index f6159c79b..544752b1a 100644 --- a/cypress/support/step_definitions/UserProfile.ChangePassword/I_cannot_login_anymore.js +++ b/cypress/support/step_definitions/UserProfile.ChangePassword/I_cannot_login_anymore.js @@ -1,7 +1,6 @@ -import { Then } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -Then("I cannot login anymore", password => { - //cy.reload(); - cy.get(".iziToast-wrapper") - .should("contain", "Incorrect email address or password."); -}); +defineStep('I cannot login anymore', password => { + cy.get('.iziToast-wrapper') + .should('contain', 'Incorrect email address or password.') +}) diff --git a/cypress/support/step_definitions/UserProfile.ChangePassword/I_cannot_submit_the_form.js b/cypress/support/step_definitions/UserProfile.ChangePassword/I_cannot_submit_the_form.js index 02a2c7d83..643b44e20 100644 --- a/cypress/support/step_definitions/UserProfile.ChangePassword/I_cannot_submit_the_form.js +++ b/cypress/support/step_definitions/UserProfile.ChangePassword/I_cannot_submit_the_form.js @@ -1,6 +1,6 @@ -import { When } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -When("I cannot submit the form", () => { - cy.get("button[type=submit]") - .should('be.disabled'); -}); +defineStep('I cannot submit the form', () => { + cy.get('button[type=submit]') + .should('be.disabled') +}) diff --git a/cypress/support/step_definitions/UserProfile.ChangePassword/I_fill_the_password_form_with.js b/cypress/support/step_definitions/UserProfile.ChangePassword/I_fill_the_password_form_with.js index af0c6639b..e430f6af6 100644 --- a/cypress/support/step_definitions/UserProfile.ChangePassword/I_fill_the_password_form_with.js +++ b/cypress/support/step_definitions/UserProfile.ChangePassword/I_fill_the_password_form_with.js @@ -1,11 +1,11 @@ -import { When } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -When("I fill the password form with:", table => { - table = table.rowsHash(); - cy.get("input[id=oldPassword]") - .type(table["Your old password"]) - .get("input[id=password]") - .type(table["Your new password"]) - .get("input[id=passwordConfirmation]") - .type(table["Confirm new password"]); -}); +defineStep('I fill the password form with:', table => { + table = table.rowsHash() + cy.get('input[id=oldPassword]') + .type(table['Your old password']) + .get('input[id=password]') + .type(table['Your new password']) + .get('input[id=passwordConfirmation]') + .type(table['Confirm new password']) +}) diff --git a/cypress/support/step_definitions/UserProfile.ChangePassword/I_submit_the_form.js b/cypress/support/step_definitions/UserProfile.ChangePassword/I_submit_the_form.js index 8b17f6de1..268615560 100644 --- a/cypress/support/step_definitions/UserProfile.ChangePassword/I_submit_the_form.js +++ b/cypress/support/step_definitions/UserProfile.ChangePassword/I_submit_the_form.js @@ -1,5 +1,5 @@ -import { When } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -When("I submit the form", () => { - cy.get("form").submit(); -}); +defineStep('I submit the form', () => { + cy.get('form').submit() +}) diff --git a/cypress/support/step_definitions/UserProfile.NameDescriptionLocation/I_can_see_my_new_name_{string}_when_I_click_on_my_profile_picture_in_the_top_right.js b/cypress/support/step_definitions/UserProfile.NameDescriptionLocation/I_can_see_my_new_name_{string}_when_I_click_on_my_profile_picture_in_the_top_right.js index c5dd84bf0..f63508ce8 100644 --- a/cypress/support/step_definitions/UserProfile.NameDescriptionLocation/I_can_see_my_new_name_{string}_when_I_click_on_my_profile_picture_in_the_top_right.js +++ b/cypress/support/step_definitions/UserProfile.NameDescriptionLocation/I_can_see_my_new_name_{string}_when_I_click_on_my_profile_picture_in_the_top_right.js @@ -1,10 +1,10 @@ -import { Then } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -Then('I can see my new name {string} when I click on my profile picture in the top right', name => { +defineStep('I can see my new name {string} when I click on my profile picture in the top right', name => { cy.get(".avatar-menu").then(($menu) => { if (!$menu.is(':visible')){ - cy.scrollTo("top"); - cy.wait(500); + cy.scrollTo("top") + cy.wait(500) } }) cy.get('.avatar-menu').click() // open diff --git a/cypress/support/step_definitions/UserProfile.NameDescriptionLocation/I_have_the_following_self-description.js b/cypress/support/step_definitions/UserProfile.NameDescriptionLocation/I_have_the_following_self-description.js index f0f6ba4da..950f320ef 100644 --- a/cypress/support/step_definitions/UserProfile.NameDescriptionLocation/I_have_the_following_self-description.js +++ b/cypress/support/step_definitions/UserProfile.NameDescriptionLocation/I_have_the_following_self-description.js @@ -1,6 +1,6 @@ -import { When } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -When('I have the following self-description:', text => { +defineStep('I have the following self-description:', text => { cy.get('textarea[id=about]') .clear() .type(text) diff --git a/cypress/support/step_definitions/UserProfile.NameDescriptionLocation/I_save_{string}_as_my_location.js b/cypress/support/step_definitions/UserProfile.NameDescriptionLocation/I_save_{string}_as_my_location.js index 00d5141f8..2e708564c 100644 --- a/cypress/support/step_definitions/UserProfile.NameDescriptionLocation/I_save_{string}_as_my_location.js +++ b/cypress/support/step_definitions/UserProfile.NameDescriptionLocation/I_save_{string}_as_my_location.js @@ -1,6 +1,6 @@ -import { When } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -When('I save {string} as my location', location => { +defineStep('I save {string} as my location', location => { cy.get('input[id=city]').type(location) cy.get('.ds-select-option') .contains(location) diff --git a/cypress/support/step_definitions/UserProfile.NameDescriptionLocation/I_save_{string}_as_my_new_name.js b/cypress/support/step_definitions/UserProfile.NameDescriptionLocation/I_save_{string}_as_my_new_name.js index b94683a5b..487458b26 100644 --- a/cypress/support/step_definitions/UserProfile.NameDescriptionLocation/I_save_{string}_as_my_new_name.js +++ b/cypress/support/step_definitions/UserProfile.NameDescriptionLocation/I_save_{string}_as_my_new_name.js @@ -1,6 +1,6 @@ -import { When } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -When('I save {string} as my new name', name => { +defineStep('I save {string} as my new name', name => { cy.get('input[id=name]') .clear() .type(name) diff --git a/cypress/support/step_definitions/UserProfile.NameDescriptionLocation/they_can_see_the_following_text_in_the_info_box_below_my_avatar.js b/cypress/support/step_definitions/UserProfile.NameDescriptionLocation/they_can_see_the_following_text_in_the_info_box_below_my_avatar.js index d416c8d10..823aec202 100644 --- a/cypress/support/step_definitions/UserProfile.NameDescriptionLocation/they_can_see_the_following_text_in_the_info_box_below_my_avatar.js +++ b/cypress/support/step_definitions/UserProfile.NameDescriptionLocation/they_can_see_the_following_text_in_the_info_box_below_my_avatar.js @@ -1,5 +1,5 @@ -import { When } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -When('they can see the following text in the info box below my avatar:', text => { +defineStep('they can see the following text in the info box below my avatar:', text => { cy.contains(text) }) diff --git a/cypress/support/step_definitions/UserProfile.NameDescriptionLocation/they_can_see_{string}_in_the_info_box_below_my_avatar.js b/cypress/support/step_definitions/UserProfile.NameDescriptionLocation/they_can_see_{string}_in_the_info_box_below_my_avatar.js index ea8cf2158..58aa2b3c0 100644 --- a/cypress/support/step_definitions/UserProfile.NameDescriptionLocation/they_can_see_{string}_in_the_info_box_below_my_avatar.js +++ b/cypress/support/step_definitions/UserProfile.NameDescriptionLocation/they_can_see_{string}_in_the_info_box_below_my_avatar.js @@ -1,5 +1,5 @@ -import { Then } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -Then('they can see {string} in the info box below my avatar', location => { +defineStep('they can see {string} in the info box below my avatar', location => { cy.contains(location) }) diff --git a/cypress/support/step_definitions/UserProfile.SocialMedia/I_add_a_social_media_link.js b/cypress/support/step_definitions/UserProfile.SocialMedia/I_add_a_social_media_link.js index eab8ba0d6..d1100e035 100644 --- a/cypress/support/step_definitions/UserProfile.SocialMedia/I_add_a_social_media_link.js +++ b/cypress/support/step_definitions/UserProfile.SocialMedia/I_add_a_social_media_link.js @@ -1,6 +1,6 @@ -import { When } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -When('I add a social media link', () => { +defineStep('I add a social media link', () => { cy.get('[data-test="add-save-button"]') .click() .get('#editSocialMedia') diff --git a/cypress/support/step_definitions/UserProfile.SocialMedia/I_can_cancel_editing.js b/cypress/support/step_definitions/UserProfile.SocialMedia/I_can_cancel_editing.js index 72d83c9a3..9a17cee7f 100644 --- a/cypress/support/step_definitions/UserProfile.SocialMedia/I_can_cancel_editing.js +++ b/cypress/support/step_definitions/UserProfile.SocialMedia/I_can_cancel_editing.js @@ -1,6 +1,6 @@ -import { Then } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -Then('I can cancel editing', () => { +defineStep('I can cancel editing', () => { cy.get('button#cancel') .click() .get('input#editSocialMedia') diff --git a/cypress/support/step_definitions/UserProfile.SocialMedia/I_delete_a_social_media_link.js b/cypress/support/step_definitions/UserProfile.SocialMedia/I_delete_a_social_media_link.js index 1f0f3e22e..4adf6d0d6 100644 --- a/cypress/support/step_definitions/UserProfile.SocialMedia/I_delete_a_social_media_link.js +++ b/cypress/support/step_definitions/UserProfile.SocialMedia/I_delete_a_social_media_link.js @@ -1,6 +1,6 @@ -import { When } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -When('I delete a social media link', () => { +defineStep('I delete a social media link', () => { cy.get(".base-button[title='Delete']") .click() }) diff --git a/cypress/support/step_definitions/UserProfile.SocialMedia/I_delete_the_social_media_link_{string}.js b/cypress/support/step_definitions/UserProfile.SocialMedia/I_delete_the_social_media_link_{string}.js index 3acba4756..3c3e4286e 100644 --- a/cypress/support/step_definitions/UserProfile.SocialMedia/I_delete_the_social_media_link_{string}.js +++ b/cypress/support/step_definitions/UserProfile.SocialMedia/I_delete_the_social_media_link_{string}.js @@ -1,6 +1,6 @@ -import { When } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -When('I delete the social media link {string}', (link) => { +defineStep('I delete the social media link {string}', (link) => { cy.get('[data-test="delete-button"]') .click() cy.get('[data-test="confirm-modal"]') diff --git a/cypress/support/step_definitions/UserProfile.SocialMedia/I_edit_and_save_the_link.js b/cypress/support/step_definitions/UserProfile.SocialMedia/I_edit_and_save_the_link.js index 3cec61688..d1b11e322 100644 --- a/cypress/support/step_definitions/UserProfile.SocialMedia/I_edit_and_save_the_link.js +++ b/cypress/support/step_definitions/UserProfile.SocialMedia/I_edit_and_save_the_link.js @@ -1,6 +1,6 @@ -import { When } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -When('I edit and save the link', () => { +defineStep('I edit and save the link', () => { cy.get('input#editSocialMedia') .clear() .type('https://freeradical.zone/tinkerbell') diff --git a/cypress/support/step_definitions/UserProfile.SocialMedia/I_have_added_a_social_media_link.js b/cypress/support/step_definitions/UserProfile.SocialMedia/I_have_added_a_social_media_link.js index b1eb698de..9567ab8a6 100644 --- a/cypress/support/step_definitions/UserProfile.SocialMedia/I_have_added_a_social_media_link.js +++ b/cypress/support/step_definitions/UserProfile.SocialMedia/I_have_added_a_social_media_link.js @@ -1,6 +1,6 @@ -import { Given } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -Given('I have added a social media link', () => { +defineStep('I have added a social media link', () => { cy.visit('/settings/my-social-media') .get('button') .contains('Add link') diff --git a/cypress/support/step_definitions/UserProfile.SocialMedia/I_have_added_the_social_media_link_{string}.js b/cypress/support/step_definitions/UserProfile.SocialMedia/I_have_added_the_social_media_link_{string}.js index d0daab843..709f45831 100644 --- a/cypress/support/step_definitions/UserProfile.SocialMedia/I_have_added_the_social_media_link_{string}.js +++ b/cypress/support/step_definitions/UserProfile.SocialMedia/I_have_added_the_social_media_link_{string}.js @@ -1,6 +1,6 @@ -import { Given } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -Given('I have added the social media link {string}', (link) => { +defineStep('I have added the social media link {string}', (link) => { cy.visit('/settings/my-social-media') .get('[data-test="add-save-button"]') .click() diff --git a/cypress/support/step_definitions/UserProfile.SocialMedia/I_start_editing_a_social_media_link.js b/cypress/support/step_definitions/UserProfile.SocialMedia/I_start_editing_a_social_media_link.js index 11ee84084..0d533e152 100644 --- a/cypress/support/step_definitions/UserProfile.SocialMedia/I_start_editing_a_social_media_link.js +++ b/cypress/support/step_definitions/UserProfile.SocialMedia/I_start_editing_a_social_media_link.js @@ -1,6 +1,6 @@ -import { When } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -When('I start editing a social media link', () => { +defineStep('I start editing a social media link', () => { cy.get('[data-test="edit-button"]') .click() }) diff --git a/cypress/support/step_definitions/UserProfile.SocialMedia/the_new_social_media_link_shows_up_on_the_page.js b/cypress/support/step_definitions/UserProfile.SocialMedia/the_new_social_media_link_shows_up_on_the_page.js index fd1bf9350..76a921ca2 100644 --- a/cypress/support/step_definitions/UserProfile.SocialMedia/the_new_social_media_link_shows_up_on_the_page.js +++ b/cypress/support/step_definitions/UserProfile.SocialMedia/the_new_social_media_link_shows_up_on_the_page.js @@ -1,6 +1,6 @@ -import { Then } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -Then('the new social media link shows up on the page', () => { +defineStep('the new social media link shows up on the page', () => { cy.get('a[href="https://freeradical.zone/peter-pan"]') .should('have.length', 1) }) diff --git a/cypress/support/step_definitions/UserProfile.SocialMedia/the_new_url_is_displayed.js b/cypress/support/step_definitions/UserProfile.SocialMedia/the_new_url_is_displayed.js index c9c7dd906..576d6d3a9 100644 --- a/cypress/support/step_definitions/UserProfile.SocialMedia/the_new_url_is_displayed.js +++ b/cypress/support/step_definitions/UserProfile.SocialMedia/the_new_url_is_displayed.js @@ -1,6 +1,6 @@ -import { Then } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -Then('the new url is displayed', () => { +defineStep('the new url is displayed', () => { cy.get("a[href='https://freeradical.zone/tinkerbell']") .should('have.length', 1) }) diff --git a/cypress/support/step_definitions/UserProfile.SocialMedia/the_old_url_is_not_displayed.js b/cypress/support/step_definitions/UserProfile.SocialMedia/the_old_url_is_not_displayed.js index c0941f600..6d71c8eb6 100644 --- a/cypress/support/step_definitions/UserProfile.SocialMedia/the_old_url_is_not_displayed.js +++ b/cypress/support/step_definitions/UserProfile.SocialMedia/the_old_url_is_not_displayed.js @@ -1,6 +1,6 @@ -import { Then } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -Then('the old url is not displayed', () => { +defineStep('the old url is not displayed', () => { cy.get("a[href='https://freeradical.zone/peter-pan']") .should('have.length', 0) }) diff --git a/cypress/support/step_definitions/UserProfile.SocialMedia/they_should_be_able_to_see_my_social_media_links.js b/cypress/support/step_definitions/UserProfile.SocialMedia/they_should_be_able_to_see_my_social_media_links.js index 3b1692b59..d800c9a05 100644 --- a/cypress/support/step_definitions/UserProfile.SocialMedia/they_should_be_able_to_see_my_social_media_links.js +++ b/cypress/support/step_definitions/UserProfile.SocialMedia/they_should_be_able_to_see_my_social_media_links.js @@ -1,6 +1,6 @@ -import { Then } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -Then('they should be able to see my social media links', () => { +defineStep('they should be able to see my social media links', () => { cy.get('[data-test="social-media-list-headline"]') .contains('Peter Pan') .get('a[href="https://freeradical.zone/peter-pan"]') diff --git a/cypress/support/step_definitions/common/I_am_logged_in_as_{string}.js b/cypress/support/step_definitions/common/I_am_logged_in_as_{string}.js index 833a21c6a..b8153190c 100644 --- a/cypress/support/step_definitions/common/I_am_logged_in_as_{string}.js +++ b/cypress/support/step_definitions/common/I_am_logged_in_as_{string}.js @@ -1,7 +1,7 @@ -import { Given } from '@badeball/cypress-cucumber-preprocessor' +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' import encode from '../../../../backend/build/src/jwt/encode' -Given('I am logged in as {string}', slug => { +defineStep('I am logged in as {string}', slug => { cy.neode() .firstOf('User', { slug }) .then(user => { @@ -15,4 +15,4 @@ Given('I am logged in as {string}', slug => { .then(user => { cy.setCookie('ocelot-social-token', encode(user)) }) -}); +}) diff --git a/cypress/support/step_definitions/common/I_am_on_page_{string}.js b/cypress/support/step_definitions/common/I_am_on_page_{string}.js index 44b10c4c4..b12b24ae5 100644 --- a/cypress/support/step_definitions/common/I_am_on_page_{string}.js +++ b/cypress/support/step_definitions/common/I_am_on_page_{string}.js @@ -1,6 +1,6 @@ -import { Then } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -Then("I am on page {string}", page => { - cy.location("pathname") - .should("match", new RegExp(page)); -}); +defineStep('I am on page {string}', page => { + cy.location('pathname') + .should('match', new RegExp(page)) +}) diff --git a/cypress/support/step_definitions/common/I_can_see_the_following_table.js b/cypress/support/step_definitions/common/I_can_see_the_following_table.js index f62e1a99a..7bbc5201e 100644 --- a/cypress/support/step_definitions/common/I_can_see_the_following_table.js +++ b/cypress/support/step_definitions/common/I_can_see_the_following_table.js @@ -1,6 +1,6 @@ -import { Then } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -Then('I can see the following table:', table => { +defineStep('I can see the following table:', table => { const headers = table.raw()[0] headers.forEach((expected, i) => { cy.get('thead th') diff --git a/cypress/support/step_definitions/common/I_choose_the_following_text_as_content.js b/cypress/support/step_definitions/common/I_choose_the_following_text_as_content.js index 51d77d8e1..97e2865d2 100644 --- a/cypress/support/step_definitions/common/I_choose_the_following_text_as_content.js +++ b/cypress/support/step_definitions/common/I_choose_the_following_text_as_content.js @@ -1,9 +1,9 @@ -import { When } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -When("I choose the following text as content:", text => { +defineStep('I choose the following text as content:', text => { cy.task('getValue', 'lastPost').then(lastPost => { - lastPost.content = text.replace("\n", " "); + lastPost.content = text.replace('\n', ' ') cy.task('pushValue', { name: 'lastPost', value: lastPost }) - cy.get(".editor .ProseMirror").type(lastPost.content); + cy.get('.editor .ProseMirror').type(lastPost.content) }) -}); +}) diff --git a/cypress/support/step_definitions/common/I_click_on_{string}.js b/cypress/support/step_definitions/common/I_click_on_{string}.js index 9d51f27f7..799357784 100644 --- a/cypress/support/step_definitions/common/I_click_on_{string}.js +++ b/cypress/support/step_definitions/common/I_click_on_{string}.js @@ -1,6 +1,6 @@ -import { When } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -When("I click on {string}", element => { +defineStep('I click on {string}', element => { const elementSelectors = { 'submit button': 'button[name=submit]', 'create post button': '.post-add-button', @@ -15,5 +15,5 @@ When("I click on {string}", element => { cy.get(elementSelectors[element]) .click() - .wait(750); -}); + .wait(750) +}) diff --git a/cypress/support/step_definitions/common/I_click_on_{string}_from_the_content_menu_in_the_user_info_box.js b/cypress/support/step_definitions/common/I_click_on_{string}_from_the_content_menu_in_the_user_info_box.js index 66373037e..8912b5974 100644 --- a/cypress/support/step_definitions/common/I_click_on_{string}_from_the_content_menu_in_the_user_info_box.js +++ b/cypress/support/step_definitions/common/I_click_on_{string}_from_the_content_menu_in_the_user_info_box.js @@ -1,12 +1,12 @@ -import { When } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -When("I click on {string} from the content menu in the user info box", +defineStep('I click on {string} from the content menu in the user info box', button => { - cy.get(".user-content-menu .base-button").click(); - cy.get(".popover .ds-menu-item-link") + cy.get('.user-content-menu .base-button').click() + cy.get('.popover .ds-menu-item-link') .contains(button) .click({ force: true - }); + }) } -); +) diff --git a/cypress/support/step_definitions/common/I_click_the_checkbox_show_donations_progress_bar_and_save.js b/cypress/support/step_definitions/common/I_click_the_checkbox_show_donations_progress_bar_and_save.js index 257b2b556..2c588e635 100644 --- a/cypress/support/step_definitions/common/I_click_the_checkbox_show_donations_progress_bar_and_save.js +++ b/cypress/support/step_definitions/common/I_click_the_checkbox_show_donations_progress_bar_and_save.js @@ -1,8 +1,8 @@ -import { Then } from "@badeball/cypress-cucumber-preprocessor"; -import 'cypress-network-idle'; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' +import 'cypress-network-idle' -Then("I click the checkbox show donations progress bar and save", () => { - cy.get("#showDonations").click() - cy.get(".donations-info-button").click() +defineStep('I click the checkbox show donations progress bar and save', () => { + cy.get('#showDonations').click() + cy.get('.donations-info-button').click() cy.waitForNetworkIdle(2000) }) diff --git a/cypress/support/step_definitions/common/I_fill_in_my_credentials_{string}_{string}.js b/cypress/support/step_definitions/common/I_fill_in_my_credentials_{string}_{string}.js index 3c0b0d02e..00db6344c 100644 --- a/cypress/support/step_definitions/common/I_fill_in_my_credentials_{string}_{string}.js +++ b/cypress/support/step_definitions/common/I_fill_in_my_credentials_{string}_{string}.js @@ -1,12 +1,12 @@ -import { When } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -When("I fill in my credentials {string} {string}", (email,password) => { - cy.get("input[name=email]") - .trigger("focus") +defineStep('I fill in my credentials {string} {string}', (email,password) => { + cy.get('input[name=email]') + .trigger('focus') .type('{selectall}{backspace}') .type(email) - .get("input[name=password]") - .trigger("focus") + .get('input[name=password]') + .trigger('focus') .type('{selectall}{backspace}') - .type(password); -}); + .type(password) +}) diff --git a/cypress/support/step_definitions/common/I_follow_the_user_{string}.js b/cypress/support/step_definitions/common/I_follow_the_user_{string}.js index 3698daee8..bca16ad8a 100644 --- a/cypress/support/step_definitions/common/I_follow_the_user_{string}.js +++ b/cypress/support/step_definitions/common/I_follow_the_user_{string}.js @@ -1,13 +1,13 @@ -import { Given } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -Given("I follow the user {string}", name => { +defineStep('I follow the user {string}', name => { cy.neode() - .firstOf("User", {name}) + .firstOf('User', {name}) .then(followed => { cy.neode() - .firstOf("User", { - name: "Peter Pan" + .firstOf('User', { + name: 'Peter Pan' }) - .relateTo(followed, "following"); - }); -}); + .relateTo(followed, 'following') + }) +}) diff --git a/cypress/support/step_definitions/common/I_get_removed_from_his_follower_collection.js b/cypress/support/step_definitions/common/I_get_removed_from_his_follower_collection.js index 7721efda0..36ef62a54 100644 --- a/cypress/support/step_definitions/common/I_get_removed_from_his_follower_collection.js +++ b/cypress/support/step_definitions/common/I_get_removed_from_his_follower_collection.js @@ -1,8 +1,8 @@ -import { Then } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -Then("I get removed from his follower collection", () => { - cy.get(".base-card") - .not(".post-link"); - cy.get(".main-container") - .contains(".base-card","is not followed by anyone"); - }); +defineStep('I get removed from his follower collection', () => { + cy.get('.base-card') + .not('.post-link') + cy.get('.main-container') + .contains('.base-card','is not followed by anyone') + }) diff --git a/cypress/support/step_definitions/common/I_log_out.js b/cypress/support/step_definitions/common/I_log_out.js index c97506535..efe14a9c9 100644 --- a/cypress/support/step_definitions/common/I_log_out.js +++ b/cypress/support/step_definitions/common/I_log_out.js @@ -1,15 +1,15 @@ -import { When } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -When("I log out", () => { - cy.get(".avatar-menu").then(($menu) => { +defineStep('I log out', () => { + cy.get('.avatar-menu').then(($menu) => { if (!$menu.is(':visible')){ - cy.scrollTo("top"); - cy.wait(500); + cy.scrollTo('top') + cy.wait(500) } }) - cy.get(".avatar-menu") - .click(); - cy.get(".avatar-menu-popover") + cy.get('.avatar-menu') + .click() + cy.get('.avatar-menu-popover') .find('a[href="/logout"]') - .click(); -}); + .click() +}) diff --git a/cypress/support/step_definitions/common/I_navigate_to_my_{string}_settings_page.js b/cypress/support/step_definitions/common/I_navigate_to_my_{string}_settings_page.js index 91a3b58d9..73b93d3d3 100644 --- a/cypress/support/step_definitions/common/I_navigate_to_my_{string}_settings_page.js +++ b/cypress/support/step_definitions/common/I_navigate_to_my_{string}_settings_page.js @@ -1,10 +1,10 @@ -import { When } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -When("I navigate to my {string} settings page", settingsPage => { - cy.get(".avatar-menu-trigger").click(); - cy.get(".avatar-menu-popover") - .find("a[href]") - .contains("Settings") - .click(); - cy.contains(".ds-menu-item-link", settingsPage).click(); -}); +defineStep('I navigate to my {string} settings page', settingsPage => { + cy.get('.avatar-menu-trigger').click() + cy.get('.avatar-menu-popover') + .find('a[href]') + .contains('Settings') + .click() + cy.contains('.ds-menu-item-link', settingsPage).click() +}) diff --git a/cypress/support/step_definitions/common/I_navigate_to_page_{string}.js b/cypress/support/step_definitions/common/I_navigate_to_page_{string}.js index d90cc906c..509a49f43 100644 --- a/cypress/support/step_definitions/common/I_navigate_to_page_{string}.js +++ b/cypress/support/step_definitions/common/I_navigate_to_page_{string}.js @@ -1,7 +1,7 @@ -import { Given } from "@badeball/cypress-cucumber-preprocessor"; -import 'cypress-network-idle'; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' +import 'cypress-network-idle' -Given("I navigate to page {string}", page => { - cy.visit(page); +defineStep('I navigate to page {string}', page => { + cy.visit(page) cy.waitForNetworkIdle(2000) -}); +}) diff --git a/cypress/support/step_definitions/common/I_refresh_the_page.js b/cypress/support/step_definitions/common/I_refresh_the_page.js index 47e493fe4..5c48ad671 100644 --- a/cypress/support/step_definitions/common/I_refresh_the_page.js +++ b/cypress/support/step_definitions/common/I_refresh_the_page.js @@ -1,6 +1,6 @@ -import { When } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -When('I refresh the page', () => { +defineStep('I refresh the page', () => { cy.visit('/') - .reload(); -}); + .reload() +}) diff --git a/cypress/support/step_definitions/common/I_search_for_{string}.js b/cypress/support/step_definitions/common/I_search_for_{string}.js index f91959182..d568d1332 100644 --- a/cypress/support/step_definitions/common/I_search_for_{string}.js +++ b/cypress/support/step_definitions/common/I_search_for_{string}.js @@ -1,12 +1,12 @@ -import { When } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -When("I search for {string}", value => { +defineStep('I search for {string}', value => { cy.intercept({ - method: "POST", - url: "http://localhost:3000/api", - }).as("graphqlRequest"); - cy.get(".searchable-input .ds-select input") + method: 'POST', + url: 'http://localhost:3000/api', + }).as('graphqlRequest') + cy.get('.searchable-input .ds-select input') .focus() - .type(value); - cy.wait("@graphqlRequest"); -}); + .type(value) + cy.wait('@graphqlRequest') +}) diff --git a/cypress/support/step_definitions/common/I_see_a_toaster_with_status_{string}.js b/cypress/support/step_definitions/common/I_see_a_toaster_with_status_{string}.js index c7bd91e29..059f60184 100644 --- a/cypress/support/step_definitions/common/I_see_a_toaster_with_status_{string}.js +++ b/cypress/support/step_definitions/common/I_see_a_toaster_with_status_{string}.js @@ -1,9 +1,9 @@ -import { Then } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -Then("I see a toaster with status {string}", (status) => { +defineStep('I see a toaster with status {string}', (status) => { switch (status) { - case "success": - cy.get(".iziToast.iziToast-color-green").should("be.visible"); - break; + case 'success': + cy.get('.iziToast.iziToast-color-green').should('be.visible') + break } }) diff --git a/cypress/support/step_definitions/common/I_see_a_toaster_with_{string}.js b/cypress/support/step_definitions/common/I_see_a_toaster_with_{string}.js index e1496b8de..332e525a4 100644 --- a/cypress/support/step_definitions/common/I_see_a_toaster_with_{string}.js +++ b/cypress/support/step_definitions/common/I_see_a_toaster_with_{string}.js @@ -1,5 +1,5 @@ -import { Then } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -Then("I see a toaster with {string}", (title) => { - cy.get(".iziToast-message").should("contain", title); +defineStep('I see a toaster with {string}', (title) => { + cy.get('.iziToast-message').should('contain', title) }) diff --git a/cypress/support/step_definitions/common/I_see_a_{string}_message.js b/cypress/support/step_definitions/common/I_see_a_{string}_message.js index cc8deca5f..8add062ec 100644 --- a/cypress/support/step_definitions/common/I_see_a_{string}_message.js +++ b/cypress/support/step_definitions/common/I_see_a_{string}_message.js @@ -1,5 +1,5 @@ -import { Then } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -Then("I see a {string} message:", (_type, message) => { - cy.contains(message); -}); +defineStep('I see a {string} message:', (_type, message) => { + cy.contains(message) +}) diff --git a/cypress/support/step_definitions/common/I_should_see_the_following_posts_in_the_select_dropdown.js b/cypress/support/step_definitions/common/I_should_see_the_following_posts_in_the_select_dropdown.js index 88e18a280..cef95198b 100644 --- a/cypress/support/step_definitions/common/I_should_see_the_following_posts_in_the_select_dropdown.js +++ b/cypress/support/step_definitions/common/I_should_see_the_following_posts_in_the_select_dropdown.js @@ -1,8 +1,8 @@ -import { Then } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -Then("I should see the following posts in the select dropdown:", table => { +defineStep('I should see the following posts in the select dropdown:', table => { table.hashes().forEach(({ title }) => { - cy.get(".ds-select-dropdown") - .should("contain", title); - }); -}); + cy.get('.ds-select-dropdown') + .should('contain', title) + }) +}) diff --git a/cypress/support/step_definitions/common/I_wait_for_{int}_milliseconds.js b/cypress/support/step_definitions/common/I_wait_for_{int}_milliseconds.js index 2ed462340..aa884d08c 100644 --- a/cypress/support/step_definitions/common/I_wait_for_{int}_milliseconds.js +++ b/cypress/support/step_definitions/common/I_wait_for_{int}_milliseconds.js @@ -1,5 +1,5 @@ -import { When } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -When("I wait for {int} milliseconds", time => { +defineStep('I wait for {int} milliseconds', time => { cy.wait(time) -}); +}) diff --git a/cypress/support/step_definitions/common/the_checkbox_with_ID_{string}_should_{string}.js b/cypress/support/step_definitions/common/the_checkbox_with_ID_{string}_should_{string}.js index 603524804..1e223909c 100644 --- a/cypress/support/step_definitions/common/the_checkbox_with_ID_{string}_should_{string}.js +++ b/cypress/support/step_definitions/common/the_checkbox_with_ID_{string}_should_{string}.js @@ -1,5 +1,5 @@ -import { When } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -When("the checkbox with ID {string} should {string}", (id, value) => { +defineStep('the checkbox with ID {string} should {string}', (id, value) => { cy.get('#' + id).should(value) }) diff --git a/cypress/support/step_definitions/common/the_first_post_on_the_newsfeed_has_the_title.js b/cypress/support/step_definitions/common/the_first_post_on_the_newsfeed_has_the_title.js index ba98a7a8e..0e3a804cc 100644 --- a/cypress/support/step_definitions/common/the_first_post_on_the_newsfeed_has_the_title.js +++ b/cypress/support/step_definitions/common/the_first_post_on_the_newsfeed_has_the_title.js @@ -1,6 +1,6 @@ -import { Then } from "@badeball/cypress-cucumber-preprocessor"; +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' -Then("the first post on the newsfeed has the title:", title => { - cy.get(".post-teaser:first") - .should("contain", title); -}); +defineStep('the first post on the newsfeed has the title:', title => { + cy.get('.post-teaser:first') + .should('contain', title) +}) diff --git a/cypress/support/step_definitions/common/the_following_{string}_are_in_the_database.js b/cypress/support/step_definitions/common/the_following_{string}_are_in_the_database.js index 066b5ef3d..94c647745 100644 --- a/cypress/support/step_definitions/common/the_following_{string}_are_in_the_database.js +++ b/cypress/support/step_definitions/common/the_following_{string}_are_in_the_database.js @@ -1,7 +1,7 @@ -import { Given } from '@badeball/cypress-cucumber-preprocessor' +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' import './../../factories' -Given('the following {string} are in the database:', (table,data) => { +defineStep('the following {string} are in the database:', (table,data) => { switch(table){ case 'posts': data.hashes().forEach( entry => { @@ -13,29 +13,29 @@ Given('the following {string} are in the database:', (table,data) => { },{ ...entry, tagIds: entry.tagIds ? entry.tagIds.split(',').map(item => item.trim()) : [], - }); + }) }) break case 'comments': data.hashes().forEach( entry => { cy.factory() - .build('comment', entry, entry); + .build('comment', entry, entry) }) break case 'users': data.hashes().forEach( entry => { - cy.factory().build('user', entry, entry); - }); + cy.factory().build('user', entry, entry) + }) break case 'tags': data.hashes().forEach( entry => { cy.factory().build('tag', entry, entry) - }); + }) break case 'donations': data.hashes().forEach( entry => { cy.factory().build('donations', entry, entry) - }); + }) break } }) diff --git a/cypress/support/step_definitions/common/{string}_wrote_a_post_{string}.js b/cypress/support/step_definitions/common/{string}_wrote_a_post_{string}.js index 086432b30..af509d8b3 100644 --- a/cypress/support/step_definitions/common/{string}_wrote_a_post_{string}.js +++ b/cypress/support/step_definitions/common/{string}_wrote_a_post_{string}.js @@ -1,11 +1,11 @@ -import { Given } from '@badeball/cypress-cucumber-preprocessor' +import { defineStep } from '@badeball/cypress-cucumber-preprocessor' import './../../factories' -Given('{string} wrote a post {string}', (author, title) => { +defineStep('{string} wrote a post {string}', (author, title) => { cy.factory() .build('post', { title, }, { authorId: author, - }); -}); + }) +}) diff --git a/deployment/helm/charts/ocelot-neo4j/Chart.yaml b/deployment/helm/charts/ocelot-neo4j/Chart.yaml index e9f8e2354..461ed90a3 100644 --- a/deployment/helm/charts/ocelot-neo4j/Chart.yaml +++ b/deployment/helm/charts/ocelot-neo4j/Chart.yaml @@ -21,4 +21,4 @@ version: 0.1.0 # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "3.4.0" +appVersion: "3.5.3" diff --git a/deployment/helm/charts/ocelot-social/Chart.yaml b/deployment/helm/charts/ocelot-social/Chart.yaml index 2a872480e..81febe4a7 100644 --- a/deployment/helm/charts/ocelot-social/Chart.yaml +++ b/deployment/helm/charts/ocelot-social/Chart.yaml @@ -21,4 +21,4 @@ version: 0.1.0 # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "3.4.0" +appVersion: "3.5.3" diff --git a/docker-compose.override.yml b/docker-compose.override.yml index e0f91c358..ae77abd5e 100644 --- a/docker-compose.override.yml +++ b/docker-compose.override.yml @@ -30,6 +30,8 @@ services: environment: - NODE_ENV="development" - DEBUG=true + - SMTP_PORT=1025 + - SMTP_HOST=mailserver volumes: - ./backend:/app diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 4a8a0442c..250de7cce 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1,12 +1,12 @@ { "name": "ocelot-social-frontend", - "version": "3.4.0", + "version": "3.5.3", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "ocelot-social-frontend", - "version": "3.4.0", + "version": "3.5.3", "license": "Apache-2.0", "dependencies": { "@intlify/unplugin-vue-i18n": "^2.0.0", diff --git a/frontend/package.json b/frontend/package.json index 5e2235be5..62532f76f 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "ocelot-social-frontend", - "version": "3.4.0", + "version": "3.5.3", "description": "ocelot.social new Frontend (in development and not fully implemented) by IT4C Boilerplate for frontends", "main": "build/index.js", "type": "module", diff --git a/package-lock.json b/package-lock.json index 17de00757..f9ad43b0b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,17 +1,17 @@ { "name": "ocelot-social", - "version": "3.3.0", + "version": "3.4.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "ocelot-social", - "version": "3.3.0", + "version": "3.4.0", "license": "MIT", "devDependencies": { - "@babel/core": "^7.26.10", - "@babel/preset-env": "^7.26.9", - "@babel/register": "^7.25.9", + "@babel/core": "^7.27.1", + "@babel/preset-env": "^7.27.1", + "@babel/register": "^7.27.1", "@badeball/cypress-cucumber-preprocessor": "^22.0.1", "@cucumber/cucumber": "11.2.0", "@cypress/browserify-preprocessor": "^3.0.2", @@ -102,24 +102,24 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.26.2", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", - "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.25.9", + "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", - "picocolors": "^1.0.0" + "picocolors": "^1.1.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/compat-data": { - "version": "7.26.8", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.8.tgz", - "integrity": "sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.27.1.tgz", + "integrity": "sha512-Q+E+rd/yBzNQhXkG+zQnF58e4zoZfBedaxwzPmicKsiK3nt8iJYrSrDbjwFFDGC4f+rPafqRaPH6TsDoSvMf7A==", "dev": true, "license": "MIT", "engines": { @@ -127,22 +127,22 @@ } }, "node_modules/@babel/core": { - "version": "7.26.10", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.10.tgz", - "integrity": "sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.27.1.tgz", + "integrity": "sha512-IaaGWsQqfsQWVLqMn9OB92MNN7zukfVA4s7KKAI0KfrrDsZ0yhi5uV4baBuLuN7n3vsZpwP8asPPcVwApxvjBQ==", "dev": true, "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.26.2", - "@babel/generator": "^7.26.10", - "@babel/helper-compilation-targets": "^7.26.5", - "@babel/helper-module-transforms": "^7.26.0", - "@babel/helpers": "^7.26.10", - "@babel/parser": "^7.26.10", - "@babel/template": "^7.26.9", - "@babel/traverse": "^7.26.10", - "@babel/types": "^7.26.10", + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.27.1", + "@babel/helper-compilation-targets": "^7.27.1", + "@babel/helper-module-transforms": "^7.27.1", + "@babel/helpers": "^7.27.1", + "@babel/parser": "^7.27.1", + "@babel/template": "^7.27.1", + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -158,14 +158,14 @@ } }, "node_modules/@babel/generator": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.0.tgz", - "integrity": "sha512-VybsKvpiN1gU1sdMZIp7FcqphVVKEwcuj02x73uvcHE0PTihx1nlBcowYWhDwjpoAXRv43+gDzyggGnn1XZhVw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.1.tgz", + "integrity": "sha512-UnJfnIpc/+JO0/+KRVQNGU+y5taA5vCbwN8+azkX6beii/ZF+enZJSOKo11ZSzGJjlNfJHfQtmQT8H+9TXPG2w==", "dev": true, "license": "MIT", "dependencies": { - "@babel/parser": "^7.27.0", - "@babel/types": "^7.27.0", + "@babel/parser": "^7.27.1", + "@babel/types": "^7.27.1", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^3.0.2" @@ -175,26 +175,27 @@ } }, "node_modules/@babel/helper-annotate-as-pure": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.25.9.tgz", - "integrity": "sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.1.tgz", + "integrity": "sha512-WnuuDILl9oOBbKnb4L+DyODx7iC47XfzmNCpTttFsSp6hTG7XZxu60+4IO+2/hPfcGOoKbFiwoI/+zwARbNQow==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/types": "^7.25.9" + "@babel/types": "^7.27.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.26.5", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.26.5.tgz", - "integrity": "sha512-IXuyn5EkouFJscIDuFF5EsiSolseme1s0CZB+QxVugqJLYmKdxI1VfIBOst0SUu4rnk2Z7kqTwmoO1lp3HIfnA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.1.tgz", + "integrity": "sha512-2YaDd/Rd9E598B5+WIc8wJPmWETiiJXFYVE60oX8FDohv7rAUU3CQj+A1MgeEmcsk2+dQuEjIe/GDvig0SqL4g==", "dev": true, "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.26.5", - "@babel/helper-validator-option": "^7.25.9", + "@babel/compat-data": "^7.27.1", + "@babel/helper-validator-option": "^7.27.1", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" @@ -204,17 +205,18 @@ } }, "node_modules/@babel/helper-create-class-features-plugin": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.25.9.tgz", - "integrity": "sha512-UTZQMvt0d/rSz6KI+qdu7GQze5TIajwTS++GUozlw8VBJDEOAqSXwm1WvmYEZwqdqSGQshRocPDqrt4HBZB3fQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.27.1.tgz", + "integrity": "sha512-QwGAmuvM17btKU5VqXfb+Giw4JcN0hjuufz3DYnpeVDvZLAObloM77bhMXiqry3Iio+Ai4phVRDwl6WU10+r5A==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.25.9", - "@babel/helper-member-expression-to-functions": "^7.25.9", - "@babel/helper-optimise-call-expression": "^7.25.9", - "@babel/helper-replace-supers": "^7.25.9", - "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9", - "@babel/traverse": "^7.25.9", + "@babel/helper-annotate-as-pure": "^7.27.1", + "@babel/helper-member-expression-to-functions": "^7.27.1", + "@babel/helper-optimise-call-expression": "^7.27.1", + "@babel/helper-replace-supers": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", + "@babel/traverse": "^7.27.1", "semver": "^6.3.1" }, "engines": { @@ -225,13 +227,14 @@ } }, "node_modules/@babel/helper-create-regexp-features-plugin": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.25.9.tgz", - "integrity": "sha512-ORPNZ3h6ZRkOyAa/SaHU+XsLZr0UQzRwuDQ0cczIA17nAzZ+85G5cVkOJIj7QavLZGSe8QXUmNFxSZzjcZF9bw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.27.1.tgz", + "integrity": "sha512-uVDC72XVf8UbrH5qQTc18Agb8emwjTiZrQE11Nv3CuBEZmVvTwwE9CBUEvHku06gQCAyYf8Nv6ja1IN+6LMbxQ==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.25.9", - "regexpu-core": "^6.1.1", + "@babel/helper-annotate-as-pure": "^7.27.1", + "regexpu-core": "^6.2.0", "semver": "^6.3.1" }, "engines": { @@ -258,40 +261,43 @@ } }, "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.25.9.tgz", - "integrity": "sha512-wbfdZ9w5vk0C0oyHqAJbc62+vet5prjj01jjJ8sKn3j9h3MQQlflEdXYvuqRWjHnM12coDEqiC1IRCi0U/EKwQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.27.1.tgz", + "integrity": "sha512-E5chM8eWjTp/aNoVpcbfM7mLxu9XGLWYise2eBKGQomAk/Mb4XoxyqXTZbuTohbsl8EKqdlMhnDI2CCLfcs9wA==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/traverse": "^7.25.9", - "@babel/types": "^7.25.9" + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-imports": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", - "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/traverse": "^7.25.9", - "@babel/types": "^7.25.9" + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz", - "integrity": "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.27.1.tgz", + "integrity": "sha512-9yHn519/8KvTU5BjTVEEeIM3w9/2yXNKoD82JifINImhpKkARMJKPP59kLo+BafpdN5zgNeIcS4jsGDmd3l58g==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-module-imports": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9", - "@babel/traverse": "^7.25.9" + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -301,21 +307,22 @@ } }, "node_modules/@babel/helper-optimise-call-expression": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.25.9.tgz", - "integrity": "sha512-FIpuNaz5ow8VyrYcnXQTDRGvV6tTjkNtCK/RYNDXGSLlUD6cBuQTSw43CShGxjvfBTfcUA/r6UhUCbtYqkhcuQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.27.1.tgz", + "integrity": "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/types": "^7.25.9" + "@babel/types": "^7.27.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.26.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.26.5.tgz", - "integrity": "sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", + "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", "dev": true, "license": "MIT", "engines": { @@ -323,14 +330,15 @@ } }, "node_modules/@babel/helper-remap-async-to-generator": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.25.9.tgz", - "integrity": "sha512-IZtukuUeBbhgOcaW2s06OXTzVNJR0ybm4W5xC1opWFFJMZbwRj5LCk+ByYH7WdZPZTt8KnFwA8pvjN2yqcPlgw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.27.1.tgz", + "integrity": "sha512-7fiA521aVw8lSPeI4ZOD3vRFkoqkJcS+z4hFo82bFSH/2tNd6eJ5qCVMS5OzDmZh/kaHQeBaeyxK6wljcPtveA==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.25.9", - "@babel/helper-wrap-function": "^7.25.9", - "@babel/traverse": "^7.25.9" + "@babel/helper-annotate-as-pure": "^7.27.1", + "@babel/helper-wrap-function": "^7.27.1", + "@babel/traverse": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -340,14 +348,15 @@ } }, "node_modules/@babel/helper-replace-supers": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.25.9.tgz", - "integrity": "sha512-IiDqTOTBQy0sWyeXyGSC5TBJpGFXBkRynjBeXsvbhQFKj2viwJC76Epz35YLU1fpe/Am6Vppb7W7zM4fPQzLsQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.27.1.tgz", + "integrity": "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-member-expression-to-functions": "^7.25.9", - "@babel/helper-optimise-call-expression": "^7.25.9", - "@babel/traverse": "^7.25.9" + "@babel/helper-member-expression-to-functions": "^7.27.1", + "@babel/helper-optimise-call-expression": "^7.27.1", + "@babel/traverse": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -357,81 +366,86 @@ } }, "node_modules/@babel/helper-skip-transparent-expression-wrappers": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.25.9.tgz", - "integrity": "sha512-K4Du3BFa3gvyhzgPcntrkDgZzQaq6uozzcpGbOO1OEJaI+EJdqWIMTLgFgQf6lrfiDFo5FU+BxKepI9RmZqahA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.27.1.tgz", + "integrity": "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/traverse": "^7.25.9", - "@babel/types": "^7.25.9" + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-string-parser": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", - "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", "devOptional": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", - "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", "devOptional": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-option": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz", - "integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-wrap-function": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.25.9.tgz", - "integrity": "sha512-ETzz9UTjQSTmw39GboatdymDq4XIQbR8ySgVrylRhPOFpsd+JrKHIuF0de7GCWmem+T4uC5z7EZguod7Wj4A4g==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.27.1.tgz", + "integrity": "sha512-NFJK2sHUvrjo8wAU/nQTWU890/zB2jj0qBcCbZbbf+005cAsv6tMjXz31fBign6M5ov1o0Bllu+9nbqkfsjjJQ==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/template": "^7.25.9", - "@babel/traverse": "^7.25.9", - "@babel/types": "^7.25.9" + "@babel/template": "^7.27.1", + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.0.tgz", - "integrity": "sha512-U5eyP/CTFPuNE3qk+WZMxFkp/4zUzdceQlfzf7DdGdhp+Fezd7HD+i8Y24ZuTMKX3wQBld449jijbGq6OdGNQg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.1.tgz", + "integrity": "sha512-FCvFTm0sWV8Fxhpp2McP5/W53GPllQ9QeQ7SiqGWjMf/LVG07lFa5+pgK05IRhVwtvafT22KF+ZSnM9I545CvQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/template": "^7.27.0", - "@babel/types": "^7.27.0" + "@babel/template": "^7.27.1", + "@babel/types": "^7.27.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.0.tgz", - "integrity": "sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.1.tgz", + "integrity": "sha512-I0dZ3ZpCrJ1c04OqlNsQcKiZlsrXf/kkE4FXzID9rIOYICsAbA8mMDzhW/luRNAHdCNt7os/u8wenklZDlUVUQ==", "devOptional": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.27.0" + "@babel/types": "^7.27.1" }, "bin": { "parser": "bin/babel-parser.js" @@ -441,13 +455,14 @@ } }, "node_modules/@babel/plugin-bugfix-firefox-class-in-computed-class-key": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.25.9.tgz", - "integrity": "sha512-ZkRyVkThtxQ/J6nv3JFYv1RYY+JT5BvU0y3k5bWrmuG4woXypRa4PXmm9RhOwodRkYFWqC0C0cqcJ4OqR7kW+g==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-firefox-class-in-computed-class-key/-/plugin-bugfix-firefox-class-in-computed-class-key-7.27.1.tgz", + "integrity": "sha512-QPG3C9cCVRQLxAVwmefEmwdTanECuUBMQZ/ym5kiw3XKCGA7qkuQLcjWWHcrD/GKbn/WmJwaezfuuAOcyKlRPA==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/traverse": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/traverse": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -457,12 +472,13 @@ } }, "node_modules/@babel/plugin-bugfix-safari-class-field-initializer-scope": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.25.9.tgz", - "integrity": "sha512-MrGRLZxLD/Zjj0gdU15dfs+HH/OXvnw/U4jJD8vpcP2CJQapPEv1IWwjc/qMg7ItBlPwSv1hRBbb7LeuANdcnw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-class-field-initializer-scope/-/plugin-bugfix-safari-class-field-initializer-scope-7.27.1.tgz", + "integrity": "sha512-qNeq3bCKnGgLkEXUuFry6dPlGfCdQNZbn7yUAPCInwAJHMU7THJfrBSozkcWq5sNM6RcF3S8XyQL2A52KNR9IA==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -472,12 +488,13 @@ } }, "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.25.9.tgz", - "integrity": "sha512-2qUwwfAFpJLZqxd02YW9btUCZHl+RFvdDkNfZwaIJrvB8Tesjsk8pEQkTvGwZXLqXUx/2oyY3ySRhm6HOXuCug==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.27.1.tgz", + "integrity": "sha512-g4L7OYun04N1WyqMNjldFwlfPCLVkgB54A/YCXICZYBsvJJE3kByKv9c9+R/nAfmIfjl2rKYLNyMHboYbZaWaA==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -487,14 +504,15 @@ } }, "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.25.9.tgz", - "integrity": "sha512-6xWgLZTJXwilVjlnV7ospI3xi+sl8lN8rXXbBD6vYn3UYDlGsag8wrZkKcSI8G6KgqKP7vNFaDgeDnfAABq61g==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.27.1.tgz", + "integrity": "sha512-oO02gcONcD5O1iTLi/6frMJBIwWEHceWGSGqrpCmEL8nogiS6J9PBlE48CaK20/Jx1LuRml9aDftLgdjXT8+Cw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9", - "@babel/plugin-transform-optional-chaining": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", + "@babel/plugin-transform-optional-chaining": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -504,13 +522,14 @@ } }, "node_modules/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.25.9.tgz", - "integrity": "sha512-aLnMXYPnzwwqhYSCyXfKkIkYgJ8zv9RK+roo9DkTXz38ynIhd9XCbN08s3MGvqL2MYGVUGdRQLL/JqBIeJhJBg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly/-/plugin-bugfix-v8-static-class-fields-redefine-readonly-7.27.1.tgz", + "integrity": "sha512-6BpaYGDavZqkI6yT+KSPdpZFfpnd68UKXbcjI9pJ13pvHhPrCKWOOLp+ysvMeA+DxnhuPpgIaRpxRxo5A9t5jw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/traverse": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/traverse": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -569,12 +588,13 @@ } }, "node_modules/@babel/plugin-syntax-import-assertions": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.26.0.tgz", - "integrity": "sha512-QCWT5Hh830hK5EQa7XzuqIkQU9tT/whqbDz7kuaZMHFl1inRRg7JnuAEOQ0Ur0QUl0NufCk1msK2BeY79Aj/eg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.27.1.tgz", + "integrity": "sha512-UT/Jrhw57xg4ILHLFnzFpPDlMbcdEicaAtjPQpbj9wa8T4r5KVWCimHcL/460g8Ht0DMxDyjsLgiWSkVjnwPFg==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -584,12 +604,13 @@ } }, "node_modules/@babel/plugin-syntax-import-attributes": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.26.0.tgz", - "integrity": "sha512-e2dttdsJ1ZTpi3B9UYGLw41hifAubg19AtCu/2I/F1QNVclOBr1dYpTdmdyZ84Xiz43BS/tCUkMAZNLv12Pi+A==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.27.1.tgz", + "integrity": "sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -642,12 +663,13 @@ } }, "node_modules/@babel/plugin-transform-arrow-functions": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.25.9.tgz", - "integrity": "sha512-6jmooXYIwn9ca5/RylZADJ+EnSxVUS5sjeJ9UPk6RWRzXCmOJCy6dqItPJFpw2cuCangPK4OYr5uhGKcmrm5Qg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.27.1.tgz", + "integrity": "sha512-8Z4TGic6xW70FKThA5HYEKKyBpOOsucTOD1DjU3fZxDg+K3zBJcXMFnt/4yQiZnf5+MiOMSXQ9PaEK/Ilh1DeA==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -657,15 +679,15 @@ } }, "node_modules/@babel/plugin-transform-async-generator-functions": { - "version": "7.26.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.26.8.tgz", - "integrity": "sha512-He9Ej2X7tNf2zdKMAGOsmg2MrFc+hfoAhd3po4cWfo/NWjzEAKa0oQruj1ROVUdl0e6fb6/kE/G3SSxE0lRJOg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.27.1.tgz", + "integrity": "sha512-eST9RrwlpaoJBDHShc+DS2SG4ATTi2MYNb4OxYkf3n+7eb49LWpnS+HSpVfW4x927qQwgk8A2hGNVaajAEw0EA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.26.5", - "@babel/helper-remap-async-to-generator": "^7.25.9", - "@babel/traverse": "^7.26.8" + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-remap-async-to-generator": "^7.27.1", + "@babel/traverse": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -675,14 +697,15 @@ } }, "node_modules/@babel/plugin-transform-async-to-generator": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.25.9.tgz", - "integrity": "sha512-NT7Ejn7Z/LjUH0Gv5KsBCxh7BH3fbLTV0ptHvpeMvrt3cPThHfJfst9Wrb7S8EvJ7vRTFI7z+VAvFVEQn/m5zQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.27.1.tgz", + "integrity": "sha512-NREkZsZVJS4xmTr8qzE5y8AfIPqsdQfRuUiLRTEzb7Qii8iFWCyDKaUV2c0rCuh4ljDZ98ALHP/PetiBV2nddA==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-module-imports": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/helper-remap-async-to-generator": "^7.25.9" + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-remap-async-to-generator": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -692,13 +715,13 @@ } }, "node_modules/@babel/plugin-transform-block-scoped-functions": { - "version": "7.26.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.26.5.tgz", - "integrity": "sha512-chuTSY+hq09+/f5lMj8ZSYgCFpppV2CbYrhNFJ1BFoXpiWPnnAb7R0MqrafCpN8E1+YRrtM1MXZHJdIx8B6rMQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.27.1.tgz", + "integrity": "sha512-cnqkuOtZLapWYZUYM5rVIdv1nXYuFVIltZ6ZJ7nIj585QsjKM5dhL2Fu/lICXZ1OyIAFc7Qy+bvDAtTXqGrlhg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.26.5" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -708,12 +731,13 @@ } }, "node_modules/@babel/plugin-transform-block-scoping": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.25.9.tgz", - "integrity": "sha512-1F05O7AYjymAtqbsFETboN1NvBdcnzMerO+zlMyJBEz6WkMdejvGWw9p05iTSjC85RLlBseHHQpYaM4gzJkBGg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.27.1.tgz", + "integrity": "sha512-QEcFlMl9nGTgh1rn2nIeU5bkfb9BAjaQcWbiP4LvKxUot52ABcTkpcyJ7f2Q2U2RuQ84BNLgts3jRme2dTx6Fw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -723,13 +747,14 @@ } }, "node_modules/@babel/plugin-transform-class-properties": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.25.9.tgz", - "integrity": "sha512-bbMAII8GRSkcd0h0b4X+36GksxuheLFjP65ul9w6C3KgAamI3JqErNgSrosX6ZPj+Mpim5VvEbawXxJCyEUV3Q==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.27.1.tgz", + "integrity": "sha512-D0VcalChDMtuRvJIu3U/fwWjf8ZMykz5iZsg77Nuj821vCKI3zCyRLwRdWbsuJ/uRwZhZ002QtCqIkwC/ZkvbA==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-create-class-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -739,13 +764,14 @@ } }, "node_modules/@babel/plugin-transform-class-static-block": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.26.0.tgz", - "integrity": "sha512-6J2APTs7BDDm+UMqP1useWqhcRAXo0WIoVj26N7kPFB6S73Lgvyka4KTZYIxtgYXiN5HTyRObA72N2iu628iTQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-static-block/-/plugin-transform-class-static-block-7.27.1.tgz", + "integrity": "sha512-s734HmYU78MVzZ++joYM+NkJusItbdRcbm+AGRgJCt3iA+yux0QpD9cBVdz3tKyrjVYWRl7j0mHSmv4lhV0aoA==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-create-class-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -755,16 +781,17 @@ } }, "node_modules/@babel/plugin-transform-classes": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.25.9.tgz", - "integrity": "sha512-mD8APIXmseE7oZvZgGABDyM34GUmK45Um2TXiBUt7PnuAxrgoSVf123qUzPxEr/+/BHrRn5NMZCdE2m/1F8DGg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.27.1.tgz", + "integrity": "sha512-7iLhfFAubmpeJe/Wo2TVuDrykh/zlWXLzPNdL0Jqn/Xu8R3QQ8h9ff8FQoISZOsw74/HFqFI7NX63HN7QFIHKA==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.25.9", - "@babel/helper-compilation-targets": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/helper-replace-supers": "^7.25.9", - "@babel/traverse": "^7.25.9", + "@babel/helper-annotate-as-pure": "^7.27.1", + "@babel/helper-compilation-targets": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-replace-supers": "^7.27.1", + "@babel/traverse": "^7.27.1", "globals": "^11.1.0" }, "engines": { @@ -775,13 +802,14 @@ } }, "node_modules/@babel/plugin-transform-computed-properties": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.25.9.tgz", - "integrity": "sha512-HnBegGqXZR12xbcTHlJ9HGxw1OniltT26J5YpfruGqtUHlz/xKf/G2ak9e+t0rVqrjXa9WOhvYPz1ERfMj23AA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.27.1.tgz", + "integrity": "sha512-lj9PGWvMTVksbWiDT2tW68zGS/cyo4AkZ/QTp0sQT0mjPopCmrSkzxeXkznjqBxzDI6TclZhOJbBmbBLjuOZUw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/template": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/template": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -791,12 +819,13 @@ } }, "node_modules/@babel/plugin-transform-destructuring": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.25.9.tgz", - "integrity": "sha512-WkCGb/3ZxXepmMiX101nnGiU+1CAdut8oHyEOHxkKuS1qKpU2SMXE2uSvfz8PBuLd49V6LEsbtyPhWC7fnkgvQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.27.1.tgz", + "integrity": "sha512-ttDCqhfvpE9emVkXbPD8vyxxh4TWYACVybGkDj+oReOGwnp066ITEivDlLwe0b1R0+evJ13IXQuLNB5w1fhC5Q==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -806,13 +835,14 @@ } }, "node_modules/@babel/plugin-transform-dotall-regex": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.25.9.tgz", - "integrity": "sha512-t7ZQ7g5trIgSRYhI9pIJtRl64KHotutUJsh4Eze5l7olJv+mRSg4/MmbZ0tv1eeqRbdvo/+trvJD/Oc5DmW2cA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.27.1.tgz", + "integrity": "sha512-gEbkDVGRvjj7+T1ivxrfgygpT7GUd4vmODtYpbs0gZATdkX8/iSnOtZSxiZnsgm1YjTgjI6VKBGSJJevkrclzw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -822,12 +852,13 @@ } }, "node_modules/@babel/plugin-transform-duplicate-keys": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.25.9.tgz", - "integrity": "sha512-LZxhJ6dvBb/f3x8xwWIuyiAHy56nrRG3PeYTpBkkzkYRRQ6tJLu68lEF5VIqMUZiAV7a8+Tb78nEoMCMcqjXBw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.27.1.tgz", + "integrity": "sha512-MTyJk98sHvSs+cvZ4nOauwTTG1JeonDjSGvGGUNHreGQns+Mpt6WX/dVzWBHgg+dYZhkC4X+zTDfkTU+Vy9y7Q==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -837,13 +868,14 @@ } }, "node_modules/@babel/plugin-transform-duplicate-named-capturing-groups-regex": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.25.9.tgz", - "integrity": "sha512-0UfuJS0EsXbRvKnwcLjFtJy/Sxc5J5jhLHnFhy7u4zih97Hz6tJkLU+O+FMMrNZrosUPxDi6sYxJ/EA8jDiAog==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-named-capturing-groups-regex/-/plugin-transform-duplicate-named-capturing-groups-regex-7.27.1.tgz", + "integrity": "sha512-hkGcueTEzuhB30B3eJCbCYeCaaEQOmQR0AdvzpD4LoN0GXMWzzGSuRrxR2xTnCrvNbVwK9N6/jQ92GSLfiZWoQ==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -853,12 +885,13 @@ } }, "node_modules/@babel/plugin-transform-dynamic-import": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.25.9.tgz", - "integrity": "sha512-GCggjexbmSLaFhqsojeugBpeaRIgWNTcgKVq/0qIteFEqY2A+b9QidYadrWlnbWQUrW5fn+mCvf3tr7OeBFTyg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dynamic-import/-/plugin-transform-dynamic-import-7.27.1.tgz", + "integrity": "sha512-MHzkWQcEmjzzVW9j2q8LGjwGWpG2mjwaaB0BNQwst3FIjqsg8Ct/mIZlvSPJvfi9y2AC8mi/ktxbFVL9pZ1I4A==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -868,13 +901,13 @@ } }, "node_modules/@babel/plugin-transform-exponentiation-operator": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.26.3.tgz", - "integrity": "sha512-7CAHcQ58z2chuXPWblnn1K6rLDnDWieghSOEmqQsrBenH0P9InCUtOJYD89pvngljmZlJcz3fcmgYsXFNGa1ZQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.27.1.tgz", + "integrity": "sha512-uspvXnhHvGKf2r4VVtBpeFnuDWsJLQ6MF6lGJLC89jBR1uoVeqM416AZtTuhTezOfgHicpJQmoD5YUakO/YmXQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -884,12 +917,13 @@ } }, "node_modules/@babel/plugin-transform-export-namespace-from": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.25.9.tgz", - "integrity": "sha512-2NsEz+CxzJIVOPx2o9UsW1rXLqtChtLoVnwYHHiB04wS5sgn7mrV45fWMBX0Kk+ub9uXytVYfNP2HjbVbCB3Ww==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-export-namespace-from/-/plugin-transform-export-namespace-from-7.27.1.tgz", + "integrity": "sha512-tQvHWSZ3/jH2xuq/vZDy0jNn+ZdXJeM8gHvX4lnJmsc3+50yPlWdZXIc5ay+umX+2/tJIqHqiEqcJvxlmIvRvQ==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -899,14 +933,14 @@ } }, "node_modules/@babel/plugin-transform-for-of": { - "version": "7.26.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.26.9.tgz", - "integrity": "sha512-Hry8AusVm8LW5BVFgiyUReuoGzPUpdHQQqJY5bZnbbf+ngOHWuCuYFKw/BqaaWlvEUrF91HMhDtEaI1hZzNbLg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.27.1.tgz", + "integrity": "sha512-BfbWFFEJFQzLCQ5N8VocnCtA8J1CLkNTe2Ms2wocj75dd6VpiqS5Z5quTYcUoo4Yq+DN0rtikODccuv7RU81sw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.26.5", - "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -916,14 +950,15 @@ } }, "node_modules/@babel/plugin-transform-function-name": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.25.9.tgz", - "integrity": "sha512-8lP+Yxjv14Vc5MuWBpJsoUCd3hD6V9DgBon2FVYL4jJgbnVQ9fTgYmonchzZJOVNgzEgbxp4OwAf6xz6M/14XA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.27.1.tgz", + "integrity": "sha512-1bQeydJF9Nr1eBCMMbC+hdwmRlsv5XYOMu03YSWFwNs0HsAmtSxxF1fyuYPqemVldVyFmlCU7w8UE14LupUSZQ==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-compilation-targets": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/traverse": "^7.25.9" + "@babel/helper-compilation-targets": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/traverse": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -933,12 +968,13 @@ } }, "node_modules/@babel/plugin-transform-json-strings": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.25.9.tgz", - "integrity": "sha512-xoTMk0WXceiiIvsaquQQUaLLXSW1KJ159KP87VilruQm0LNNGxWzahxSS6T6i4Zg3ezp4vA4zuwiNUR53qmQAw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-json-strings/-/plugin-transform-json-strings-7.27.1.tgz", + "integrity": "sha512-6WVLVJiTjqcQauBhn1LkICsR2H+zm62I3h9faTDKt1qP4jn2o72tSvqMwtGFKGTpojce0gJs+76eZ2uCHRZh0Q==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -948,12 +984,13 @@ } }, "node_modules/@babel/plugin-transform-literals": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.25.9.tgz", - "integrity": "sha512-9N7+2lFziW8W9pBl2TzaNht3+pgMIRP74zizeCSrtnSKVdUl8mAjjOP2OOVQAfZ881P2cNjDj1uAMEdeD50nuQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.27.1.tgz", + "integrity": "sha512-0HCFSepIpLTkLcsi86GG3mTUzxV5jpmbv97hTETW3yzrAij8aqlD36toB1D0daVFJM8NK6GvKO0gslVQmm+zZA==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -963,12 +1000,13 @@ } }, "node_modules/@babel/plugin-transform-logical-assignment-operators": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.25.9.tgz", - "integrity": "sha512-wI4wRAzGko551Y8eVf6iOY9EouIDTtPb0ByZx+ktDGHwv6bHFimrgJM/2T021txPZ2s4c7bqvHbd+vXG6K948Q==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-logical-assignment-operators/-/plugin-transform-logical-assignment-operators-7.27.1.tgz", + "integrity": "sha512-SJvDs5dXxiae4FbSL1aBJlG4wvl594N6YEVVn9e3JGulwioy6z3oPjx/sQBO3Y4NwUu5HNix6KJ3wBZoewcdbw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -978,12 +1016,13 @@ } }, "node_modules/@babel/plugin-transform-member-expression-literals": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.25.9.tgz", - "integrity": "sha512-PYazBVfofCQkkMzh2P6IdIUaCEWni3iYEerAsRWuVd8+jlM1S9S9cz1dF9hIzyoZ8IA3+OwVYIp9v9e+GbgZhA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.27.1.tgz", + "integrity": "sha512-hqoBX4dcZ1I33jCSWcXrP+1Ku7kdqXf1oeah7ooKOIiAdKQ+uqftgCFNOSzA5AMS2XIHEYeGFg4cKRCdpxzVOQ==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -993,13 +1032,14 @@ } }, "node_modules/@babel/plugin-transform-modules-amd": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.25.9.tgz", - "integrity": "sha512-g5T11tnI36jVClQlMlt4qKDLlWnG5pP9CSM4GhdRciTNMRgkfpo5cR6b4rGIOYPgRRuFAvwjPQ/Yk+ql4dyhbw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.27.1.tgz", + "integrity": "sha512-iCsytMg/N9/oFq6n+gFTvUYDZQOMK5kEdeYxmxt91fcJGycfxVP9CnrxoliM0oumFERba2i8ZtwRUCMhvP1LnA==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-module-transforms": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-module-transforms": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1009,14 +1049,14 @@ } }, "node_modules/@babel/plugin-transform-modules-commonjs": { - "version": "7.26.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.26.3.tgz", - "integrity": "sha512-MgR55l4q9KddUDITEzEFYn5ZsGDXMSsU9E+kh7fjRXTIC3RHqfCo8RPRbyReYJh44HQ/yomFkqbOFohXvDCiIQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.27.1.tgz", + "integrity": "sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-module-transforms": "^7.26.0", - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-module-transforms": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1026,15 +1066,16 @@ } }, "node_modules/@babel/plugin-transform-modules-systemjs": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.25.9.tgz", - "integrity": "sha512-hyss7iIlH/zLHaehT+xwiymtPOpsiwIIRlCAOwBB04ta5Tt+lNItADdlXw3jAWZ96VJ2jlhl/c+PNIQPKNfvcA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.27.1.tgz", + "integrity": "sha512-w5N1XzsRbc0PQStASMksmUeqECuzKuTJer7kFagK8AXgpCMkeDMO5S+aaFb7A51ZYDF7XI34qsTX+fkHiIm5yA==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-module-transforms": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9", - "@babel/traverse": "^7.25.9" + "@babel/helper-module-transforms": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1044,13 +1085,14 @@ } }, "node_modules/@babel/plugin-transform-modules-umd": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.25.9.tgz", - "integrity": "sha512-bS9MVObUgE7ww36HEfwe6g9WakQ0KF07mQF74uuXdkoziUPfKyu/nIm663kz//e5O1nPInPFx36z7WJmJ4yNEw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.27.1.tgz", + "integrity": "sha512-iQBE/xC5BV1OxJbp6WG7jq9IWiD+xxlZhLrdwpPkTX3ydmXdvoCpyfJN7acaIBZaOqTfr76pgzqBJflNbeRK+w==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-module-transforms": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-module-transforms": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1060,13 +1102,14 @@ } }, "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.25.9.tgz", - "integrity": "sha512-oqB6WHdKTGl3q/ItQhpLSnWWOpjUJLsOCLVyeFgeTktkBSCiurvPOsyt93gibI9CmuKvTUEtWmG5VhZD+5T/KA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.27.1.tgz", + "integrity": "sha512-SstR5JYy8ddZvD6MhV0tM/j16Qds4mIpJTOd1Yu9J9pJjH93bxHECF7pgtc28XvkzTD6Pxcm/0Z73Hvk7kb3Ng==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1076,12 +1119,13 @@ } }, "node_modules/@babel/plugin-transform-new-target": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.25.9.tgz", - "integrity": "sha512-U/3p8X1yCSoKyUj2eOBIx3FOn6pElFOKvAAGf8HTtItuPyB+ZeOqfn+mvTtg9ZlOAjsPdK3ayQEjqHjU/yLeVQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.27.1.tgz", + "integrity": "sha512-f6PiYeqXQ05lYq3TIfIDu/MtliKUbNwkGApPUvyo6+tc7uaR4cPjPe7DFPr15Uyycg2lZU6btZ575CuQoYh7MQ==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1091,13 +1135,13 @@ } }, "node_modules/@babel/plugin-transform-nullish-coalescing-operator": { - "version": "7.26.6", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.26.6.tgz", - "integrity": "sha512-CKW8Vu+uUZneQCPtXmSBUC6NCAUdya26hWCElAWh5mVSlSRsmiCPUUDKb3Z0szng1hiAJa098Hkhg9o4SE35Qw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.27.1.tgz", + "integrity": "sha512-aGZh6xMo6q9vq1JGcw58lZ1Z0+i0xB2x0XaauNIUXd6O1xXc3RwoWEBlsTQrY4KQ9Jf0s5rgD6SiNkaUdJegTA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.26.5" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1107,12 +1151,13 @@ } }, "node_modules/@babel/plugin-transform-numeric-separator": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.25.9.tgz", - "integrity": "sha512-TlprrJ1GBZ3r6s96Yq8gEQv82s8/5HnCVHtEJScUj90thHQbwe+E5MLhi2bbNHBEJuzrvltXSru+BUxHDoog7Q==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.27.1.tgz", + "integrity": "sha512-fdPKAcujuvEChxDBJ5c+0BTaS6revLV7CJL08e4m3de8qJfNIuCc2nc7XJYOjBoTMJeqSmwXJ0ypE14RCjLwaw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1122,14 +1167,15 @@ } }, "node_modules/@babel/plugin-transform-object-rest-spread": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.25.9.tgz", - "integrity": "sha512-fSaXafEE9CVHPweLYw4J0emp1t8zYTXyzN3UuG+lylqkvYd7RMrsOQ8TYx5RF231be0vqtFC6jnx3UmpJmKBYg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.27.1.tgz", + "integrity": "sha512-/sSliVc9gHE20/7D5qsdGlq7RG5NCDTWsAhyqzGuq174EtWJoGzIu1BQ7G56eDsTcy1jseBZwv50olSdXOlGuA==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-compilation-targets": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/plugin-transform-parameters": "^7.25.9" + "@babel/helper-compilation-targets": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/plugin-transform-parameters": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1139,13 +1185,14 @@ } }, "node_modules/@babel/plugin-transform-object-super": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.25.9.tgz", - "integrity": "sha512-Kj/Gh+Rw2RNLbCK1VAWj2U48yxxqL2x0k10nPtSdRa0O2xnHXalD0s+o1A6a0W43gJ00ANo38jxkQreckOzv5A==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.27.1.tgz", + "integrity": "sha512-SFy8S9plRPbIcxlJ8A6mT/CxFdJx/c04JEctz4jf8YZaVS2px34j7NXRrlGlHkN/M2gnpL37ZpGRGVFLd3l8Ng==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/helper-replace-supers": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-replace-supers": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1155,12 +1202,13 @@ } }, "node_modules/@babel/plugin-transform-optional-catch-binding": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.25.9.tgz", - "integrity": "sha512-qM/6m6hQZzDcZF3onzIhZeDHDO43bkNNlOX0i8n3lR6zLbu0GN2d8qfM/IERJZYauhAHSLHy39NF0Ctdvcid7g==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.27.1.tgz", + "integrity": "sha512-txEAEKzYrHEX4xSZN4kJ+OfKXFVSWKB2ZxM9dpcE3wT7smwkNmXo5ORRlVzMVdJbD+Q8ILTgSD7959uj+3Dm3Q==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1170,13 +1218,14 @@ } }, "node_modules/@babel/plugin-transform-optional-chaining": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.25.9.tgz", - "integrity": "sha512-6AvV0FsLULbpnXeBjrY4dmWF8F7gf8QnvTEoO/wX/5xm/xE1Xo8oPuD3MPS+KS9f9XBEAWN7X1aWr4z9HdOr7A==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.27.1.tgz", + "integrity": "sha512-BQmKPPIuc8EkZgNKsv0X4bPmOoayeu4F1YCwx2/CfmDSXDbp7GnzlUH+/ul5VGfRg1AoFPsrIThlEBj2xb4CAg==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1186,12 +1235,13 @@ } }, "node_modules/@babel/plugin-transform-parameters": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.25.9.tgz", - "integrity": "sha512-wzz6MKwpnshBAiRmn4jR8LYz/g8Ksg0o80XmwZDlordjwEk9SxBzTWC7F5ef1jhbrbOW2DJ5J6ayRukrJmnr0g==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.27.1.tgz", + "integrity": "sha512-018KRk76HWKeZ5l4oTj2zPpSh+NbGdt0st5S6x0pga6HgrjBOJb24mMDHorFopOOd6YHkLgOZ+zaCjZGPO4aKg==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1201,13 +1251,14 @@ } }, "node_modules/@babel/plugin-transform-private-methods": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.25.9.tgz", - "integrity": "sha512-D/JUozNpQLAPUVusvqMxyvjzllRaF8/nSrP1s2YGQT/W4LHK4xxsMcHjhOGTS01mp9Hda8nswb+FblLdJornQw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-methods/-/plugin-transform-private-methods-7.27.1.tgz", + "integrity": "sha512-10FVt+X55AjRAYI9BrdISN9/AQWHqldOeZDUoLyif1Kn05a56xVBXb8ZouL8pZ9jem8QpXaOt8TS7RHUIS+GPA==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-create-class-features-plugin": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-create-class-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1217,14 +1268,15 @@ } }, "node_modules/@babel/plugin-transform-private-property-in-object": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.25.9.tgz", - "integrity": "sha512-Evf3kcMqzXA3xfYJmZ9Pg1OvKdtqsDMSWBDzZOPLvHiTt36E75jLDQo5w1gtRU95Q4E5PDttrTf25Fw8d/uWLw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-private-property-in-object/-/plugin-transform-private-property-in-object-7.27.1.tgz", + "integrity": "sha512-5J+IhqTi1XPa0DXF83jYOaARrX+41gOewWbkPyjMNRDqgOCqdffGh8L3f/Ek5utaEBZExjSAzcyjmV9SSAWObQ==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-annotate-as-pure": "^7.25.9", - "@babel/helper-create-class-features-plugin": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-annotate-as-pure": "^7.27.1", + "@babel/helper-create-class-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1234,12 +1286,13 @@ } }, "node_modules/@babel/plugin-transform-property-literals": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.25.9.tgz", - "integrity": "sha512-IvIUeV5KrS/VPavfSM/Iu+RE6llrHrYIKY1yfCzyO/lMXHQ+p7uGhonmGVisv6tSBSVgWzMBohTcvkC9vQcQFA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.27.1.tgz", + "integrity": "sha512-oThy3BCuCha8kDZ8ZkgOg2exvPYUlprMukKQXI1r1pJ47NCvxfkEy8vK+r/hT9nF0Aa4H1WUPZZjHTFtAhGfmQ==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1314,13 +1367,13 @@ } }, "node_modules/@babel/plugin-transform-regenerator": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.25.9.tgz", - "integrity": "sha512-vwDcDNsgMPDGP0nMqzahDWE5/MLcX8sv96+wfX7as7LoF/kr97Bo/7fI00lXY4wUXYfVmwIIyG80fGZ1uvt2qg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.27.1.tgz", + "integrity": "sha512-B19lbbL7PMrKr52BNPjCqg1IyNUIjTcxKj8uX9zHO+PmWN93s19NDr/f69mIkEp2x9nmDJ08a7lgHaTTzvW7mw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9", - "regenerator-transform": "^0.15.2" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1330,13 +1383,14 @@ } }, "node_modules/@babel/plugin-transform-regexp-modifiers": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.26.0.tgz", - "integrity": "sha512-vN6saax7lrA2yA/Pak3sCxuD6F5InBjn9IcrIKQPjpsLvuHYLVroTxjdlVRHjjBWxKOqIwpTXDkOssYT4BFdRw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regexp-modifiers/-/plugin-transform-regexp-modifiers-7.27.1.tgz", + "integrity": "sha512-TtEciroaiODtXvLZv4rmfMhkCv8jx3wgKpL68PuiPh2M4fvz5jhsA7697N1gMvkvr/JTF13DrFYyEbY9U7cVPA==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1346,12 +1400,13 @@ } }, "node_modules/@babel/plugin-transform-reserved-words": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.25.9.tgz", - "integrity": "sha512-7DL7DKYjn5Su++4RXu8puKZm2XBPHyjWLUidaPEkCUBbE7IPcsrkRHggAOOKydH1dASWdcUBxrkOGNxUv5P3Jg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.27.1.tgz", + "integrity": "sha512-V2ABPHIJX4kC7HegLkYoDpfg9PVmuWy/i6vUM5eGK22bx4YVFD3M5F0QQnWQoDs6AGsUWTVOopBiMFQgHaSkVw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1381,12 +1436,13 @@ } }, "node_modules/@babel/plugin-transform-shorthand-properties": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.25.9.tgz", - "integrity": "sha512-MUv6t0FhO5qHnS/W8XCbHmiRWOphNufpE1IVxhK5kuN3Td9FT1x4rx4K42s3RYdMXCXpfWkGSbCSd0Z64xA7Ng==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.27.1.tgz", + "integrity": "sha512-N/wH1vcn4oYawbJ13Y/FxcQrWk63jhfNa7jef0ih7PHSIHX2LB7GWE1rkPrOnka9kwMxb6hMl19p7lidA+EHmQ==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1396,13 +1452,14 @@ } }, "node_modules/@babel/plugin-transform-spread": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.25.9.tgz", - "integrity": "sha512-oNknIB0TbURU5pqJFVbOOFspVlrpVwo2H1+HUIsVDvp5VauGGDP1ZEvO8Nn5xyMEs3dakajOxlmkNW7kNgSm6A==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.27.1.tgz", + "integrity": "sha512-kpb3HUqaILBJcRFVhFUs6Trdd4mkrzcGXss+6/mxUd273PfbWqSDHRzMT2234gIg2QYfAjvXLSquP1xECSg09Q==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9", - "@babel/helper-skip-transparent-expression-wrappers": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1412,12 +1469,13 @@ } }, "node_modules/@babel/plugin-transform-sticky-regex": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.25.9.tgz", - "integrity": "sha512-WqBUSgeVwucYDP9U/xNRQam7xV8W5Zf+6Eo7T2SRVUFlhRiMNFdFz58u0KZmCVVqs2i7SHgpRnAhzRNmKfi2uA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.27.1.tgz", + "integrity": "sha512-lhInBO5bi/Kowe2/aLdBAawijx+q1pQzicSgnkB6dUPc1+RC8QmJHKf2OjvU+NZWitguJHEaEmbV6VWEouT58g==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1427,13 +1485,13 @@ } }, "node_modules/@babel/plugin-transform-template-literals": { - "version": "7.26.8", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.26.8.tgz", - "integrity": "sha512-OmGDL5/J0CJPJZTHZbi2XpO0tyT2Ia7fzpW5GURwdtp2X3fMmN8au/ej6peC/T33/+CRiIpA8Krse8hFGVmT5Q==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.27.1.tgz", + "integrity": "sha512-fBJKiV7F2DxZUkg5EtHKXQdbsbURW3DZKQUWphDum0uRP6eHGGa/He9mc0mypL680pb+e/lDIthRohlv8NCHkg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.26.5" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1443,13 +1501,13 @@ } }, "node_modules/@babel/plugin-transform-typeof-symbol": { - "version": "7.26.7", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.26.7.tgz", - "integrity": "sha512-jfoTXXZTgGg36BmhqT3cAYK5qkmqvJpvNrPhaK/52Vgjhw4Rq29s9UqpWWV0D6yuRmgiFH/BUVlkl96zJWqnaw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.27.1.tgz", + "integrity": "sha512-RiSILC+nRJM7FY5srIyc4/fGIwUhyDuuBSdWn4y6yT6gm652DpCHZjIipgn6B7MQ1ITOUnAKWixEUjQRIBIcLw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.26.5" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1459,12 +1517,13 @@ } }, "node_modules/@babel/plugin-transform-unicode-escapes": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.25.9.tgz", - "integrity": "sha512-s5EDrE6bW97LtxOcGj1Khcx5AaXwiMmi4toFWRDP9/y0Woo6pXC+iyPu/KuhKtfSrNFd7jJB+/fkOtZy6aIC6Q==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.27.1.tgz", + "integrity": "sha512-Ysg4v6AmF26k9vpfFuTZg8HRfVWzsh1kVfowA23y9j/Gu6dOuahdUVhkLqpObp3JIv27MLSii6noRnuKN8H0Mg==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1474,13 +1533,14 @@ } }, "node_modules/@babel/plugin-transform-unicode-property-regex": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.25.9.tgz", - "integrity": "sha512-Jt2d8Ga+QwRluxRQ307Vlxa6dMrYEMZCgGxoPR8V52rxPyldHu3hdlHspxaqYmE7oID5+kB+UKUB/eWS+DkkWg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-property-regex/-/plugin-transform-unicode-property-regex-7.27.1.tgz", + "integrity": "sha512-uW20S39PnaTImxp39O5qFlHLS9LJEmANjMG7SxIhap8rCHqu0Ik+tLEPX5DKmHn6CsWQ7j3lix2tFOa5YtL12Q==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1490,13 +1550,14 @@ } }, "node_modules/@babel/plugin-transform-unicode-regex": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.25.9.tgz", - "integrity": "sha512-yoxstj7Rg9dlNn9UQxzk4fcNivwv4nUYz7fYXBaKxvw/lnmPuOm/ikoELygbYq68Bls3D/D+NBPHiLwZdZZ4HA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.27.1.tgz", + "integrity": "sha512-xvINq24TRojDuyt6JGtHmkVkrfVV3FPT16uytxImLeBZqW3/H52yN+kM1MGuyPkIQxrzKwPHs5U/MP3qKyzkGw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1506,13 +1567,14 @@ } }, "node_modules/@babel/plugin-transform-unicode-sets-regex": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.25.9.tgz", - "integrity": "sha512-8BYqO3GeVNHtx69fdPshN3fnzUNLrWdHhk/icSwigksJGczKSizZ+Z6SBCxTs723Fr5VSNorTIK7a+R2tISvwQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-sets-regex/-/plugin-transform-unicode-sets-regex-7.27.1.tgz", + "integrity": "sha512-EtkOujbc4cgvb0mlpQefi4NTPBzhSIevblFevACNLUspmrALgmEBdL/XfnyyITfd8fKBZrZys92zOWcik7j9Tw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-create-regexp-features-plugin": "^7.25.9", - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-create-regexp-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -1522,75 +1584,75 @@ } }, "node_modules/@babel/preset-env": { - "version": "7.26.9", - "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.26.9.tgz", - "integrity": "sha512-vX3qPGE8sEKEAZCWk05k3cpTAE3/nOYca++JA+Rd0z2NCNzabmYvEiSShKzm10zdquOIAVXsy2Ei/DTW34KlKQ==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.27.1.tgz", + "integrity": "sha512-TZ5USxFpLgKDpdEt8YWBR7p6g+bZo6sHaXLqP2BY/U0acaoI8FTVflcYCr/v94twM1C5IWFdZ/hscq9WjUeLXA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.26.8", - "@babel/helper-compilation-targets": "^7.26.5", - "@babel/helper-plugin-utils": "^7.26.5", - "@babel/helper-validator-option": "^7.25.9", - "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.25.9", - "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.25.9", - "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.25.9", - "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.25.9", - "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.25.9", + "@babel/compat-data": "^7.27.1", + "@babel/helper-compilation-targets": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/helper-validator-option": "^7.27.1", + "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "^7.27.1", + "@babel/plugin-bugfix-safari-class-field-initializer-scope": "^7.27.1", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.27.1", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.27.1", + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "^7.27.1", "@babel/plugin-proposal-private-property-in-object": "7.21.0-placeholder-for-preset-env.2", - "@babel/plugin-syntax-import-assertions": "^7.26.0", - "@babel/plugin-syntax-import-attributes": "^7.26.0", + "@babel/plugin-syntax-import-assertions": "^7.27.1", + "@babel/plugin-syntax-import-attributes": "^7.27.1", "@babel/plugin-syntax-unicode-sets-regex": "^7.18.6", - "@babel/plugin-transform-arrow-functions": "^7.25.9", - "@babel/plugin-transform-async-generator-functions": "^7.26.8", - "@babel/plugin-transform-async-to-generator": "^7.25.9", - "@babel/plugin-transform-block-scoped-functions": "^7.26.5", - "@babel/plugin-transform-block-scoping": "^7.25.9", - "@babel/plugin-transform-class-properties": "^7.25.9", - "@babel/plugin-transform-class-static-block": "^7.26.0", - "@babel/plugin-transform-classes": "^7.25.9", - "@babel/plugin-transform-computed-properties": "^7.25.9", - "@babel/plugin-transform-destructuring": "^7.25.9", - "@babel/plugin-transform-dotall-regex": "^7.25.9", - "@babel/plugin-transform-duplicate-keys": "^7.25.9", - "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.25.9", - "@babel/plugin-transform-dynamic-import": "^7.25.9", - "@babel/plugin-transform-exponentiation-operator": "^7.26.3", - "@babel/plugin-transform-export-namespace-from": "^7.25.9", - "@babel/plugin-transform-for-of": "^7.26.9", - "@babel/plugin-transform-function-name": "^7.25.9", - "@babel/plugin-transform-json-strings": "^7.25.9", - "@babel/plugin-transform-literals": "^7.25.9", - "@babel/plugin-transform-logical-assignment-operators": "^7.25.9", - "@babel/plugin-transform-member-expression-literals": "^7.25.9", - "@babel/plugin-transform-modules-amd": "^7.25.9", - "@babel/plugin-transform-modules-commonjs": "^7.26.3", - "@babel/plugin-transform-modules-systemjs": "^7.25.9", - "@babel/plugin-transform-modules-umd": "^7.25.9", - "@babel/plugin-transform-named-capturing-groups-regex": "^7.25.9", - "@babel/plugin-transform-new-target": "^7.25.9", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.26.6", - "@babel/plugin-transform-numeric-separator": "^7.25.9", - "@babel/plugin-transform-object-rest-spread": "^7.25.9", - "@babel/plugin-transform-object-super": "^7.25.9", - "@babel/plugin-transform-optional-catch-binding": "^7.25.9", - "@babel/plugin-transform-optional-chaining": "^7.25.9", - "@babel/plugin-transform-parameters": "^7.25.9", - "@babel/plugin-transform-private-methods": "^7.25.9", - "@babel/plugin-transform-private-property-in-object": "^7.25.9", - "@babel/plugin-transform-property-literals": "^7.25.9", - "@babel/plugin-transform-regenerator": "^7.25.9", - "@babel/plugin-transform-regexp-modifiers": "^7.26.0", - "@babel/plugin-transform-reserved-words": "^7.25.9", - "@babel/plugin-transform-shorthand-properties": "^7.25.9", - "@babel/plugin-transform-spread": "^7.25.9", - "@babel/plugin-transform-sticky-regex": "^7.25.9", - "@babel/plugin-transform-template-literals": "^7.26.8", - "@babel/plugin-transform-typeof-symbol": "^7.26.7", - "@babel/plugin-transform-unicode-escapes": "^7.25.9", - "@babel/plugin-transform-unicode-property-regex": "^7.25.9", - "@babel/plugin-transform-unicode-regex": "^7.25.9", - "@babel/plugin-transform-unicode-sets-regex": "^7.25.9", + "@babel/plugin-transform-arrow-functions": "^7.27.1", + "@babel/plugin-transform-async-generator-functions": "^7.27.1", + "@babel/plugin-transform-async-to-generator": "^7.27.1", + "@babel/plugin-transform-block-scoped-functions": "^7.27.1", + "@babel/plugin-transform-block-scoping": "^7.27.1", + "@babel/plugin-transform-class-properties": "^7.27.1", + "@babel/plugin-transform-class-static-block": "^7.27.1", + "@babel/plugin-transform-classes": "^7.27.1", + "@babel/plugin-transform-computed-properties": "^7.27.1", + "@babel/plugin-transform-destructuring": "^7.27.1", + "@babel/plugin-transform-dotall-regex": "^7.27.1", + "@babel/plugin-transform-duplicate-keys": "^7.27.1", + "@babel/plugin-transform-duplicate-named-capturing-groups-regex": "^7.27.1", + "@babel/plugin-transform-dynamic-import": "^7.27.1", + "@babel/plugin-transform-exponentiation-operator": "^7.27.1", + "@babel/plugin-transform-export-namespace-from": "^7.27.1", + "@babel/plugin-transform-for-of": "^7.27.1", + "@babel/plugin-transform-function-name": "^7.27.1", + "@babel/plugin-transform-json-strings": "^7.27.1", + "@babel/plugin-transform-literals": "^7.27.1", + "@babel/plugin-transform-logical-assignment-operators": "^7.27.1", + "@babel/plugin-transform-member-expression-literals": "^7.27.1", + "@babel/plugin-transform-modules-amd": "^7.27.1", + "@babel/plugin-transform-modules-commonjs": "^7.27.1", + "@babel/plugin-transform-modules-systemjs": "^7.27.1", + "@babel/plugin-transform-modules-umd": "^7.27.1", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.27.1", + "@babel/plugin-transform-new-target": "^7.27.1", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.27.1", + "@babel/plugin-transform-numeric-separator": "^7.27.1", + "@babel/plugin-transform-object-rest-spread": "^7.27.1", + "@babel/plugin-transform-object-super": "^7.27.1", + "@babel/plugin-transform-optional-catch-binding": "^7.27.1", + "@babel/plugin-transform-optional-chaining": "^7.27.1", + "@babel/plugin-transform-parameters": "^7.27.1", + "@babel/plugin-transform-private-methods": "^7.27.1", + "@babel/plugin-transform-private-property-in-object": "^7.27.1", + "@babel/plugin-transform-property-literals": "^7.27.1", + "@babel/plugin-transform-regenerator": "^7.27.1", + "@babel/plugin-transform-regexp-modifiers": "^7.27.1", + "@babel/plugin-transform-reserved-words": "^7.27.1", + "@babel/plugin-transform-shorthand-properties": "^7.27.1", + "@babel/plugin-transform-spread": "^7.27.1", + "@babel/plugin-transform-sticky-regex": "^7.27.1", + "@babel/plugin-transform-template-literals": "^7.27.1", + "@babel/plugin-transform-typeof-symbol": "^7.27.1", + "@babel/plugin-transform-unicode-escapes": "^7.27.1", + "@babel/plugin-transform-unicode-property-regex": "^7.27.1", + "@babel/plugin-transform-unicode-regex": "^7.27.1", + "@babel/plugin-transform-unicode-sets-regex": "^7.27.1", "@babel/preset-modules": "0.1.6-no-external-plugins", "babel-plugin-polyfill-corejs2": "^0.4.10", "babel-plugin-polyfill-corejs3": "^0.11.0", @@ -1683,10 +1745,11 @@ } }, "node_modules/@babel/register": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/register/-/register-7.25.9.tgz", - "integrity": "sha512-8D43jXtGsYmEeDvm4MWHYUpWf8iiXgWYx3fW7E7Wb7Oe6FWqJPl5K6TuFW0dOwNZzEE5rjlaSJYH9JjrUKJszA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/register/-/register-7.27.1.tgz", + "integrity": "sha512-K13lQpoV54LATKkzBpBAEu1GGSIRzxR9f4IN4V8DCDgiUMo2UDGagEZr3lPeVNJPLkWUi5JE4hCHKneVTwQlYQ==", "dev": true, + "license": "MIT", "dependencies": { "clone-deep": "^4.0.1", "find-cache-dir": "^2.0.0", @@ -1714,32 +1777,32 @@ } }, "node_modules/@babel/template": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.0.tgz", - "integrity": "sha512-2ncevenBqXI6qRMukPlXwHKHchC7RyMuu4xv5JBXRfOGVcTy1mXCD12qrp7Jsoxll1EV3+9sE4GugBVRjT2jFA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.1.tgz", + "integrity": "sha512-Fyo3ghWMqkHHpHQCoBs2VnYjR4iWFFjguTDEqA5WgZDOrFesVjMhMM2FSqTKSoUSDO1VQtavj8NFpdRBEvJTtg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.26.2", - "@babel/parser": "^7.27.0", - "@babel/types": "^7.27.0" + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.1", + "@babel/types": "^7.27.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.0.tgz", - "integrity": "sha512-19lYZFzYVQkkHkl4Cy4WrAVcqBkgvV2YM2TU3xG6DIwO7O3ecbDPfW3yM3bjAGcqcQHi+CCtjMR3dIEHxsd6bA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.1.tgz", + "integrity": "sha512-ZCYtZciz1IWJB4U61UPu4KEaqyfj+r5T1Q5mqPo+IBpcG9kHv30Z0aD8LXPgC1trYa6rK0orRyAhqUgk4MjmEg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.26.2", - "@babel/generator": "^7.27.0", - "@babel/parser": "^7.27.0", - "@babel/template": "^7.27.0", - "@babel/types": "^7.27.0", + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.27.1", + "@babel/parser": "^7.27.1", + "@babel/template": "^7.27.1", + "@babel/types": "^7.27.1", "debug": "^4.3.1", "globals": "^11.1.0" }, @@ -1748,14 +1811,14 @@ } }, "node_modules/@babel/types": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.0.tgz", - "integrity": "sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.1.tgz", + "integrity": "sha512-+EzkxvLNfiUeKMgy/3luqfsCWFRXLb7U6wNQTk60tovuckwB15B191tJWvpp4HjiQWdJkCxO3Wbvc6jlk3Xb2Q==", "devOptional": true, "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9" + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -14016,13 +14079,15 @@ "version": "1.4.2", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", "integrity": "sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/regenerate-unicode-properties": { "version": "10.2.0", "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.2.0.tgz", "integrity": "sha512-DqHn3DwbmmPVzeKj9woBadqmXxLvQoQIwu7nopMc72ztvxVmVk2SBhSnx67zuye5TP+lJsb/TBQsjLKhnDf3MA==", "dev": true, + "license": "MIT", "dependencies": { "regenerate": "^1.4.2" }, @@ -14036,15 +14101,6 @@ "integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==", "dev": true }, - "node_modules/regenerator-transform": { - "version": "0.15.2", - "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.2.tgz", - "integrity": "sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==", - "dev": true, - "dependencies": { - "@babel/runtime": "^7.8.4" - } - }, "node_modules/regex": { "version": "4.3.3", "resolved": "https://registry.npmjs.org/regex/-/regex-4.3.3.tgz", @@ -14088,15 +14144,16 @@ } }, "node_modules/regexpu-core": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.1.1.tgz", - "integrity": "sha512-k67Nb9jvwJcJmVpw0jPttR1/zVfnKf8Km0IPatrU/zJ5XeG3+Slx0xLXs9HByJSzXzrlz5EDvN6yLNMDc2qdnw==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-6.2.0.tgz", + "integrity": "sha512-H66BPQMrv+V16t8xtmq+UC0CBpiTBA60V8ibS1QVReIp8T1z8hwFxqcGzm9K6lgsN7sB5edVH8a+ze6Fqm4weA==", "dev": true, + "license": "MIT", "dependencies": { "regenerate": "^1.4.2", "regenerate-unicode-properties": "^10.2.0", "regjsgen": "^0.8.0", - "regjsparser": "^0.11.0", + "regjsparser": "^0.12.0", "unicode-match-property-ecmascript": "^2.0.0", "unicode-match-property-value-ecmascript": "^2.1.0" }, @@ -14108,13 +14165,15 @@ "version": "0.8.0", "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.8.0.tgz", "integrity": "sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/regjsparser": { - "version": "0.11.1", - "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.11.1.tgz", - "integrity": "sha512-1DHODs4B8p/mQHU9kr+jv8+wIC9mtG4eBHxWxIq5mhjE3D5oORhCc6deRKzTjs9DcfRFmj9BHSDguZklqCGFWQ==", + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.12.0.tgz", + "integrity": "sha512-cnE+y8bz4NhMjISKbgeVJtqNbtf5QpjZP+Bslo+UqkIt9QPnX9q095eiRRASJG1/tz6dlNr6Z5NsBiWYokp6EQ==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "jsesc": "~3.0.2" }, @@ -15790,6 +15849,7 @@ "resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz", "integrity": "sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==", "dev": true, + "license": "MIT", "engines": { "node": ">=4" } @@ -15799,6 +15859,7 @@ "resolved": "https://registry.npmjs.org/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-2.0.0.tgz", "integrity": "sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==", "dev": true, + "license": "MIT", "dependencies": { "unicode-canonical-property-names-ecmascript": "^2.0.0", "unicode-property-aliases-ecmascript": "^2.0.0" @@ -15812,6 +15873,7 @@ "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-2.2.0.tgz", "integrity": "sha512-4IehN3V/+kkr5YeSSDDQG8QLqO26XpL2XP3GQtqwlT/QYSECAwFztxVHjlbh0+gjJ3XmNLS0zDsbgs9jWKExLg==", "dev": true, + "license": "MIT", "engines": { "node": ">=4" } @@ -15821,6 +15883,7 @@ "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-2.1.0.tgz", "integrity": "sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==", "dev": true, + "license": "MIT", "engines": { "node": ">=4" } diff --git a/package.json b/package.json index 58d4fd768..4581d134a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ocelot-social", - "version": "3.4.0", + "version": "3.5.3", "description": "Free and open source software program code available to run social networks.", "author": "ocelot.social Community", "license": "MIT", @@ -33,9 +33,9 @@ "release": "./scripts/release.sh" }, "devDependencies": { - "@babel/core": "^7.26.10", - "@babel/preset-env": "^7.26.9", - "@babel/register": "^7.25.9", + "@babel/core": "^7.27.1", + "@babel/preset-env": "^7.27.1", + "@babel/register": "^7.27.1", "@badeball/cypress-cucumber-preprocessor": "^22.0.1", "@cucumber/cucumber": "11.2.0", "@cypress/browserify-preprocessor": "^3.0.2", diff --git a/webapp/Dockerfile b/webapp/Dockerfile index 7ec65cbf9..f22501184 100644 --- a/webapp/Dockerfile +++ b/webapp/Dockerfile @@ -1,4 +1,4 @@ -FROM node:20.12.1-alpine3.19 AS base +FROM node:20.12.1-alpine AS base LABEL org.label-schema.name="ocelot.social:webapp" LABEL org.label-schema.description="Web Frontend of the Social Network Software ocelot.social" LABEL org.label-schema.usage="https://github.com/Ocelot-Social-Community/Ocelot-Social/blob/master/README.md" diff --git a/webapp/Dockerfile.maintenance b/webapp/Dockerfile.maintenance index 93d104fd5..bc930f7d0 100644 --- a/webapp/Dockerfile.maintenance +++ b/webapp/Dockerfile.maintenance @@ -8,7 +8,7 @@ LABEL org.label-schema.vendor="ocelot.social Community" LABEL org.label-schema.schema-version="1.0" LABEL maintainer="devops@ocelot.social" -FROM node:20.12.1-alpine3.19 AS build +FROM node:20.12.1-alpine AS build ENV NODE_ENV="production" RUN apk --no-cache add git python3 make g++ bash jq RUN mkdir -p /app @@ -36,6 +36,6 @@ ONBUILD RUN yarn run generate FROM build AS production_build -FROM base as production +FROM base AS production COPY --from=production_build ./app/dist/ /usr/share/nginx/html/ COPY --from=production_build ./app/maintenance/nginx/custom.conf /etc/nginx/conf.d/default.conf diff --git a/webapp/assets/_new/styles/tokens.scss b/webapp/assets/_new/styles/tokens.scss index 578484355..42c01d3a8 100644 --- a/webapp/assets/_new/styles/tokens.scss +++ b/webapp/assets/_new/styles/tokens.scss @@ -150,7 +150,7 @@ $font-size-xx-large: 2rem; $font-size-x-large: 1.5rem; $font-size-large: 1.25rem; $font-size-base: 1rem; -$font-size-body: 15px; +$font-size-body: 0.938rem; $font-size-small: 0.8rem; $font-size-x-small: 0.7rem; $font-size-xx-small: 0.6rem; @@ -359,37 +359,37 @@ $media-query-medium: (min-width: 768px); $media-query-large: (min-width: 1024px); $media-query-x-large: (min-width: 1200px); -/** +/** * @tokens Background Images */ -/** +/** * @tokens Header Color */ $color-header-background: $color-neutral-100; -/** +/** * @tokens Footer Color */ $color-footer-background: $color-neutral-100; $color-footer-link: $color-primary; -/** +/** * @tokens Locale Menu Color */ $color-locale-menu: $text-color-soft; -/** +/** * @tokens Donation Bar Color */ $color-donation-bar: $color-primary; $color-donation-bar-light: $color-primary-light; -/** +/** * @tokens Toast Color */ diff --git a/webapp/assets/styles/main.scss b/webapp/assets/styles/main.scss index b726758c7..4fba0b5e0 100644 --- a/webapp/assets/styles/main.scss +++ b/webapp/assets/styles/main.scss @@ -52,11 +52,6 @@ $easeOut: cubic-bezier(0.19, 1, 0.22, 1); background: #fff; } -body.dropdown-open { - height: 100vh; - overflow: hidden; -} - blockquote { display: block; padding: 15px 20px 15px 45px; @@ -140,6 +135,10 @@ hr { opacity: 1; transition-delay: 0; transition: opacity 80ms ease-out; + + @media(hover: none) { + pointer-events: all; + } } } @@ -155,7 +154,6 @@ hr { [class$='menu-popover'] { min-width: 130px; - a, button { display: flex; align-content: center; @@ -179,4 +177,4 @@ hr { .dropdown-arrow { font-size: $font-size-xx-small; -} \ No newline at end of file +} diff --git a/webapp/components/Dropdown.vue b/webapp/components/Dropdown.vue index 7e4d21223..dd2b4a822 100644 --- a/webapp/components/Dropdown.vue +++ b/webapp/components/Dropdown.vue @@ -43,32 +43,12 @@ export default { }, watch: { isPopoverOpen: { - immediate: true, handler(isOpen) { - try { - if (isOpen) { - this.$nextTick(() => { - setTimeout(() => { - const paddingRightStyle = `${ - window.innerWidth - document.documentElement.clientWidth - }px` - const navigationElement = document.querySelector('.main-navigation') - document.body.style.paddingRight = paddingRightStyle - document.body.classList.add('dropdown-open') - if (navigationElement) { - navigationElement.style.paddingRight = paddingRightStyle - } - }, 20) - }) - } else { - const navigationElement = document.querySelector('.main-navigation') - document.body.style.paddingRight = null - document.body.classList.remove('dropdown-open') - if (navigationElement) { - navigationElement.style.paddingRight = null - } - } - } catch (err) {} + if (isOpen) { + document.body.classList.add('dropdown-open') + } else { + document.body.classList.remove('dropdown-open') + } }, }, }, diff --git a/webapp/components/Notification/Notification.vue b/webapp/components/Notification/Notification.vue index a657b10ba..d83995b9b 100644 --- a/webapp/components/Notification/Notification.vue +++ b/webapp/components/Notification/Notification.vue @@ -4,6 +4,7 @@

{{ $t(`notifications.reason.${notification.reason}`) }}

diff --git a/webapp/components/NotificationMenu/NotificationMenu.vue b/webapp/components/NotificationMenu/NotificationMenu.vue index 276da8490..576abb213 100644 --- a/webapp/components/NotificationMenu/NotificationMenu.vue +++ b/webapp/components/NotificationMenu/NotificationMenu.vue @@ -127,7 +127,7 @@ export default { apollo: { notifications: { query() { - return notificationQuery(this.$i18n) + return notificationQuery() }, variables() { return { diff --git a/webapp/components/NotificationsTable/NotificationsTable.spec.js b/webapp/components/NotificationsTable/NotificationsTable.spec.js index 0d3560787..5fbfc338a 100644 --- a/webapp/components/NotificationsTable/NotificationsTable.spec.js +++ b/webapp/components/NotificationsTable/NotificationsTable.spec.js @@ -88,7 +88,7 @@ describe('NotificationsTable.vue', () => { }) it('renders the author', () => { - const userinfo = firstRowNotification.find('.user-teaser > .info') + const userinfo = firstRowNotification.find('.user-teaser .info') expect(userinfo.text()).toContain(postNotification.from.author.name) }) @@ -121,7 +121,7 @@ describe('NotificationsTable.vue', () => { }) it('renders the author', () => { - const userinfo = secondRowNotification.find('.user-teaser > .info') + const userinfo = secondRowNotification.find('.user-teaser .info') expect(userinfo.text()).toContain(commentNotification.from.author.name) }) diff --git a/webapp/components/PasswordReset/Request.spec.js b/webapp/components/PasswordReset/Request.spec.js index 50d6495bd..e2f082242 100644 --- a/webapp/components/PasswordReset/Request.spec.js +++ b/webapp/components/PasswordReset/Request.spec.js @@ -59,7 +59,12 @@ describe('Request', () => { }) it('delivers email to backend', () => { - const expected = expect.objectContaining({ variables: { email: 'mail@example.org' } }) + const expected = expect.objectContaining({ + variables: { + email: 'mail@example.org', + locale: 'en', + }, + }) expect(mocks.$apollo.mutate).toHaveBeenCalledWith(expected) }) @@ -92,7 +97,12 @@ describe('Request', () => { }) it('normalizes email to lower case letters', () => { - const expected = expect.objectContaining({ variables: { email: 'mail@gmail.com' } }) + const expected = expect.objectContaining({ + variables: { + email: 'mail@gmail.com', + locale: 'en', + }, + }) expect(mocks.$apollo.mutate).toHaveBeenCalledWith(expected) }) }) diff --git a/webapp/components/PasswordReset/Request.vue b/webapp/components/PasswordReset/Request.vue index 5398c13ed..3eebeba65 100644 --- a/webapp/components/PasswordReset/Request.vue +++ b/webapp/components/PasswordReset/Request.vue @@ -85,13 +85,13 @@ export default { }, async handleSubmit() { const mutation = gql` - mutation ($email: String!) { - requestPasswordReset(email: $email) + mutation ($email: String!, $locale: String!) { + requestPasswordReset(email: $email, locale: $locale) } ` try { const { email } = this - await this.$apollo.mutate({ mutation, variables: { email } }) + await this.$apollo.mutate({ mutation, variables: { email, locale: this.$i18n.locale() } }) this.submitted = true setTimeout(() => { diff --git a/webapp/components/Registration/RegistrationSlideEmail.vue b/webapp/components/Registration/RegistrationSlideEmail.vue index 6d6454ac9..96441dee8 100644 --- a/webapp/components/Registration/RegistrationSlideEmail.vue +++ b/webapp/components/Registration/RegistrationSlideEmail.vue @@ -36,8 +36,8 @@ import normalizeEmail from '~/components/utils/NormalizeEmail' import translateErrorMessage from '~/components/utils/TranslateErrorMessage' export const SignupMutation = gql` - mutation ($email: String!, $inviteCode: String) { - Signup(email: $email, inviteCode: $inviteCode) { + mutation ($email: String!, $locale: String!, $inviteCode: String) { + Signup(email: $email, locale: $locale, inviteCode: $inviteCode) { email } } @@ -140,7 +140,7 @@ export default { async onNextClick() { const { email } = this.formData const { inviteCode = null } = this.sliderData.collectedInputData - const variables = { email, inviteCode } + const variables = { email, inviteCode, locale: this.$i18n.locale() } if (this.sliderData.collectedInputData.emailSend && !this.sendEmailAgain) { return true diff --git a/webapp/components/Registration/Signup.spec.js b/webapp/components/Registration/Signup.spec.js index 7ef2dc994..2ee413b8b 100644 --- a/webapp/components/Registration/Signup.spec.js +++ b/webapp/components/Registration/Signup.spec.js @@ -25,6 +25,9 @@ describe('Signup', () => { loading: false, mutate: jest.fn().mockResolvedValue({ data: { Signup: { email: 'mail@example.org' } } }), }, + $i18n: { + locale: () => 'de', + }, } propsData = {} }) @@ -64,7 +67,7 @@ describe('Signup', () => { it('delivers email to backend', () => { const expected = expect.objectContaining({ mutation: SignupMutation, - variables: { email: 'mAIL@exAMPLE.org', inviteCode: null }, + variables: { email: 'mAIL@exAMPLE.org', locale: 'de', inviteCode: null }, }) expect(mocks.$apollo.mutate).toHaveBeenCalledWith(expected) }) diff --git a/webapp/components/Registration/Signup.vue b/webapp/components/Registration/Signup.vue index 91b9ecd61..156c43d4e 100644 --- a/webapp/components/Registration/Signup.vue +++ b/webapp/components/Registration/Signup.vue @@ -70,8 +70,8 @@ import { SweetalertIcon } from 'vue-sweetalert-icons' import translateErrorMessage from '~/components/utils/TranslateErrorMessage' export const SignupMutation = gql` - mutation ($email: String!, $inviteCode: String) { - Signup(email: $email, inviteCode: $inviteCode) { + mutation ($email: String!, $locale: String!, $inviteCode: String) { + Signup(email: $email, locale: $locale, inviteCode: $inviteCode) { email } } @@ -121,7 +121,7 @@ export default { try { const response = await this.$apollo.mutate({ mutation: SignupMutation, - variables: { email, inviteCode: null }, + variables: { email, locale: this.$i18n.locale(), inviteCode: null }, }) this.data = response.data setTimeout(() => { diff --git a/webapp/components/UserTeaser/LocationInfo.spec.js b/webapp/components/UserTeaser/LocationInfo.spec.js new file mode 100644 index 000000000..2b100e66d --- /dev/null +++ b/webapp/components/UserTeaser/LocationInfo.spec.js @@ -0,0 +1,31 @@ +import { render } from '@testing-library/vue' +import LocationInfo from './LocationInfo.vue' + +const localVue = global.localVue + +describe('LocationInfo', () => { + const Wrapper = ({ withDistance }) => { + return render(LocationInfo, { + localVue, + propsData: { + locationData: { + name: 'Paris', + distanceToMe: withDistance ? 100 : null, + }, + }, + mocks: { + $t: jest.fn((t) => t), + }, + }) + } + + it('renders with distance', () => { + const wrapper = Wrapper({ withDistance: true }) + expect(wrapper.container).toMatchSnapshot() + }) + + it('renders without distance', () => { + const wrapper = Wrapper({ withDistance: false }) + expect(wrapper.container).toMatchSnapshot() + }) +}) diff --git a/webapp/components/UserTeaser/LocationInfo.vue b/webapp/components/UserTeaser/LocationInfo.vue new file mode 100644 index 000000000..67dc46c27 --- /dev/null +++ b/webapp/components/UserTeaser/LocationInfo.vue @@ -0,0 +1,44 @@ + + + + + diff --git a/webapp/components/UserTeaser/UserTeaser.spec.js b/webapp/components/UserTeaser/UserTeaser.spec.js index 354308109..8a67285ac 100644 --- a/webapp/components/UserTeaser/UserTeaser.spec.js +++ b/webapp/components/UserTeaser/UserTeaser.spec.js @@ -1,113 +1,250 @@ -import { mount, RouterLinkStub } from '@vue/test-utils' +import { render, screen, fireEvent } from '@testing-library/vue' +import { RouterLinkStub } from '@vue/test-utils' import UserTeaser from './UserTeaser.vue' import Vuex from 'vuex' const localVue = global.localVue -const filter = jest.fn((str) => str) -localVue.filter('truncate', filter) +// Mock Math.random, used in Dropdown +Object.assign(Math, { + random: () => 0, +}) + +const waitForPopover = async () => await new Promise((resolve) => setTimeout(resolve, 1000)) + +let mockIsTouchDevice +jest.mock('../utils/isTouchDevice', () => ({ + isTouchDevice: jest.fn(() => mockIsTouchDevice), +})) + +const userTilda = { + name: 'Tilda Swinton', + slug: 'tilda-swinton', + id: 'user1', + avatar: '/avatars/tilda-swinton', + badgeVerification: { + id: 'bv1', + icon: '/icons/verified', + description: 'Verified', + isDefault: false, + }, + badgeTrophiesSelected: [ + { + id: 'trophy1', + icon: '/icons/trophy1', + description: 'Trophy 1', + isDefault: false, + }, + { + id: 'trophy2', + icon: '/icons/trophy2', + description: 'Trophy 2', + isDefault: false, + }, + { + id: 'empty', + icon: '/icons/empty', + description: 'Empty', + isDefault: true, + }, + ], +} describe('UserTeaser', () => { - let propsData - let mocks - let stubs - let getters + const Wrapper = ({ + isModerator = false, + withLinkToProfile = true, + onTouchScreen = false, + withAvatar = true, + user = userTilda, + withPopoverEnabled = true, + }) => { + mockIsTouchDevice = onTouchScreen - beforeEach(() => { - propsData = {} - - mocks = { - $t: jest.fn(), - } - stubs = { - NuxtLink: RouterLinkStub, - } - getters = { - 'auth/user': () => { - return {} + const store = new Vuex.Store({ + getters: { + 'auth/user': () => { + return {} + }, + 'auth/isModerator': () => isModerator, }, - 'auth/isModerator': () => false, - } + }) + return render(UserTeaser, { + localVue, + store, + propsData: { + user, + linkToProfile: withLinkToProfile, + showAvatar: withAvatar, + showPopover: withPopoverEnabled, + }, + stubs: { + NuxtLink: RouterLinkStub, + 'user-teaser-popover': true, + 'v-popover': true, + 'client-only': true, + }, + mocks: { + $t: jest.fn((t) => t), + }, + }) + } + + it('renders anonymous user', () => { + const wrapper = Wrapper({ user: null }) + expect(wrapper.container).toMatchSnapshot() }) - describe('mount', () => { - const Wrapper = () => { - const store = new Vuex.Store({ - getters, + describe('given an user', () => { + describe('without linkToProfile, on touch screen', () => { + let wrapper + beforeEach(() => { + wrapper = Wrapper({ withLinkToProfile: false, onTouchScreen: true }) }) - return mount(UserTeaser, { store, propsData, mocks, stubs, localVue }) - } - it('renders anonymous user', () => { - const wrapper = Wrapper() - expect(wrapper.text()).toBe('') - expect(mocks.$t).toHaveBeenCalledWith('profile.userAnonym') + it('renders', () => { + expect(wrapper.container).toMatchSnapshot() + }) + + describe('when clicking the user name', () => { + beforeEach(async () => { + const userName = screen.getByText('Tilda Swinton') + await fireEvent.click(userName) + await waitForPopover() + }) + + it('renders the popover', () => { + expect(wrapper.container).toMatchSnapshot() + }) + }) + + describe('when clicking the user avatar', () => { + beforeEach(async () => { + const userAvatar = screen.getByAltText('Tilda Swinton') + await fireEvent.click(userAvatar) + await waitForPopover() + }) + + it('renders the popover', () => { + expect(wrapper.container).toMatchSnapshot() + }) + }) }) - describe('given an user', () => { + describe('with linkToProfile, on touch screen', () => { + let wrapper beforeEach(() => { - propsData.user = { - name: 'Tilda Swinton', - slug: 'tilda-swinton', - } + wrapper = Wrapper({ withLinkToProfile: true, onTouchScreen: true }) }) - it('renders user name', () => { - const wrapper = Wrapper() - expect(mocks.$t).not.toHaveBeenCalledWith('profile.userAnonym') - expect(wrapper.text()).toMatch('Tilda Swinton') + it('renders', () => { + expect(wrapper.container).toMatchSnapshot() }) - describe('user is deleted', () => { - beforeEach(() => { - propsData.user.deleted = true + describe('when clicking the user name', () => { + beforeEach(async () => { + const userName = screen.getByText('Tilda Swinton') + await fireEvent.click(userName) }) + it('renders the popover', () => { + expect(wrapper.container).toMatchSnapshot() + }) + }) + }) + + describe('without linkToProfile, on desktop', () => { + let wrapper + beforeEach(() => { + wrapper = Wrapper({ withLinkToProfile: false, onTouchScreen: false }) + }) + + it('renders', () => { + expect(wrapper.container).toMatchSnapshot() + }) + + describe('when hovering the user name', () => { + beforeEach(async () => { + const userName = screen.getByText('Tilda Swinton') + await fireEvent.mouseOver(userName) + await waitForPopover() + }) + + it('renders the popover', () => { + expect(wrapper.container).toMatchSnapshot() + }) + }) + + describe('when hovering the user avatar', () => { + beforeEach(async () => { + const userAvatar = screen.getByAltText('Tilda Swinton') + await fireEvent.mouseOver(userAvatar) + await waitForPopover() + }) + + it('renders the popover', () => { + expect(wrapper.container).toMatchSnapshot() + }) + }) + }) + + describe('with linkToProfile, on desktop', () => { + let wrapper + beforeEach(() => { + wrapper = Wrapper({ withLinkToProfile: true, onTouchScreen: false }) + }) + + it('renders', () => { + expect(wrapper.container).toMatchSnapshot() + }) + + describe('when hovering the user name', () => { + beforeEach(async () => { + const userName = screen.getByText('Tilda Swinton') + await fireEvent.mouseOver(userName) + await waitForPopover() + }) + + it('renders the popover', () => { + expect(wrapper.container).toMatchSnapshot() + }) + }) + }) + + describe('avatar is disabled', () => { + it('does not render the avatar', () => { + const wrapper = Wrapper({ withAvatar: false }) + expect(wrapper.container).toMatchSnapshot() + }) + }) + + describe('user is deleted', () => { + it('renders anonymous user', () => { + const wrapper = Wrapper({ user: { ...userTilda, deleted: true } }) + expect(wrapper.container).toMatchSnapshot() + }) + + describe('even if the current user is a moderator', () => { it('renders anonymous user', () => { - const wrapper = Wrapper() - expect(wrapper.text()).not.toMatch('Tilda Swinton') - expect(mocks.$t).toHaveBeenCalledWith('profile.userAnonym') - }) - - describe('even if the current user is a moderator', () => { - beforeEach(() => { - getters['auth/isModerator'] = () => true - }) - - it('renders anonymous user', () => { - const wrapper = Wrapper() - expect(wrapper.text()).not.toMatch('Tilda Swinton') - expect(mocks.$t).toHaveBeenCalledWith('profile.userAnonym') + const wrapper = Wrapper({ + user: { ...userTilda, deleted: true }, + isModerator: true, }) + expect(wrapper.container).toMatchSnapshot() }) }) + }) - describe('user is disabled', () => { - beforeEach(() => { - propsData.user.disabled = true - }) + describe('user is disabled', () => { + it('renders anonymous user', () => { + const wrapper = Wrapper({ user: { ...userTilda, disabled: true } }) + expect(wrapper.container).toMatchSnapshot() + }) - it('renders anonymous user', () => { - const wrapper = Wrapper() - expect(wrapper.text()).not.toMatch('Tilda Swinton') - expect(mocks.$t).toHaveBeenCalledWith('profile.userAnonym') - }) - - describe('current user is a moderator', () => { - beforeEach(() => { - getters['auth/isModerator'] = () => true - }) - - it('renders user name', () => { - const wrapper = Wrapper() - expect(wrapper.text()).not.toMatch('Anonymous') - expect(wrapper.text()).toMatch('Tilda Swinton') - }) - - it('has "disabled-content" class', () => { - const wrapper = Wrapper() - expect(wrapper.classes()).toContain('disabled-content') - }) + describe('current user is a moderator', () => { + it('renders user name', () => { + const wrapper = Wrapper({ user: { ...userTilda, disabled: true }, isModerator: true }) + expect(wrapper.container).toMatchSnapshot() }) }) }) diff --git a/webapp/components/UserTeaser/UserTeaser.vue b/webapp/components/UserTeaser/UserTeaser.vue index a9e556bf4..4bc72576e 100644 --- a/webapp/components/UserTeaser/UserTeaser.vue +++ b/webapp/components/UserTeaser/UserTeaser.vue @@ -4,57 +4,34 @@ {{ $t('profile.userAnonym') }}
-
- - - - -
-
- - - {{ userSlug }} - {{ userName }} - - - - {{ userSlug }} - {{ userName }} - -   - - - {{ $t('group.in') }} - - - - {{ groupSlug }} - {{ groupName }} - - - -
- - - - - -
-
+ + + +
diff --git a/webapp/components/UserTeaser/__snapshots__/LocationInfo.spec.js.snap b/webapp/components/UserTeaser/__snapshots__/LocationInfo.spec.js.snap new file mode 100644 index 000000000..50ce23f9a --- /dev/null +++ b/webapp/components/UserTeaser/__snapshots__/LocationInfo.spec.js.snap @@ -0,0 +1,51 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`LocationInfo renders with distance 1`] = ` +
+
+
+ + + + + Paris + +
+ +
+ location.distance +
+
+
+`; + +exports[`LocationInfo renders without distance 1`] = ` +
+
+
+ + + + + Paris + +
+ + +
+
+`; diff --git a/webapp/components/UserTeaser/__snapshots__/UserTeaser.spec.js.snap b/webapp/components/UserTeaser/__snapshots__/UserTeaser.spec.js.snap new file mode 100644 index 000000000..b8fc6cae9 --- /dev/null +++ b/webapp/components/UserTeaser/__snapshots__/UserTeaser.spec.js.snap @@ -0,0 +1,1136 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`UserTeaser given an user avatar is disabled does not render the avatar 1`] = ` +
+
+ + + + + + +
+ + +
+
+`; + +exports[`UserTeaser given an user user is deleted even if the current user is a moderator renders anonymous user 1`] = ` +
+
+
+ + + + + + + + + +
+ + + profile.userAnonym + +
+
+`; + +exports[`UserTeaser given an user user is deleted renders anonymous user 1`] = ` +
+
+
+ + + + + + + + + +
+ + + profile.userAnonym + +
+
+`; + +exports[`UserTeaser given an user user is disabled current user is a moderator renders user name 1`] = ` +
+ +`; + +exports[`UserTeaser given an user user is disabled renders anonymous user 1`] = ` +
+
+
+ + + + + + + + + +
+ + + profile.userAnonym + +
+
+`; + +exports[`UserTeaser given an user with linkToProfile, on desktop renders 1`] = ` +
+ +`; + +exports[`UserTeaser given an user with linkToProfile, on desktop when hovering the user name renders the popover 1`] = ` + +`; + +exports[`UserTeaser given an user with linkToProfile, on touch screen renders 1`] = ` +
+
+ + + + +
+
+ + + + + +
+ + +
+ +
+ + +
+
+`; + +exports[`UserTeaser given an user with linkToProfile, on touch screen when clicking the user name renders the popover 1`] = ` +
+
+ + + + +
+
+ + + + + +
+ + +
+ +
+ +
+
+
+
+
+`; + +exports[`UserTeaser given an user without linkToProfile, on desktop renders 1`] = ` +
+
+ + + +
+ + TS + + + + + Tilda Swinton +
+
+ +
+
+ + + @tilda-swinton + + + + Tilda Swinton + + + + + + +
+ + +
+ +
+ + +
+
+`; + +exports[`UserTeaser given an user without linkToProfile, on desktop when hovering the user avatar renders the popover 1`] = ` +
+
+ + + +
+ + TS + + + + + Tilda Swinton +
+
+ +
+
+ + + @tilda-swinton + + + + Tilda Swinton + + + + + + +
+ + +
+ +
+ +
+
+
+
+
+`; + +exports[`UserTeaser given an user without linkToProfile, on desktop when hovering the user name renders the popover 1`] = ` +
+
+ + + +
+ + TS + + + + + Tilda Swinton +
+
+ +
+
+ + + @tilda-swinton + + + + Tilda Swinton + + + + + + +
+ + +
+ +
+ +
+
+
+
+
+`; + +exports[`UserTeaser given an user without linkToProfile, on touch screen renders 1`] = ` +
+
+ + + + +
+
+ + + + + +
+ + +
+ +
+ + +
+
+`; + +exports[`UserTeaser given an user without linkToProfile, on touch screen when clicking the user avatar renders the popover 1`] = ` +
+
+ + + + +
+
+ + + + + +
+ + +
+ +
+ +
+
+
+
+
+`; + +exports[`UserTeaser given an user without linkToProfile, on touch screen when clicking the user name renders the popover 1`] = ` +
+
+ + + + +
+
+ + + + + +
+ + +
+ +
+ +
+
+
+
+
+`; + +exports[`UserTeaser renders anonymous user 1`] = ` +
+
+
+ + + + + + + + + +
+ + + profile.userAnonym + +
+
+`; diff --git a/webapp/components/UserTeaser/__snapshots__/UserTeaserHelper.spec.js.snap b/webapp/components/UserTeaser/__snapshots__/UserTeaserHelper.spec.js.snap new file mode 100644 index 000000000..2257e8a51 --- /dev/null +++ b/webapp/components/UserTeaser/__snapshots__/UserTeaserHelper.spec.js.snap @@ -0,0 +1,19 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`UserTeaserHelper with linkToProfile and popover enabled, on touch screen renders button 1`] = ` +
+
+`; + +exports[`UserTeaserHelper with linkToProfile, on desktop renders link 1`] = ` +
+ +
+`; + +exports[`UserTeaserHelper without linkToProfile renders span 1`] = ` +
+ +
+`; diff --git a/webapp/components/UserTeaser/__snapshots__/UserTeaserPopover.spec.js.snap b/webapp/components/UserTeaser/__snapshots__/UserTeaserPopover.spec.js.snap new file mode 100644 index 000000000..3eab03611 --- /dev/null +++ b/webapp/components/UserTeaser/__snapshots__/UserTeaserPopover.spec.js.snap @@ -0,0 +1,547 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`UserTeaserPopover does not show badges when disabled 1`] = ` +
+
+ + + + +
    +
  • +
    +

    + 0 +

    +

    + + profile.followers + +

    +
    +
  • + +
  • +
    +

    + 0 +

    +

    + + common.post + +

    +
    +
  • + +
  • +
    +

    + 0 +

    +

    + + common.comment + +

    +
    +
  • +
+ + +
+
+`; + +exports[`UserTeaserPopover given a non-touch device does not show button when userLink is provided 1`] = ` +
+
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+ + + +
    +
  • +
    +

    + 0 +

    +

    + + profile.followers + +

    +
    +
  • + +
  • +
    +

    + 0 +

    +

    + + common.post + +

    +
    +
  • + +
  • +
    +

    + 0 +

    +

    + + common.comment + +

    +
    +
  • +
+ + +
+
+`; + +exports[`UserTeaserPopover given a touch device does not show button when userLink is not provided 1`] = ` +
+
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+ + + +
    +
  • +
    +

    + 0 +

    +

    + + profile.followers + +

    +
    +
  • + +
  • +
    +

    + 0 +

    +

    + + common.post + +

    +
    +
  • + +
  • +
    +

    + 0 +

    +

    + + common.comment + +

    +
    +
  • +
+ + +
+
+`; + +exports[`UserTeaserPopover given a touch device shows button when userLink is provided 1`] = ` +
+
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+ + + +
    +
  • +
    +

    + 0 +

    +

    + + profile.followers + +

    +
    +
  • + +
  • +
    +

    + 0 +

    +

    + + common.post + +

    +
    +
  • + +
  • +
    +

    + 0 +

    +

    + + common.comment + +

    +
    +
  • +
+ + +
+
+`; + +exports[`UserTeaserPopover shows badges when enabled 1`] = ` +
+
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+ + + +
    +
  • +
    +

    + 0 +

    +

    + + profile.followers + +

    +
    +
  • + +
  • +
    +

    + 0 +

    +

    + + common.post + +

    +
    +
  • + +
  • +
    +

    + 0 +

    +

    + + common.comment + +

    +
    +
  • +
+ + +
+
+`; diff --git a/webapp/components/utils/isTouchDevice.js b/webapp/components/utils/isTouchDevice.js new file mode 100644 index 000000000..a6bc17752 --- /dev/null +++ b/webapp/components/utils/isTouchDevice.js @@ -0,0 +1,2 @@ +export const isTouchDevice = () => + 'ontouchstart' in window || navigator.MaxTouchPoints > 0 || navigator.msMaxTouchPoints > 0 diff --git a/webapp/graphql/Fragments.js b/webapp/graphql/Fragments.js index 77af830e8..e1704923f 100644 --- a/webapp/graphql/Fragments.js +++ b/webapp/graphql/Fragments.js @@ -17,9 +17,11 @@ export const locationFragment = (lang) => gql` fragment location on User { locationName location { + id name: name${lang} lng lat + distanceToMe } } ` @@ -50,6 +52,19 @@ export const userCountsFragment = gql` } ` +export const userTeaserFragment = (lang) => gql` + ${badgesFragment} + ${locationFragment(lang)} + + fragment userTeaser on User { + followedByCount + contributionsCount + commentedCount + ...badges + ...location + } +` + export const postFragment = gql` fragment post on Post { id diff --git a/webapp/graphql/User.js b/webapp/graphql/User.js index 75342ef2a..7440b5051 100644 --- a/webapp/graphql/User.js +++ b/webapp/graphql/User.js @@ -7,6 +7,7 @@ import { postFragment, commentFragment, groupFragment, + userTeaserFragment, } from './Fragments' export const profileUserQuery = (i18n) => { @@ -125,7 +126,7 @@ export const mapUserQuery = (i18n) => { ` } -export const notificationQuery = (_i18n) => { +export const notificationQuery = () => { return gql` ${userFragment} ${commentFragment} @@ -483,6 +484,18 @@ export const userDataQuery = (i18n) => { ` } +export const userTeaserQuery = (i18n) => { + const lang = i18n.locale().toUpperCase() + return gql` + ${userTeaserFragment(lang)} + query ($id: ID!) { + User(id: $id) { + ...userTeaser + } + } + ` +} + export const setTrophyBadgeSelected = gql` mutation ($slot: Int!, $badgeId: ID) { setTrophyBadgeSelected(slot: $slot, badgeId: $badgeId) { diff --git a/webapp/layouts/blank.vue b/webapp/layouts/blank.vue index bd8d4f29a..06010ee3b 100644 --- a/webapp/layouts/blank.vue +++ b/webapp/layouts/blank.vue @@ -1,7 +1,7 @@